kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
SoapboxConfig: break extraneous components out into their own files
rodzic
37b0b972e0
commit
1bd81a2f0f
|
@ -0,0 +1,49 @@
|
||||||
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { SketchPicker, ColorChangeHandler } from 'react-color';
|
||||||
|
|
||||||
|
import { isMobile } from 'soapbox/is_mobile';
|
||||||
|
|
||||||
|
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||||
|
|
||||||
|
interface IColorPicker {
|
||||||
|
style?: React.CSSProperties,
|
||||||
|
value: string,
|
||||||
|
onChange: ColorChangeHandler,
|
||||||
|
onClose: () => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColorPicker: React.FC<IColorPicker> = ({ style, value, onClose, onChange }) => {
|
||||||
|
const node = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const handleDocumentClick = (e: MouseEvent | TouchEvent) => {
|
||||||
|
if (node.current && !node.current.contains(e.target as HTMLElement)) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('click', handleDocumentClick, false);
|
||||||
|
document.addEventListener('touchend', handleDocumentClick, listenerOptions);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleDocumentClick, false);
|
||||||
|
document.removeEventListener('touchend', handleDocumentClick);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerStyle: React.CSSProperties = {
|
||||||
|
...style,
|
||||||
|
marginLeft: isMobile(window.innerWidth) ? '20px' : '12px',
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id='SketchPickerContainer' ref={node} style={pickerStyle}>
|
||||||
|
<SketchPicker color={value} disableAlpha onChange={onChange} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColorPicker;
|
|
@ -0,0 +1,61 @@
|
||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
// @ts-ignore: TODO: upgrade react-overlays. v3.1 and above have TS definitions
|
||||||
|
import Overlay from 'react-overlays/lib/Overlay';
|
||||||
|
|
||||||
|
import { isMobile } from 'soapbox/is_mobile';
|
||||||
|
|
||||||
|
import ColorPicker from './color-picker';
|
||||||
|
|
||||||
|
import type { ColorChangeHandler } from 'react-color';
|
||||||
|
|
||||||
|
interface IColorWithPicker {
|
||||||
|
buttonId: string,
|
||||||
|
label: React.ReactNode,
|
||||||
|
value: string,
|
||||||
|
onChange: ColorChangeHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ColorWithPicker: React.FC<IColorWithPicker> = ({ buttonId, label, value, onChange }) => {
|
||||||
|
const node = useRef<HTMLDivElement>(null);
|
||||||
|
const [active, setActive] = useState(false);
|
||||||
|
const [placement, setPlacement] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const hidePicker = () => {
|
||||||
|
setActive(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPicker = () => {
|
||||||
|
setActive(true);
|
||||||
|
setPlacement(isMobile(window.innerWidth) ? 'bottom' : 'right');
|
||||||
|
};
|
||||||
|
|
||||||
|
const onToggle: React.MouseEventHandler = () => {
|
||||||
|
if (active) {
|
||||||
|
hidePicker();
|
||||||
|
} else {
|
||||||
|
showPicker();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='label_input__color'>
|
||||||
|
<label>{label}</label>
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref={node}
|
||||||
|
id={buttonId}
|
||||||
|
className='color-swatch'
|
||||||
|
role='presentation'
|
||||||
|
style={{ background: value }}
|
||||||
|
title={value}
|
||||||
|
onClick={onToggle}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Overlay show={active} placement={placement} target={node.current}>
|
||||||
|
<ColorPicker value={value} onChange={onChange} onClose={hidePicker} />
|
||||||
|
</Overlay>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColorWithPicker;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import IconPickerDropdown from './icon_picker_dropdown';
|
||||||
|
|
||||||
|
interface IIconPicker {
|
||||||
|
label: React.ReactNode,
|
||||||
|
value: string,
|
||||||
|
onChange: React.ChangeEventHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
const IconPicker: React.FC<IIconPicker> = ({ onChange, value, label }) => {
|
||||||
|
return (
|
||||||
|
<div className='input with_label font_icon_picker'>
|
||||||
|
<div className='label_input__font_icon_picker'>
|
||||||
|
{label && (<label>{label}</label>)}
|
||||||
|
<div className='label_input_wrapper'>
|
||||||
|
<IconPickerDropdown value={value} onPickEmoji={onChange} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IconPicker;
|
|
@ -1,12 +1,9 @@
|
||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
|
||||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SketchPicker } from 'react-color';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import Overlay from 'react-overlays/lib/Overlay';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { updateConfig } from 'soapbox/actions/admin';
|
import { updateConfig } from 'soapbox/actions/admin';
|
||||||
|
@ -24,12 +21,12 @@ import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
} from 'soapbox/features/forms';
|
} from 'soapbox/features/forms';
|
||||||
import ThemeToggle from 'soapbox/features/ui/components/theme-toggle';
|
import ThemeToggle from 'soapbox/features/ui/components/theme-toggle';
|
||||||
import { isMobile } from 'soapbox/is_mobile';
|
|
||||||
import { normalizeSoapboxConfig } from 'soapbox/normalizers';
|
import { normalizeSoapboxConfig } from 'soapbox/normalizers';
|
||||||
|
|
||||||
import Accordion from '../ui/components/accordion';
|
import Accordion from '../ui/components/accordion';
|
||||||
|
|
||||||
import IconPickerDropdown from './components/icon_picker_dropdown';
|
import ColorWithPicker from './components/color-with-picker';
|
||||||
|
import IconPicker from './components/icon-picker';
|
||||||
import SitePreview from './components/site_preview';
|
import SitePreview from './components/site_preview';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -60,8 +57,6 @@ const messages = defineMessages({
|
||||||
singleUserModeProfileHint: { id: 'soapbox_config.single_user_mode_profile_hint', defaultMessage: '@handle' },
|
singleUserModeProfileHint: { id: 'soapbox_config.single_user_mode_profile_hint', defaultMessage: '@handle' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
|
||||||
|
|
||||||
const templates = {
|
const templates = {
|
||||||
promoPanelItem: ImmutableMap({ icon: '', text: '', url: '' }),
|
promoPanelItem: ImmutableMap({ icon: '', text: '', url: '' }),
|
||||||
footerItem: ImmutableMap({ title: '', url: '' }),
|
footerItem: ImmutableMap({ title: '', url: '' }),
|
||||||
|
@ -461,121 +456,3 @@ class SoapboxConfig extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ColorPicker extends React.PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
style: PropTypes.object,
|
|
||||||
value: PropTypes.string.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
onClose: PropTypes.func,
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDocumentClick = e => {
|
|
||||||
if (this.node && !this.node.contains(e.target)) {
|
|
||||||
this.props.onClose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.addEventListener('click', this.handleDocumentClick, false);
|
|
||||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('click', this.handleDocumentClick, false);
|
|
||||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
setRef = c => {
|
|
||||||
this.node = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { style, value, onChange } = this.props;
|
|
||||||
const margin_left_picker = isMobile(window.innerWidth) ? '20px' : '12px';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div id='SketchPickerContainer' ref={this.setRef} style={{ ...style, marginLeft: margin_left_picker, position: 'absolute', zIndex: 1000 }}>
|
|
||||||
<SketchPicker color={value} disableAlpha onChange={onChange} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ColorWithPicker extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
buttonId: PropTypes.string.isRequired,
|
|
||||||
label: PropTypes.node,
|
|
||||||
value: PropTypes.string.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggle = (e) => {
|
|
||||||
if (!e.key || e.key === 'Enter') {
|
|
||||||
if (this.state.active) {
|
|
||||||
this.onHidePicker();
|
|
||||||
} else {
|
|
||||||
this.onShowPicker(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
|
||||||
active: false,
|
|
||||||
placement: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
onHidePicker = () => {
|
|
||||||
this.setState({ active: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowPicker = ({ target }) => {
|
|
||||||
this.setState({ active: true });
|
|
||||||
this.setState({ placement: isMobile(window.innerWidth) ? 'bottom' : 'right' });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { buttonId, label, value, onChange } = this.props;
|
|
||||||
const { active, placement } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='label_input__color'>
|
|
||||||
<label>{label}</label>
|
|
||||||
<div id={buttonId} className='color-swatch' role='presentation' style={{ background: value }} title={value} value={value} onClick={this.onToggle} />
|
|
||||||
<Overlay show={active} placement={placement} target={this}>
|
|
||||||
<ColorPicker value={value} onChange={onChange} onClose={this.onHidePicker} />
|
|
||||||
</Overlay>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IconPicker extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
icons: PropTypes.object,
|
|
||||||
label: PropTypes.node,
|
|
||||||
value: PropTypes.string,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { onChange, value, label } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='input with_label font_icon_picker'>
|
|
||||||
<div className='label_input__font_icon_picker'>
|
|
||||||
{label && (<label>{label}</label>)}
|
|
||||||
<div className='label_input_wrapper'>
|
|
||||||
<IconPickerDropdown value={value} onPickEmoji={onChange} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
"@types/object-assign": "^4.0.30",
|
"@types/object-assign": "^4.0.30",
|
||||||
"@types/object-fit-images": "^3.2.3",
|
"@types/object-fit-images": "^3.2.3",
|
||||||
"@types/qrcode.react": "^1.0.2",
|
"@types/qrcode.react": "^1.0.2",
|
||||||
|
"@types/react-color": "^3.0.6",
|
||||||
"@types/react-datepicker": "^4.4.0",
|
"@types/react-datepicker": "^4.4.0",
|
||||||
"@types/react-helmet": "^6.1.5",
|
"@types/react-helmet": "^6.1.5",
|
||||||
"@types/react-motion": "^0.0.32",
|
"@types/react-motion": "^0.0.32",
|
||||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -2184,6 +2184,14 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-color@^3.0.6":
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-color/-/react-color-3.0.6.tgz#602fed023802b2424e7cd6ff3594ccd3d5055f9a"
|
||||||
|
integrity sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
"@types/reactcss" "*"
|
||||||
|
|
||||||
"@types/react-datepicker@^4.4.0":
|
"@types/react-datepicker@^4.4.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-4.4.0.tgz#0072e18536ad305fd57786f9b6f9e499eed2b475"
|
resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-4.4.0.tgz#0072e18536ad305fd57786f9b6f9e499eed2b475"
|
||||||
|
@ -2272,6 +2280,13 @@
|
||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/reactcss@*":
|
||||||
|
version "1.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/reactcss/-/reactcss-1.2.6.tgz#133c1e7e896f2726370d1d5a26bf06a30a038bcc"
|
||||||
|
integrity sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/redux-mock-store@^1.0.3":
|
"@types/redux-mock-store@^1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz#895de4a364bc4836661570aec82f2eef5989d1fb"
|
resolved "https://registry.yarnpkg.com/@types/redux-mock-store/-/redux-mock-store-1.0.3.tgz#895de4a364bc4836661570aec82f2eef5989d1fb"
|
||||||
|
|
Ładowanie…
Reference in New Issue