UNPKG

chakra-ui-select

Version:
723 lines (652 loc) 21.8 kB
import { mode, getColor } from '@chakra-ui/theme-tools'; import React, { useState, useRef, useImperativeHandle, useEffect, Fragment, useCallback } from 'react'; import { forwardRef, chakra, useStyles, useMultiStyleConfig, StylesProvider } from '@chakra-ui/system'; import { cx, callAllHandlers, dataAttr, runIfFn } from '@chakra-ui/utils'; import Icon from '@chakra-ui/icon'; import Downshift, { useMultipleSelection } from 'downshift'; import { useFormControl } from '@chakra-ui/form-control'; import { createContext } from '@chakra-ui/react-utils'; import { Tag, TagLabel, TagCloseButton } from '@chakra-ui/tag'; var parts = ['control', 'menu', 'list', 'option', 'label', 'button']; var baseStyleMenu = { pos: 'absolute', mt: 1, w: 'full', zIndex: 2, overflow: 'auto', maxH: 60, rounded: 'md' }; function baseStyleList(props) { return { py: 1, rounded: 'md', w: 'full', bg: mode("#fff", "gray.700")(props), boxShadow: mode("lg", "dark-lg")(props), border: '1px', borderColor: 'gray.100' }; } function baseStyleOption(props) { return { py: 2, pl: 3, pr: 9, color: mode("gray.900", "gray.50")(props), pos: 'relative', userSelect: 'none', cursor: 'default', fontWeight: 'normal', transition: 'background 50ms ease-in 0s', _focus: { bg: mode("gray.100", "whiteAlpha.100")(props) }, _active: { bg: mode("gray.100", "whiteAlpha.200")(props) }, _expanded: { bg: mode("gray.100", "whitxeAlpha.100")(props) }, _selected: { bg: 'gray.50', fontWeight: 'semibold' }, _disabled: { opacity: 0.4, cursor: 'not-allowed' } }; } function baseStyleControl(props) { var theme = props.theme; return { bg: 'white', position: 'relative', w: 'full', border: '1px', borderColor: 'gray.300', rounded: 'md', shadow: 'base', textAlign: 'left', cursor: 'default', display: 'flex', alignItems: 'center', flexWrap: 'wrap', justifyContent: 'space-between', minH: 10, transition: 'all 0.2s', outline: 0, _focusWithin: { outline: 'none', borderColor: 'gray.400', boxShadow: "0 0 0 1px " + getColor(theme, 'gray.400') }, _focus: { outline: 'none', borderColor: 'gray.400', boxShadow: "0 0 0 1px " + getColor(theme, 'gray.400') }, _readOnly: { boxShadow: 'none !important', userSelect: 'all' }, _disabled: { opacity: 0.4, cursor: 'not-allowed' }, _hover: { borderColor: 'gray.400', _disabled: { borderColor: 'gray.300' } } }; } function baseStyleLabel(props) { return { d: 'block', fontSize: 'sm', fontWeight: 'medium', color: mode("gray.700", "gray.50")(props) }; } var baseStyleButton = { zIndex: 0, pos: 'absolute', inset: 0, w: '100%', h: '100%', cursor: 'default', _focus: { outline: 'none' }, _disabled: { opacity: 0.4, cursor: 'not-allowed' } }; var baseStyle = function baseStyle(props) { return { menu: baseStyleMenu, list: baseStyleList(props), option: baseStyleOption(props), control: baseStyleControl(props), label: baseStyleLabel(props), button: baseStyleButton }; }; var SelectSingle = { parts: parts, baseStyle: baseStyle }; var parts$1 = ['control', 'menu', 'list', 'option', 'label', 'button']; var baseStyle$1 = SelectSingle.baseStyle; var SelectMultiple = { parts: parts$1, baseStyle: baseStyle$1 }; var index = { SelectSingle: SelectSingle, SelectMultiple: SelectMultiple }; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var sizerStyle = { position: 'absolute', top: 0, left: 0, visibility: 'hidden', height: 0, overflow: 'scroll', whiteSpace: 'pre' }; var copyStyles = function copyStyles(styles, node) { node.style.fontSize = styles.fontSize; node.style.fontFamily = styles.fontFamily; node.style.fontWeight = styles.fontWeight; node.style.fontStyle = styles.fontStyle; node.style.letterSpacing = styles.letterSpacing; node.style.textTransform = styles.textTransform; }; var SearchInput = forwardRef(function (props, ref) { var wrapperStyle = props.wrapperStyle, className = props.className, placeholder = props.placeholder, minWidth = props.minWidth, placeholderIsMinWidth = props.placeholderIsMinWidth, isDisabled = props.isDisabled, inputProps = _objectWithoutPropertiesLoose(props, ["wrapperStyle", "className", "placeholder", "minWidth", "placeholderIsMinWidth", "isDisabled"]); var _useState = useState(minWidth), inputWidth = _useState[0], setInputWidth = _useState[1]; var inputRef = useRef(null); var sizerRef = useRef(null); var placeHolderSizerRef = useRef(null); var _className = cx('chakra-select__search-input', className); var sizerValue = props.defaultValue || props.value || ''; useImperativeHandle(ref, function () { return { focus: function focus() { var _inputRef$current; inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus(); } }; }); useEffect(function () { copyInputStyles(); }, []); useEffect(function () { var _sizerRef$current; if (!sizerRef || typeof ((_sizerRef$current = sizerRef.current) === null || _sizerRef$current === void 0 ? void 0 : _sizerRef$current.scrollWidth) === 'undefined') { return; } var newInputWidth; if (placeholder && (!props.value || props.value && placeholderIsMinWidth)) { var _placeHolderSizerRef$; newInputWidth = Math.max(sizerRef.current.scrollWidth, (_placeHolderSizerRef$ = placeHolderSizerRef.current) === null || _placeHolderSizerRef$ === void 0 ? void 0 : _placeHolderSizerRef$.scrollWidth) + 2; } else { var _sizerRef$current2; newInputWidth = ((_sizerRef$current2 = sizerRef.current) === null || _sizerRef$current2 === void 0 ? void 0 : _sizerRef$current2.scrollWidth) + 2; } if (minWidth && newInputWidth < minWidth) { newInputWidth = minWidth; } if (newInputWidth !== inputWidth) { setInputWidth(newInputWidth); } }, [sizerValue, minWidth, inputWidth, placeholderIsMinWidth, placeholder]); var copyInputStyles = function copyInputStyles() { if (!window.getComputedStyle || !(inputRef !== null && inputRef !== void 0 && inputRef.current)) { return; } var inputNode = inputRef.current; var inputStyles = inputNode && window.getComputedStyle(inputNode); if (!inputStyles) { return; } if (sizerRef.current) { copyStyles(inputStyles, sizerRef.current); } if (placeHolderSizerRef.current) { copyStyles(inputStyles, placeHolderSizerRef.current); } }; var _wrapperStyle = _extends({ d: 'inline-block', visibility: isDisabled ? 'hidden' : 'visible', color: 'gray.800' }, wrapperStyle); var _inputProps = _extends({ w: inputWidth + "px", border: 0, fontSize: 'inherit', outline: 0, padding: 0, color: 'inherit', boxSizing: 'content-box', background: '0px center' }, inputProps); return React.createElement(chakra.div, Object.assign({ className: _className }, _wrapperStyle), React.createElement(chakra.input, Object.assign({ placeholder: placeholder }, _inputProps, { ref: inputRef })), React.createElement(chakra.div, { ref: sizerRef, sx: sizerStyle }, sizerValue), placeholder && React.createElement(chakra.div, { ref: placeHolderSizerRef, sx: sizerStyle }, placeholder)); }); var _createContext = createContext({ strict: false, name: 'DownshiftContext' }), SelectProvider = _createContext[0], useSelect = _createContext[1]; function SelectValueContainer(props) { return React.createElement(chakra.div, Object.assign({ d: 'flex', alignItems: 'center', flex: '1 1 0%', flexWrap: 'wrap', padding: '2px 8px', pos: 'relative', overflow: 'hidden' }, props)); } var ArrowIndicator = forwardRef(function (props, ref) { return React.createElement(chakra.div, Object.assign({ ref: ref, pos: 'absolute', insetY: 0, right: 0, pr: 2, display: 'flex', alignItems: 'center', pointerEvents: 'none', color: 'gray.500' }, props)); }); var SelectClearIndicator = forwardRef(function (props, ref) { var onClick = props.onClick, className = props.className, rest = _objectWithoutPropertiesLoose(props, ["onClick", "className"]); var _useSelect = useSelect(), selectedItem = _useSelect.selectedItem, clearSelection = _useSelect.clearSelection, inputRef = _useSelect.inputRef, isDisabled = _useSelect.isDisabled; var _className = cx('chakra-select__clean-btn', className); if (!selectedItem || isDisabled) return null; return React.createElement(chakra.div, Object.assign({ d: 'flex', p: 2, ref: ref, "aria-hidden": true, className: _className, zIndex: 1, tabIndex: -1, outline: 'none', color: 'gray.500', w: '100%', h: '100%', alignItems: 'center', justifyContent: 'center', _hover: { color: 'gray.600' } }, rest, { onClick: callAllHandlers(onClick, function (event) { var _inputRef$current; event.stopPropagation(); clearSelection(); inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus(); }) }), React.createElement(Icon, { focusable: 'false', "aria-hidden": true, boxSize: '1em', stroke: 'currentColor' }, React.createElement("path", { strokeLinecap: 'round', strokeLinejoin: 'round', strokeWidth: '2', d: 'M6 18L18 6M6 6l12 12' }))); }); var SelectControl = forwardRef(function (props, ref) { var _useSelect2 = useSelect(), isDisabled = _useSelect2.isDisabled; var ownProps = useFormControl(_extends({ isDisabled: isDisabled }, props)); var styles = useStyles(); return React.createElement(chakra.div, Object.assign({ ref: ref, __css: styles.control }, ownProps)); }); var SelectButton = forwardRef(function (props, ref) { var onClick = props.onClick; var _useSelect3 = useSelect(), getToggleButtonProps = _useSelect3.getToggleButtonProps, inputRef = _useSelect3.inputRef, isDisabled = _useSelect3.isDisabled, isOpen = _useSelect3.isOpen, getDropdownProps = _useSelect3.getDropdownProps; var button = useFormControl(_extends({ isDisabled: isDisabled }, props)); var styles = useStyles(); return React.createElement(chakra.button, Object.assign({ __css: styles.button, ref: ref }, button, getToggleButtonProps(_extends({}, getDropdownProps === null || getDropdownProps === void 0 ? void 0 : getDropdownProps({ preventKeyAction: isOpen }), { onClick: callAllHandlers(onClick, function (event) { var _inputRef$current2; event.stopPropagation(); inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current2 = inputRef.current) === null || _inputRef$current2 === void 0 ? void 0 : _inputRef$current2.focus(); }) })))); }); var SelectSearchInput = forwardRef(function (props, ref) { var _useSelect4 = useSelect(), getInputProps = _useSelect4.getInputProps, isDisabled = _useSelect4.isDisabled, inputRef = _useSelect4.inputRef, getDropdownProps = _useSelect4.getDropdownProps, selectedItems = _useSelect4.selectedItems; var input = useFormControl(_extends({ isDisabled: isDisabled }, props)); useImperativeHandle(ref, function () { return { focus: function focus() { var _inputRef$current3; inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current3 = inputRef.current) === null || _inputRef$current3 === void 0 ? void 0 : _inputRef$current3.focus(); } }; }); var placeholder = selectedItems && selectedItems.length > 0 ? '' : props.placeholder; return React.createElement(Fragment, null, React.createElement(chakra.div, { zIndex: 1, m: 0.5, pb: 0.5, pt: 0.5, visibility: isDisabled ? 'hidden' : 'visible' }, React.createElement(SearchInput, Object.assign({ tabIndex: -1, isDisabled: isDisabled, type: 'text', autoCapitalize: 'none' }, input, getInputProps(_extends({ ref: inputRef }, getDropdownProps === null || getDropdownProps === void 0 ? void 0 : getDropdownProps({ placeholder: placeholder, ref: inputRef }))))))); }); function SelectOption(_ref) { var children = _ref.children, value = _ref.value, index = _ref.index, isDisabled = _ref.isDisabled, props = _objectWithoutPropertiesLoose(_ref, ["children", "value", "index", "isDisabled"]); var _useSelect5 = useSelect(), getItemProps = _useSelect5.getItemProps, selectedItem = _useSelect5.selectedItem, highlightedIndex = _useSelect5.highlightedIndex; var styles = useStyles(); var isSelected = selectedItem === value; var isActive = highlightedIndex === index; return React.createElement(chakra.li, Object.assign({ bg: isActive ? 'gray.50' : 'white', "data-disabled": dataAttr(isDisabled) }, getItemProps({ item: value, index: index }), { "aria-selected": props.isSelected ? 'true' : "" + isSelected, __css: styles.option }, props), runIfFn(children, { isSelected: isSelected, isActive: isActive })); } var SelectMenuList = forwardRef(function (props, ref) { var _useSelect6 = useSelect(), isOpen = _useSelect6.isOpen; var styles = useStyles(); if (!isOpen) return null; return React.createElement(chakra.ul, Object.assign({ ref: ref, __css: styles.list }, props)); }); var SelectMenu = forwardRef(function (props, ref) { var styles = useStyles(); var _useSelect7 = useSelect(), getMenuProps = _useSelect7.getMenuProps; return React.createElement(chakra.div, Object.assign({ ref: ref, __css: styles.menu }, getMenuProps(), props)); }); function SelectSingle$1(_ref2) { var id = _ref2.id, children = _ref2.children, isOpen = _ref2.isOpen, defaultValue = _ref2.defaultValue, defaultIsOpen = _ref2.defaultIsOpen, _ref2$defaultHighligh = _ref2.defaultHighlightedIndex, defaultHighlightedIndex = _ref2$defaultHighligh === void 0 ? 0 : _ref2$defaultHighligh, onChange = _ref2.onChange, itemToString = _ref2.itemToString, isDisabled = _ref2.isDisabled, props = _objectWithoutPropertiesLoose(_ref2, ["id", "children", "isOpen", "defaultValue", "defaultIsOpen", "defaultHighlightedIndex", "onChange", "itemToString", "isDisabled"]); var styles = useMultiStyleConfig('SelectSingle', {}); var inputRef = useRef(null); return React.createElement(Downshift, { id: id, onChange: onChange, initialSelectedItem: defaultValue, initialIsOpen: defaultIsOpen, isOpen: isOpen, itemToString: itemToString, initialHighlightedIndex: defaultHighlightedIndex }, function (downshift) { return React.createElement(chakra.div, Object.assign({ pos: 'relative' }, props, downshift.getRootProps()), React.createElement(StylesProvider, { value: styles }, React.createElement(SelectProvider, { value: _extends({}, downshift, { isDisabled: isDisabled, inputRef: inputRef }) }, runIfFn(children, { inputValue: downshift.inputValue, isOpen: downshift.isOpen, highlightedIndex: downshift.highlightedIndex, selectedItem: downshift.selectedItem, getLabelProps: downshift.getLabelProps })))); }); } function SelectedItemTag(_ref) { var children = _ref.children, selectedItem = _ref.selectedItem, index = _ref.index, props = _objectWithoutPropertiesLoose(_ref, ["children", "selectedItem", "index"]); var _useSelect = useSelect(), removeSelectedItem = _useSelect.removeSelectedItem, getSelectedItemProps = _useSelect.getSelectedItemProps, inputRef = _useSelect.inputRef; return React.createElement(Tag, Object.assign({ size: 'sm', m: '2px', zIndex: 1 }, props, getSelectedItemProps === null || getSelectedItemProps === void 0 ? void 0 : getSelectedItemProps({ selectedItem: selectedItem, index: index })), React.createElement(TagLabel, { color: 'primary', fontWeight: 'semibold' }, children), React.createElement(TagCloseButton, { cursor: 'default', _focus: { outline: 'none' }, onClick: function onClick(e) { var _inputRef$current; e.stopPropagation(); removeSelectedItem === null || removeSelectedItem === void 0 ? void 0 : removeSelectedItem(selectedItem); (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus(); } })); } function SelectMultiple$1(_ref2) { var id = _ref2.id, children = _ref2.children, onChange = _ref2.onChange, _ref2$initialSelected = _ref2.initialSelectedItems, initialSelectedItems = _ref2$initialSelected === void 0 ? [] : _ref2$initialSelected, defaultSelectedItems = _ref2.defaultSelectedItems, itemToString = _ref2.itemToString, value = _ref2.value, isDisabled = _ref2.isDisabled, _ref2$defaultHighligh = _ref2.defaultHighlightedIndex, defaultHighlightedIndex = _ref2$defaultHighligh === void 0 ? 0 : _ref2$defaultHighligh, defaultIsOpen = _ref2.defaultIsOpen, isOpen = _ref2.isOpen, props = _objectWithoutPropertiesLoose(_ref2, ["id", "children", "onChange", "initialSelectedItems", "defaultSelectedItems", "itemToString", "value", "isDisabled", "defaultHighlightedIndex", "defaultIsOpen", "isOpen"]); var inputRef = useRef(null); var styles = useMultiStyleConfig('SelectMultiple', {}); var _useMultipleSelection = useMultipleSelection(_extends({ defaultSelectedItems: defaultSelectedItems, initialSelectedItems: initialSelectedItems, onSelectedItemsChange: onChange }, value && { selectedItems: value })), getSelectedItemProps = _useMultipleSelection.getSelectedItemProps, getDropdownProps = _useMultipleSelection.getDropdownProps, addSelectedItem = _useMultipleSelection.addSelectedItem, removeSelectedItem = _useMultipleSelection.removeSelectedItem, selectedItems = _useMultipleSelection.selectedItems; var getStateAndHelpers = useCallback(function (downshift) { return _extends({}, downshift, { selectedItems: selectedItems, getDropdownProps: getDropdownProps, getSelectedItemProps: getSelectedItemProps, removeSelectedItem: removeSelectedItem }); }, [selectedItems, getDropdownProps, getSelectedItemProps, removeSelectedItem]); var stateReducer = useCallback(function (state, changes) { switch (changes.type) { case Downshift.stateChangeTypes.keyDownEnter: case Downshift.stateChangeTypes.keyDownSpaceButton: case Downshift.stateChangeTypes.clickItem: return _extends({}, changes, { highlightedIndex: state.highlightedIndex, isOpen: true, inputValue: '' }); default: return changes; } }, []); var onStateChange = useCallback(function (_ref3) { var type = _ref3.type, selectedItem = _ref3.selectedItem; switch (type) { case Downshift.stateChangeTypes.keyDownEnter: case Downshift.stateChangeTypes.keyDownSpaceButton: case Downshift.stateChangeTypes.clickItem: if (selectedItem) { addSelectedItem(selectedItem); } break; } }, [addSelectedItem]); return React.createElement(Downshift, { id: id, stateReducer: stateReducer, onStateChange: onStateChange, selectedItem: null, itemToString: itemToString, initialHighlightedIndex: defaultHighlightedIndex, initialIsOpen: defaultIsOpen, isOpen: isOpen }, function (downshift) { var ctx = _extends({}, getStateAndHelpers(downshift), { isDisabled: isDisabled, inputRef: inputRef }); return React.createElement(chakra.div, Object.assign({ position: 'relative' }, props, downshift.getRootProps()), React.createElement(StylesProvider, { value: styles }, React.createElement(SelectProvider, { value: ctx }, runIfFn(children, { inputValue: downshift.inputValue, isOpen: downshift.isOpen, highlightedIndex: downshift.highlightedIndex, selectedItems: selectedItems, getLabelProps: downshift.getLabelProps })))); }); } export { ArrowIndicator, SelectButton, SelectClearIndicator, SelectControl, SelectMenu, SelectMenuList, SelectMultiple$1 as SelectMultiple, SelectOption, SelectSearchInput, SelectSingle$1 as SelectSingle, SelectValueContainer, SelectedItemTag, index as theme }; //# sourceMappingURL=index.modern.js.map