UNPKG

@spark-web/combobox

Version:

--- title: Combobox storybookPath: forms-combobox--default isExperimentalPackage: true ---

338 lines (333 loc) 12.3 kB
import _toConsumableArray from '@babel/runtime/helpers/esm/toConsumableArray'; import _typeof from '@babel/runtime/helpers/esm/typeof'; import _asyncToGenerator from '@babel/runtime/helpers/esm/asyncToGenerator'; import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray'; import _regeneratorRuntime from '@babel/runtime/regenerator'; import { useFieldContext } from '@spark-web/field'; import { useMemo, useRef, useState, useEffect } from 'react'; import ReactSelect, { components, createFilter } from 'react-select'; import _objectWithoutProperties from '@babel/runtime/helpers/esm/objectWithoutProperties'; import _objectSpread from '@babel/runtime/helpers/esm/objectSpread2'; import { useFocusRing } from '@spark-web/a11y'; import { Box } from '@spark-web/box'; import { ChevronDownIcon } from '@spark-web/icon'; import { Spinner } from '@spark-web/spinner'; import { Text, useText } from '@spark-web/text'; import { useTheme } from '@spark-web/theme'; import { buildDataAttributes } from '@spark-web/utils/internal'; import { jsx } from 'react/jsx-runtime'; var _excluded = ["children"]; var useReactSelectComponentsOverride = function useReactSelectComponentsOverride(data) { var _useFieldContext = useFieldContext(), _useFieldContext2 = _slicedToArray(_useFieldContext, 2), invalid = _useFieldContext2[0].invalid, fieldProps = _useFieldContext2[1]; return useMemo(function () { return { DropdownIndicator: function DropdownIndicator(props) { return /*#__PURE__*/jsx(components.DropdownIndicator, _objectSpread(_objectSpread({}, props), {}, { children: /*#__PURE__*/jsx(ChevronDownIcon, { size: "xxsmall", tone: "muted" }) })); }, Input: function Input(props) { return /*#__PURE__*/jsx(components.Input, _objectSpread(_objectSpread(_objectSpread({}, props), data ? buildDataAttributes(data) : undefined), {}, { "aria-invalid": fieldProps['aria-invalid'], "aria-describedby": fieldProps['aria-describedby'] })); }, IndicatorSeparator: function IndicatorSeparator() { return null; }, LoadingIndicator: function LoadingIndicator() { return null; }, LoadingMessage: function LoadingMessage(props) { return /*#__PURE__*/jsx(components.LoadingMessage, _objectSpread(_objectSpread({}, props), {}, { children: /*#__PURE__*/jsx(Box, { paddingY: "large", children: /*#__PURE__*/jsx(Spinner, { size: "xsmall", tone: "primary" }) }) })); }, NoOptionsMessage: function NoOptionsMessage(props) { return /*#__PURE__*/jsx(components.NoOptionsMessage, _objectSpread(_objectSpread({}, props), {}, { children: /*#__PURE__*/jsx(Box, { paddingY: "large", children: /*#__PURE__*/jsx(Text, { children: "No matching results" }) }) })); }, SingleValue: function SingleValue(_ref) { var children = _ref.children, props = _objectWithoutProperties(_ref, _excluded); return /*#__PURE__*/jsx(components.SingleValue, _objectSpread(_objectSpread({}, props), {}, { children: /*#__PURE__*/jsx(Box, { data: invalid ? { invalid: invalid } : undefined, children: children }) })); } }; }, [data, fieldProps, invalid]); }; var useReactSelectStylesOverride = function useReactSelectStylesOverride(_ref2) { var invalid = _ref2.invalid; var theme = useTheme(); var _useText = useText({ baseline: false, tone: 'neutral', size: 'standard', weight: 'regular' }), _useText2 = _slicedToArray(_useText, 1), textStyles = _useText2[0]; var _useText3 = useText({ baseline: true, tone: 'muted', size: 'xsmall', weight: 'semibold' }), _useText4 = _slicedToArray(_useText3, 1), groupHeadingStyles = _useText4[0]; var focusRingStyles = useFocusRing({ always: true }); return { control: function control(provided, state) { return _objectSpread(_objectSpread(_objectSpread({}, provided), textStyles), state.isFocused ? focusRingStyles : invalid ? { borderColor: theme.color.foreground.critical } : { boxShadow: theme.shadow.small }); }, dropdownIndicator: function dropdownIndicator(provided, state) { return _objectSpread(_objectSpread({}, provided), {}, { transitionProperty: 'transform', transitionTimingFunction: 'linear', transitionDuration: '150ms' }, state.isFocused ? { transform: 'rotate(180deg)' } : {}); }, group: function group(provided) { return _objectSpread(_objectSpread(_objectSpread({}, provided), groupHeadingStyles), {}, { padding: 0, margin: 0 }); }, groupHeading: function groupHeading(provided) { return _objectSpread(_objectSpread(_objectSpread({}, provided), groupHeadingStyles), {}, { padding: theme.spacing.medium, paddingBottom: theme.spacing.small, margin: 0 }); }, menu: function menu(provided) { return _objectSpread(_objectSpread({}, provided), {}, { padding: theme.spacing.small, boxShadow: theme.shadow.medium, borderRadius: theme.border.radius.medium }); }, menuList: function menuList(provided) { return _objectSpread(_objectSpread({}, provided), {}, { padding: 0, display: 'flex', flexDirection: 'column', gap: theme.spacing.xsmall }); }, option: function option(provided, state) { return _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, provided), textStyles), {}, { borderRadius: theme.border.radius.small }, state.isSelected ? { color: theme.color.foreground.primaryActive, backgroundColor: theme.color.background.primaryMuted } : {}), state.isFocused ? { backgroundColor: state.isSelected ? theme.backgroundInteractions.primaryLowHover : theme.color.background.surfaceMuted, '> *': { color: state.isSelected ? theme.color.foreground.primaryHover : undefined, stroke: state.isSelected ? theme.color.foreground.primaryHover : undefined } } : {}), {}, { ':active': { backgroundColor: state.isSelected ? theme.backgroundInteractions.positiveLowActive : theme.color.background.surfacePressed, '> *': { color: state.isSelected ? theme.color.foreground.primaryActive : undefined, stroke: state.isSelected ? theme.color.foreground.primaryActive : undefined } } }); }, singleValue: function singleValue(provided) { return _objectSpread(_objectSpread({}, provided), {}, { '[data-invalid=true]': { color: theme.color.foreground.muted } }); } }; }; var useReactSelectThemeOverride = function useReactSelectThemeOverride() { var theme = useTheme(); return function (selectTheme) { return _objectSpread(_objectSpread({}, selectTheme), {}, { borderRadius: theme.border.radius.small, colors: _objectSpread(_objectSpread({}, selectTheme.colors), {}, { // TODO: map from theme object when tokens are revised primary: '#00a87b', primary75: '#00c28d', primary50: '#9acbb8', primary25: '#c8eada', danger: '#e61e32', dangerLight: '#fec1b5', neutral0: 'white', neutral5: '#fafcfe', neutral10: '#f1f4fb', neutral20: '#dce1ec', neutral30: '#c7cedb', // neutral40, neutral50: '#98a2b8', neutral60: '#646f84', neutral70: '#1a2a3a' // neutral80, // neutral90, }), spacing: { baseUnit: theme.spacing.xsmall, controlHeight: theme.sizing.medium, menuGutter: theme.spacing.xxsmall } }); }; }; var isBrowser = typeof window !== 'undefined'; var useAwaitableItems = function useAwaitableItems(awaitableItems) { var ref = useRef(); var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), loading = _useState2[0], setLoading = _useState2[1]; var _useState3 = useState([]), _useState4 = _slicedToArray(_useState3, 2), items = _useState4[0], setItems = _useState4[1]; useEffect(function () { _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() { var itemsResult; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: ref.current = awaitableItems; setLoading(true); _context.next = 4; return awaitableItems; case 4: itemsResult = _context.sent; if (!(ref.current !== awaitableItems)) { _context.next = 7; break; } return _context.abrupt("return"); case 7: setItems(itemsResult); setLoading(false); case 9: case "end": return _context.stop(); } } }, _callee); }))(); }, [awaitableItems]); return { loading: loading, items: items }; }; var Combobox = function Combobox(_ref2) { var data = _ref2.data, getOptionLabel = _ref2.getOptionLabel, getOptionValue = _ref2.getOptionValue, inputValue = _ref2.inputValue, isLoading = _ref2.isLoading, _items = _ref2.items, menuPortalTarget = _ref2.menuPortalTarget, onChange = _ref2.onChange, onInputChange = _ref2.onInputChange, placeholder = _ref2.placeholder, value = _ref2.value, defaultOption = _ref2.defaultOption; var _useFieldContext = useFieldContext(), _useFieldContext2 = _slicedToArray(_useFieldContext, 2), _useFieldContext2$ = _useFieldContext2[0], disabled = _useFieldContext2$.disabled, invalid = _useFieldContext2$.invalid, inputId = _useFieldContext2[1].id; var _useAwaitableItems = useAwaitableItems(_items), items = _useAwaitableItems.items, loading = _useAwaitableItems.loading; var components = useReactSelectComponentsOverride(data); var styles = useReactSelectStylesOverride({ invalid: invalid }); var theme = useReactSelectThemeOverride(); var defaultFilter = createFilter(); var getDefaultOptionValue = function getDefaultOptionValue() { if (!defaultOption) { return undefined; } var option = defaultOption.option; if (option && _typeof(option) === 'object' && 'value' in option) { return option.value; } return getOptionValue === null || getOptionValue === void 0 ? void 0 : getOptionValue(defaultOption.option); }; var filterOptions = function filterOptions(candidate, input) { var defaultOptionValue = getDefaultOptionValue(); if (input) { var isDefault = candidate.value === defaultOptionValue; return isDefault || defaultFilter(candidate, input); } return true; }; var getOptions = function getOptions() { if (defaultOption) { if (defaultOption.position === 'end') { return [].concat(_toConsumableArray(items), [defaultOption.option]); } return [defaultOption.option].concat(_toConsumableArray(items)); } return items; }; return /*#__PURE__*/jsx(ReactSelect, { components: components, getOptionLabel: getOptionLabel, getOptionValue: getOptionValue, inputId: inputId, inputValue: inputValue, isDisabled: disabled, isLoading: isLoading !== null && isLoading !== void 0 ? isLoading : loading, menuPortalTarget: menuPortalTarget !== null && menuPortalTarget !== void 0 ? menuPortalTarget : isBrowser ? document.body : undefined, onChange: onChange, onInputChange: onInputChange, options: getOptions(), placeholder: placeholder, styles: styles, theme: theme, value: value, filterOption: defaultOption ? filterOptions : undefined }); }; export { Combobox, useAwaitableItems };