UNPKG

@clayui/drop-down

Version:
364 lines (363 loc) 11.9 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Items_exports = {}; __export(Items_exports, { ClayDropDownContext: () => ClayDropDownContext, DropDownItems: () => DropDownItems, findNested: () => findNested }); module.exports = __toCommonJS(Items_exports); var import_form = require("@clayui/form"); var import_icon = __toESM(require("@clayui/icon")); var import_shared = require("@clayui/shared"); var import_classnames = __toESM(require("classnames")); var import_react = __toESM(require("react")); var import_warning = __toESM(require("warning")); var import_Divider = __toESM(require("./Divider")); var import_DropDown = __toESM(require("./DropDown")); var import_DropDownContext = require("./DropDownContext"); var import_FocusMenu = require("./FocusMenu"); var import_Group = __toESM(require("./Group")); function findNested(items, key) { return items.some((item) => { if (item[key]) { return true; } if (item.items && item["type"] !== "contextual") { return findNested(item.items, key); } return false; }); } function Checkbox({ checked = false, onChange = () => { }, title, ...otherProps }) { const [value, setValue] = (0, import_react.useState)(checked); const { tabFocus } = (0, import_react.useContext)(import_DropDownContext.DropDownContext); return /* @__PURE__ */ import_react.default.createElement(import_DropDown.default.Section, { role: "none" }, /* @__PURE__ */ import_react.default.createElement( import_form.ClayCheckbox, { ...otherProps, checked: value, label: title, onChange: () => { setValue((val) => !val); onChange(!value); }, tabIndex: !tabFocus ? -1 : void 0 } )); } const ClayDropDownContext = import_react.default.createContext({ close: () => { } }); function Item({ child, label, onClick, spritemap, title, ...props }) { const { back, close, messages, onForward } = (0, import_react.useContext)(ClayDropDownContext); title = label || title; return /* @__PURE__ */ import_react.default.createElement( import_DropDown.default.Item, { ...props, onClick: (event) => { if (onForward && child && title) { event.preventDefault(); onForward(title, child); return; } if (onClick) { onClick(event); } close(); }, onKeyDown: (event) => { if (back && event.key === import_shared.Keys.Left) { back(); } }, spritemap }, title, child && /* @__PURE__ */ import_react.default.createElement( "span", { "aria-label": `${messages["goTo"]} ${title}`, className: "dropdown-item-indicator-end", title: `${messages["goTo"]} ${title}` }, /* @__PURE__ */ import_react.default.createElement(import_icon.default, { spritemap, symbol: "angle-right" }) ) ); } function Group({ items, label, spritemap, title }) { title = label || title; (0, import_warning.default)( typeof items !== "undefined", `ClayDropDownWithItems -> The '${title}' group contains no items to render.` ); if (typeof items === "undefined") { return null; } return /* @__PURE__ */ import_react.default.createElement(import_Group.default, { header: title }, items && /* @__PURE__ */ import_react.default.createElement(Items, { items, spritemap })); } const BOTTOM_OFFSET = [0, 1]; const LEFT_OFFSET = [-1, 6]; const RIGHT_OFFSET = [1, -6]; const TOP_OFFSET = [0, -1]; const OFFSET_MAP = { bctc: TOP_OFFSET, blbr: RIGHT_OFFSET, bltl: TOP_OFFSET, brbl: LEFT_OFFSET, brtr: TOP_OFFSET, clcr: RIGHT_OFFSET, crcl: LEFT_OFFSET, tcbc: BOTTOM_OFFSET, tlbl: BOTTOM_OFFSET, tltr: RIGHT_OFFSET, trbr: BOTTOM_OFFSET, trtl: LEFT_OFFSET }; function offsetFn(points) { return OFFSET_MAP[points.join("")]; } function Contextual({ items, label, spritemap, title, ...otherProps }) { const [visible, setVisible] = (0, import_react.useState)(false); const { close } = (0, import_react.useContext)(ClayDropDownContext); const triggerElementRef = (0, import_react.useRef)(null); const menuElementRef = (0, import_react.useRef)(null); const timeoutHandleRef = (0, import_react.useRef)(null); const keyboardRef = (0, import_react.useRef)(false); const hasRightSymbols = import_react.default.useMemo( () => items && findNested(items, "symbolRight"), [items] ); const hasLeftSymbols = import_react.default.useMemo( () => items && findNested(items, "symbolLeft"), [items] ); const { navigationProps } = (0, import_shared.useNavigation)({ activation: "manual", containerRef: menuElementRef, loop: true, orientation: "vertical", typeahead: true, visible }); const setThrottleVisible = (0, import_react.useCallback)( // eslint-disable-next-line react-compiler/react-compiler (0, import_shared.throttle)((value) => setVisible(value), 100), [] ); title = label || title; return /* @__PURE__ */ import_react.default.createElement( import_DropDown.default.Item, { ...otherProps, "aria-expanded": visible, "aria-haspopup": Boolean(items), className: (0, import_classnames.default)({ active: visible }), onClick: (event) => { keyboardRef.current = false; if (event.currentTarget === event.target) { setVisible(true); clearTimeout(timeoutHandleRef.current); timeoutHandleRef.current = null; } }, onKeyDown: (event) => { switch (event.key) { case import_shared.Keys.Enter: case import_shared.Keys.Right: setVisible(true); keyboardRef.current = true; break; default: break; } }, onMouseEnter: () => { if (!visible) { keyboardRef.current = false; timeoutHandleRef.current = setTimeout( () => setThrottleVisible(true), 400 ); } }, onMouseLeave: () => { keyboardRef.current = false; setThrottleVisible(false); clearTimeout(timeoutHandleRef.current); timeoutHandleRef.current = null; }, ref: triggerElementRef, spritemap, symbolRight: "angle-right" }, title, items && /* @__PURE__ */ import_react.default.createElement( import_DropDown.default.Menu, { active: visible, alignElementRef: triggerElementRef, alignmentPosition: 8, hasLeftSymbols, hasRightSymbols, offsetFn, onActiveChange: setVisible, onKeyDown: navigationProps.onKeyDown, ref: menuElementRef }, visible && /* @__PURE__ */ import_react.default.createElement(import_shared.MouseSafeArea, { parentRef: menuElementRef }), /* @__PURE__ */ import_react.default.createElement( ClayDropDownContext.Provider, { value: { back: () => { (triggerElementRef.current?.children[0]).focus(); setVisible(false); }, close: () => { setVisible(false); close(); } } }, /* @__PURE__ */ import_react.default.createElement( import_FocusMenu.FocusMenu, { condition: visible, onRender: () => { if (menuElementRef.current && keyboardRef.current) { const first = menuElementRef.current.querySelector( import_shared.FOCUSABLE_ELEMENTS.join(",") ); if (first) { first.focus(); } } } }, /* @__PURE__ */ import_react.default.createElement( DropDownItems, { items, spritemap } ) ) ) ) ); } const RadioGroupContext = import_react.default.createContext({}); function Radio({ title, value = "", ...otherProps }) { const { checked, name, onChange } = (0, import_react.useContext)(RadioGroupContext); const { tabFocus } = (0, import_react.useContext)(import_DropDownContext.DropDownContext); return /* @__PURE__ */ import_react.default.createElement(import_DropDown.default.Section, { role: "none" }, /* @__PURE__ */ import_react.default.createElement( import_form.ClayRadio, { ...otherProps, checked: checked === value, inline: true, label: title, name, onChange: () => onChange(value), tabIndex: !tabFocus ? -1 : void 0, value } )); } function RadioGroup({ items, label, name, onChange = () => { }, spritemap, title, value: defaultValue = "" }) { const [value, setValue] = (0, import_react.useState)(defaultValue); const params = { checked: value, name, onChange: (value2) => { onChange(value2); setValue(value2); } }; title = label || title; (0, import_warning.default)( items && !items.filter((item) => item.type !== "radio").length, "ClayDropDownWithItems -> Items of type `radiogroup` should be used `radio` if you need to use others, it is recommended to use type `group`." ); return /* @__PURE__ */ import_react.default.createElement(import_Group.default, { header: title, role: "radiogroup" }, items && /* @__PURE__ */ import_react.default.createElement(RadioGroupContext.Provider, { value: params }, /* @__PURE__ */ import_react.default.createElement(Items, { items, spritemap }))); } function DividerWithItem() { return /* @__PURE__ */ import_react.default.createElement(import_Divider.default, null); } const TYPE_MAP = { checkbox: Checkbox, contextual: Contextual, divider: DividerWithItem, group: Group, item: Item, radio: Radio, radiogroup: RadioGroup }; function DropDownItems({ items, role, spritemap, ...otherProps }) { return /* @__PURE__ */ import_react.default.createElement(import_DropDown.default.ItemList, { "aria-label": otherProps["aria-label"], role }, /* @__PURE__ */ import_react.default.createElement(Items, { items, spritemap })); } function Items({ items, spritemap }) { return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, items.map(({ type, ...item }, key) => { const Item2 = TYPE_MAP[type || "item"]; return /* @__PURE__ */ import_react.default.createElement(Item2, { ...item, key, spritemap }); })); }