@spark-web/text-input
Version:
--- title: Text Input storybookPath: forms-textinput--default isExperimentalPackage: false ---
281 lines (271 loc) • 9.14 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _objectSpread = require('@babel/runtime/helpers/objectSpread2');
var box = require('@spark-web/box');
var field = require('@spark-web/field');
var theme = require('@spark-web/theme');
var react = require('react');
var jsxRuntime = require('@emotion/react/jsx-runtime');
var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
var react$1 = require('@emotion/react');
var a11y = require('@spark-web/a11y');
var text = require('@spark-web/text');
/**
* Components like the `SelectInput` may subscribe to the adornment context and
* change their appearance or behaviour.
*/
var InputAdornmentContext = /*#__PURE__*/react.createContext(null);
var placementToPadding = {
start: {
paddingLeft: 'medium',
paddingRight: 'xsmall'
},
end: {
paddingLeft: 'xsmall',
paddingRight: 'medium'
}
};
/**
* Places an element at the "start" or "end" of the input, only one adornment
* may be provided for each placement. By default, the adornment element will be
* wrapped to provide alignment and spacing, use the "raw" property to opt-out
* of this behaviour.
*
* @example
* <TextInput>
* <InputAdornment placement="start">
* <Text tone="placeholder">$</Text>
* </InputAdornment>
* </TextInput>
*/
var InputAdornment = function InputAdornment(_ref) {
var children = _ref.children,
fieldLabel = _ref.fieldLabel,
placement = _ref.placement,
raw = _ref.raw;
var theme$1 = theme.useTheme();
var adornmentContext = react.useMemo(function () {
return {
placement: placement
};
}, [placement]);
var _placementToPadding$p = placementToPadding[placement],
paddingLeft = _placementToPadding$p.paddingLeft,
paddingRight = _placementToPadding$p.paddingRight;
var content = children;
if (!raw) {
content = jsxRuntime.jsx(box.Box, {
paddingLeft: paddingLeft,
paddingRight: paddingRight,
children: jsxRuntime.jsx(box.Box, {
display: "flex",
alignItems: "center",
justifyContent: "center",
style: {
minWidth: theme$1.sizing.xxsmall
},
children: children
})
});
}
var wrappedContent = jsxRuntime.jsx(InputAdornmentContext.Provider, {
value: adornmentContext,
children: content
});
if (fieldLabel) {
return jsxRuntime.jsx(FieldAdornment, {
fieldLabel: fieldLabel,
children: wrappedContent
});
}
return wrappedContent;
};
/**
* Wrap the element with a field provider to override the parent field label.
* Only split-out from `InputAdornment` to avoid the conditional hook rule.
*/
var FieldAdornment = function FieldAdornment(_ref2) {
var children = _ref2.children,
fieldLabel = _ref2.fieldLabel;
var parentFieldContext = field.useFieldContext();
var fieldContext = react.useMemo(function () {
return _objectSpread(_objectSpread({}, parentFieldContext), {}, {
accessibilityLabel: fieldLabel
});
}, [fieldLabel, parentFieldContext]);
return jsxRuntime.jsx(field.FieldContextProvider, {
value: fieldContext,
children: children
});
};
var _excluded$1 = ["children", "startAdornment", "endAdornment"];
var InputContainer = function InputContainer(_ref) {
var children = _ref.children,
startAdornment = _ref.startAdornment,
endAdornment = _ref.endAdornment,
boxProps = _objectWithoutProperties(_ref, _excluded$1);
var _useFieldContext = field.useFieldContext(),
_useFieldContext2 = _slicedToArray(_useFieldContext, 1),
_useFieldContext2$ = _useFieldContext2[0],
disabled = _useFieldContext2$.disabled,
invalid = _useFieldContext2$.invalid,
readOnly = _useFieldContext2$.readOnly;
return jsxRuntime.jsxs(box.Box, _objectSpread(_objectSpread({
borderRadius: "small",
position: "relative",
background: disabled || readOnly ? 'inputDisabled' : 'input'
}, boxProps), {}, {
children: [startAdornment, children, jsxRuntime.jsx(FocusIndicator, {
invalid: invalid
}), endAdornment]
}));
};
var FocusIndicator = function FocusIndicator(_ref2) {
var invalid = _ref2.invalid;
var theme$1 = theme.useTheme();
return jsxRuntime.jsx(box.Box, {
"aria-hidden": "true",
as: "span",
data: {
'focus-indicator': 'true'
}
// Styles
,
border: invalid ? 'critical' : theme$1.components.textInput.borderColor,
borderRadius: "small",
position: "absolute",
bottom: 0,
left: 0,
right: 0,
top: 0,
shadow: "small",
css: react$1.css({
pointerEvents: 'none'
})
});
};
// NOTE: `null | undefined` allow consumers to conditionally render adornments
/**
* Map children for placement within the `TextInput` flex container. Ensures that
* placeholders are provided for unused placements.
*/
var childrenToAdornments = function childrenToAdornments(children) {
var startAdornment = null;
var endAdornment = null;
if (!children) {
return {
startAdornment: startAdornment,
endAdornment: endAdornment
};
}
react.Children.forEach(children, function (child) {
if ( /*#__PURE__*/react.isValidElement(child)) {
if (child.props.placement === 'end') {
endAdornment = child;
}
if (child.props.placement === 'start') {
startAdornment = child;
}
}
});
return {
startAdornment: startAdornment,
endAdornment: endAdornment
};
};
var _excluded = ["children", "data", "overflowStrategy"];
/** Organize and emphasize information quickly and effectively in a list of text elements. */
var TextInput = /*#__PURE__*/react.forwardRef(function (_ref, forwardedRef) {
var children = _ref.children,
data = _ref.data,
overflowStrategy = _ref.overflowStrategy,
consumerProps = _objectWithoutProperties(_ref, _excluded);
var _useFieldContext = field.useFieldContext(),
_useFieldContext2 = _slicedToArray(_useFieldContext, 2),
_useFieldContext2$ = _useFieldContext2[0],
disabled = _useFieldContext2$.disabled,
invalid = _useFieldContext2$.invalid,
readOnly = _useFieldContext2$.readOnly,
a11yProps = _useFieldContext2[1];
var _childrenToAdornments = childrenToAdornments(children),
startAdornment = _childrenToAdornments.startAdornment,
endAdornment = _childrenToAdornments.endAdornment;
var _useInputStyles = useInputStyles({
disabled: disabled,
invalid: invalid,
startAdornment: Boolean(startAdornment),
endAdornment: Boolean(endAdornment),
readOnly: readOnly,
overflowStrategy: overflowStrategy
}),
_useInputStyles2 = _slicedToArray(_useInputStyles, 2),
boxProps = _useInputStyles2[0],
inputStyles = _useInputStyles2[1];
return jsxRuntime.jsx(InputContainer, {
display: "inline-flex",
alignItems: "center",
startAdornment: startAdornment,
endAdornment: endAdornment,
children: jsxRuntime.jsx(box.Box, _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, boxProps), consumerProps), a11yProps), {}, {
as: "input",
css: react$1.css(inputStyles),
data: data,
disabled: disabled,
readOnly: readOnly,
ref: forwardedRef
}))
});
});
TextInput.displayName = 'TextInput';
/**
* Returns a tuple where the first item is an object of props to spread onto the
* underlying Box component that our inputs are created with, and the second
* item is a CSS object to be passed to Emotion's `css` function
**/
var useInputStyles = function useInputStyles(_ref2) {
var disabled = _ref2.disabled,
startAdornment = _ref2.startAdornment,
endAdornment = _ref2.endAdornment,
readOnly = _ref2.readOnly,
_ref2$overflowStrateg = _ref2.overflowStrategy,
overflowStrategy = _ref2$overflowStrateg === void 0 ? 'truncate' : _ref2$overflowStrateg;
var theme$1 = theme.useTheme();
var overflowStyles = text.useOverflowStrategy(overflowStrategy);
var focusRingStyles = a11y.useFocusRing({
always: true
});
var textStyles = text.useText({
baseline: false,
tone: disabled || readOnly ? 'field' : 'neutral',
size: 'standard',
weight: 'regular'
});
var _textStyles = _slicedToArray(textStyles, 2),
typographyStyles = _textStyles[0],
responsiveStyles = _textStyles[1];
return [{
flex: 1,
position: 'relative',
height: 'medium',
paddingLeft: startAdornment ? 'none' : 'medium',
paddingRight: endAdornment ? 'none' : 'medium',
width: 'full'
}, _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, typographyStyles), responsiveStyles), overflowStyles), {}, {
':enabled': {
':focus + [data-focus-indicator]': _objectSpread({
borderColor: theme$1.border.color.fieldAccent
}, focusRingStyles)
},
':focus': {
outline: 'none'
},
'&[aria-invalid=true]': {
color: theme$1.color.foreground.muted
}
})];
};
exports.InputAdornment = InputAdornment;
exports.InputContainer = InputContainer;
exports.TextInput = TextInput;
exports.useInputStyles = useInputStyles;