@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
186 lines (184 loc) • 7.14 kB
JavaScript
import Icon_default from "../Icon/Icon.mjs";
import { preventDefaultAndStopPropagation } from "../utils/dom.mjs";
import { styles } from "../Menu/sharedStyle.mjs";
import common_default from "../i18n/resources/en/common.mjs";
import { useTranslation } from "../i18n/useTranslation.mjs";
import { isValidElement, memo } from "react";
import { jsx, jsxs } from "react/jsx-runtime";
import { cx } from "antd-style";
import { Check, ChevronRight } from "lucide-react";
import { ContextMenu } from "@base-ui/react/context-menu";
//#region src/ContextMenu/renderItems.tsx
const EmptyMenuItem = memo(() => {
const { t } = useTranslation(common_default);
return /* @__PURE__ */ jsx(ContextMenu.Item, {
className: cx(styles.item, styles.empty),
disabled: true,
children: /* @__PURE__ */ jsx("div", {
className: styles.itemContent,
children: /* @__PURE__ */ jsx("span", {
className: styles.label,
children: t("common.empty")
})
})
});
});
EmptyMenuItem.displayName = "EmptyMenuItem";
const getItemKey = (item, fallback) => {
if (item && "key" in item && item.key !== void 0) return item.key;
return fallback;
};
const getItemLabel = (item) => {
if (item.label !== void 0) return item.label;
if ("title" in item && item.title !== void 0) return item.title;
return item.key;
};
const renderIcon = (icon) => {
if (!icon) return null;
if (isValidElement(icon)) return icon;
return /* @__PURE__ */ jsx(Icon_default, {
icon,
size: "small"
});
};
const getReserveIconSpaceMap = (items) => {
const flags = Array.from({ length: items.length }).fill(false);
let segmentIndices = [];
let segmentHasIcon = false;
const flush = () => {
if (segmentHasIcon) for (const index of segmentIndices) flags[index] = true;
segmentIndices = [];
segmentHasIcon = false;
};
items.forEach((item, index) => {
if (!item) return;
if (item.type === "divider" || item.type === "group") {
flush();
return;
}
segmentIndices.push(index);
if (item.type === "checkbox") {
segmentHasIcon = true;
return;
}
if ("icon" in item && item.icon) segmentHasIcon = true;
});
flush();
return flags;
};
const renderItemContent = (item, options, iconNode) => {
const label = getItemLabel(item);
const extra = "extra" in item ? item.extra : void 0;
const hasCustomIcon = iconNode !== void 0;
const hasIcon = hasCustomIcon ? Boolean(iconNode) : Boolean(item.icon);
const shouldRenderIcon = hasCustomIcon ? Boolean(options?.reserveIconSpace || iconNode) : Boolean(hasIcon || options?.reserveIconSpace);
return /* @__PURE__ */ jsxs("div", {
className: styles.itemContent,
children: [
shouldRenderIcon ? /* @__PURE__ */ jsx("span", {
"aria-hidden": !hasIcon,
className: styles.icon,
children: hasCustomIcon ? iconNode : hasIcon ? renderIcon(item.icon) : null
}) : null,
/* @__PURE__ */ jsx("span", {
className: styles.label,
children: label
}),
extra ? /* @__PURE__ */ jsx("span", {
className: styles.extra,
children: extra
}) : null,
options?.submenu ? /* @__PURE__ */ jsx("span", {
className: styles.submenuArrow,
children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 })
}) : null
]
});
};
const invokeItemClick = (item, keyPath, event) => {
if (!item.onClick) return;
const key = item.key ?? keyPath.at(-1) ?? "";
const info = {
domEvent: event,
item: event.currentTarget,
key: String(key),
keyPath
};
item.onClick(info);
};
const renderContextMenuItems = (items, keyPath = [], options) => {
const reserveIconSpaceMap = options?.reserveIconSpace === void 0 ? getReserveIconSpaceMap(items) : null;
return items.map((item, index) => {
if (!item) return null;
const itemKey = getItemKey(item, `${keyPath.join("-") || "root"}-${index}`);
const nextKeyPath = [...keyPath, String(itemKey)];
const reserveIconSpace = options?.reserveIconSpace ?? Boolean(reserveIconSpaceMap?.[index]);
if (item.type === "checkbox") {
const checkboxItem = item;
const label$1 = getItemLabel(checkboxItem);
const labelText$1 = typeof label$1 === "string" ? label$1 : void 0;
const isDanger$1 = Boolean(checkboxItem.danger);
const indicator = /* @__PURE__ */ jsx(ContextMenu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(Icon_default, {
icon: Check,
size: "small"
}) });
return /* @__PURE__ */ jsx(ContextMenu.CheckboxItem, {
checked: checkboxItem.checked,
className: cx(styles.item, isDanger$1 && styles.danger),
closeOnClick: checkboxItem.closeOnClick,
defaultChecked: checkboxItem.defaultChecked,
disabled: checkboxItem.disabled,
label: labelText$1,
onCheckedChange: (checked) => checkboxItem.onCheckedChange?.(checked),
children: renderItemContent(checkboxItem, { reserveIconSpace }, indicator)
}, itemKey);
}
if (item.type === "divider") return /* @__PURE__ */ jsx(ContextMenu.Separator, { className: styles.separator }, itemKey);
if (item.type === "group") {
const group = item;
const groupReserveIconSpace = Boolean(group.children?.some((child) => Boolean(child && "icon" in child && child.icon)));
return /* @__PURE__ */ jsxs(ContextMenu.Group, { children: [group.label ? /* @__PURE__ */ jsx(ContextMenu.GroupLabel, {
className: styles.groupLabel,
children: group.label
}) : null, group.children ? renderContextMenuItems(group.children, nextKeyPath, { reserveIconSpace: groupReserveIconSpace }) : null] }, itemKey);
}
if (item.type === "submenu" || "children" in item && item.children) {
const submenu = item;
const label$1 = getItemLabel(submenu);
const labelText$1 = typeof label$1 === "string" ? label$1 : void 0;
const isDanger$1 = "danger" in submenu && Boolean(submenu.danger);
return /* @__PURE__ */ jsxs(ContextMenu.SubmenuRoot, { children: [/* @__PURE__ */ jsx(ContextMenu.SubmenuTrigger, {
className: cx(styles.item, isDanger$1 && styles.danger),
disabled: submenu.disabled,
label: labelText$1,
children: renderItemContent(submenu, {
reserveIconSpace,
submenu: true
})
}), /* @__PURE__ */ jsx(ContextMenu.Portal, { children: /* @__PURE__ */ jsx(ContextMenu.Positioner, {
alignOffset: -4,
className: styles.positioner,
onContextMenu: preventDefaultAndStopPropagation,
sideOffset: -1,
children: /* @__PURE__ */ jsx(ContextMenu.Popup, {
className: styles.popup,
children: submenu.children && submenu.children.length > 0 ? renderContextMenuItems(submenu.children, nextKeyPath) : /* @__PURE__ */ jsx(EmptyMenuItem, {})
})
}) })] }, itemKey);
}
const menuItem = item;
const label = getItemLabel(menuItem);
const labelText = typeof label === "string" ? label : void 0;
const isDanger = "danger" in menuItem && Boolean(menuItem.danger);
return /* @__PURE__ */ jsx(ContextMenu.Item, {
className: cx(styles.item, isDanger && styles.danger),
disabled: menuItem.disabled,
label: labelText,
onClick: (event) => invokeItemClick(menuItem, nextKeyPath, event),
children: renderItemContent(menuItem, { reserveIconSpace })
}, itemKey);
});
};
//#endregion
export { renderContextMenuItems };
//# sourceMappingURL=renderItems.mjs.map