@wix/design-system
Version:
@wix/design-system
196 lines • 9.79 kB
JavaScript
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