UNPKG

@wix/design-system

Version:

@wix/design-system

196 lines 9.79 kB
import PropTypes from 'prop-types'; import React, { Component } from 'react'; import InfoIcon from '../InfoIcon'; import uniqueId from 'lodash/uniqueId'; import Text, { SIZES, SKINS, WEIGHTS } from '../Text'; import { ALIGN, DATA_HOOKS, PLACEMENT, STATUS } from './FormField.constants'; import { st, classes } from './FormField.st.css.js'; import { StatusAlertFilledSmall, StatusWarningFilledSmall, } from '@wix/wix-ui-icons-common'; import { Asterisk } from '@wix/wix-ui-icons-common/system'; import { StatusContext } from './StatusContext'; import { FieldSetContext } from '../FieldSet/FieldSet.context'; const asterisk = (React.createElement("div", { "data-hook": DATA_HOOKS.asterisk, className: classes.asterisk }, React.createElement(Asterisk, null))); export class FormField extends Component { constructor(props) { super(props); this.state = { lengthLeft: undefined, }; this.childrenRenderPropInterface = { setCharactersLeft: (lengthLeft) => this.setState({ lengthLeft }), }; this._hasCharCounter = () => this.props.charCount !== undefined || typeof this.state.lengthLeft === 'number'; this.charactersLeft = (lengthLeft) => { const { labelSize } = this.props; const colorProps = lengthLeft && lengthLeft >= 0 ? { light: true, secondary: true } : { skin: SKINS.error }; return (React.createElement(Text, { className: st(classes.charCount, { labelSize }), size: SIZES.small, weight: WEIGHTS.normal, ...colorProps, dataHook: DATA_HOOKS.counter, children: lengthLeft })); }; this._renderCharCounter = () => { if (!this._hasCharCounter()) { return; } const { charCount } = this.props; return this.charactersLeft(charCount !== undefined ? charCount : this.state.lengthLeft); }; this._renderInfoIcon = () => { const { infoContent, infoTooltipProps, showInfoIconOnHover } = this.props; return (infoContent && (React.createElement(InfoIcon, { dataHook: DATA_HOOKS.infoIcon, className: st(classes.infoIcon, { showInfoIconOnHover: String(showInfoIconOnHover), }), content: infoContent, tooltipProps: infoTooltipProps, size: "small" }))); }; this._renderSuffix = () => { const { suffix } = this.props; return ((suffix || this._hasCharCounter()) && (React.createElement("div", { "data-hook": DATA_HOOKS.suffix, className: st(classes.suffix, { noLabel: !this.props.label || this.props.labelPlacement !== PLACEMENT.top, }) }, suffix ? suffix : this._renderCharCounter()))); }; this._hasInlineElements = (label, labelPlacement) => { const hasInlineLabel = label && (labelPlacement === PLACEMENT.left || labelPlacement === PLACEMENT.right); const hasInlineIndicator = !label && (this.props.required || !!this.props.infoContent); return hasInlineLabel || hasInlineIndicator; }; this._renderLabel = ({ trimLongText, labelSize, }) => { const { label, id, required, infoContent, ellipsis, maxLines } = this.props; const weight = labelSize === 'tiny' ? 'normal' : undefined; const hasSuffix = required || infoContent; return (React.createElement(Text, { size: labelSize, weight: weight, htmlFor: id, id: this.labelId, tagName: "label", dataHook: DATA_HOOKS.label, ellipsis: ellipsis ?? trimLongText, maxLines: maxLines, style: { display: 'block', // allows the label to be centered vertically }, secondary: true, className: st(classes.label, { labelSize, size: labelSize, }), suffix: hasSuffix ? (React.createElement(React.Fragment, null, required && asterisk, this._renderInfoIcon())) : undefined }, label)); }; this._renderStatusIcon = () => { const { status } = this.props; if (!status || status === 'loading') return; const iconByStatus = { [STATUS.error]: StatusAlertFilledSmall, [STATUS.warning]: StatusWarningFilledSmall, }; const Icon = iconByStatus[status]; return (React.createElement("div", { className: classes.statusIcon }, React.createElement(Icon, null))); }; this._renderStatusMessage = () => { const { statusMessage, status, labelSize } = this.props; return (React.createElement("div", { className: st(classes.statusMessage, { status, labelSize, }) }, (status === STATUS.error || status === STATUS.warning) && this._renderStatusIcon(), React.createElement(Text, { id: this.statusId, dataHook: DATA_HOOKS.statusMessage, skin: status === STATUS.error ? 'error' : 'standard', secondary: status !== STATUS.error, size: "tiny" }, statusMessage))); }; this._getCustomColumns = () => { const { labelPlacement, labelWidth, inputWidth } = this.props; const labelColumn = labelWidth ?? 'auto'; const inputColumn = inputWidth ?? 'auto'; if (labelPlacement === PLACEMENT.left) { return `${labelColumn} ${inputColumn}`; } else if (labelPlacement === PLACEMENT.right) { return `${inputColumn} ${labelColumn}`; } else return undefined; }; this.labelId = props.labelId || uniqueId('formfield-'); this.statusId = props.labelId ? `${props.labelId}-status` : uniqueId('formfield-status-'); } _renderChildren() { const { children } = this.props; if (typeof children === 'function') { return children(this.childrenRenderPropInterface); } return children; } render() { const { label, labelPlacement, labelAlignment, labelSize, required, dataHook, children, classNames, stretchContent, statusMessage, status, labelWidth, inputWidth, ellipsis, } = this.props; const rootStyles = label ? { labelPlacement, labelAlignment, stretchContent, required, minLabelHeight: !children, } : { stretchContent, required, minLabelHeight: !children, }; const hasInlineElements = this._hasInlineElements(label, labelPlacement); const hasCustomWidth = !!labelWidth || !!inputWidth; return (React.createElement("div", { ...(this.props.showInfoIconOnHover && { 'data-hoverable-action-trigger': true, }), "data-hook": dataHook, "data-status": status, className: st(classes.root, { ...rootStyles, ...(label ? { labelSize } : {}), hasInlineElements }, classNames), style: labelPlacement !== PLACEMENT.top && hasCustomWidth ? { gridTemplateColumns: this._getCustomColumns() } : {} }, label && labelPlacement === PLACEMENT.top && (React.createElement("div", { className: classes.labelRow }, React.createElement("div", { className: classes.labelRowMain }, this._renderLabel({ trimLongText: true, labelSize })), this._renderSuffix())), children && (React.createElement("div", { "data-hook": DATA_HOOKS.children, className: st(classes.children, { childrenWithInlineLabel: hasInlineElements, }) }, !label && this._renderSuffix(), React.createElement(FieldSetContext.Consumer, null, fieldSet => (React.createElement(StatusContext.Provider, { value: { status, ariaLabelledBy: this.labelId, ariaDescribedBy: [ this.statusId, status === fieldSet.status && fieldSet.statusId, ] .filter(Boolean) .join(' '), } }, this._renderChildren()))))), hasInlineElements && (React.createElement("div", { className: st(classes.labelRow, { ellipsis, }) }, React.createElement("div", { className: classes.labelRowMain }, this._renderLabel({ trimLongText: false, labelSize })), !!label && this._renderSuffix())), statusMessage && this._renderStatusMessage())); } } FormField.displayName = 'FormField'; FormField.propTypes = { children: PropTypes.any, dataHook: PropTypes.any, id: PropTypes.any, infoContent: PropTypes.any, infoTooltipProps: PropTypes.any, showInfoIconOnHover: PropTypes.any, label: PropTypes.any, labelAlignment: PropTypes.any, labelPlacement: PropTypes.any, labelSize: PropTypes.any, required: PropTypes.any, stretchContent: PropTypes.any, suffix: PropTypes.any, charCount: PropTypes.any, labelId: PropTypes.any, statusMessage: PropTypes.any, status: PropTypes.any, ellipsis: PropTypes.any, maxLines: PropTypes.any, labelWidth: PropTypes.any, inputWidth: PropTypes.any, }; FormField.defaultProps = { required: false, stretchContent: true, labelSize: 'small', labelPlacement: PLACEMENT.top, labelAlignment: ALIGN.middle, }; export default FormField; //# sourceMappingURL=FormField.js.map