35769dede6
* separate out advanced textarea, fix comment channel selector width, add advanced text icon * fix master conflicts * fixes * fix channel description edit
171 lines
4.8 KiB
JavaScript
171 lines
4.8 KiB
JavaScript
// @flow
|
|
import 'easymde/dist/easymde.min.css';
|
|
import { FF_MAX_CHARS_DEFAULT } from 'constants/form-field';
|
|
import React from 'react';
|
|
import type { ElementRef, Node } from 'react';
|
|
|
|
type Props = {
|
|
affixClass?: string, // class applied to prefix/postfix label
|
|
autoFocus?: boolean,
|
|
blockWrap: boolean,
|
|
charCount?: number,
|
|
children?: React$Node,
|
|
defaultValue?: string | number,
|
|
disabled?: boolean,
|
|
error?: string | boolean,
|
|
helper?: string | React$Node,
|
|
inputButton?: React$Node,
|
|
label?: string | Node,
|
|
labelOnLeft: boolean,
|
|
max?: number,
|
|
min?: number,
|
|
name: string,
|
|
placeholder?: string | number,
|
|
postfix?: string,
|
|
prefix?: string,
|
|
range?: number,
|
|
readOnly?: boolean,
|
|
stretch?: boolean,
|
|
textAreaMaxLength?: number,
|
|
type?: string,
|
|
value?: string | number,
|
|
onChange?: (any) => any,
|
|
render?: () => React$Node,
|
|
};
|
|
|
|
export class FormField extends React.PureComponent<Props> {
|
|
static defaultProps = { labelOnLeft: false, blockWrap: true };
|
|
|
|
input: { current: ElementRef<any> };
|
|
|
|
constructor(props: Props) {
|
|
super(props);
|
|
this.input = React.createRef();
|
|
}
|
|
|
|
componentDidMount() {
|
|
const { autoFocus } = this.props;
|
|
const input = this.input.current;
|
|
|
|
if (input && autoFocus) input.focus();
|
|
}
|
|
|
|
render() {
|
|
const {
|
|
affixClass,
|
|
autoFocus,
|
|
blockWrap,
|
|
charCount,
|
|
children,
|
|
error,
|
|
helper,
|
|
inputButton,
|
|
label,
|
|
labelOnLeft,
|
|
name,
|
|
postfix,
|
|
prefix,
|
|
stretch,
|
|
textAreaMaxLength,
|
|
type,
|
|
render,
|
|
...inputProps
|
|
} = this.props;
|
|
|
|
const errorMessage = typeof error === 'object' ? error.message : error;
|
|
|
|
// Ideally, the character count should (and can) be appended to the
|
|
// SimpleMDE's "options::status" bar. However, I couldn't figure out how
|
|
// to pass the current value to it's callback, nor query the current
|
|
// text length from the callback. So, we'll use our own widget.
|
|
const hasCharCount = charCount !== undefined && charCount >= 0;
|
|
const countInfo = hasCharCount && textAreaMaxLength !== undefined && (
|
|
<span className="comment__char-count-mde">{`${charCount || '0'}/${textAreaMaxLength}`}</span>
|
|
);
|
|
const Wrapper = blockWrap
|
|
? ({ children: innerChildren }) => <fieldset-section class="radio">{innerChildren}</fieldset-section>
|
|
: ({ children: innerChildren }) => <span className="radio">{innerChildren}</span>;
|
|
|
|
const inputSimple = (type: string) => (
|
|
<>
|
|
<input id={name} type={type} {...inputProps} />
|
|
<label htmlFor={name}>{label}</label>
|
|
</>
|
|
);
|
|
|
|
const inputSelect = (selectClass: string) => (
|
|
<fieldset-section class={selectClass}>
|
|
{(label || errorMessage) && (
|
|
<label htmlFor={name}>{errorMessage ? <span className="error__text">{errorMessage}</span> : label}</label>
|
|
)}
|
|
<select id={name} {...inputProps}>
|
|
{children}
|
|
</select>
|
|
</fieldset-section>
|
|
);
|
|
|
|
const input = () => {
|
|
switch (type) {
|
|
case 'radio':
|
|
return <Wrapper>{inputSimple('radio')}</Wrapper>;
|
|
case 'checkbox':
|
|
return <div className="checkbox">{inputSimple('checkbox')}</div>;
|
|
case 'range':
|
|
return <div>{inputSimple('range')}</div>;
|
|
case 'select':
|
|
return inputSelect('');
|
|
case 'select-tiny':
|
|
return inputSelect('select--slim');
|
|
case 'textarea':
|
|
return (
|
|
<fieldset-section>
|
|
{label && (
|
|
<div className="form-field__two-column">
|
|
<label htmlFor={name}>{label}</label>
|
|
</div>
|
|
)}
|
|
<textarea
|
|
type={type}
|
|
id={name}
|
|
maxLength={textAreaMaxLength || FF_MAX_CHARS_DEFAULT}
|
|
ref={this.input}
|
|
{...inputProps}
|
|
/>
|
|
<div className="form-field__textarea-info">{countInfo}</div>
|
|
</fieldset-section>
|
|
);
|
|
default:
|
|
const inputElement = <input type={type} id={name} {...inputProps} ref={this.input} />;
|
|
const inner = inputButton ? (
|
|
<input-submit>
|
|
{inputElement}
|
|
{inputButton}
|
|
</input-submit>
|
|
) : (
|
|
inputElement
|
|
);
|
|
|
|
return (
|
|
<fieldset-section>
|
|
{(label || errorMessage) && (
|
|
<label htmlFor={name}>
|
|
{errorMessage ? <span className="error__text">{errorMessage}</span> : label}
|
|
</label>
|
|
)}
|
|
{prefix && <label htmlFor={name}>{prefix}</label>}
|
|
{inner}
|
|
</fieldset-section>
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{type && input()}
|
|
{helper && <div className="form-field__help">{helper}</div>}
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default FormField;
|