2018-03-26 23:32:43 +02:00
|
|
|
// @flow
|
|
|
|
import * as React from 'react';
|
2018-06-10 23:57:46 +02:00
|
|
|
import ReactDOMServer from 'react-dom/server';
|
|
|
|
import MarkdownPreview from 'component/common/markdown-preview';
|
2018-03-26 23:32:43 +02:00
|
|
|
import SimpleMDE from 'react-simplemde-editor';
|
2019-03-05 08:33:53 +01:00
|
|
|
import 'easymde/dist/easymde.min.css'; // eslint-disable-line import/no-extraneous-dependencies
|
2018-06-15 22:11:02 +02:00
|
|
|
import Toggle from 'react-toggle';
|
2018-11-21 22:20:55 +01:00
|
|
|
import { openEditorMenu, stopContextMenu } from 'util/context-menu';
|
2018-03-26 23:32:43 +02:00
|
|
|
|
|
|
|
type Props = {
|
|
|
|
name: string,
|
|
|
|
label?: string,
|
|
|
|
render?: () => React.Node,
|
|
|
|
prefix?: string,
|
|
|
|
postfix?: string,
|
|
|
|
error?: string | boolean,
|
|
|
|
helper?: string | React.Node,
|
|
|
|
type?: string,
|
|
|
|
onChange?: any => any,
|
|
|
|
defaultValue?: string | number,
|
|
|
|
placeholder?: string | number,
|
|
|
|
children?: React.Node,
|
|
|
|
stretch?: boolean,
|
2018-06-14 22:10:50 +02:00
|
|
|
affixClass?: string, // class applied to prefix/postfix label
|
2018-07-25 20:21:41 +02:00
|
|
|
firstInList?: boolean, // at the top of a list, no padding top
|
2018-09-22 03:20:58 +02:00
|
|
|
autoFocus?: boolean,
|
2019-02-13 17:27:20 +01:00
|
|
|
labelOnLeft: boolean,
|
2018-10-21 10:57:39 +02:00
|
|
|
inputProps?: {
|
2018-09-04 19:18:11 +02:00
|
|
|
disabled?: boolean,
|
|
|
|
},
|
2019-02-20 06:20:29 +01:00
|
|
|
inputButton?: React.Node,
|
2019-02-18 18:24:56 +01:00
|
|
|
blockWrap: boolean,
|
2018-03-26 23:32:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export class FormField extends React.PureComponent<Props> {
|
2019-02-13 17:27:20 +01:00
|
|
|
static defaultProps = {
|
|
|
|
labelOnLeft: false,
|
2019-02-18 18:24:56 +01:00
|
|
|
blockWrap: true,
|
2019-02-13 17:27:20 +01:00
|
|
|
};
|
|
|
|
|
2019-02-20 06:20:29 +01:00
|
|
|
input: { current: React.ElementRef<any> };
|
|
|
|
|
2019-02-18 18:24:56 +01:00
|
|
|
constructor(props: Props) {
|
2018-09-22 03:20:58 +02:00
|
|
|
super(props);
|
|
|
|
this.input = React.createRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
const { autoFocus } = this.props;
|
|
|
|
const input = this.input.current;
|
|
|
|
|
|
|
|
if (input && autoFocus) {
|
|
|
|
input.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-26 23:32:43 +02:00
|
|
|
render() {
|
|
|
|
const {
|
|
|
|
render,
|
|
|
|
label,
|
|
|
|
prefix,
|
|
|
|
postfix,
|
|
|
|
error,
|
|
|
|
helper,
|
|
|
|
name,
|
|
|
|
type,
|
|
|
|
children,
|
|
|
|
stretch,
|
2018-06-14 22:10:50 +02:00
|
|
|
affixClass,
|
2018-09-22 03:20:58 +02:00
|
|
|
autoFocus,
|
2019-02-13 17:27:20 +01:00
|
|
|
inputButton,
|
|
|
|
labelOnLeft,
|
2019-02-18 18:24:56 +01:00
|
|
|
blockWrap,
|
2018-03-26 23:32:43 +02:00
|
|
|
...inputProps
|
|
|
|
} = this.props;
|
2018-04-05 23:26:20 +02:00
|
|
|
const errorMessage = typeof error === 'object' ? error.message : error;
|
|
|
|
|
2019-02-18 18:24:56 +01:00
|
|
|
const Wrapper = blockWrap
|
|
|
|
? ({ children: innerChildren }) => <fieldset-section>{innerChildren}</fieldset-section>
|
|
|
|
: ({ children: innerChildren }) => <React.Fragment>{innerChildren}</React.Fragment>;
|
|
|
|
|
2018-03-26 23:32:43 +02:00
|
|
|
let input;
|
|
|
|
if (type) {
|
2019-02-13 17:27:20 +01:00
|
|
|
if (type === 'radio') {
|
|
|
|
input = (
|
2019-02-18 18:24:56 +01:00
|
|
|
<Wrapper>
|
2019-02-13 17:27:20 +01:00
|
|
|
<radio-element>
|
|
|
|
<input id={name} type="radio" {...inputProps} />
|
|
|
|
<label htmlFor={name}>{label}</label>
|
|
|
|
<radio-toggle onClick={inputProps.onChange} />
|
|
|
|
</radio-element>
|
2019-02-18 18:24:56 +01:00
|
|
|
</Wrapper>
|
2019-02-13 17:27:20 +01:00
|
|
|
);
|
|
|
|
} else if (type === 'checkbox') {
|
2019-02-18 18:24:56 +01:00
|
|
|
// web components treat props weird
|
|
|
|
// we need to fully remove it for proper component:attribute css styling
|
2019-02-20 06:20:29 +01:00
|
|
|
// $FlowFixMe
|
2019-02-18 18:24:56 +01:00
|
|
|
const elementProps = inputProps.disabled ? { disabled: true } : {};
|
2019-02-13 17:27:20 +01:00
|
|
|
input = (
|
2019-02-18 18:24:56 +01:00
|
|
|
<Wrapper>
|
|
|
|
<checkbox-element {...elementProps}>
|
2019-02-13 17:27:20 +01:00
|
|
|
<input id={name} type="checkbox" {...inputProps} />
|
|
|
|
<label htmlFor={name}>{label}</label>
|
|
|
|
<checkbox-toggle onClick={inputProps.onChange} />
|
|
|
|
</checkbox-element>
|
2019-02-18 18:24:56 +01:00
|
|
|
</Wrapper>
|
2019-02-13 17:27:20 +01:00
|
|
|
);
|
|
|
|
} else if (type === 'setting') {
|
|
|
|
// 'setting' should only be used for settings. Forms should use "checkbox"
|
|
|
|
input = (
|
|
|
|
<input-submit>
|
|
|
|
{labelOnLeft && <label htmlFor={name}>{label}</label>}
|
|
|
|
<Toggle id={name} {...inputProps} />
|
|
|
|
{!labelOnLeft && <label htmlFor={name}>{label}</label>}
|
|
|
|
</input-submit>
|
|
|
|
);
|
|
|
|
} else if (type === 'select') {
|
2018-03-26 23:32:43 +02:00
|
|
|
input = (
|
2019-02-13 17:27:20 +01:00
|
|
|
<fieldset-section>
|
|
|
|
{label && <label htmlFor={name}>{label}</label>}
|
|
|
|
<select id={name} {...inputProps}>
|
2018-12-19 06:44:53 +01:00
|
|
|
{children}
|
|
|
|
</select>
|
2019-02-13 17:27:20 +01:00
|
|
|
</fieldset-section>
|
2018-03-26 23:32:43 +02:00
|
|
|
);
|
|
|
|
} else if (type === 'markdown') {
|
2018-06-25 22:42:49 +02:00
|
|
|
const handleEvents = {
|
2018-07-29 01:48:54 +02:00
|
|
|
contextmenu: openEditorMenu,
|
2018-06-25 22:42:49 +02:00
|
|
|
};
|
2018-07-29 01:48:54 +02:00
|
|
|
|
2018-03-26 23:32:43 +02:00
|
|
|
input = (
|
2018-06-25 22:42:49 +02:00
|
|
|
<div className="form-field--SimpleMDE" onContextMenu={stopContextMenu}>
|
2019-02-13 17:27:20 +01:00
|
|
|
<fieldset-section>
|
|
|
|
<label htmlFor={name}>{label}</label>
|
|
|
|
<SimpleMDE
|
|
|
|
{...inputProps}
|
|
|
|
id={name}
|
|
|
|
type="textarea"
|
|
|
|
events={handleEvents}
|
|
|
|
options={{
|
|
|
|
hideIcons: ['heading', 'image', 'fullscreen', 'side-by-side'],
|
|
|
|
previewRender(plainText) {
|
|
|
|
const preview = <MarkdownPreview content={plainText} />;
|
|
|
|
return ReactDOMServer.renderToString(preview);
|
|
|
|
},
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</fieldset-section>
|
2018-03-26 23:32:43 +02:00
|
|
|
</div>
|
|
|
|
);
|
2018-04-06 07:15:29 +02:00
|
|
|
} else if (type === 'textarea') {
|
2019-02-13 17:27:20 +01:00
|
|
|
input = (
|
|
|
|
<fieldset-section>
|
|
|
|
<textarea type={type} id={name} {...inputProps} />
|
|
|
|
</fieldset-section>
|
|
|
|
);
|
2018-03-26 23:32:43 +02:00
|
|
|
} else {
|
2019-02-13 17:27:20 +01:00
|
|
|
const inputElement = <input type={type} id={name} {...inputProps} ref={this.input} />;
|
|
|
|
const inner = inputButton ? (
|
|
|
|
<input-submit>
|
|
|
|
{inputElement}
|
|
|
|
{inputButton}
|
|
|
|
</input-submit>
|
|
|
|
) : (
|
|
|
|
inputElement
|
|
|
|
);
|
|
|
|
|
|
|
|
input = (
|
|
|
|
<React.Fragment>
|
|
|
|
<fieldset-section>
|
2019-02-21 23:45:17 +01:00
|
|
|
<label htmlFor={name}>
|
|
|
|
{errorMessage ? <span className="error-text">{errorMessage}</span> : label}
|
|
|
|
</label>
|
2019-02-20 06:20:29 +01:00
|
|
|
{prefix && (
|
|
|
|
<label className="form-field--inline-prefix" htmlFor={name}>
|
|
|
|
{prefix}
|
|
|
|
</label>
|
|
|
|
)}
|
2019-02-13 17:27:20 +01:00
|
|
|
{inner}
|
|
|
|
</fieldset-section>
|
|
|
|
</React.Fragment>
|
|
|
|
);
|
2018-03-26 23:32:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2019-02-13 17:27:20 +01:00
|
|
|
<React.Fragment>
|
|
|
|
{input}
|
|
|
|
|
2018-06-13 07:20:53 +02:00
|
|
|
{helper && <div className="form-field__help">{helper}</div>}
|
2019-02-13 17:27:20 +01:00
|
|
|
</React.Fragment>
|
2018-03-26 23:32:43 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default FormField;
|