UNPKG

react-widgets

Version:

An à la carte set of polished, extensible, and accessible inputs built for React

198 lines (166 loc) 7.83 kB
"use strict"; exports.__esModule = true; exports.useHandleSelect = useHandleSelect; exports.default = exports.useScrollFocusedIntoView = void 0; var _classnames = _interopRequireDefault(require("classnames")); var _propTypes = _interopRequireDefault(require("prop-types")); var _react = _interopRequireWildcard(require("react")); var _ListOption = _interopRequireDefault(require("./ListOption")); var _ListOptionGroup = _interopRequireDefault(require("./ListOptionGroup")); var _messages = require("./messages"); var CustomPropTypes = _interopRequireWildcard(require("./PropTypes")); var _ = require("./_"); var _WidgetHelpers = require("./WidgetHelpers"); var _useMutationObserver = _interopRequireDefault(require("@restart/hooks/useMutationObserver")); var _useCallbackRef = _interopRequireDefault(require("@restart/hooks/useCallbackRef")); var _useMergedRefs = _interopRequireDefault(require("@restart/hooks/useMergedRefs")); const _excluded = ["multiple", "data", "value", "onChange", "accessors", "className", "messages", "disabled", "renderItem", "renderGroup", "searchTerm", "groupBy", "elementRef", "optionComponent", "renderList"]; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && 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 _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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; } const whitelist = ['style', 'className', 'role', 'id', 'autocomplete', 'size', 'tabIndex', 'maxLength', 'name']; const whitelistRegex = [/^aria-/, /^data-/, /^on[A-Z]\w+/]; function pickElementProps(props) { const result = {}; Object.keys(props).forEach(key => { if (whitelist.indexOf(key) !== -1 || whitelistRegex.some(r => !!key.match(r))) result[key] = props[key]; }); return result; } const propTypes = { data: _propTypes.default.array, dataKey: CustomPropTypes.accessor, textField: CustomPropTypes.accessor, onSelect: _propTypes.default.func, onMove: _propTypes.default.func, onHoverOption: _propTypes.default.func, optionComponent: _propTypes.default.elementType, renderItem: _propTypes.default.func, renderGroup: _propTypes.default.func, focusedItem: _propTypes.default.any, selectedItem: _propTypes.default.any, searchTerm: _propTypes.default.string, disabled: CustomPropTypes.disabled.acceptsArray, messages: _propTypes.default.shape({ emptyList: _propTypes.default.func.isRequired }) }; const useScrollFocusedIntoView = (element, observeChanges = false) => { const scrollIntoView = (0, _react.useCallback)(() => { if (!element) return; let selectedItem = element.querySelector('[data-rw-focused]'); if (selectedItem && selectedItem.scrollIntoView) { selectedItem.scrollIntoView({ block: 'nearest', inline: 'nearest' }); } }, [element]); (0, _useMutationObserver.default)(observeChanges ? element : null, { subtree: true, attributes: true, attributeFilter: ['data-rw-focused'] }, scrollIntoView); return scrollIntoView; }; exports.useScrollFocusedIntoView = useScrollFocusedIntoView; function useHandleSelect(multiple, dataItems, onChange) { return (dataItem, event) => { if (multiple === false) { onChange(dataItem, { dataItem, lastValue: dataItems[0], originalEvent: event }); return; } const checked = dataItems.includes(dataItem); onChange(checked ? dataItems.filter(d => d !== dataItem) : [...dataItems, dataItem], { dataItem, lastValue: dataItems, action: checked ? 'remove' : 'insert', originalEvent: event }); }; } const List = /*#__PURE__*/_react.default.forwardRef(function List(_ref, outerRef) { var _elementProps$role; let { multiple = false, data = [], value, onChange, accessors, className, messages, disabled, renderItem, renderGroup, searchTerm, groupBy, elementRef, optionComponent: Option = _ListOption.default, renderList } = _ref, props = _objectWithoutPropertiesLoose(_ref, _excluded); const id = (0, _WidgetHelpers.useInstanceId)(); const dataItems = (0, _.makeArray)(value, multiple); const groupedData = (0, _react.useMemo)(() => groupBy ? (0, _.groupBySortedKeys)(groupBy, data) : undefined, [data, groupBy]); const [element, ref] = (0, _useCallbackRef.default)(); const disabledItems = (0, _.toItemArray)(disabled); const { emptyList } = (0, _messages.useMessagesWithDefaults)(messages); const divRef = (0, _useMergedRefs.default)(ref, elementRef); const handleSelect = useHandleSelect(multiple, dataItems, onChange); const scrollIntoView = useScrollFocusedIntoView(element, true); let elementProps = pickElementProps(props); (0, _react.useImperativeHandle)(outerRef, () => ({ scrollIntoView }), [scrollIntoView]); function renderOption(item, idx) { const textValue = accessors.text(item); const itemIsDisabled = disabledItems.includes(item); const itemIsSelected = dataItems.includes(item); return /*#__PURE__*/_react.default.createElement(Option, { dataItem: item, key: `item_${idx}`, searchTerm: searchTerm, onSelect: handleSelect, disabled: itemIsDisabled, selected: itemIsSelected }, renderItem ? renderItem({ item, searchTerm, index: idx, text: textValue, // TODO: probably remove value: accessors.value(item), disabled: itemIsDisabled }) : textValue); } const items = groupedData ? groupedData.map(([group, items], idx) => /*#__PURE__*/_react.default.createElement("div", { role: "group", key: `group_${idx}` }, /*#__PURE__*/_react.default.createElement(_ListOptionGroup.default, null, renderGroup ? renderGroup({ group }) : group), items.map(renderOption))) : data.map(renderOption); const rootProps = Object.assign({ id, tabIndex: 0, ref: divRef }, elementProps, { 'aria-multiselectable': !!multiple, className: (0, _classnames.default)(className, 'rw-list'), role: (_elementProps$role = elementProps.role) != null ? _elementProps$role : 'listbox', children: _react.default.Children.count(items) ? items : /*#__PURE__*/_react.default.createElement("div", { className: "rw-list-empty" }, emptyList()) }); return renderList ? renderList(rootProps) : /*#__PURE__*/_react.default.createElement("div", rootProps); }); List.displayName = 'List'; List.propTypes = propTypes; var _default = List; exports.default = _default;