preact-material-components
Version:
preact wrapper for "Material Components for the web"
257 lines (230 loc) • 6.13 kB
JSX
import {Component, h} from 'preact';
import Icon from '../Icon';
import {MDCTextField} from '@material/textfield';
import MaterialComponent from '../MaterialComponent';
/**
* @prop persistent = false
* @prop validation-msg = false
*/
class HelperText extends MaterialComponent {
constructor() {
super();
this.componentName = 'text-field-helper-text';
this._mdcProps = ['persistent', 'validation-msg'];
}
materialDom(props) {
return (
<p {...props} aria-hidden="true">
{props.children}
</p>
);
}
}
class Label extends MaterialComponent {
constructor() {
super();
this.componentName = 'floating-label';
}
materialDom(props) {
return <label {...props}>{props.children}</label>;
}
}
const defaultProps = {
valid: true
};
/**
* @prop fullwidth = false
* @prop textarea = false
* @prop dense = false
* @prop disabled = false
* @prop outlined = false
* @prop box = false
* @prop type = 'text'
* @prop outerStyle = {[key: string]: string}
* @prop value = ''
* @prop label = ''
*/
class TextFieldInput extends MaterialComponent {
constructor() {
super();
this.componentName = 'text-field';
this._mdcProps = [
'fullwidth',
'textarea',
'dense',
'disabled',
'box',
'outlined'
];
this.state = {
showFloatingLabel: false
};
}
componentDidMount() {
this.setState(
{
showFloatingLabel: true
},
() => {
this.MDComponent = new MDCTextField(this.control);
this.props.onInit && this.props.onInit(this.MDComponent);
setValid(defaultProps, this.props, this.MDComponent);
}
);
}
componentWillUpdate(nextProps) {
setValid(this.props, nextProps, this.MDComponent);
}
componentWillUnmount() {
this.MDComponent && this.MDComponent.destroy && this.MDComponent.destroy();
}
getValue() {
return this.MDComponent ? this.MDComponent.value : null;
}
materialDom(allprops) {
let {className, outerStyle, outlined, ...props} = allprops;
className = className || '';
if ('leadingIcon' in props) {
className += ' mdc-text-field--box mdc-text-field--with-leading-icon';
}
if ('trailingIcon' in props) {
className += ' mdc-text-field--box mdc-text-field--with-trailing-icon';
}
if ('value' in props && this.state.showFloatingLabel) {
className = [className, 'mdc-text-field--upgraded'].join(' ');
}
if (props.label && props.fullwidth) {
console.log(
'Passing a "label" prop is not supported when using a "fullwidth" text field.'
);
}
return (
<div className={className} ref={this.setControlRef} style={outerStyle}>
{props.leadingIcon ? (
<Icon className="mdc-text-field__icon">{props.leadingIcon}</Icon>
) : null}
{props.textarea ? (
<textarea className="mdc-text-field__input" {...props} />
) : (
<input
type={props.type || 'text'}
className="mdc-text-field__input"
{...props}
/>
)}
{props.label &&
this.state.showFloatingLabel && (
<Label for={props.id}>{props.label}</Label>
)}
{props.trailingIcon ? (
<Icon className="mdc-text-field__icon">{props.trailingIcon}</Icon>
) : null}
{props.textarea || outlined ? null : <div class="mdc-line-ripple" />}
{outlined ? (
<div class="mdc-notched-outline">
<svg>
<path className="mdc-notched-outline__path" />
</svg>
</div>
) : null}
{outlined ? <div className="mdc-notched-outline__idle" /> : null}
</div>
);
}
}
/**
* @prop fullwidth = false
* @prop textarea = false
* @prop dense = false
* @prop disabled = false
* @prop outlined = false
* @prop box = false
* @prop type = 'text'
* @prop outerStyle = {}
* @prop value = ''
* @prop label = ''
* @prop helperText = ''
* @prop helperTextPersistent = false
* @prop helperTextValidationMsg = false
*/
class TextField extends Component {
constructor() {
super();
this.id = TextField.uid();
this.state = {
showFloatingLabel: false
};
}
componentDidMount() {
this.setState({
showFloatingLabel: true
});
}
static uid() {
if (!this.uidCounter) {
this.uidCounter = 0;
}
return ++this.uidCounter;
}
render(allprops, {showFloatingLabel}) {
const {
className,
outerStyle,
helperTextPersistent,
helperTextValidationMsg,
...props
} = allprops;
const showDiv = props.helperText || (props.label && !showFloatingLabel);
if ((props.helperText || props.label) && !props.id) {
props.id = 'tf-' + this.id;
}
// Helper text
const helperTextProps = {
persistent: helperTextPersistent,
'validation-msg': helperTextValidationMsg
};
return showDiv ? (
<div className={className} style={outerStyle}>
{props.label &&
!showFloatingLabel && (
<label for={props.id}>{props.cssLabel || `${props.label}: `}</label>
)}
<TextFieldInput
{...props}
onInit={MDComponent => {
this.MDComponent = MDComponent;
}}
aria-controls={props.helperText && props.id + '-helper-text'}
/>
{props.helperText && (
<HelperText id={props.id + '-helper-text'} {...helperTextProps}>
{props.helperText}
</HelperText>
)}
</div>
) : (
<TextFieldInput
{...props}
className={className}
outerStyle={outerStyle}
onInit={MDComponent => {
this.MDComponent = MDComponent;
}}
/>
);
}
}
TextField.defaultProps = {
outerStyle: {}
};
function setValid(oldprops, newprops, textfield) {
if (
'valid' in oldprops &&
'valid' in newprops &&
oldprops.valid !== newprops.valid
) {
textfield.valid = newprops.valid;
}
}
TextField.Helptext = HelperText;
export default TextField;