@shopify/polaris
Version:
Shopify’s admin product component library
169 lines (165 loc) • 6.62 kB
JavaScript
'use strict';
var React = require('react');
var polarisIcons = require('@shopify/polaris-icons');
var string = require('../../utilities/string.js');
var Activator = require('./components/Activator/Activator.js');
var SearchField = require('./components/SearchField/SearchField.js');
var Popover = require('../Popover/Popover.js');
var Box = require('../Box/Box.js');
var context = require('../../utilities/combobox/context.js');
var Icon = require('../Icon/Icon.js');
var Listbox = require('../Listbox/Listbox.js');
const FILTER_REGEX = value => new RegExp(value, 'i');
const QUERY_REGEX = value => new RegExp(`^${string.escapeRegex(value)}$`, 'i');
function Picker({
activator,
allowMultiple,
searchField,
options = [],
willLoadMoreOptions,
height,
addAction,
onScrolledToBottom,
onClose,
...listboxProps
}) {
const activatorRef = /*#__PURE__*/React.createRef();
const [activeItems, setActiveItems] = React.useState([]);
const [popoverActive, setPopoverActive] = React.useState(false);
const [activeOptionId, setActiveOptionId] = React.useState();
const [textFieldLabelId, setTextFieldLabelId] = React.useState();
const [listboxId, setListboxId] = React.useState();
const [query, setQuery] = React.useState('');
const filteredOptions = React.useRef(options);
const shouldOpen = !popoverActive;
const handleClose = React.useCallback(() => {
setPopoverActive(false);
onClose?.();
activatorRef.current?.focus();
}, [activatorRef, onClose]);
const handleOpen = React.useCallback(() => {
setPopoverActive(true);
filteredOptions.current = options;
}, [options]);
const handleFocus = React.useCallback(() => {
if (shouldOpen) handleOpen();
}, [shouldOpen, handleOpen]);
const handleChange = React.useCallback(() => {
if (shouldOpen) handleOpen();
}, [shouldOpen, handleOpen]);
const handleBlur = React.useCallback(() => {
if (popoverActive) {
handleClose();
setQuery('');
}
}, [popoverActive, handleClose]);
const textFieldContextValue = React.useMemo(() => ({
activeOptionId,
expanded: popoverActive,
listboxId,
setTextFieldLabelId,
onTextFieldFocus: handleFocus,
onTextFieldChange: handleChange,
onTextFieldBlur: handleBlur
}), [activeOptionId, popoverActive, listboxId, setTextFieldLabelId, handleFocus, handleChange, handleBlur]);
const listboxOptionContextValue = React.useMemo(() => ({
allowMultiple
}), [allowMultiple]);
const listboxContextValue = React.useMemo(() => ({
listboxId,
textFieldLabelId,
textFieldFocused: popoverActive,
willLoadMoreOptions,
setActiveOptionId,
setListboxId,
onKeyToBottom: onScrolledToBottom
}), [listboxId, textFieldLabelId, popoverActive, willLoadMoreOptions, setActiveOptionId, setListboxId, onScrolledToBottom]);
const updateText = React.useCallback(value => {
setQuery(value);
if (value === '') {
filteredOptions.current = options;
return;
}
const resultOptions = options?.filter(option => FILTER_REGEX(value).exec(reactChildrenText(option.children)));
filteredOptions.current = resultOptions ?? [];
}, [options]);
const handleSelect = React.useCallback(selected => {
setQuery('');
updateText('');
listboxProps.onSelect?.(selected);
if (!filteredOptions.current.some(option => option.value === selected)) {
filteredOptions.current = [...filteredOptions.current, {
value: selected,
children: selected
}];
}
if (!allowMultiple) {
handleClose();
setActiveItems([selected]);
return;
}
setActiveItems(selectedOptions => {
return activeItems.includes(selected) ? selectedOptions.filter(option => option !== selected) : [...selectedOptions, selected];
});
}, [updateText, listboxProps, allowMultiple, handleClose, activeItems]);
const showList = options.length > 0 || query !== '';
const firstSelectedOption = reactChildrenText(options.find(option => option.value === activeItems?.[0])?.children);
const queryMatchesExistingOption = options.some(option => {
const matcher = QUERY_REGEX(query);
return reactChildrenText(option.children).match(matcher);
});
return /*#__PURE__*/React.createElement(Popover.Popover, {
activator: /*#__PURE__*/React.createElement(Activator.Activator, Object.assign({}, activator, {
onClick: handleOpen,
selected: firstSelectedOption || '',
placeholder: activator.placeholder,
ref: activatorRef
})),
active: popoverActive,
autofocusTarget: "none",
onClose: handleClose,
preferredPosition: "cover",
preventFocusOnClose: true
}, /*#__PURE__*/React.createElement(Popover.Popover.Pane, {
onScrolledToBottom: onScrolledToBottom,
height: height
}, searchField ? /*#__PURE__*/React.createElement(Box.Box, {
paddingBlockStart: "200",
paddingBlockEnd: "100",
paddingInline: "200",
borderBlockEndWidth: "025",
borderColor: "border",
minHeight: showList ? undefined : '58px'
}, /*#__PURE__*/React.createElement(context.ComboboxTextFieldContext.Provider, {
value: textFieldContextValue
}, /*#__PURE__*/React.createElement(SearchField.SearchField, Object.assign({}, searchField, {
value: query,
onChange: value => {
updateText(value);
searchField.onChange?.(value, searchField.id ?? '');
},
prefix: /*#__PURE__*/React.createElement(Icon.Icon, {
source: polarisIcons.SearchIcon
}),
labelHidden: true,
focused: popoverActive
})))) : null, showList && /*#__PURE__*/React.createElement(context.ComboboxListboxContext.Provider, {
value: listboxContextValue
}, /*#__PURE__*/React.createElement(context.ComboboxListboxOptionContext.Provider, {
value: listboxOptionContextValue
}, /*#__PURE__*/React.createElement(Box.Box, {
paddingBlock: "200"
}, /*#__PURE__*/React.createElement(Listbox.Listbox, Object.assign({}, listboxProps, {
onSelect: handleSelect
}), filteredOptions.current?.map(option => /*#__PURE__*/React.createElement(Listbox.Listbox.Option, Object.assign({
key: option.value,
selected: activeItems.some(item => item === option.value)
}, option))), addAction && query !== '' && !queryMatchesExistingOption ? /*#__PURE__*/React.createElement(Listbox.Listbox.Action, Object.assign({}, addAction, {
value: query
})) : null))))));
}
const reactChildrenText = children => {
if (typeof children === 'string') return children;
return /*#__PURE__*/React.isValidElement(children) ? reactChildrenText(children?.props?.children) : '';
};
exports.Picker = Picker;