@onesy/ui-react
Version:
UI for React
254 lines (253 loc) • 15.8 kB
JavaScript
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;
;