UNPKG

react-elegant-ui

Version:

Elegant UI components, made by BEM best practices for react

267 lines (266 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _exportNames = { Select: true }; exports.Select = void 0; var _react = _interopRequireWildcard(require("react")); var _di = require("../../lib/di"); var _makeChain = require("../../lib/makeChain"); var _keyboard = require("../../lib/keyboard"); var _merge = require("../../lib/merge"); var _getDisplayName = require("../../lib/getDisplayName"); var _useRefMix = require("../../hooks/useRefMix"); var _useToggleable = require("../../hooks/behavior/useToggleable"); var _Select = require("./Select"); Object.keys(_Select).forEach(function (key) { if (key === "default" || key === "__esModule") return; if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; if (key in exports && exports[key] === _Select[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function () { return _Select[key]; } }); }); var _SelectTrigger = require("./Trigger/Select-Trigger"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } var __assign = void 0 && (void 0).__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = void 0 && (void 0).__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __read = void 0 && (void 0).__read || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; /** * Implementation of select for desktop * * It implement as HOC to allow use any `BaseSelect` implementation */ var withDesktopSelect = function (BaseSelect) { return function (_a) { var nocollapse = _a.nocollapse, addonBeforeMenu = _a.addonBeforeMenu, addonAfterMenu = _a.addonAfterMenu, props = __rest(_a, ["nocollapse", "addonBeforeMenu", "addonAfterMenu"]); var options = props.options, value = props.value, opened = props.opened, disabled = props.disabled, addonAfter = props.addonAfter, innerRef = props.innerRef, triggerRefExternal = props.triggerRef, setValue = props.setValue, setOpenedExternal = props.setOpened; var isMultiple = Array.isArray(value); var pickKeys = (0, _react.useMemo)(function () { return [_keyboard.Keys.ENTER, _keyboard.Keys.SPACE]; }, []); // Nodes refs var selectRef = (0, _react.useRef)(null); var triggerRef = (0, _react.useRef)(null); // Opened state management var _b = (0, _useToggleable.useToggleable)({ state: opened, setState: setOpenedExternal }), toggleOpened = _b.toggle, setOpened = _b.setState; // Value setter var setValueProxy = (0, _react.useCallback)(function (value) { if (setValue !== undefined) { setValue(value); } }, [setValue]); // Enable ignore keyboard press events for trigger while listbox is open var pressLock = (0, _react.useRef)(false); // It should be disabled only by keyup or blur event on press key var disablePressLock = (0, _react.useCallback)(function (force) { // Never unlock for multiple type. User must close it by escape if (force || !isMultiple) { pressLock.current = false; } }, [isMultiple]); var updatePressLock = (0, _react.useCallback)(function () { if (opened) { pressLock.current = true; } }, [opened]); (0, _react.useEffect)(updatePressLock, [updatePressLock]); // Focus to trigger by close var focusToTrigger = (0, _react.useRef)(false); (0, _react.useEffect)(function () { if (!opened && focusToTrigger.current && triggerRef.current !== null) { triggerRef.current.focus(); } focusToTrigger.current = false; }, [opened]); // Close by pick when type is not multiple var onPick = (0, _react.useCallback)(function () { if (!isMultiple && !nocollapse) { focusToTrigger.current = true; setOpened(false); } }, [isMultiple, nocollapse, setOpened]); // Handle close event var onCloseHandler = (0, _react.useCallback)(function (_, source) { focusToTrigger.current = source === 'esc'; disablePressLock(true); setOpened(false); }, [disablePressLock, setOpened]); // Set handler for close by unfocus // NOTE: actually by focus on other elements, but maybe should handle blur? (0, _react.useEffect)(function () { if (selectRef.current === null || !opened || disabled) return; var hostNode = selectRef.current; var rootNode = hostNode.getRootNode(); var shadowHost = rootNode instanceof ShadowRoot ? rootNode.host : null; // Close when focus outside root of component var closeByFocusOutside = function (evt) { // Skip empty target if (!(evt.target instanceof Node)) return; // Skip global event while target is ShadowRoot // It will handle in next call and target will contain real node, instead ShadowRoot host if (shadowHost !== null && evt.target === shadowHost) return; if (!hostNode.contains(evt.target)) { setOpened(false); } }; // Add global handler document.addEventListener('focusin', closeByFocusOutside); // Add handler for ShadowRoot for work even with `mode: close` if (shadowHost !== null) { rootNode.addEventListener('focusin', closeByFocusOutside); } return function () { document.removeEventListener('focusin', closeByFocusOutside); if (shadowHost !== null) { rootNode.addEventListener('focusin', closeByFocusOutside); } }; }, [opened, disabled, setOpened, disablePressLock]); // Toggle by press var onPressHandler = (0, _react.useCallback)(function (evt) { // Skip synthetic press event (by enter only) due to focus after press if (evt.pointerType === 'virtual') return; toggleOpened(); }, [toggleOpened]); // Toggle by keyboard var onKeyDownHandler = (0, _react.useCallback)(function (evt) { if (disabled) return; if ((0, _keyboard.isKeyCode)(evt.key, [_keyboard.Keys.UP, _keyboard.Keys.DOWN])) { // Prevent scroll of parent evt.preventDefault(); // Toggle by release key if (evt.type === 'keyup') { setOpened(true); } } else if ((0, _keyboard.isKeyCode)(evt.nativeEvent.code, pickKeys)) { // Prevent keyboard events while key is down and until keyup or blur events if (evt.type === 'keydown' && pressLock.current) { evt.stopPropagation(); } else if (evt.type === 'keyup') { disablePressLock(); } } }, [disablePressLock, disabled, pickKeys, setOpened]); var _c = __read((0, _react.useState)(), 2), cursorIdRef = _c[0], cursorIdRefSet = _c[1]; var cursorNodeId = opened ? cursorIdRef !== null && cursorIdRef !== void 0 ? cursorIdRef : undefined : undefined; // Inject props to `Trigger` var SelectTriggerCtxObj = (0, _react.useContext)(_SelectTrigger.SelectTriggerContext); var onKeyDownCapture = SelectTriggerCtxObj.onKeyDownCapture, onKeyUpCapture = SelectTriggerCtxObj.onKeyUpCapture, onBlurCapture = SelectTriggerCtxObj.onBlurCapture, onFocusCapture = SelectTriggerCtxObj.onFocusCapture; var SelectTriggerContextMix = (0, _react.useMemo)(function () { return (0, _merge.mergeProps)(SelectTriggerCtxObj, { 'aria-activedescendant': cursorNodeId, onPress: onPressHandler, onKeyDownCapture: (0, _makeChain.makeChain)(onKeyDownHandler, onKeyDownCapture), onKeyUpCapture: (0, _makeChain.makeChain)(onKeyDownHandler, onKeyUpCapture), // Force disable pressLock while blur onBlurCapture: (0, _makeChain.makeChain)(function () { disablePressLock(true); }, onBlurCapture), // Restore pressLock after focus on inner elements onFocusCapture: (0, _makeChain.makeChain)(updatePressLock, onFocusCapture) }); }, [SelectTriggerCtxObj, cursorNodeId, onPressHandler, onKeyDownHandler, onKeyDownCapture, onKeyUpCapture, onBlurCapture, updatePressLock, onFocusCapture, disablePressLock]); // Ref mixes var innerRefMix = (0, _useRefMix.useRefMix)(selectRef, innerRef); var triggerRefMix = (0, _useRefMix.useRefMix)(triggerRef, triggerRefExternal); // Get deps components var _d = (0, _di.useComponentRegistry)((0, _Select.cnSelect)()), List = _d.List, Popup = _d.Popup; // TODO: check acessability, maybe need bind menu to button by `id` return /*#__PURE__*/_react.default.createElement(_SelectTrigger.SelectTriggerContext.Provider, { value: SelectTriggerContextMix }, /*#__PURE__*/_react.default.createElement(BaseSelect, __assign({}, props, { options: options, value: value, opened: opened, disabled: disabled, innerRef: innerRefMix, triggerRef: triggerRefMix, addonAfter: /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(Popup, { target: "anchor", anchor: selectRef, visible: opened, onClose: onCloseHandler }, addonBeforeMenu, /*#__PURE__*/_react.default.createElement(List, { visible: opened, type: isMultiple ? 'checkbox' : 'radio', disabled: disabled, isFocused: opened, items: options, value: value, setValue: setValueProxy, pickKeys: pickKeys, onPick: onPick, cursorIdRef: cursorIdRefSet }), addonAfterMenu), addonAfter) }))); }; }; var Select = exports.Select = withDesktopSelect(_Select.Select); Select.displayName = (0, _getDisplayName.getDisplayName)(_Select.Select);