@dotconnor/grommet
Version:
focus on the essential experience
387 lines (340 loc) • 14.8 kB
JavaScript
"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 _Button = require("../Button");
var _InfiniteScroll = require("../InfiniteScroll");
var _Keyboard = require("../Keyboard");
var _Text = require("../Text");
var _TextInput = require("../TextInput");
var _StyledSelect = require("./StyledSelect");
var _utils2 = require("./utils");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } 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); }
// position relative is so scroll can be managed correctly
var OptionsBox = _styledComponents["default"].div.withConfig({
displayName: "SelectContainer__OptionsBox",
componentId: "sc-1wi0ul8-0"
})(["position:relative;scroll-behavior:smooth;overflow:auto;outline:none;"]);
var OptionBox = (0, _styledComponents["default"])(_Box.Box).withConfig({
displayName: "SelectContainer__OptionBox",
componentId: "sc-1wi0ul8-1"
})(["", ""], function (props) {
return props.selected && _utils.selectedStyle;
});
var SelectOption = (0, _styledComponents["default"])(_Button.Button).withConfig({
displayName: "SelectContainer__SelectOption",
componentId: "sc-1wi0ul8-2"
})(["display:block;width:100%;"]);
var ClearButton = function ClearButton(_ref) {
var clear = _ref.clear,
onClear = _ref.onClear,
name = _ref.name,
theme = _ref.theme,
setFocus = _ref.setFocus;
var label = clear.label,
position = clear.position;
var align = position !== 'bottom' ? 'start' : 'center';
var buttonLabel = label || "Clear " + (name || 'selection');
return /*#__PURE__*/_react["default"].createElement(_Button.Button, {
onClick: onClear,
onFocus: function onFocus() {
return setFocus(true);
},
onBlur: function onBlur() {
return setFocus(false);
}
}, /*#__PURE__*/_react["default"].createElement(_Box.Box, _extends({}, theme.select.clear.container, {
align: align
}), /*#__PURE__*/_react["default"].createElement(_Text.Text, theme.select.clear.text, buttonLabel)));
};
var SelectContainer = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, ref) {
var clear = _ref2.clear,
_ref2$children = _ref2.children,
children = _ref2$children === void 0 ? null : _ref2$children,
disabled = _ref2.disabled,
disabledKey = _ref2.disabledKey,
dropHeight = _ref2.dropHeight,
_ref2$emptySearchMess = _ref2.emptySearchMessage,
emptySearchMessage = _ref2$emptySearchMess === void 0 ? 'No matches found' : _ref2$emptySearchMess,
id = _ref2.id,
labelKey = _ref2.labelKey,
multiple = _ref2.multiple,
name = _ref2.name,
onChange = _ref2.onChange,
onKeyDown = _ref2.onKeyDown,
onMore = _ref2.onMore,
onSearch = _ref2.onSearch,
optionIndexesInValue = _ref2.optionIndexesInValue,
options = _ref2.options,
allOptions = _ref2.allOptions,
searchPlaceholder = _ref2.searchPlaceholder,
search = _ref2.search,
setSearch = _ref2.setSearch,
selected = _ref2.selected,
_ref2$value = _ref2.value,
value = _ref2$value === void 0 ? '' : _ref2$value,
valueKey = _ref2.valueKey,
_ref2$replace = _ref2.replace,
replace = _ref2$replace === void 0 ? true : _ref2$replace;
var theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _defaultProps.defaultProps.theme;
var _useState = (0, _react.useState)(-1),
activeIndex = _useState[0],
setActiveIndex = _useState[1];
var _useState2 = (0, _react.useState)(),
keyboardNavigation = _useState2[0],
setKeyboardNavigation = _useState2[1];
var _useState3 = (0, _react.useState)(false),
focus = _useState3[0],
setFocus = _useState3[1];
var searchRef = (0, _react.useRef)();
var optionsRef = (0, _react.useRef)(); // adjust activeIndex when options change
(0, _react.useEffect)(function () {
if (activeIndex === -1 && search && optionIndexesInValue.length) {
setActiveIndex(optionIndexesInValue[0]);
}
}, [activeIndex, optionIndexesInValue, search]); // set initial focus
(0, _react.useEffect)(function () {
// need to wait for Drop to be ready
var timer = setTimeout(function () {
var optionsNode = optionsRef.current;
if (onSearch) {
var searchInput = searchRef.current;
if (searchInput && searchInput.focus) {
(0, _utils.setFocusWithoutScroll)(searchInput);
}
} else if (optionsNode) {
(0, _utils.setFocusWithoutScroll)(optionsNode);
}
}, 100);
return function () {
return clearTimeout(timer);
};
}, [onSearch]); // clear keyboardNavigation after a while
(0, _react.useEffect)(function () {
if (keyboardNavigation) {
// 100ms was empirically determined
var timer = setTimeout(function () {
return setKeyboardNavigation(false);
}, 100);
return function () {
return clearTimeout(timer);
};
}
return undefined;
}, [keyboardNavigation]);
var optionLabel = (0, _react.useCallback)(function (index) {
return (0, _utils2.applyKey)(options[index], labelKey);
}, [labelKey, options]);
var optionValue = (0, _react.useCallback)(function (index) {
return (0, _utils2.applyKey)(options[index], valueKey);
}, [options, valueKey]);
var isDisabled = (0, _react.useCallback)(function (index) {
var option = options[index];
var result;
if (disabledKey) {
result = (0, _utils2.applyKey)(option, disabledKey);
} else if (Array.isArray(disabled)) {
if (typeof disabled[0] === 'number') {
result = disabled.indexOf(index) !== -1;
} else {
var optionVal = optionValue(index);
result = disabled.indexOf(optionVal) !== -1;
}
}
return result;
}, [disabled, disabledKey, options, optionValue]);
var isSelected = (0, _react.useCallback)(function (index) {
var result;
if (selected) {
// deprecated in favor of value
result = selected.indexOf(index) !== -1;
} else {
var optionVal = optionValue(index);
if (Array.isArray(value)) {
if (value.length === 0) {
result = false;
} else if (typeof value[0] !== 'object') {
result = value.indexOf(optionVal) !== -1;
} else if (valueKey) {
result = value.some(function (valueItem) {
var valueValue = typeof valueKey === 'function' ? valueKey(valueItem) : valueItem[valueKey];
return valueValue === optionVal;
});
}
} else if (valueKey && typeof value === 'object') {
var valueValue = typeof valueKey === 'function' ? valueKey(value) : value[valueKey];
result = valueValue === optionVal;
} else {
result = value === optionVal;
}
}
return result;
}, [optionValue, selected, value, valueKey]);
var selectOption = (0, _react.useCallback)(function (index) {
return function (event) {
if (onChange) {
var nextValue;
var nextSelected;
if (multiple) {
var nextOptionIndexesInValue = optionIndexesInValue.slice(0);
var allOptionsIndex = allOptions.indexOf(options[index]);
var valueIndex = optionIndexesInValue.indexOf(allOptionsIndex);
if (valueIndex === -1) {
nextOptionIndexesInValue.push(allOptionsIndex);
} else {
nextOptionIndexesInValue.splice(valueIndex, 1);
}
nextValue = nextOptionIndexesInValue.map(function (i) {
return valueKey && valueKey.reduce ? (0, _utils2.applyKey)(allOptions[i], valueKey) : allOptions[i];
});
nextSelected = nextOptionIndexesInValue;
} else {
nextValue = valueKey && valueKey.reduce ? (0, _utils2.applyKey)(options[index], valueKey) : options[index];
nextSelected = index;
}
onChange(event, {
option: options[index],
value: nextValue,
selected: nextSelected
});
}
};
}, [multiple, onChange, optionIndexesInValue, options, allOptions, valueKey]);
var onClear = (0, _react.useCallback)(function (event) {
onChange(event, {
option: undefined,
value: '',
selected: ''
});
}, [onChange]);
var onNextOption = (0, _react.useCallback)(function (event) {
event.preventDefault();
var nextActiveIndex = activeIndex + 1;
while (nextActiveIndex < options.length && isDisabled(nextActiveIndex)) {
nextActiveIndex += 1;
}
if (nextActiveIndex !== options.length) {
setActiveIndex(nextActiveIndex);
setKeyboardNavigation(true);
}
}, [activeIndex, isDisabled, options]);
var onPreviousOption = (0, _react.useCallback)(function (event) {
event.preventDefault();
var nextActiveIndex = activeIndex - 1;
while (nextActiveIndex >= 0 && isDisabled(nextActiveIndex)) {
nextActiveIndex -= 1;
}
if (nextActiveIndex >= 0) {
setActiveIndex(nextActiveIndex);
setKeyboardNavigation(true);
}
}, [activeIndex, isDisabled]);
var onActiveOption = (0, _react.useCallback)(function (index) {
return function () {
if (!keyboardNavigation) setActiveIndex(index);
};
}, [keyboardNavigation]);
var onSelectOption = (0, _react.useCallback)(function (event) {
if (activeIndex >= 0 && !focus) {
event.preventDefault(); // prevent submitting forms
selectOption(activeIndex)(event);
}
}, [activeIndex, selectOption, focus]);
var customSearchInput = theme.select.searchInput;
var SelectTextInput = customSearchInput || _TextInput.TextInput;
var selectOptionsStyle = theme.select.options ? _extends({}, theme.select.options.box, theme.select.options.container) : {};
return /*#__PURE__*/_react["default"].createElement(_Keyboard.Keyboard, {
onEnter: onSelectOption,
onUp: onPreviousOption,
onDown: onNextOption,
onKeyDown: onKeyDown
}, /*#__PURE__*/_react["default"].createElement(_StyledSelect.StyledContainer, {
ref: ref,
as: _Box.Box,
id: id ? id + "__select-drop" : undefined,
dropHeight: dropHeight
}, onSearch && /*#__PURE__*/_react["default"].createElement(_Box.Box, {
pad: !customSearchInput ? 'xsmall' : undefined,
flex: false
}, /*#__PURE__*/_react["default"].createElement(SelectTextInput, {
focusIndicator: !customSearchInput,
size: "small",
ref: searchRef,
type: "search",
value: search || '',
placeholder: searchPlaceholder,
onChange: function onChange(event) {
var nextSearch = event.target.value;
setSearch(nextSearch);
setActiveIndex(-1);
onSearch(nextSearch);
}
})), clear && clear.position !== 'bottom' && value && /*#__PURE__*/_react["default"].createElement(ClearButton, {
clear: clear,
name: name,
onClear: onClear,
theme: theme,
setFocus: setFocus
}), /*#__PURE__*/_react["default"].createElement(OptionsBox, {
role: "menubar",
tabIndex: "-1",
ref: optionsRef
}, options.length > 0 ? /*#__PURE__*/_react["default"].createElement(_InfiniteScroll.InfiniteScroll, {
items: options,
step: theme.select.step,
onMore: onMore,
replace: replace,
show: activeIndex !== -1 ? activeIndex : undefined
}, function (option, index, optionRef) {
var optionDisabled = isDisabled(index);
var optionSelected = isSelected(index);
var optionActive = activeIndex === index; // Determine whether the label is done as a child or
// as an option Button kind property.
var child;
if (children) child = children(option, index, options, {
active: optionActive,
disabled: optionDisabled,
selected: optionSelected
});else if (theme.select.options) child = /*#__PURE__*/_react["default"].createElement(OptionBox, _extends({}, selectOptionsStyle, {
selected: optionSelected
}), /*#__PURE__*/_react["default"].createElement(_Text.Text, theme.select.options.text, optionLabel(index))); // if we have a child, turn on plain, and hoverIndicator
return /*#__PURE__*/_react["default"].createElement(SelectOption // eslint-disable-next-line react/no-array-index-key
, {
key: index,
ref: optionRef,
tabIndex: "-1",
role: "menuitem",
plain: !child ? undefined : true,
align: "start",
kind: !child ? 'option' : undefined,
hoverIndicator: !child ? undefined : 'background',
label: !child ? optionLabel(index) : undefined,
disabled: optionDisabled || undefined,
active: optionActive,
selected: optionSelected,
option: option,
onMouseOver: !optionDisabled ? onActiveOption(index) : undefined,
onClick: !optionDisabled ? selectOption(index) : undefined
}, child);
}) : /*#__PURE__*/_react["default"].createElement(SelectOption, {
key: "search_empty",
tabIndex: "-1",
role: "menuitem",
hoverIndicator: "background",
disabled: true,
option: emptySearchMessage
}, /*#__PURE__*/_react["default"].createElement(OptionBox, selectOptionsStyle, /*#__PURE__*/_react["default"].createElement(_Text.Text, theme.select.container.text, emptySearchMessage)))), clear && clear.position === 'bottom' && value && /*#__PURE__*/_react["default"].createElement(ClearButton, {
clear: clear,
name: name,
onClear: onClear,
theme: theme,
setFocus: setFocus
})));
});
exports.SelectContainer = SelectContainer;