UNPKG

@spark-web/field

Version:

--- title: Field isExperimentalPackage: false ---

213 lines (201 loc) 6.37 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); var react$1 = require('@emotion/react'); var a11y = require('@spark-web/a11y'); var box = require('@spark-web/box'); var icon = require('@spark-web/icon'); var stack = require('@spark-web/stack'); var text = require('@spark-web/text'); var theme = require('@spark-web/theme'); var jsxRuntime = require('@emotion/react/jsx-runtime'); var FieldContext = /*#__PURE__*/react.createContext(null); var FieldContextProvider = FieldContext.Provider; var FIELD_CONTEXT_ERROR_MESSAGE = 'Input components must be inside a `Field`.'; function useFieldContext() { var ctx = react.useContext(FieldContext); if (!ctx) { throw new Error(FIELD_CONTEXT_ERROR_MESSAGE); } return ctx; } /** * Using a [context](https://reactjs.org/docs/context.html), the field * component connects the label, description, and message to the input element. */ var Field = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) { var children = _ref.children, idProp = _ref.id, data = _ref.data, description = _ref.description, _ref$disabled = _ref.disabled, disabled = _ref$disabled === void 0 ? false : _ref$disabled, label = _ref.label, adornment = _ref.adornment, _ref$labelVisibility = _ref.labelVisibility, labelVisibility = _ref$labelVisibility === void 0 ? 'visible' : _ref$labelVisibility, message = _ref.message, secondaryLabel = _ref.secondaryLabel, _ref$tone = _ref.tone, tone = _ref$tone === void 0 ? 'neutral' : _ref$tone, _ref$readOnly = _ref.readOnly, readOnly = _ref$readOnly === void 0 ? false : _ref$readOnly; var _useFieldIds = useFieldIds(idProp), descriptionId = _useFieldIds.descriptionId, inputId = _useFieldIds.inputId, messageId = _useFieldIds.messageId; // field context var invalid = Boolean(message && tone === 'critical'); var fieldContext = react.useMemo(function () { return [{ disabled: disabled, invalid: invalid, readOnly: readOnly }, { 'aria-describedby': a11y.mergeIds(message && messageId, description ? descriptionId : undefined), 'aria-invalid': invalid || undefined, id: inputId }]; }, [description, descriptionId, disabled, inputId, invalid, message, messageId, readOnly]); // label prep var hiddenLabel = jsxRuntime.jsxs(a11y.VisuallyHidden, { as: "label", htmlFor: inputId, children: [label, " ", secondaryLabel] }); var labelElement = { hidden: hiddenLabel, visible: jsxRuntime.jsx(box.Box, { as: "label", htmlFor: inputId, children: jsxRuntime.jsxs(text.Text, { tone: disabled || readOnly ? 'field' : 'neutral', weight: "semibold", children: [label, ' ', secondaryLabel && jsxRuntime.jsx(text.Text, { inline: true, tone: disabled || readOnly ? 'field' : 'muted', weight: "regular", children: secondaryLabel })] }) }), 'reserve-space': jsxRuntime.jsxs(react.Fragment, { children: [hiddenLabel, jsxRuntime.jsx(text.Text, { "aria-hidden": true, children: "\xA0" })] }) }; var LabelWrapper = labelVisibility === 'hidden' ? react.Fragment : FieldLabelWrapper; return jsxRuntime.jsx(FieldContextProvider, { value: fieldContext, children: jsxRuntime.jsxs(stack.Stack, { ref: forwardedRef, data: data, gap: "medium", children: [jsxRuntime.jsxs(LabelWrapper, { children: [labelElement[labelVisibility], adornment] }), description && (typeof description === 'string' ? jsxRuntime.jsx(text.Text, { tone: "muted", size: "small", id: descriptionId, children: description }) : jsxRuntime.jsx(box.Box, { as: "label", htmlFor: descriptionId, children: description })), children, message && jsxRuntime.jsx(FieldMessage, { tone: tone, id: messageId, message: message })] }) }); }); Field.displayName = 'Field'; // Utils // ------------------------------ function useFieldIds(id) { var inputId = a11y.useId(id); var descriptionId = a11y.composeId(inputId, 'description'); var messageId = a11y.composeId(inputId, 'message'); return { descriptionId: descriptionId, inputId: inputId, messageId: messageId }; } // Styled components // ------------------------------ function FieldLabelWrapper(_ref2) { var children = _ref2.children; return jsxRuntime.jsx(box.Box, { display: "flex", alignItems: "center", justifyContent: "spaceBetween", gap: "large", children: children }); } var messageToneMap = { critical: 'critical', neutral: 'muted', positive: 'positive' }; // NOTE: use icons in addition to color for folks with visions issues var messageIconMap = { critical: icon.ExclamationCircleIcon, neutral: null, positive: icon.CheckCircleIcon }; var FieldMessage = function FieldMessage(_ref3) { var message = _ref3.message, id = _ref3.id, tone = _ref3.tone; var textTone = messageToneMap[tone]; var Icon = messageIconMap[tone]; return jsxRuntime.jsxs(box.Box, { display: "flex", gap: "xsmall", children: [Icon ? jsxRuntime.jsx(IndicatorContainer, { children: jsxRuntime.jsx(Icon, { size: "xxsmall", tone: tone }) }) : null, jsxRuntime.jsx(text.Text, { tone: textTone, size: "small", id: id, children: message })] }); }; function IndicatorContainer(_ref4) { var children = _ref4.children; var theme$1 = theme.useTheme(); var _theme$typography$tex = theme$1.typography.text.small, mobile = _theme$typography$tex.mobile, tablet = _theme$typography$tex.tablet; var responsiveStyles = theme$1.utils.responsiveStyles({ mobile: { height: mobile.capHeight }, tablet: { height: tablet.capHeight } }); return jsxRuntime.jsx(box.Box, { display: "flex", alignItems: "center", "aria-hidden": true, cursor: "default", flexShrink: 0, css: react$1.css(responsiveStyles), children: children }); } exports.Field = Field; exports.FieldContextProvider = FieldContextProvider; exports.FieldMessage = FieldMessage; exports.useFieldContext = useFieldContext; exports.useFieldIds = useFieldIds;