UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

1 lines 11.5 kB
{"version":3,"file":"renderItems.mjs","names":["Icon","segmentIndices: number[]","info: MenuInfo","label","labelText","isDanger"],"sources":["../../src/DropdownMenu/renderItems.tsx"],"sourcesContent":["import { Menu } from '@base-ui/react/menu';\nimport { cx } from 'antd-style';\nimport { Check, ChevronRight } from 'lucide-react';\nimport type { MenuInfo } from 'rc-menu/es/interface';\nimport type {\n Key,\n KeyboardEvent as ReactKeyboardEvent,\n MouseEvent as ReactMouseEvent,\n ReactNode,\n} from 'react';\nimport { isValidElement } from 'react';\n\nimport Icon from '@/Icon';\nimport type {\n ItemType,\n MenuDividerType,\n MenuItemGroupType,\n MenuItemType,\n SubMenuType,\n} from '@/Menu';\nimport { styles } from '@/Menu/sharedStyle';\n\nimport type { DropdownItem, DropdownMenuCheckboxItem } from './type';\n\nconst getItemKey = (item: ItemType | DropdownItem, fallback: string): Key => {\n if (item && 'key' in item && item.key !== undefined) return item.key;\n return fallback;\n};\n\ntype LabelableItem = {\n key?: Key;\n label?: ReactNode;\n title?: ReactNode;\n};\n\nconst getItemLabel = (item: MenuItemType | SubMenuType | LabelableItem): ReactNode => {\n if (item.label !== undefined) return item.label;\n if ('title' in item && item.title !== undefined) return item.title;\n return item.key;\n};\n\nconst renderIcon = (icon: MenuItemType['icon']) => {\n if (!icon) return null;\n if (isValidElement(icon)) return icon;\n return <Icon icon={icon} />;\n};\n\nconst getReserveIconSpaceMap = (items: DropdownItem[]) => {\n const flags = Array.from({ length: items.length }).fill(false);\n let segmentIndices: number[] = [];\n let segmentHasIcon = false;\n\n const flush = () => {\n if (segmentHasIcon) {\n for (const index of segmentIndices) flags[index] = true;\n }\n segmentIndices = [];\n segmentHasIcon = false;\n };\n\n items.forEach((item, index) => {\n if (!item) return;\n if (\n (item as MenuDividerType).type === 'divider' ||\n (item as MenuItemGroupType).type === 'group'\n ) {\n flush();\n return;\n }\n\n segmentIndices.push(index);\n if ((item as DropdownMenuCheckboxItem).type === 'checkbox') {\n segmentHasIcon = true;\n return;\n }\n if ('icon' in item && item.icon) segmentHasIcon = true;\n });\n\n flush();\n return flags;\n};\n\nconst renderItemContent = (\n item: MenuItemType | SubMenuType | DropdownMenuCheckboxItem,\n options?: { reserveIconSpace?: boolean; submenu?: boolean },\n iconNode?: ReactNode,\n) => {\n const label = getItemLabel(item);\n const extra = 'extra' in item ? item.extra : undefined;\n const hasCustomIcon = iconNode !== undefined;\n const hasIcon = hasCustomIcon ? Boolean(iconNode) : Boolean(item.icon);\n const shouldRenderIcon = hasCustomIcon\n ? Boolean(options?.reserveIconSpace || iconNode)\n : Boolean(hasIcon || options?.reserveIconSpace);\n\n return (\n <div className={styles.itemContent}>\n {shouldRenderIcon ? (\n <span aria-hidden={!hasIcon} className={styles.icon}>\n {hasCustomIcon ? iconNode : hasIcon ? renderIcon(item.icon) : null}\n </span>\n ) : null}\n <span className={styles.label}>{label}</span>\n {extra ? <span className={styles.extra}>{extra}</span> : null}\n {options?.submenu ? (\n <span className={styles.submenuArrow}>\n <ChevronRight size={16} />\n </span>\n ) : null}\n </div>\n );\n};\n\nconst invokeItemClick = (\n item: MenuItemType,\n keyPath: string[],\n event: ReactMouseEvent<HTMLElement> | ReactKeyboardEvent<HTMLElement>,\n) => {\n if (!item.onClick) return;\n const key = item.key ?? keyPath.at(-1) ?? '';\n const info: MenuInfo = {\n domEvent: event,\n item: event.currentTarget as MenuInfo['item'],\n key: String(key),\n keyPath,\n };\n item.onClick(info);\n};\n\nexport const renderDropdownMenuItems = (\n items: DropdownItem[],\n keyPath: string[] = [],\n options?: { reserveIconSpace?: boolean },\n): ReactNode[] => {\n const reserveIconSpaceMap =\n options?.reserveIconSpace === undefined ? getReserveIconSpaceMap(items) : null;\n\n return items.map((item, index) => {\n if (!item) return null;\n\n const fallbackKey = `${keyPath.join('-') || 'root'}-${index}`;\n const itemKey = getItemKey(item, fallbackKey);\n const nextKeyPath = [...keyPath, String(itemKey)];\n const reserveIconSpace = options?.reserveIconSpace ?? Boolean(reserveIconSpaceMap?.[index]);\n\n if ((item as DropdownMenuCheckboxItem).type === 'checkbox') {\n const checkboxItem = item as DropdownMenuCheckboxItem;\n const label = getItemLabel(checkboxItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = Boolean(checkboxItem.danger);\n const indicator = (\n <Menu.CheckboxItemIndicator>\n <Icon icon={Check} />\n </Menu.CheckboxItemIndicator>\n );\n\n return (\n <Menu.CheckboxItem\n checked={checkboxItem.checked}\n className={cx(styles.item, isDanger && styles.danger)}\n closeOnClick={checkboxItem.closeOnClick}\n defaultChecked={checkboxItem.defaultChecked}\n disabled={checkboxItem.disabled}\n key={itemKey}\n label={labelText}\n onCheckedChange={(checked) => checkboxItem.onCheckedChange?.(checked)}\n >\n {renderItemContent(checkboxItem, { reserveIconSpace }, indicator)}\n </Menu.CheckboxItem>\n );\n }\n\n if ((item as MenuDividerType).type === 'divider') {\n return <Menu.Separator className={styles.separator} key={itemKey} />;\n }\n\n if ((item as MenuItemGroupType).type === 'group') {\n const group = item as MenuItemGroupType;\n const groupReserveIconSpace = Boolean(\n group.children?.some((child) => Boolean(child && 'icon' in child && child.icon)),\n );\n return (\n <Menu.Group key={itemKey}>\n {group.label ? (\n <Menu.GroupLabel className={styles.groupLabel}>{group.label}</Menu.GroupLabel>\n ) : null}\n {group.children\n ? renderDropdownMenuItems(group.children, nextKeyPath, {\n reserveIconSpace: groupReserveIconSpace,\n })\n : null}\n </Menu.Group>\n );\n }\n\n if ((item as SubMenuType).type === 'submenu' || 'children' in item) {\n const submenu = item as SubMenuType;\n const label = getItemLabel(submenu);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = 'danger' in submenu && Boolean(submenu.danger);\n\n return (\n <Menu.SubmenuRoot key={itemKey}>\n <Menu.SubmenuTrigger\n className={cx(styles.item, isDanger && styles.danger)}\n disabled={submenu.disabled}\n label={labelText}\n >\n {renderItemContent(submenu, {\n reserveIconSpace,\n submenu: true,\n })}\n </Menu.SubmenuTrigger>\n <Menu.Portal>\n <Menu.Positioner alignOffset={-4} className={styles.positioner} sideOffset={-1}>\n <Menu.Popup className={styles.popup}>\n {submenu.children ? renderDropdownMenuItems(submenu.children, nextKeyPath) : null}\n </Menu.Popup>\n </Menu.Positioner>\n </Menu.Portal>\n </Menu.SubmenuRoot>\n );\n }\n\n const menuItem = item as MenuItemType;\n const label = getItemLabel(menuItem);\n const labelText = typeof label === 'string' ? label : undefined;\n const isDanger = 'danger' in menuItem && Boolean(menuItem.danger);\n\n return (\n <Menu.Item\n className={cx(styles.item, isDanger && styles.danger)}\n disabled={menuItem.disabled}\n key={itemKey}\n label={labelText}\n onClick={(event) => invokeItemClick(menuItem, nextKeyPath, event)}\n >\n {renderItemContent(menuItem, { reserveIconSpace })}\n </Menu.Item>\n );\n });\n};\n"],"mappings":"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"}