UNPKG

@onesy/ui-react

Version:
254 lines (253 loc) 15.8 kB
"use strict"; var __rest = (this && this.__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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MENUS = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const utils_1 = require("@onesy/utils"); const style_react_1 = require("@onesy/style-react"); const List_1 = __importDefault(require("../List")); const Tooltip_1 = __importDefault(require("../Tooltip")); const ClickListener_1 = __importDefault(require("../ClickListener")); const utils_2 = require("../utils"); const useStyle = (0, style_react_1.style)(theme => ({ root: {} }), { name: 'onesy-Menu' }); exports.MENUS = { open: [], priority: (value) => exports.MENUS.open[exports.MENUS.open.length - 1] === value, add: (value) => { const index = exports.MENUS.open.findIndex(item => item === value); if (index === -1) exports.MENUS.open.push(value); exports.MENUS.open = exports.MENUS.open.filter(Boolean); }, remove: (value) => { const index = exports.MENUS.open.findIndex(item => item === value); if (index > -1) exports.MENUS.open.splice(index, 1); exports.MENUS.open = exports.MENUS.open.filter(Boolean); } }; const Menu = react_1.default.forwardRef((props_, ref) => { const theme = (0, style_react_1.useOnesyTheme)(); const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.onesyMenu) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]); const List = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.List) || List_1.default; }, [theme]); const Tooltip = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Tooltip) || Tooltip_1.default; }, [theme]); const ClickListener = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.ClickListener) || ClickListener_1.default; }, [theme]); const { open: open_, openDefault, name, label, menuItems, arrow, anchor, anchorElement, autoSelect, autoSelectOnBlur, resetKeyboardNavigation = false, closeOnClickAway = true, include = [], includeParentQueries = [], includeQueries = [], ignoreNonExisting, onSelect, onOpen: onOpen_, onClose: onClose_, ListProps, ModalProps, WrapperProps: WrapperProps_, ClickListenerProps, className, children } = props, other = __rest(props, ["open", "openDefault", "name", "label", "menuItems", "arrow", "anchor", "anchorElement", "autoSelect", "autoSelectOnBlur", "resetKeyboardNavigation", "closeOnClickAway", "include", "includeParentQueries", "includeQueries", "ignoreNonExisting", "onSelect", "onOpen", "onClose", "ListProps", "ModalProps", "WrapperProps", "ClickListenerProps", "className", "children"]); const { classes } = useStyle(); const id = react_1.default.useId(); const [open, setOpen] = react_1.default.useState(openDefault !== undefined ? openDefault : open_); const [preselected, setPreselected] = react_1.default.useState(); const refs = { root: react_1.default.useRef(undefined), id: react_1.default.useRef(undefined), main: react_1.default.useRef(undefined), props: react_1.default.useRef(undefined), preselected: react_1.default.useRef(undefined), include: react_1.default.useRef([]), menuClassName: react_1.default.useRef(`a-${new Date().getTime()}`) }; refs.id.current = id; refs.preselected.current = preselected; refs.props.current = props; react_1.default.useEffect(() => { var _a; if (open) { exports.MENUS.add(refs.id.current); if (autoSelect) { const values = react_1.default.Children.toArray(refs.props.current.children).map((item, index) => { var _a, _b, _c; return ((((_a = item.props) === null || _a === void 0 ? void 0 : _a.button) || ((_b = item.props) === null || _b === void 0 ? void 0 : _b.href)) && !((_c = item.props) === null || _c === void 0 ? void 0 : _c.disabled)) ? index : undefined; }).filter(item => (0, utils_1.is)('number', item)); setPreselected(values[0]); } } const onKeyDown = (event) => { if (refs.props.current.open && exports.MENUS.priority(refs.id.current)) { const values = react_1.default.Children.toArray(refs.props.current.children).map((item, index) => { var _a, _b, _c; return ((((_a = item.props) === null || _a === void 0 ? void 0 : _a.button) || ((_b = item.props) === null || _b === void 0 ? void 0 : _b.href)) && !((_c = item.props) === null || _c === void 0 ? void 0 : _c.disabled)) ? index : undefined; }).filter(item => (0, utils_1.is)('number', item)); switch (event.key) { case 'ArrowUp': event.preventDefault(); return setPreselected(() => { let value = refs.preselected.current; const index = values.findIndex(item_ => item_ === value); if (index === -1) value = values[values.length - 1]; else if (index > 0) value = values[index - 1]; else if (refs.props.current.resetKeyboardNavigation) value = values[values.length - 1]; return value; }); case 'ArrowDown': event.preventDefault(); return setPreselected(() => { let value = refs.preselected.current; const index = values.findIndex(item_ => item_ === value); if (index === -1) value = values[0]; else if (index < values.length - 1) value = values[index + 1]; else if (refs.props.current.resetKeyboardNavigation) value = values[0]; return value; }); case 'Escape': event.preventDefault(); return onClose(); case 'Home': event.preventDefault(); return setPreselected(values[0]); case 'End': event.preventDefault(); return setPreselected(values[values.length - 1]); default: break; } } }; const rootDocument = (0, utils_1.isEnvironment)('browser') ? (((_a = refs.root.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || window.document) : undefined; rootDocument.addEventListener('keydown', onKeyDown); return () => { exports.MENUS.remove(refs.id.current); rootDocument.removeEventListener('keydown', onKeyDown); }; }, []); react_1.default.useEffect(() => { if (open) { if (autoSelect) { const values = react_1.default.Children.toArray(refs.props.current.children).map((item, index) => { var _a, _b, _c; return ((((_a = item.props) === null || _a === void 0 ? void 0 : _a.button) || ((_b = item.props) === null || _b === void 0 ? void 0 : _b.href)) && !((_c = item.props) === null || _c === void 0 ? void 0 : _c.disabled)) ? index : undefined; }).filter(item => (0, utils_1.is)('number', item)); setPreselected(values[0]); } } setTimeout(() => { if (include && refs.root.current) include.push(refs.root.current); }); return () => { if (include) { const index = include.findIndex(item => item === refs.root.current); if (index > -1) include.splice(index, 1); } }; }, [open]); react_1.default.useEffect(() => { if (open !== open_) setOpen(open_); }, [open_]); const onMouseLeave = react_1.default.useCallback(() => { setPreselected(''); }, []); const onOpen = () => { if (open_ === undefined) setOpen(true); if ((0, utils_1.is)('function', onOpen_)) onOpen_(); }; const onClose = () => { var _a, _b, _c; if (open_ === undefined) setOpen(false); if (refs.props.current.autoSelectOnBlur) { const item = react_1.default.Children.toArray(refs.props.current.children)[refs.preselected.current]; if (item && (0, utils_1.is)('function', refs.props.current.onSelect)) refs.props.current.onSelect(((_a = item.props) === null || _a === void 0 ? void 0 : _a.value) !== undefined ? (_b = item.props) === null || _b === void 0 ? void 0 : _b.value : (_c = item.props) === null || _c === void 0 ? void 0 : _c.primary); } setPreselected(''); if ((0, utils_1.is)('function', onClose_)) onClose_(); }; const Wrapper = closeOnClickAway ? ClickListener : react_1.default.Fragment; let WrapperProps = Object.assign({}, WrapperProps_); if (closeOnClickAway) { WrapperProps.onClickOutside = onClose; WrapperProps.include = [refs.main.current, ...(refs.include.current || []), ...include].filter(Boolean); WrapperProps.includeParentQueries = [...includeParentQueries, `.${refs.menuClassName.current}`]; WrapperProps.includeQueries = [...includeQueries, `.${refs.menuClassName.current}`]; WrapperProps.ignoreNonExisting = ignoreNonExisting; WrapperProps = Object.assign(Object.assign({}, WrapperProps), ClickListenerProps); } if (open) exports.MENUS.add(id); else exports.MENUS.remove(id); const methodItem = (item, index) => { var _a, _b, _c, _d, _e; return (Object.assign({ key: item.key || index, role: 'menuitem', menuId: id, MenuProps: Object.assign(Object.assign({}, (_a = item.props) === null || _a === void 0 ? void 0 : _a.MenuProps), { className: (0, style_react_1.classNames)([ (_c = (_b = item.props) === null || _b === void 0 ? void 0 : _b.MenuProps) === null || _c === void 0 ? void 0 : _c.className, refs.menuClassName.current ]) }), onClose }, (((((_d = item.props) === null || _d === void 0 ? void 0 : _d.button) || ((_e = item.props) === null || _e === void 0 ? void 0 : _e.href)) && !item.props.disabled) ? { onMouseEnter: () => { setPreselected(index); }, onMouseLeave: () => { setPreselected(''); }, preselected: index === preselected, onClick: (event) => { var _a, _b, _c; if ((0, utils_1.is)('function', (_a = item.props) === null || _a === void 0 ? void 0 : _a.onClick)) (_b = item.props) === null || _b === void 0 ? void 0 : _b.onClick(event); if ((_c = item.props) === null || _c === void 0 ? void 0 : _c.menuCloseOnClick) onClose(); }, onKeyDown: (event) => { var _a, _b, _c, _d, _e; if (event.target === event.currentTarget && event.key === 'Enter') { if ((0, utils_1.is)('function', (_a = item.props) === null || _a === void 0 ? void 0 : _a.onClick)) (_b = item.props) === null || _b === void 0 ? void 0 : _b.onClick(); if ((0, utils_1.is)('function', (_c = item.props) === null || _c === void 0 ? void 0 : _c.onKeyDown)) (_d = item.props) === null || _d === void 0 ? void 0 : _d.onKeyDown(event); if ((_e = item.props) === null || _e === void 0 ? void 0 : _e.menuCloseOnClick) onClose(); } } } : {}))); }; const nameValue = name || label; return ((0, jsx_runtime_1.jsx)(Wrapper, Object.assign({}, WrapperProps, { children: (0, jsx_runtime_1.jsx)(Tooltip, Object.assign({ ref: item => { if (ref) { if ((0, utils_1.is)('function', ref)) ref(item); else ref.current = item; } refs.root.current = item; }, open: open, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('Menu', theme) && [ 'onesy-Menu-root', open && `onesy-Menu-open` ], className, classes.root ]), onMouseLeave: onMouseLeave, anchor: anchor, anchorElement: anchorElement, label: nameValue ? (0, utils_1.is)('function', nameValue) ? nameValue(methodItem) : nameValue : (menuItems && ((0, jsx_runtime_1.jsx)(List, Object.assign({ menu: true, menuOpen: open, include: refs.include.current, role: 'menu' }, ListProps, { className: (0, style_react_1.classNames)([ 'onesy-Menu-list', ListProps === null || ListProps === void 0 ? void 0 : ListProps.className, classes.list ]) }, { children: (ListProps === null || ListProps === void 0 ? void 0 : ListProps.noChildrenTransform) ? menuItems : react_1.default.Children.toArray(menuItems).map((item, index) => (react_1.default.cloneElement(item, methodItem(item, index)))) })))), click: true, arrow: arrow, hover: false, focus: false, longPress: false, noMargin: !arrow, onOpen: onOpen, onClose: onClose, ModalProps: Object.assign({ background: true, backgroundInvisible: true, freezeScroll: false }, ModalProps) }, other, { children: children && react_1.default.cloneElement(children, { ref: item => { if (children.ref) { if ((0, utils_1.is)('function', children.ref)) children.ref(item); else children.ref.current = item; } refs.main.current = item; } }) })) }))); }); Menu.displayName = 'onesy-Menu'; exports.default = Menu;