UNPKG

@spark-web/field

Version:

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

207 lines (189 loc) 6.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var react = require('react'); var css = require('@emotion/css'); 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('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; 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 }, { 'aria-describedby': a11y.mergeIds(message && messageId, description && descriptionId), 'aria-invalid': invalid || undefined, id: inputId }]; }, [description, descriptionId, disabled, inputId, invalid, message, messageId]); // label prep var hiddenLabel = /*#__PURE__*/jsxRuntime.jsxs(a11y.VisuallyHidden, { as: "label", htmlFor: inputId, children: [label, " ", secondaryLabel] }); var labelElement = { hidden: hiddenLabel, visible: /*#__PURE__*/jsxRuntime.jsx(box.Box, { as: "label", htmlFor: inputId, children: /*#__PURE__*/jsxRuntime.jsxs(text.Text, { tone: disabled ? 'disabled' : 'neutral', weight: "semibold", children: [label, ' ', secondaryLabel && /*#__PURE__*/jsxRuntime.jsx(text.Text, { inline: true, tone: disabled ? 'disabled' : 'muted', weight: "regular", children: secondaryLabel })] }) }), 'reserve-space': /*#__PURE__*/jsxRuntime.jsxs(react.Fragment, { children: [hiddenLabel, /*#__PURE__*/jsxRuntime.jsx(text.Text, { "aria-hidden": true, children: "\xA0" })] }) }; var LabelWrapper = labelVisibility === 'hidden' ? react.Fragment : FieldLabelWrapper; return /*#__PURE__*/jsxRuntime.jsx(FieldContextProvider, { value: fieldContext, children: /*#__PURE__*/jsxRuntime.jsxs(stack.Stack, { ref: forwardedRef, data: data, gap: "medium", children: [/*#__PURE__*/jsxRuntime.jsxs(LabelWrapper, { children: [labelElement[labelVisibility], adornment] }), description && /*#__PURE__*/jsxRuntime.jsx(text.Text, { tone: "muted", size: "small", id: descriptionId, children: description }), children, message && /*#__PURE__*/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 /*#__PURE__*/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 /*#__PURE__*/jsxRuntime.jsxs(box.Box, { display: "flex", gap: "xsmall", children: [Icon ? /*#__PURE__*/jsxRuntime.jsx(IndicatorContainer, { children: /*#__PURE__*/jsxRuntime.jsx(Icon, { size: "xxsmall", tone: tone }) }) : null, /*#__PURE__*/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 /*#__PURE__*/jsxRuntime.jsx(box.Box, { display: "flex", alignItems: "center", "aria-hidden": true, cursor: "default", flexShrink: 0, className: css.css(responsiveStyles), children: children }); } exports.Field = Field; exports.FieldContextProvider = FieldContextProvider; exports.FieldMessage = FieldMessage; exports.useFieldContext = useFieldContext; exports.useFieldIds = useFieldIds;