@spark-web/field
Version:
--- title: Field isExperimentalPackage: false ---
207 lines (189 loc) • 6.3 kB
JavaScript
'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;