UNPKG

grommet

Version:

focus on the essential experience

497 lines (408 loc) 17.3 kB
"use strict"; exports.__esModule = true; exports.SelectContainer = void 0; var _react = _interopRequireWildcard(require("react")); var _styledComponents = _interopRequireWildcard(require("styled-components")); var _utils = require("../../utils"); var _defaultProps = require("../../default-props"); var _Box = require("../Box"); var _InfiniteScroll = require("../InfiniteScroll"); var _Keyboard = require("../Keyboard"); var _Text = require("../Text"); var _TextInput = require("../TextInput"); var _SelectOption = require("./SelectOption"); var _StyledSelect = require("./StyledSelect"); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 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 _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } // position relative is so scroll can be managed correctly var OptionsBox = (0, _styledComponents.default)(_Box.Box).withConfig({ displayName: "SelectContainer__OptionsBox", componentId: "sc-1wi0ul8-0" })(["position:relative;scroll-behavior:smooth;"]); var OptionBox = (0, _styledComponents.default)(_Box.Box).withConfig({ displayName: "SelectContainer__OptionBox", componentId: "sc-1wi0ul8-1" })(["", ""], function (props) { return props.selected && _utils.selectedStyle; }); var SelectContainer = /*#__PURE__*/ function (_Component) { _inheritsLoose(SelectContainer, _Component); function SelectContainer() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _Component.call.apply(_Component, [this].concat(args)) || this; _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "optionRefs", {}); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "searchRef", (0, _react.createRef)()); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "optionsRef", (0, _react.createRef)()); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", { search: '', activeIndex: -1 }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onSearchChange", function (event) { _this.setState({ search: event.target.value, activeIndex: -1 }, function () { var search = _this.state.search; _this.onSearch(search); }); }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onSearch", (0, _utils.debounce)(function (search) { var onSearch = _this.props.onSearch; onSearch(search); }, (0, _utils.debounceDelay)(_this.props))); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "selectOption", function (option, index) { return function () { var _this$props = _this.props, multiple = _this$props.multiple, onChange = _this$props.onChange, options = _this$props.options, selected = _this$props.selected, value = _this$props.value; if (onChange) { var nextValue = option; var nextSelected = index; if (multiple) { nextValue = []; nextSelected = []; var removed = false; var selectedIndexes = []; if (Array.isArray(selected)) { selectedIndexes = selected; } else if (Array.isArray(value)) { selectedIndexes = value.map(function (v) { return options.indexOf(v); }); } selectedIndexes.forEach(function (selectedIndex) { if (selectedIndex === index) { removed = true; } else { nextValue.push(options[selectedIndex]); nextSelected.push(selectedIndex); } }); if (!removed) { nextValue.push(option); nextSelected.push(index); } } onChange({ option: option, value: nextValue, selected: nextSelected }); } }; }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "clearKeyboardNavigation", function () { clearTimeout(_this.keyboardNavTimer); _this.keyboardNavTimer = setTimeout(function () { _this.setState({ keyboardNavigating: false }); }, 100); // 100ms was empirically determined }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onNextOption", function (event) { var options = _this.props.options; var activeIndex = _this.state.activeIndex; event.preventDefault(); var nextActiveIndex = activeIndex + 1; while (nextActiveIndex < options.length && _this.isDisabled(nextActiveIndex)) { nextActiveIndex += 1; } if (nextActiveIndex !== options.length) { _this.setState({ activeIndex: nextActiveIndex, keyboardNavigating: true }, function () { var buttonNode = _this.optionRefs[nextActiveIndex]; var optionsNode = _this.optionsRef.current; if (buttonNode && (0, _utils.isNodeAfterScroll)(buttonNode, optionsNode) && optionsNode.scrollTo) { optionsNode.scrollTo(0, buttonNode.offsetTop - (optionsNode.getBoundingClientRect().height - buttonNode.getBoundingClientRect().height)); } _this.clearKeyboardNavigation(); }); } }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onPreviousOption", function (event) { var activeIndex = _this.state.activeIndex; event.preventDefault(); var nextActiveIndex = activeIndex - 1; while (nextActiveIndex >= 0 && _this.isDisabled(nextActiveIndex)) { nextActiveIndex -= 1; } if (nextActiveIndex >= 0) { _this.setState({ activeIndex: nextActiveIndex, keyboardNavigating: true }, function () { var buttonNode = _this.optionRefs[nextActiveIndex]; var optionsNode = _this.optionsRef.current; if (buttonNode && (0, _utils.isNodeBeforeScroll)(buttonNode, optionsNode) && optionsNode.scrollTo) { optionsNode.scrollTo(0, buttonNode.offsetTop); } _this.clearKeyboardNavigation(); }); } }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onActiveOption", function (index) { return function () { var keyboardNavigating = _this.state.keyboardNavigating; if (!keyboardNavigating) { _this.setState({ activeIndex: index }); } }; }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onSelectOption", function (event) { var options = _this.props.options; var activeIndex = _this.state.activeIndex; if (activeIndex >= 0) { event.preventDefault(); // prevent submitting forms _this.selectOption(options[activeIndex], activeIndex)(); } }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "optionLabel", function (index) { var _this$props2 = _this.props, options = _this$props2.options, labelKey = _this$props2.labelKey; var option = options[index]; var optionLabel; if (labelKey) { if (typeof labelKey === 'function') { optionLabel = labelKey(option); } else { optionLabel = option[labelKey]; } } else { optionLabel = option; } return optionLabel; }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "optionValue", function (index) { var _this$props3 = _this.props, options = _this$props3.options, valueKey = _this$props3.valueKey; var option = options[index]; var optionValue; if (valueKey) { if (typeof valueKey === 'function') { optionValue = valueKey(option); } else { optionValue = option[valueKey]; } } else { optionValue = option; } return optionValue; }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "isDisabled", function (index) { var _this$props4 = _this.props, disabled = _this$props4.disabled, disabledKey = _this$props4.disabledKey, options = _this$props4.options; var option = options[index]; var result; if (disabledKey) { if (typeof disabledKey === 'function') { result = disabledKey(option, index); } else { result = option[disabledKey]; } } else if (Array.isArray(disabled)) { if (typeof disabled[0] === 'number') { result = disabled.indexOf(index) !== -1; } else { var optionValue = _this.optionValue(index); result = disabled.indexOf(optionValue) !== -1; } } return result; }); _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "isSelected", function (index) { var _this$props5 = _this.props, selected = _this$props5.selected, value = _this$props5.value, valueKey = _this$props5.valueKey; var result; if (selected) { // deprecated in favor of value result = selected.indexOf(index) !== -1; } else { var optionValue = _this.optionValue(index); if (Array.isArray(value)) { if (value.length === 0) { result = false; } else if (typeof value[0] !== 'object') { result = value.indexOf(optionValue) !== -1; } else if (valueKey) { result = value.some(function (valueItem) { var valueValue = typeof valueKey === 'function' ? valueKey(valueItem) : valueItem[valueKey]; return valueValue === optionValue; }); } } else if (valueKey && typeof value === 'object') { var valueValue = typeof valueKey === 'function' ? valueKey(value) : value[valueKey]; result = valueValue === optionValue; } else { result = value === optionValue; } } return result; }); return _this; } SelectContainer.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) { var options = nextProps.options, value = nextProps.value, onSearch = nextProps.onSearch; if (onSearch) { if (prevState.activeIndex === -1 && prevState.search === '' && options && value) { var optionValue = Array.isArray(value) && value.length ? value[0] : value; var activeIndex = options.indexOf(optionValue); return { activeIndex: activeIndex }; } if (prevState.activeIndex === -1 && prevState.search !== '') { return { activeIndex: 0 }; } } return null; }; var _proto = SelectContainer.prototype; _proto.componentDidMount = function componentDidMount() { var _this2 = this; var onSearch = this.props.onSearch; var activeIndex = this.state.activeIndex; // timeout need to send the operation through event loop and allow time to the portal // to be available setTimeout(function () { var optionsNode = _this2.optionsRef.current; if (onSearch) { var input = _this2.searchRef.current; if (input && input.focus) { (0, _utils.setFocusWithoutScroll)(input); } } else if (optionsNode) { (0, _utils.setFocusWithoutScroll)(optionsNode); } // scroll to active option if it is below the fold if (activeIndex >= 0 && optionsNode) { var optionNode = _this2.optionRefs[activeIndex]; var _optionsNode$getBound = optionsNode.getBoundingClientRect(), containerBottom = _optionsNode$getBound.bottom; if (optionNode) { var _optionNode$getBoundi = optionNode.getBoundingClientRect(), optionTop = _optionNode$getBoundi.bottom; if (containerBottom < optionTop) { optionNode.scrollIntoView(); } } } }, 0); }; _proto.render = function render() { var _this3 = this; var _this$props6 = this.props, children = _this$props6.children, dropHeight = _this$props6.dropHeight, emptySearchMessage = _this$props6.emptySearchMessage, id = _this$props6.id, onKeyDown = _this$props6.onKeyDown, onSearch = _this$props6.onSearch, options = _this$props6.options, searchPlaceholder = _this$props6.searchPlaceholder, theme = _this$props6.theme; var _this$state = this.state, activeIndex = _this$state.activeIndex, search = _this$state.search; var customSearchInput = theme.select.searchInput; var SelectTextInput = customSearchInput || _TextInput.TextInput; return _react.default.createElement(_Keyboard.Keyboard, { onEnter: this.onSelectOption, onUp: this.onPreviousOption, onDown: this.onNextOption, onKeyDown: onKeyDown }, _react.default.createElement(_StyledSelect.StyledContainer, { as: _Box.Box, id: id ? id + "__select-drop" : undefined, dropHeight: dropHeight }, onSearch && _react.default.createElement(_Box.Box, { pad: !customSearchInput ? 'xsmall' : undefined, flex: false }, _react.default.createElement(SelectTextInput, { focusIndicator: !customSearchInput, size: "small", ref: this.searchRef, type: "search", value: search, placeholder: searchPlaceholder, onChange: this.onSearchChange })), _react.default.createElement(OptionsBox, { flex: "shrink", role: "menubar", tabIndex: "-1", ref: this.optionsRef, overflow: "auto" }, options.length > 0 ? _react.default.createElement(_InfiniteScroll.InfiniteScroll, { items: options, step: theme.select.step, replace: true }, function (option, index) { var isDisabled = _this3.isDisabled(index); var isSelected = _this3.isSelected(index); var isActive = activeIndex === index; return _react.default.createElement(_SelectOption.SelectOption // eslint-disable-next-line react/no-array-index-key , { key: index, ref: function ref(_ref) { _this3.optionRefs[index] = _ref; }, disabled: isDisabled || undefined, active: isActive, selected: isSelected, option: option, onMouseOver: !isDisabled ? _this3.onActiveOption(index) : undefined, onClick: !isDisabled ? _this3.selectOption(option, index) : undefined }, children ? children(option, index, options, { active: isActive, disabled: isDisabled, selected: isSelected }) : _react.default.createElement(OptionBox, _extends({}, theme.select.options.box, { selected: isSelected }), _react.default.createElement(_Text.Text, theme.select.options.text, _this3.optionLabel(index)))); }) : _react.default.createElement(_SelectOption.SelectOption, { key: "search_empty", disabled: true, option: emptySearchMessage }, _react.default.createElement(OptionBox, theme.select.options.box, _react.default.createElement(_Text.Text, theme.select.container.text, emptySearchMessage)))))); }; return SelectContainer; }(_react.Component); _defineProperty(SelectContainer, "defaultProps", { children: null, disabled: undefined, emptySearchMessage: 'No matches found', id: undefined, multiple: false, name: undefined, onKeyDown: undefined, onSearch: undefined, options: undefined, searchPlaceholder: undefined, selected: undefined, value: '' }); Object.setPrototypeOf(SelectContainer.defaultProps, _defaultProps.defaultProps); var SelectContainerWrapper = (0, _styledComponents.withTheme)(SelectContainer); exports.SelectContainer = SelectContainerWrapper;