@spark-web/combobox
Version:
--- title: Combobox storybookPath: forms-combobox--default isExperimentalPackage: true ---
338 lines (333 loc) • 12.3 kB
JavaScript
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 };