lbry-desktop/ui/component/common/form-components/form-field.jsx
jessopb 35769dede6
separate out advanced textarea, fix comment channel selector width, a… (#7634)
* separate out advanced textarea, fix comment channel selector width, add advanced text icon

* fix master conflicts

* fixes

* fix channel description edit
2022-11-04 08:42:36 -04:00

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;