basuicn 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui-cli.cjs +1 -1
- package/package.json +1 -1
- package/registry.json +1 -1
- package/scripts/ui-cli.ts +1 -1
package/dist/ui-cli.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var import_fs = __toESM(require("fs"), 1);
|
|
|
27
27
|
var import_path = __toESM(require("path"), 1);
|
|
28
28
|
var import_child_process = require("child_process");
|
|
29
29
|
var import_readline = __toESM(require("readline"), 1);
|
|
30
|
-
var VERSION = "0.1.
|
|
30
|
+
var VERSION = "0.1.8";
|
|
31
31
|
var REGISTRY_LOCAL = "./registry.json";
|
|
32
32
|
var REGISTRY_REMOTE = "https://raw.githubusercontent.com/Basuicn/basuicn-core/main/registry.json";
|
|
33
33
|
var c = {
|
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -363,7 +363,7 @@
|
|
|
363
363
|
"files": [
|
|
364
364
|
{
|
|
365
365
|
"path": "src/components/ui/menu-bar/MenuBar.tsx",
|
|
366
|
-
"content": "import * as React from 'react';\nimport { Menu as BaseMenu } from '@base-ui/react';\nimport { useNavigate, useMatch } from 'react-router-dom';\nimport { tv } from 'tailwind-variants';\nimport { ChevronRight, ExternalLink } from 'lucide-react';\nimport { cn } from '@/lib/utils/cn';\n\n/* ─── Types ─────────────────────────────────────────────────────────────── */\n\n/** How the menu item behaves when clicked */\nexport type MenuBarItemType = 'link' | 'button' | 'modal' | 'external';\n\n/** Config for a single item inside a menu */\nexport interface MenuBarItemConfig {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n /** @default 'button' */\n type?: MenuBarItemType;\n /** Route path for type='link', full URL for type='external' */\n href?: string;\n /** Called on click for type='button' | 'modal', and as fallback for 'link' | 'external' */\n onClick?: () => void;\n shortcut?: string;\n disabled?: boolean;\n /** Renders a separator line before this item */\n separator?: boolean;\n /** Nested items — renders as a flyout submenu (unlimited depth) */\n children?: MenuBarItemConfig[];\n}\n\n/** Config for one top-level menu (trigger + its dropdown) */\nexport interface MenuBarMenuConfig {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n items: MenuBarItemConfig[];\n disabled?: boolean;\n}\n\n/* ─── Variants ──────────────────────────────────────────────────────────── */\n\nconst menuBarVariants = tv({\n slots: {\n root: 'flex items-center gap-0.5 rounded-md border border-border bg-background p-1',\n trigger:\n 'inline-flex items-center gap-1.5 rounded-sm px-3 py-1.5 text-sm font-medium outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none',\n content:\n 'z-50 min-w-[12rem] overflow-hidden rounded-md border border-border bg-background p-1 text-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\n item:\n 'relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n itemActive: 'bg-accent/50 font-medium',\n subTrigger:\n 'relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n subContent:\n 'z-50 min-w-[12rem] overflow-hidden rounded-md border border-border bg-background p-1 text-foreground shadow-lg animate-in fade-in-0 zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\n separator: '-mx-1 my-1 h-px bg-border',\n label: 'px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider',\n shortcut: 'ml-auto text-xs tracking-widest opacity-60',\n },\n});\n\nconst styles = menuBarVariants();\n\n/* ─── MenuBar ───────────────────────────────────────────────────────────── */\n\nexport interface MenuBarProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst MenuBar = React.forwardRef<HTMLDivElement, MenuBarProps>(({ className, ...props }, ref) => (\n <div ref={ref} role=\"menubar\" className={styles.root({ className })} {...props} />\n));\nMenuBar.displayName = 'MenuBar';\n\n/* ─── MenuBarMenu ───────────────────────────────────────────────────────── */\n\nconst MenuBarMenu = BaseMenu.Root;\n\n/* ─── MenuBarTrigger ────────────────────────────────────────────────────── */\n\nexport interface MenuBarTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Trigger>, 'className'> {\n className?: string;\n}\n\nconst MenuBarTrigger = React.forwardRef<HTMLButtonElement, MenuBarTriggerProps>(\n ({ className, ...props }, ref) => (\n <BaseMenu.Trigger\n ref={ref as React.Ref<HTMLButtonElement>}\n className={styles.trigger({ className })}\n {...props}\n />\n )\n);\nMenuBarTrigger.displayName = 'MenuBarTrigger';\n\n/* ─── MenuBarContent ────────────────────────────────────────────────────── */\n\nexport interface MenuBarContentProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Popup>, 'className'> {\n className?: string;\n side?: 'top' | 'right' | 'bottom' | 'left';\n align?: 'start' | 'center' | 'end';\n sideOffset?: number;\n}\n\nconst MenuBarContent = React.forwardRef<\n React.ComponentRef<typeof BaseMenu.Popup>,\n MenuBarContentProps\n>(({ className, side = 'bottom', align = 'start', sideOffset = 4, ...props }, ref) => (\n <BaseMenu.Portal>\n <BaseMenu.Positioner side={side} align={align} sideOffset={sideOffset}>\n <BaseMenu.Popup ref={ref} className={styles.content({ className })} {...props} />\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n));\nMenuBarContent.displayName = 'MenuBarContent';\n\n/* ─── MenuBarItem ───────────────────────────────────────────────────────── */\n\nexport interface MenuBarItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Item>, 'className'> {\n className?: string;\n /** Applies active/highlighted styling (e.g. current route) */\n active?: boolean;\n}\n\nconst MenuBarItem = React.forwardRef<React.ComponentRef<typeof BaseMenu.Item>, MenuBarItemProps>(\n ({ className, active, children, ...props }, ref) => (\n <BaseMenu.Item\n ref={ref}\n className={styles.item({ className: cn(active && styles.itemActive(), className) })}\n {...props}\n >\n {children}\n </BaseMenu.Item>\n )\n);\nMenuBarItem.displayName = 'MenuBarItem';\n\n/* ─── MenuBarSeparator ──────────────────────────────────────────────────── */\n\nexport interface MenuBarSeparatorProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst MenuBarSeparator = React.forwardRef<HTMLDivElement, MenuBarSeparatorProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={styles.separator({ className })} {...props} />\n )\n);\nMenuBarSeparator.displayName = 'MenuBarSeparator';\n\n/* ─── MenuBarLabel ──────────────────────────────────────────────────────── */\n\nexport interface MenuBarLabelProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst MenuBarLabel = React.forwardRef<HTMLDivElement, MenuBarLabelProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={styles.label({ className })} {...props} />\n )\n);\nMenuBarLabel.displayName = 'MenuBarLabel';\n\n/* ─── MenuBarShortcut ───────────────────────────────────────────────────── */\n\nconst MenuBarShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span className={styles.shortcut({ className })} {...props} />\n);\nMenuBarShortcut.displayName = 'MenuBarShortcut';\n\n/* ─── MenuBarSub ────────────────────────────────────────────────────────── */\n\nconst MenuBarSub = BaseMenu.SubmenuRoot;\n\n/* ─── MenuBarSubTrigger ─────────────────────────────────────────────────── */\n\nexport interface MenuBarSubTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.SubmenuTrigger>, 'className'> {\n className?: string;\n}\n\nconst MenuBarSubTrigger = React.forwardRef<\n React.ComponentRef<typeof BaseMenu.SubmenuTrigger>,\n MenuBarSubTriggerProps\n>(({ className, children, ...props }, ref) => (\n <BaseMenu.SubmenuTrigger ref={ref} className={styles.subTrigger({ className })} {...props}>\n {children}\n <ChevronRight className=\"ml-auto\" />\n </BaseMenu.SubmenuTrigger>\n));\nMenuBarSubTrigger.displayName = 'MenuBarSubTrigger';\n\n/* ─── MenuBarSubContent ─────────────────────────────────────────────────── */\n\nexport interface MenuBarSubContentProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Popup>, 'className'> {\n className?: string;\n}\n\nconst MenuBarSubContent = React.forwardRef<\n React.ComponentRef<typeof BaseMenu.Popup>,\n MenuBarSubContentProps\n>(({ className, ...props }, ref) => (\n <BaseMenu.Portal>\n <BaseMenu.Positioner sideOffset={-4}>\n <BaseMenu.Popup ref={ref} className={styles.subContent({ className })} {...props} />\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n));\nMenuBarSubContent.displayName = 'MenuBarSubContent';\n\n/* ─── MenuBarGroup ──────────────────────────────────────────────────────── */\n\nconst MenuBarGroup = BaseMenu.Group;\n\n/* ─── Config-driven layer ───────────────────────────────────────────────── */\n\n/**\n * Internal recursive renderer for MenuBarItemConfig.\n * Handles all 4 item types, separators, and unlimited submenu depth.\n */\nconst MenuBarItemRenderer = ({ item }: { item: MenuBarItemConfig }) => {\n const navigate = useNavigate();\n const isLinkType = item.type === 'link' && !!item.href;\n const match = useMatch(isLinkType ? item.href! : '__NO_MATCH__');\n const isActive = isLinkType && !!match;\n\n const handleClick = React.useCallback(() => {\n if (item.type === 'link' && item.href) {\n navigate(item.href);\n } else if (item.type === 'external' && item.href) {\n window.open(item.href, '_blank', 'noopener,noreferrer');\n } else {\n item.onClick?.();\n }\n }, [item, navigate]);\n\n if (item.children && item.children.length > 0) {\n return (\n <>\n {item.separator && <MenuBarSeparator />}\n <MenuBarSub>\n <MenuBarSubTrigger disabled={item.disabled}>\n {item.icon}\n {item.label}\n </MenuBarSubTrigger>\n <MenuBarSubContent>\n {item.children.map((child) => (\n <MenuBarItemRenderer key={child.id} item={child} />\n ))}\n </MenuBarSubContent>\n </MenuBarSub>\n </>\n );\n }\n\n return (\n <>\n {item.separator && <MenuBarSeparator />}\n <MenuBarItem active={isActive} onClick={handleClick} disabled={item.disabled}>\n {item.icon}\n {item.label}\n {item.shortcut && <MenuBarShortcut>{item.shortcut}</MenuBarShortcut>}\n {item.type === 'external' && <ExternalLink className=\"ml-auto !size-3 opacity-50\" />}\n </MenuBarItem>\n </>\n );\n};\n\n/** Props for the config-driven MenuBarNav component */\nexport interface MenuBarNavProps extends Omit<MenuBarProps, 'children'> {\n /** Array of top-level menus, each with nested items supporting unlimited depth */\n menus: MenuBarMenuConfig[];\n}\n\n/**\n * Config-driven menu bar. Pass a `menus` array and it renders everything —\n * triggers, dropdowns, submenus, separators, active link states.\n *\n * @example\n * ```tsx\n * <MenuBarNav menus={[\n * {\n * id: 'file', label: 'File',\n * items: [\n * { id: 'new', label: 'New', type: 'button', onClick: handleNew, shortcut: '⌘N' },\n * { id: 'open', label: 'Open', type: 'link', href: '/open' },\n * { id: 'sep', label: '---', separator: true, ... },\n * ],\n * },\n * ]} />\n * ```\n */\nconst MenuBarNav = React.forwardRef<HTMLDivElement, MenuBarNavProps>(\n ({ menus, className, ...props }, ref) => (\n <MenuBar ref={ref} className={className} {...props}>\n {menus.map((menu) => (\n <MenuBarMenu key={menu.id}>\n <MenuBarTrigger disabled={menu.disabled}>\n {menu.icon}\n {menu.label}\n </MenuBarTrigger>\n <MenuBarContent>\n {menu.items.map((item) => (\n <MenuBarItemRenderer key={item.id} item={item} />\n ))}\n </MenuBarContent>\n </MenuBarMenu>\n ))}\n </MenuBar>\n )\n);\nMenuBarNav.displayName = 'MenuBarNav';\n\n/* ─── Exports ───────────────────────────────────────────────────────────── */\n\nexport {\n menuBarVariants,\n // Primitive API\n MenuBar,\n MenuBarMenu,\n MenuBarTrigger,\n MenuBarContent,\n MenuBarItem,\n MenuBarSeparator,\n MenuBarLabel,\n MenuBarShortcut,\n MenuBarSub,\n MenuBarSubTrigger,\n MenuBarSubContent,\n MenuBarGroup,\n // Config-driven API\n MenuBarNav,\n};\n"
|
|
366
|
+
"content": "import * as React from 'react';\nimport { Menu as BaseMenu } from '@base-ui/react';\nimport { useNavigate, useMatch } from 'react-router-dom';\nimport { tv } from 'tailwind-variants';\nimport { ChevronRight, ExternalLink } from 'lucide-react';\nimport { cn } from '@/lib/utils/cn';\n\n/* ─── Types ─────────────────────────────────────────────────────────────── */\n\n/** How the menu item behaves when clicked */\nexport type MenuBarItemType = 'link' | 'button' | 'modal' | 'external';\n\n/** Config for a single item inside a menu */\nexport interface MenuBarItemConfig {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n /** @default 'button' */\n type?: MenuBarItemType;\n /** Route path for type='link', full URL for type='external' */\n href?: string;\n /** Called on click for type='button' | 'modal', and as fallback for 'link' | 'external' */\n onClick?: () => void;\n shortcut?: string;\n disabled?: boolean;\n /** Renders a separator line before this item */\n separator?: boolean;\n /** Nested items — renders as a flyout submenu (unlimited depth) */\n children?: MenuBarItemConfig[];\n}\n\n/** Config for one top-level menu entry.\n *\n * - Có `items` → dropdown menu bình thường\n * - Không có `items` → click thẳng vào entry (dùng `type` + `href` / `onClick`)\n */\nexport interface MenuBarMenuConfig {\n id: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n /** Nếu có items → render dropdown. Nếu bỏ qua → render trực tiếp như button/link */\n items?: MenuBarItemConfig[];\n disabled?: boolean;\n /** Chỉ dùng khi không có items. @default 'button' */\n type?: MenuBarItemType;\n /** Route path (type='link') hoặc URL (type='external') */\n href?: string;\n /** Callback khi click (type='button' | 'modal') */\n onClick?: () => void;\n}\n\n/* ─── Variants ──────────────────────────────────────────────────────────── */\n\nconst menuBarVariants = tv({\n slots: {\n root: 'flex items-center gap-0.5 rounded-md border border-border bg-background p-1',\n trigger:\n 'inline-flex items-center gap-1.5 rounded-sm px-3 py-1.5 text-sm font-medium outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 cursor-pointer select-none',\n content:\n 'z-50 min-w-[12rem] overflow-hidden rounded-md border border-border bg-background p-1 text-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\n item:\n 'relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n itemActive: 'bg-accent/50 font-medium',\n subTrigger:\n 'relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n subContent:\n 'z-50 min-w-[12rem] overflow-hidden rounded-md border border-border bg-background p-1 text-foreground shadow-lg animate-in fade-in-0 zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\n separator: '-mx-1 my-1 h-px bg-border',\n label: 'px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider',\n shortcut: 'ml-auto text-xs tracking-widest opacity-60',\n },\n});\n\nconst styles = menuBarVariants();\n\n/* ─── MenuBar ───────────────────────────────────────────────────────────── */\n\nexport interface MenuBarProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst MenuBar = React.forwardRef<HTMLDivElement, MenuBarProps>(({ className, ...props }, ref) => (\n <div ref={ref} role=\"menubar\" className={styles.root({ className })} {...props} />\n));\nMenuBar.displayName = 'MenuBar';\n\n/* ─── MenuBarMenu ───────────────────────────────────────────────────────── */\n\nconst MenuBarMenu = BaseMenu.Root;\n\n/* ─── MenuBarTrigger ────────────────────────────────────────────────────── */\n\nexport interface MenuBarTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Trigger>, 'className'> {\n className?: string;\n}\n\nconst MenuBarTrigger = React.forwardRef<HTMLButtonElement, MenuBarTriggerProps>(\n ({ className, ...props }, ref) => (\n <BaseMenu.Trigger\n ref={ref as React.Ref<HTMLButtonElement>}\n className={styles.trigger({ className })}\n {...props}\n />\n )\n);\nMenuBarTrigger.displayName = 'MenuBarTrigger';\n\n/* ─── MenuBarButton (top-level direct item, no dropdown) ───────────────── */\n\nexport interface MenuBarButtonProps extends React.ComponentPropsWithoutRef<'button'> {\n /** Highlights the button (e.g. active route) */\n active?: boolean;\n}\n\nconst MenuBarButton = React.forwardRef<HTMLButtonElement, MenuBarButtonProps>(\n ({ className, active, ...props }, ref) => (\n <button\n ref={ref}\n className={styles.trigger({ className: cn(active && styles.itemActive(), className) })}\n {...props}\n />\n )\n);\nMenuBarButton.displayName = 'MenuBarButton';\n\n/* ─── MenuBarContent ────────────────────────────────────────────────────── */\n\nexport interface MenuBarContentProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Popup>, 'className'> {\n className?: string;\n side?: 'top' | 'right' | 'bottom' | 'left';\n align?: 'start' | 'center' | 'end';\n sideOffset?: number;\n}\n\nconst MenuBarContent = React.forwardRef<\n React.ComponentRef<typeof BaseMenu.Popup>,\n MenuBarContentProps\n>(({ className, side = 'bottom', align = 'start', sideOffset = 4, ...props }, ref) => (\n <BaseMenu.Portal>\n <BaseMenu.Positioner side={side} align={align} sideOffset={sideOffset}>\n <BaseMenu.Popup ref={ref} className={styles.content({ className })} {...props} />\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n));\nMenuBarContent.displayName = 'MenuBarContent';\n\n/* ─── MenuBarItem ───────────────────────────────────────────────────────── */\n\nexport interface MenuBarItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Item>, 'className'> {\n className?: string;\n /** Applies active/highlighted styling (e.g. current route) */\n active?: boolean;\n}\n\nconst MenuBarItem = React.forwardRef<React.ComponentRef<typeof BaseMenu.Item>, MenuBarItemProps>(\n ({ className, active, children, ...props }, ref) => (\n <BaseMenu.Item\n ref={ref}\n className={styles.item({ className: cn(active && styles.itemActive(), className) })}\n {...props}\n >\n {children}\n </BaseMenu.Item>\n )\n);\nMenuBarItem.displayName = 'MenuBarItem';\n\n/* ─── MenuBarSeparator ──────────────────────────────────────────────────── */\n\nexport interface MenuBarSeparatorProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst MenuBarSeparator = React.forwardRef<HTMLDivElement, MenuBarSeparatorProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={styles.separator({ className })} {...props} />\n )\n);\nMenuBarSeparator.displayName = 'MenuBarSeparator';\n\n/* ─── MenuBarLabel ──────────────────────────────────────────────────────── */\n\nexport interface MenuBarLabelProps extends React.ComponentPropsWithoutRef<'div'> {}\n\nconst MenuBarLabel = React.forwardRef<HTMLDivElement, MenuBarLabelProps>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={styles.label({ className })} {...props} />\n )\n);\nMenuBarLabel.displayName = 'MenuBarLabel';\n\n/* ─── MenuBarShortcut ───────────────────────────────────────────────────── */\n\nconst MenuBarShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span className={styles.shortcut({ className })} {...props} />\n);\nMenuBarShortcut.displayName = 'MenuBarShortcut';\n\n/* ─── MenuBarSub ────────────────────────────────────────────────────────── */\n\nconst MenuBarSub = BaseMenu.SubmenuRoot;\n\n/* ─── MenuBarSubTrigger ─────────────────────────────────────────────────── */\n\nexport interface MenuBarSubTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.SubmenuTrigger>, 'className'> {\n className?: string;\n}\n\nconst MenuBarSubTrigger = React.forwardRef<\n React.ComponentRef<typeof BaseMenu.SubmenuTrigger>,\n MenuBarSubTriggerProps\n>(({ className, children, ...props }, ref) => (\n <BaseMenu.SubmenuTrigger ref={ref} className={styles.subTrigger({ className })} {...props}>\n {children}\n <ChevronRight className=\"ml-auto\" />\n </BaseMenu.SubmenuTrigger>\n));\nMenuBarSubTrigger.displayName = 'MenuBarSubTrigger';\n\n/* ─── MenuBarSubContent ─────────────────────────────────────────────────── */\n\nexport interface MenuBarSubContentProps\n extends Omit<React.ComponentPropsWithoutRef<typeof BaseMenu.Popup>, 'className'> {\n className?: string;\n}\n\nconst MenuBarSubContent = React.forwardRef<\n React.ComponentRef<typeof BaseMenu.Popup>,\n MenuBarSubContentProps\n>(({ className, ...props }, ref) => (\n <BaseMenu.Portal>\n <BaseMenu.Positioner sideOffset={-4}>\n <BaseMenu.Popup ref={ref} className={styles.subContent({ className })} {...props} />\n </BaseMenu.Positioner>\n </BaseMenu.Portal>\n));\nMenuBarSubContent.displayName = 'MenuBarSubContent';\n\n/* ─── MenuBarGroup ──────────────────────────────────────────────────────── */\n\nconst MenuBarGroup = BaseMenu.Group;\n\n/* ─── Config-driven layer ───────────────────────────────────────────────── */\n\n/**\n * Internal recursive renderer for MenuBarItemConfig.\n * Handles all 4 item types, separators, and unlimited submenu depth.\n */\nconst MenuBarItemRenderer = ({ item }: { item: MenuBarItemConfig }) => {\n const navigate = useNavigate();\n const isLinkType = item.type === 'link' && !!item.href;\n const match = useMatch(isLinkType ? item.href! : '__NO_MATCH__');\n const isActive = isLinkType && !!match;\n\n const handleClick = React.useCallback(() => {\n if (item.type === 'link' && item.href) {\n navigate(item.href);\n } else if (item.type === 'external' && item.href) {\n window.open(item.href, '_blank', 'noopener,noreferrer');\n } else {\n item.onClick?.();\n }\n }, [item, navigate]);\n\n if (item.children && item.children.length > 0) {\n return (\n <>\n {item.separator && <MenuBarSeparator />}\n <MenuBarSub>\n <MenuBarSubTrigger disabled={item.disabled}>\n {item.icon}\n {item.label}\n </MenuBarSubTrigger>\n <MenuBarSubContent>\n {item.children.map((child) => (\n <MenuBarItemRenderer key={child.id} item={child} />\n ))}\n </MenuBarSubContent>\n </MenuBarSub>\n </>\n );\n }\n\n return (\n <>\n {item.separator && <MenuBarSeparator />}\n <MenuBarItem active={isActive} onClick={handleClick} disabled={item.disabled}>\n {item.icon}\n {item.label}\n {item.shortcut && <MenuBarShortcut>{item.shortcut}</MenuBarShortcut>}\n {item.type === 'external' && <ExternalLink className=\"ml-auto !size-3 opacity-50\" />}\n </MenuBarItem>\n </>\n );\n};\n\n/** Props for the config-driven MenuBarNav component */\nexport interface MenuBarNavProps extends Omit<MenuBarProps, 'children'> {\n /** Array of top-level menus, each with nested items supporting unlimited depth */\n menus: MenuBarMenuConfig[];\n}\n\n/**\n * Config-driven menu bar. Pass a `menus` array and it renders everything —\n * triggers, dropdowns, submenus, separators, active link states.\n *\n * @example\n * ```tsx\n * <MenuBarNav menus={[\n * {\n * id: 'file', label: 'File',\n * items: [\n * { id: 'new', label: 'New', type: 'button', onClick: handleNew, shortcut: '⌘N' },\n * { id: 'open', label: 'Open', type: 'link', href: '/open' },\n * { id: 'sep', label: '---', separator: true, ... },\n * ],\n * },\n * ]} />\n * ```\n */\n/** Internal: renders a direct (no-dropdown) top-level entry */\nconst MenuBarDirectRenderer = ({ menu }: { menu: MenuBarMenuConfig }) => {\n const navigate = useNavigate();\n const isLink = menu.type === 'link' && !!menu.href;\n const match = useMatch(isLink ? menu.href! : '__NO_MATCH__');\n\n const handleClick = React.useCallback(() => {\n if (menu.type === 'link' && menu.href) navigate(menu.href);\n else if (menu.type === 'external' && menu.href) window.open(menu.href, '_blank', 'noopener,noreferrer');\n else menu.onClick?.();\n }, [menu, navigate]);\n\n return (\n <MenuBarButton active={isLink && !!match} disabled={menu.disabled} onClick={handleClick}>\n {menu.icon}\n {menu.label}\n {menu.type === 'external' && <ExternalLink className=\"!size-3 opacity-50\" />}\n </MenuBarButton>\n );\n};\n\nconst MenuBarNav = React.forwardRef<HTMLDivElement, MenuBarNavProps>(\n ({ menus, className, ...props }, ref) => (\n <MenuBar ref={ref} className={className} {...props}>\n {menus.map((menu) =>\n !menu.items || menu.items.length === 0 ? (\n // Direct item — không có dropdown\n <MenuBarDirectRenderer key={menu.id} menu={menu} />\n ) : (\n // Dropdown menu bình thường\n <MenuBarMenu key={menu.id}>\n <MenuBarTrigger disabled={menu.disabled}>\n {menu.icon}\n {menu.label}\n </MenuBarTrigger>\n <MenuBarContent>\n {menu.items.map((item) => (\n <MenuBarItemRenderer key={item.id} item={item} />\n ))}\n </MenuBarContent>\n </MenuBarMenu>\n )\n )}\n </MenuBar>\n )\n);\nMenuBarNav.displayName = 'MenuBarNav';\n\n/* ─── Exports ───────────────────────────────────────────────────────────── */\n\nexport {\n menuBarVariants,\n // Primitive API\n MenuBar,\n MenuBarMenu,\n MenuBarTrigger,\n MenuBarButton,\n MenuBarContent,\n MenuBarItem,\n MenuBarSeparator,\n MenuBarLabel,\n MenuBarShortcut,\n MenuBarSub,\n MenuBarSubTrigger,\n MenuBarSubContent,\n MenuBarGroup,\n // Config-driven API\n MenuBarNav,\n};\n"
|
|
367
367
|
}
|
|
368
368
|
]
|
|
369
369
|
},
|
package/scripts/ui-cli.ts
CHANGED
|
@@ -6,7 +6,7 @@ import readline from 'readline';
|
|
|
6
6
|
|
|
7
7
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
8
8
|
|
|
9
|
-
const VERSION = '0.1.
|
|
9
|
+
const VERSION = '0.1.9';
|
|
10
10
|
const REGISTRY_LOCAL = './registry.json';
|
|
11
11
|
const REGISTRY_REMOTE = 'https://raw.githubusercontent.com/Basuicn/basuicn-core/main/registry.json';
|
|
12
12
|
|