@comitor/ui 0.5.1 → 0.6.1
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/README.md +3 -2
- package/dist/components/composite/data-table.d.ts +7 -0
- package/dist/components/composite/data-table.js +5 -6
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/form-field.js +5 -2
- package/dist/components/composite/form-field.js.map +1 -1
- package/dist/components/ui/command.js +1 -1
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/context-menu.js +4 -4
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/dropdown-menu.js +4 -4
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/field.js +6 -12
- package/dist/components/ui/field.js.map +1 -1
- package/dist/components/ui/input-group.js +21 -3
- package/dist/components/ui/input-group.js.map +1 -1
- package/dist/components/ui/item.js +1 -1
- package/dist/components/ui/item.js.map +1 -1
- package/dist/components/ui/menubar.js +4 -4
- package/dist/components/ui/menubar.js.map +1 -1
- package/dist/components/ui/select.js +1 -1
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/table.js +2 -2
- package/dist/components/ui/table.js.map +1 -1
- package/dist/shell/comitor-lockup.js +11 -6
- package/dist/shell/comitor-lockup.js.map +1 -1
- package/dist/shell/comitor-logo.d.ts +4 -3
- package/dist/shell/comitor-logo.js +8 -1
- package/dist/shell/comitor-logo.js.map +1 -1
- package/dist/shell/display-axis-constants.d.ts +35 -0
- package/dist/shell/display-axis-constants.js +27 -0
- package/dist/shell/display-axis-constants.js.map +1 -0
- package/dist/shell/display-axis-controls.d.ts +51 -0
- package/dist/shell/display-axis-controls.js +70 -0
- package/dist/shell/display-axis-controls.js.map +1 -0
- package/dist/shell/display-axis-provider.d.ts +40 -0
- package/dist/shell/display-axis-provider.js +101 -0
- package/dist/shell/display-axis-provider.js.map +1 -0
- package/dist/shell/index.d.ts +3 -0
- package/dist/shell/index.js +3 -0
- package/dist/shell/index.js.map +1 -1
- package/dist/shell/shell-context.d.ts +9 -1
- package/dist/shell/shell-context.js +14 -4
- package/dist/shell/shell-context.js.map +1 -1
- package/dist/shell/sidebar-menu-item.js +1 -1
- package/dist/shell/sidebar-menu-item.js.map +1 -1
- package/package.json +1 -1
- package/styles.css +82 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/dropdown-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * DropdownMenu — menu bung ra từ một nút (Radix DropdownMenu).\n *\n * Xương sống của UserMenu, menu thao tác trên dòng bảng, WorkspaceSwitcher.\n * `DropdownMenuItem` có `variant=\"destructive\"` cho mục nguy hiểm (Xóa, Đăng xuất)\n * và `inset` để thụt lề cho ngang hàng với mục có icon chọn.\n */\n\nexport type DropdownMenuProps = React.ComponentProps<typeof DropdownMenuPrimitive.Root>;\n\nfunction DropdownMenu({ ...props }: DropdownMenuProps) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\n}\n\nexport type DropdownMenuPortalProps = React.ComponentProps<typeof DropdownMenuPrimitive.Portal>;\n\nfunction DropdownMenuPortal({ ...props }: DropdownMenuPortalProps) {\n return <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\n}\n\nexport type DropdownMenuTriggerProps = React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>;\n\nfunction DropdownMenuTrigger({ ...props }: DropdownMenuTriggerProps) {\n return <DropdownMenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\n}\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.Content>;\n\nfunction DropdownMenuContent({ className, sideOffset = 4, ...props }: DropdownMenuContentProps) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuGroupProps = React.ComponentProps<typeof DropdownMenuPrimitive.Group>;\n\nfunction DropdownMenuGroup({ ...props }: DropdownMenuGroupProps) {\n return <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\n}\n\nexport type DropdownMenuItemVariant = \"default\" | \"destructive\";\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n /** Thụt lề trái để thẳng hàng với mục có ô đánh dấu. */\n inset?: boolean;\n /** \"destructive\" tô đỏ cho thao tác nguy hiểm. Mặc định \"default\". */\n variant?: DropdownMenuItemVariant;\n};\n\nfunction DropdownMenuItem({ className, inset, variant = \"default\", ...props }: DropdownMenuItemProps) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n // `text-destructive-ink` chứ KHÔNG `text-destructive`: `--destructive` là màu NỀN (nút xoá),\n // ghim một giá trị cho cả hai theme nên làm CHỮ trên nền popover tối chỉ được 3,53:1 ✗ —\n // và tệ thêm khi focus vì nền chuyển sang `bg-destructive/20` (2,98:1 ✗). Bậc ink đạt cả\n // hai theme, cả hai nền: 7,15:1 / 6,18:1 (light, nghỉ / focus) · 6,42:1 / 5,42:1 (dark).\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuCheckboxItemProps = React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>;\n\nfunction DropdownMenuCheckboxItem({ className, children, checked, ...props }: DropdownMenuCheckboxItemProps) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nexport type DropdownMenuRadioGroupProps = React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>;\n\nfunction DropdownMenuRadioGroup({ ...props }: DropdownMenuRadioGroupProps) {\n return <DropdownMenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" {...props} />;\n}\n\nexport type DropdownMenuRadioItemProps = React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>;\n\nfunction DropdownMenuRadioItem({ className, children, ...props }: DropdownMenuRadioItemProps) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n );\n}\n\nexport type DropdownMenuLabelProps = React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n};\n\nfunction DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLabelProps) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn(\"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuSeparatorProps = React.ComponentProps<typeof DropdownMenuPrimitive.Separator>;\n\nfunction DropdownMenuSeparator({ className, ...props }: DropdownMenuSeparatorProps) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuShortcutProps = React.ComponentProps<\"span\">;\n\nfunction DropdownMenuShortcut({ className, ...props }: DropdownMenuShortcutProps) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuSubProps = React.ComponentProps<typeof DropdownMenuPrimitive.Sub>;\n\nfunction DropdownMenuSub({ ...props }: DropdownMenuSubProps) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nexport type DropdownMenuSubTriggerProps = React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n};\n\nfunction DropdownMenuSubTrigger({ className, inset, children, ...props }: DropdownMenuSubTriggerProps) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\n/*\n * ─ VÌ SAO `SubContent` PHẢI BỌC `Portal` ─\n *\n * Không bọc thì Radix render nó (kèm lớp bọc định vị `position: fixed`) làm CON của\n * `DropdownMenuContent`, mà `DropdownMenuContent` có `overflow-x-hidden overflow-y-auto`. `position: fixed`\n * bình thường thoát được `overflow` — TRỪ KHI có tổ tiên tạo containing block cho nó, và\n * `DropdownMenuContent` tạo đúng thứ đó: các lớp `data-[state=open]:animate-in` / `zoom-in-95` /\n * `slide-in-from-*` animate `transform`, với `animation-fill-mode: both` nên transform CÒN LẠI\n * sau khi hiệu ứng chạy xong (`scale(1)` vẫn là transform, không phải `none`).\n *\n * Hệ quả: submenu mở sang BÊN CẠNH menu cha, tức hoàn toàn nằm ngoài hộp của cha theo chiều\n * ngang, và bị `overflow-x-hidden` cắt sạch. Người dùng thấy mục có mũi tên ▸ mà bấm/rê vào\n * không ra gì — không lỗi, không cảnh báo.\n *\n * Đây là hồi quy đã biết của shadcn/ui: nó xuất hiện đúng lúc `DropdownMenuContent` được thêm\n * `max-h-… overflow-y-auto`. Bọc `Portal` đưa submenu ra thẳng `document.body`, thoát cả\n * `overflow` lẫn containing block.\n *\n * ⚠ Đừng \"sửa\" bằng cách bỏ `overflow-y-auto` ở `DropdownMenuContent`: lớp đó có việc của nó (menu\n * dài hơn màn hình thì phải cuộn được).\n */\nexport type DropdownMenuSubContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>;\n\nfunction DropdownMenuSubContent({ className, ...props }: DropdownMenuSubContentProps) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuPortal,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/dropdown-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * DropdownMenu — menu bung ra từ một nút (Radix DropdownMenu).\n *\n * Xương sống của UserMenu, menu thao tác trên dòng bảng, WorkspaceSwitcher.\n * `DropdownMenuItem` có `variant=\"destructive\"` cho mục nguy hiểm (Xóa, Đăng xuất)\n * và `inset` để thụt lề cho ngang hàng với mục có icon chọn.\n */\n\nexport type DropdownMenuProps = React.ComponentProps<typeof DropdownMenuPrimitive.Root>;\n\nfunction DropdownMenu({ ...props }: DropdownMenuProps) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\n}\n\nexport type DropdownMenuPortalProps = React.ComponentProps<typeof DropdownMenuPrimitive.Portal>;\n\nfunction DropdownMenuPortal({ ...props }: DropdownMenuPortalProps) {\n return <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\n}\n\nexport type DropdownMenuTriggerProps = React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>;\n\nfunction DropdownMenuTrigger({ ...props }: DropdownMenuTriggerProps) {\n return <DropdownMenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\n}\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.Content>;\n\nfunction DropdownMenuContent({ className, sideOffset = 4, ...props }: DropdownMenuContentProps) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuGroupProps = React.ComponentProps<typeof DropdownMenuPrimitive.Group>;\n\nfunction DropdownMenuGroup({ ...props }: DropdownMenuGroupProps) {\n return <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\n}\n\nexport type DropdownMenuItemVariant = \"default\" | \"destructive\";\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n /** Thụt lề trái để thẳng hàng với mục có ô đánh dấu. */\n inset?: boolean;\n /** \"destructive\" tô đỏ cho thao tác nguy hiểm. Mặc định \"default\". */\n variant?: DropdownMenuItemVariant;\n};\n\nfunction DropdownMenuItem({ className, inset, variant = \"default\", ...props }: DropdownMenuItemProps) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n // `text-destructive-ink` chứ KHÔNG `text-destructive`: `--destructive` là màu NỀN (nút xoá),\n // ghim một giá trị cho cả hai theme nên làm CHỮ trên nền popover tối chỉ được 3,53:1 ✗ —\n // và tệ thêm khi focus vì nền chuyển sang `bg-destructive/20` (2,98:1 ✗). Bậc ink đạt cả\n // hai theme, cả hai nền: 7,15:1 / 6,18:1 (light, nghỉ / focus) · 6,42:1 / 5,42:1 (dark).\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuCheckboxItemProps = React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>;\n\nfunction DropdownMenuCheckboxItem({ className, children, checked, ...props }: DropdownMenuCheckboxItemProps) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-(--menu-item-py) pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nexport type DropdownMenuRadioGroupProps = React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>;\n\nfunction DropdownMenuRadioGroup({ ...props }: DropdownMenuRadioGroupProps) {\n return <DropdownMenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" {...props} />;\n}\n\nexport type DropdownMenuRadioItemProps = React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>;\n\nfunction DropdownMenuRadioItem({ className, children, ...props }: DropdownMenuRadioItemProps) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-(--menu-item-py) pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n );\n}\n\nexport type DropdownMenuLabelProps = React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n};\n\nfunction DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLabelProps) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn(\"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuSeparatorProps = React.ComponentProps<typeof DropdownMenuPrimitive.Separator>;\n\nfunction DropdownMenuSeparator({ className, ...props }: DropdownMenuSeparatorProps) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuShortcutProps = React.ComponentProps<\"span\">;\n\nfunction DropdownMenuShortcut({ className, ...props }: DropdownMenuShortcutProps) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport type DropdownMenuSubProps = React.ComponentProps<typeof DropdownMenuPrimitive.Sub>;\n\nfunction DropdownMenuSub({ ...props }: DropdownMenuSubProps) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nexport type DropdownMenuSubTriggerProps = React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n};\n\nfunction DropdownMenuSubTrigger({ className, inset, children, ...props }: DropdownMenuSubTriggerProps) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\n/*\n * ─ VÌ SAO `SubContent` PHẢI BỌC `Portal` ─\n *\n * Không bọc thì Radix render nó (kèm lớp bọc định vị `position: fixed`) làm CON của\n * `DropdownMenuContent`, mà `DropdownMenuContent` có `overflow-x-hidden overflow-y-auto`. `position: fixed`\n * bình thường thoát được `overflow` — TRỪ KHI có tổ tiên tạo containing block cho nó, và\n * `DropdownMenuContent` tạo đúng thứ đó: các lớp `data-[state=open]:animate-in` / `zoom-in-95` /\n * `slide-in-from-*` animate `transform`, với `animation-fill-mode: both` nên transform CÒN LẠI\n * sau khi hiệu ứng chạy xong (`scale(1)` vẫn là transform, không phải `none`).\n *\n * Hệ quả: submenu mở sang BÊN CẠNH menu cha, tức hoàn toàn nằm ngoài hộp của cha theo chiều\n * ngang, và bị `overflow-x-hidden` cắt sạch. Người dùng thấy mục có mũi tên ▸ mà bấm/rê vào\n * không ra gì — không lỗi, không cảnh báo.\n *\n * Đây là hồi quy đã biết của shadcn/ui: nó xuất hiện đúng lúc `DropdownMenuContent` được thêm\n * `max-h-… overflow-y-auto`. Bọc `Portal` đưa submenu ra thẳng `document.body`, thoát cả\n * `overflow` lẫn containing block.\n *\n * ⚠ Đừng \"sửa\" bằng cách bỏ `overflow-y-auto` ở `DropdownMenuContent`: lớp đó có việc của nó (menu\n * dài hơn màn hình thì phải cuộn được).\n */\nexport type DropdownMenuSubContentProps = React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>;\n\nfunction DropdownMenuSubContent({ className, ...props }: DropdownMenuSubContentProps) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuPortal,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger\n};\n"],"mappings":"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","names":[]}
|
|
@@ -129,24 +129,18 @@ function FieldDescription({ className, ...props }) {
|
|
|
129
129
|
);
|
|
130
130
|
}
|
|
131
131
|
function FieldSeparator({ children, className, ...props }) {
|
|
132
|
-
return /* @__PURE__ */
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
133
|
"div",
|
|
134
134
|
{
|
|
135
135
|
"data-slot": "field-separator",
|
|
136
136
|
"data-content": !!children,
|
|
137
137
|
className: cn("relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2", className),
|
|
138
138
|
...props,
|
|
139
|
-
children: [
|
|
140
|
-
/* @__PURE__ */ jsx(Separator, { className: "
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
className: "bg-background text-muted-foreground relative mx-auto block w-fit px-2",
|
|
145
|
-
"data-slot": "field-separator-content",
|
|
146
|
-
children
|
|
147
|
-
}
|
|
148
|
-
)
|
|
149
|
-
]
|
|
139
|
+
children: children ? /* @__PURE__ */ jsxs("div", { className: "flex h-full items-center gap-2", children: [
|
|
140
|
+
/* @__PURE__ */ jsx(Separator, { className: "flex-1" }),
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground shrink-0", "data-slot": "field-separator-content", children }),
|
|
142
|
+
/* @__PURE__ */ jsx(Separator, { className: "flex-1" })
|
|
143
|
+
] }) : /* @__PURE__ */ jsx(Separator, { className: "absolute inset-0 top-1/2" })
|
|
150
144
|
}
|
|
151
145
|
);
|
|
152
146
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/field.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Label, type LabelProps } from \"./label.js\";\nimport { Separator } from \"./separator.js\";\n\nexport type FieldSetProps = React.ComponentProps<\"fieldset\">;\n\nfunction FieldSet({ className, ...props }: FieldSetProps) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(\n \"flex flex-col gap-6\",\n \"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport interface FieldLegendProps extends React.ComponentProps<\"legend\"> {\n /** `legend` = tiêu đề nhóm cỡ base; `label` = cỡ sm như một nhãn thường. */\n variant?: \"legend\" | \"label\";\n}\n\nfunction FieldLegend({ className, variant = \"legend\", ...props }: FieldLegendProps) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\"mb-3 font-medium\", \"data-[variant=legend]:text-base\", \"data-[variant=label]:text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport type FieldGroupProps = React.ComponentProps<\"div\">;\n\nfunction FieldGroup({ className, ...props }: FieldGroupProps) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n \"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4\",\n className\n )}\n {...props}\n />\n );\n}\n\n// `text-destructive-ink` (bậc CHỮ đỏ) chứ không `text-destructive` (bậc NỀN đỏ của nút xoá):\n// bậc nền chỉ 3,76:1 trên `--background` và 3,53:1 trên `--card` ở dark ✗. Ink: 7,15:1 (light) ·\n// 6,84:1 / 6,42:1 (dark) ✓. Cùng một lối với `form.tsx` và `composite/form-field.tsx`.\nconst fieldVariants = cva(\"group/field flex w-full data-[invalid=true]:text-destructive-ink\", {\n variants: {\n orientation: {\n vertical: [\"flex-col gap-2 [&>*]:w-full [&>.sr-only]:w-auto\"],\n horizontal: [\n \"flex-row items-center gap-3\",\n \"[&>[data-slot=field-label]]:flex-auto\",\n \"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\"\n ],\n responsive: [\n \"flex-col gap-2 [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:gap-3 @md/field-group:[&>*]:w-auto\",\n \"@md/field-group:[&>[data-slot=field-label]]:flex-auto\",\n \"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\"\n ]\n }\n },\n defaultVariants: {\n orientation: \"vertical\"\n }\n});\n\nexport interface FieldProps extends React.ComponentProps<\"div\">, VariantProps<typeof fieldVariants> {}\n\nfunction Field({ className, orientation = \"vertical\", ...props }: FieldProps) {\n return (\n <div\n role=\"group\"\n data-slot=\"field\"\n data-orientation={orientation}\n className={cn(fieldVariants({ orientation }), className)}\n {...props}\n />\n );\n}\n\nexport type FieldContentProps = React.ComponentProps<\"div\">;\n\nfunction FieldContent({ className, ...props }: FieldContentProps) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\"group/field-content flex flex-1 flex-col gap-1.5 leading-snug\", className)}\n {...props}\n />\n );\n}\n\nexport type FieldLabelProps = LabelProps;\n\n/*\n * Thẻ chọn (label bọc cả một Field, kiểu radio-card): trạng thái ĐÃ CHỌN được kể bằng nền tint\n * `bg-primary/5` — chỉ khác nền trang cỡ 1,03:1 — nên VIỀN mới là tín hiệu thật, và nó chịu\n * ngưỡng 3:1 của WCAG 1.4.11. `border-primary` gold #E8B824 chỉ **1,86:1** với nền trang và\n * **1,80:1** với chính cái tint (light) ✗. `--primary-ink`: 6,51:1 với nền trang · 6,30:1 với\n * tint (light); 12,67:1 · 10,43:1 (dark) ✓. Tint giữ nguyên `--primary` vì đó là vai trò TÔ NỀN.\n */\nfunction FieldLabel({ className, ...props }: FieldLabelProps) {\n return (\n <Label\n data-slot=\"field-label\"\n className={cn(\n \"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50\",\n \"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4\",\n \"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary-ink dark:has-data-[state=checked]:bg-primary/10\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type FieldTitleProps = React.ComponentProps<\"div\">;\n\nfunction FieldTitle({ className, ...props }: FieldTitleProps) {\n return (\n <div\n data-slot=\"field-label\"\n className={cn(\n \"flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type FieldDescriptionProps = React.ComponentProps<\"p\">;\n\nfunction FieldDescription({ className, ...props }: FieldDescriptionProps) {\n return (\n <p\n data-slot=\"field-description\"\n className={cn(\n \"text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance\",\n \"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5\",\n \"[&>a:hover]:text-link-ink-hover [&>a]:underline [&>a]:underline-offset-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport interface FieldSeparatorProps extends React.ComponentProps<\"div\"> {\n /** Có nội dung thì chữ nằm giữa đường kẻ (kiểu \"hoặc\"). */\n children?: React.ReactNode;\n}\n\nfunction FieldSeparator({ children, className, ...props }: FieldSeparatorProps) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn(\"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2\", className)}\n {...props}\n >\n <Separator className=\"absolute inset-0 top-1/2\" />\n {children && (\n <span\n className=\"bg-background text-muted-foreground relative mx-auto block w-fit px-2\"\n data-slot=\"field-separator-content\"\n >\n {children}\n </span>\n )}\n </div>\n );\n}\n\nexport interface FieldErrorProps extends React.ComponentProps<\"div\"> {\n /**\n * Danh sách lỗi (khớp shape của react-hook-form). Bỏ qua khi đã truyền `children`.\n * Một lỗi → in thẳng; nhiều lỗi → danh sách gạch đầu dòng.\n */\n errors?: Array<{ message?: string } | undefined>;\n}\n\nfunction FieldError({ className, children, errors, ...props }: FieldErrorProps) {\n const content = useMemo(() => {\n if (children) {\n return children;\n }\n\n if (!errors) {\n return null;\n }\n\n // Lọc sạch phần tử rỗng trước để dùng chính message làm key (tránh key theo index).\n const messages = errors.flatMap((error) => (error?.message ? [error.message] : []));\n\n if (messages.length === 0) {\n return null;\n }\n\n if (messages.length === 1) {\n return messages[0];\n }\n\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {messages.map((message) => (\n <li key={message}>{message}</li>\n ))}\n </ul>\n );\n }, [children, errors]);\n\n if (!content) {\n return null;\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn(\"text-destructive-ink text-sm font-normal\", className)}\n {...props}\n >\n {content}\n </div>\n );\n}\n\nexport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldTitle,\n fieldVariants\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/field.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { useMemo } from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Label, type LabelProps } from \"./label.js\";\nimport { Separator } from \"./separator.js\";\n\nexport type FieldSetProps = React.ComponentProps<\"fieldset\">;\n\nfunction FieldSet({ className, ...props }: FieldSetProps) {\n return (\n <fieldset\n data-slot=\"field-set\"\n className={cn(\n \"flex flex-col gap-6\",\n \"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport interface FieldLegendProps extends React.ComponentProps<\"legend\"> {\n /** `legend` = tiêu đề nhóm cỡ base; `label` = cỡ sm như một nhãn thường. */\n variant?: \"legend\" | \"label\";\n}\n\nfunction FieldLegend({ className, variant = \"legend\", ...props }: FieldLegendProps) {\n return (\n <legend\n data-slot=\"field-legend\"\n data-variant={variant}\n className={cn(\"mb-3 font-medium\", \"data-[variant=legend]:text-base\", \"data-[variant=label]:text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport type FieldGroupProps = React.ComponentProps<\"div\">;\n\nfunction FieldGroup({ className, ...props }: FieldGroupProps) {\n return (\n <div\n data-slot=\"field-group\"\n className={cn(\n \"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4\",\n className\n )}\n {...props}\n />\n );\n}\n\n// `text-destructive-ink` (bậc CHỮ đỏ) chứ không `text-destructive` (bậc NỀN đỏ của nút xoá):\n// bậc nền chỉ 3,76:1 trên `--background` và 3,53:1 trên `--card` ở dark ✗. Ink: 7,15:1 (light) ·\n// 6,84:1 / 6,42:1 (dark) ✓. Cùng một lối với `form.tsx` và `composite/form-field.tsx`.\nconst fieldVariants = cva(\"group/field flex w-full data-[invalid=true]:text-destructive-ink\", {\n variants: {\n orientation: {\n vertical: [\"flex-col gap-2 [&>*]:w-full [&>.sr-only]:w-auto\"],\n horizontal: [\n \"flex-row items-center gap-3\",\n \"[&>[data-slot=field-label]]:flex-auto\",\n \"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\"\n ],\n responsive: [\n \"flex-col gap-2 [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:gap-3 @md/field-group:[&>*]:w-auto\",\n \"@md/field-group:[&>[data-slot=field-label]]:flex-auto\",\n \"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px\"\n ]\n }\n },\n defaultVariants: {\n orientation: \"vertical\"\n }\n});\n\nexport interface FieldProps extends React.ComponentProps<\"div\">, VariantProps<typeof fieldVariants> {}\n\nfunction Field({ className, orientation = \"vertical\", ...props }: FieldProps) {\n return (\n <div\n role=\"group\"\n data-slot=\"field\"\n data-orientation={orientation}\n className={cn(fieldVariants({ orientation }), className)}\n {...props}\n />\n );\n}\n\nexport type FieldContentProps = React.ComponentProps<\"div\">;\n\nfunction FieldContent({ className, ...props }: FieldContentProps) {\n return (\n <div\n data-slot=\"field-content\"\n className={cn(\"group/field-content flex flex-1 flex-col gap-1.5 leading-snug\", className)}\n {...props}\n />\n );\n}\n\nexport type FieldLabelProps = LabelProps;\n\n/*\n * Thẻ chọn (label bọc cả một Field, kiểu radio-card): trạng thái ĐÃ CHỌN được kể bằng nền tint\n * `bg-primary/5` — chỉ khác nền trang cỡ 1,03:1 — nên VIỀN mới là tín hiệu thật, và nó chịu\n * ngưỡng 3:1 của WCAG 1.4.11. `border-primary` gold #E8B824 chỉ **1,86:1** với nền trang và\n * **1,80:1** với chính cái tint (light) ✗. `--primary-ink`: 6,51:1 với nền trang · 6,30:1 với\n * tint (light); 12,67:1 · 10,43:1 (dark) ✓. Tint giữ nguyên `--primary` vì đó là vai trò TÔ NỀN.\n */\nfunction FieldLabel({ className, ...props }: FieldLabelProps) {\n return (\n <Label\n data-slot=\"field-label\"\n className={cn(\n \"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50\",\n \"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4\",\n \"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary-ink dark:has-data-[state=checked]:bg-primary/10\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type FieldTitleProps = React.ComponentProps<\"div\">;\n\nfunction FieldTitle({ className, ...props }: FieldTitleProps) {\n return (\n <div\n data-slot=\"field-label\"\n className={cn(\n \"flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type FieldDescriptionProps = React.ComponentProps<\"p\">;\n\nfunction FieldDescription({ className, ...props }: FieldDescriptionProps) {\n return (\n <p\n data-slot=\"field-description\"\n className={cn(\n \"text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance\",\n \"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5\",\n \"[&>a:hover]:text-link-ink-hover [&>a]:underline [&>a]:underline-offset-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport interface FieldSeparatorProps extends React.ComponentProps<\"div\"> {\n /** Có nội dung thì chữ nằm giữa đường kẻ (kiểu \"hoặc\"). */\n children?: React.ReactNode;\n}\n\n/*\n * ─ CÓ CHỮ THÌ CẮT ĐƯỜNG KẺ, KHÔNG PHỦ MỘT MẢNG NỀN LÊN NÓ ─\n *\n * Kiểu quen thuộc (shadcn gốc, và comitor-ds) là một đường kẻ chạy suốt cộng một `<span>` tô\n * `bg-background` đè lên giữa để \"khoét\" chỗ cho chữ. Cách đó chỉ đúng khi thành phần đứng THẲNG\n * trên nền trang — mà chỗ dùng thật gần như luôn là bên trong một `Card`:\n *\n * --background #0E1116 ≠ --card #16181D (theme tối)\n *\n * nên mảng tô hiện nguyên hình một hình chữ nhật TỐI HƠN nằm giữa thẻ. Ở theme sáng hai token\n * cùng là #FFFFFF nên lỗi vô hình — đây đúng loại lỗi chỉ lộ ra sau khi đổi theme, và không có\n * cách nào chữa từ phía app vì `<span>` nằm bên trong gói.\n *\n * Bản này dựng chữ ở GIỮA hai đoạn kẻ (`flex` + `flex-1`), nên không cần biết phía sau là nền gì:\n * đúng trên `--background`, trên `--card`, trên `--popover`, trên nền tô của một `Alert`. Nhánh\n * KHÔNG có chữ giữ nguyên đường kẻ tuyệt đối như cũ.\n */\nfunction FieldSeparator({ children, className, ...props }: FieldSeparatorProps) {\n return (\n <div\n data-slot=\"field-separator\"\n data-content={!!children}\n className={cn(\"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2\", className)}\n {...props}\n >\n {children ? (\n <div className=\"flex h-full items-center gap-2\">\n <Separator className=\"flex-1\" />\n <span className=\"text-muted-foreground shrink-0\" data-slot=\"field-separator-content\">\n {children}\n </span>\n <Separator className=\"flex-1\" />\n </div>\n ) : (\n <Separator className=\"absolute inset-0 top-1/2\" />\n )}\n </div>\n );\n}\n\nexport interface FieldErrorProps extends React.ComponentProps<\"div\"> {\n /**\n * Danh sách lỗi (khớp shape của react-hook-form). Bỏ qua khi đã truyền `children`.\n * Một lỗi → in thẳng; nhiều lỗi → danh sách gạch đầu dòng.\n */\n errors?: Array<{ message?: string } | undefined>;\n}\n\nfunction FieldError({ className, children, errors, ...props }: FieldErrorProps) {\n const content = useMemo(() => {\n if (children) {\n return children;\n }\n\n if (!errors) {\n return null;\n }\n\n // Lọc sạch phần tử rỗng trước để dùng chính message làm key (tránh key theo index).\n const messages = errors.flatMap((error) => (error?.message ? [error.message] : []));\n\n if (messages.length === 0) {\n return null;\n }\n\n if (messages.length === 1) {\n return messages[0];\n }\n\n return (\n <ul className=\"ml-4 flex list-disc flex-col gap-1\">\n {messages.map((message) => (\n <li key={message}>{message}</li>\n ))}\n </ul>\n );\n }, [children, errors]);\n\n if (!content) {\n return null;\n }\n\n return (\n <div\n role=\"alert\"\n data-slot=\"field-error\"\n className={cn(\"text-destructive-ink text-sm font-normal\", className)}\n {...props}\n >\n {content}\n </div>\n );\n}\n\nexport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSeparator,\n FieldSet,\n FieldTitle,\n fieldVariants\n};\n"],"mappings":"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","names":[]}
|
|
@@ -19,10 +19,28 @@ function InputGroup({ className, ...props }) {
|
|
|
19
19
|
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
20
20
|
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
21
21
|
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
22
|
-
// Trạng thái focus.
|
|
23
|
-
|
|
24
|
-
//
|
|
22
|
+
// Trạng thái focus. KHÔNG có `ring-offset-*`: quầng focus phải DÍNH SÁT viền, đúng như
|
|
23
|
+
// `Input`, `Textarea` và `SelectTrigger` (cả ba đều `focus:ring-2 focus:ring-ring/50`,
|
|
24
|
+
// không offset). Bản trước có `ring-offset-2 ring-offset-background` nên ô nhập trong
|
|
25
|
+
// nhóm — điển hình là ô mật khẩu có nút hiện/ẩn — vẽ ra 2px nền TRANG chen giữa viền và
|
|
26
|
+
// quầng: trên nền `--card` (khác `--background` ở theme tối) đó là một vòng tối lộ rõ, và
|
|
27
|
+
// cạnh một ô nhập thường trong CÙNG một biểu mẫu thì thành hai kiểu focus khác nhau.
|
|
28
|
+
// `ring-offset-*` là quy ước của control KHÔNG phải ô nhập chữ (Button, Checkbox, Switch,
|
|
29
|
+
// Tabs…) — đừng kéo sang họ ô nhập.
|
|
30
|
+
"has-[[data-slot=input-group-control]:focus-visible]:border-primary-ink has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2",
|
|
31
|
+
// Trạng thái lỗi — HAI đường, vì `aria-invalid` rơi vào hai chỗ khác nhau tuỳ cách dùng.
|
|
32
|
+
//
|
|
33
|
+
// 1. Tự lắp: `<InputGroup><InputGroupInput aria-invalid /></InputGroup>` → cờ nằm trên
|
|
34
|
+
// ô nhập CON, khớp bằng `has-[…]`.
|
|
35
|
+
// 2. Qua `<FormControl>` của `@comitor/ui/form`: Radix `Slot` dán `aria-invalid` lên
|
|
36
|
+
// CHÍNH thẻ `InputGroup` (nó là con trực tiếp của `FormControl`), không xuống tới ô
|
|
37
|
+
// nhập bên trong. `has-[…]` chỉ soi con cháu nên không bao giờ khớp — hệ quả là ô
|
|
38
|
+
// mật khẩu trong một `Form` KHÔNG BAO GIỜ đỏ viền khi validate hỏng, trong khi ô
|
|
39
|
+
// email ngay trên nó thì đỏ. Cùng họ với cạm bẫy `Combobox` đã ghi trong tài liệu.
|
|
40
|
+
//
|
|
41
|
+
// Giữ cả hai để không nhánh nào phải nhớ nhánh kia.
|
|
25
42
|
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive-ink dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
43
|
+
"aria-invalid:ring-destructive/20 aria-invalid:border-destructive-ink dark:aria-invalid:ring-destructive/40",
|
|
26
44
|
className
|
|
27
45
|
),
|
|
28
46
|
...props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/input-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { Textarea } from \"./textarea.js\";\n\n/*\n * ─ LỚP TÔ Ở DARK: `dark:bg-input-fill`, KHÔNG PHẢI `bg-input/30` (cùng lý do với Textarea) ─\n * Ở bảng mặc định lớp tô là thứ khoanh vùng nhóm ở dark; ở bảng tương phản cao nó trong suốt vì\n * `--input` bên đó là #7E8290 và tô 30% sẽ kéo chữ xuống dưới ngưỡng.\n *\n * Riêng ở đây cái sai còn kín hơn Textarea nếu ai đó viết thẳng `bg-input/30`: lớp tô nằm trên\n * thẻ CHA, còn chữ `--muted-foreground` nằm ở `InputGroupAddon` và ở placeholder của\n * `Input`/`Textarea` con — tức KHÁC chuỗi lớp, nên `scripts/check-contrast.mjs` không thấy được\n * cặp này (giới hạn §8.1). Con số nếu viết thẳng: 3,81 · 3,54 · 3,22:1 ✗ ở dark.\n */\nexport type InputGroupProps = React.ComponentProps<\"div\">;\n\nfunction InputGroup({ className, ...props }: InputGroupProps) {\n return (\n <div\n data-slot=\"input-group\"\n role=\"group\"\n className={cn(\n \"group/input-group border-input dark:bg-input-fill relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none\",\n \"h-9 has-[>textarea]:h-auto\",\n\n // Biến thể theo vị trí addon.\n \"has-[>[data-align=inline-start]]:[&>input]:pl-2\",\n \"has-[>[data-align=inline-end]]:[&>input]:pr-2\",\n \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3\",\n \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3\",\n\n // Trạng thái focus.\n \"has-[[data-slot=input-group-control]:focus-visible]:border-primary-ink has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-background\",\n\n // Trạng thái lỗi.\n \"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive-ink dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n\n className\n )}\n {...props}\n />\n );\n}\n\nconst inputGroupAddonVariants = cva(\n \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n {\n variants: {\n align: {\n \"inline-start\": \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n \"inline-end\": \"order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]\",\n \"block-start\":\n \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n \"block-end\": \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\"\n }\n },\n defaultVariants: {\n align: \"inline-start\"\n }\n }\n);\n\nexport interface InputGroupAddonProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof inputGroupAddonVariants> {}\n\n/*\n * Bấm vào vùng addon thì dồn focus sang ô nhập bên trong (trừ khi bấm trúng một nút).\n * Đây là tiện ích chuột thuần tuý — bàn phím vẫn Tab thẳng vào input/button nên không\n * cần onKeyDown; addon không phải phần tử tương tác.\n */\nfunction InputGroupAddon({ className, align = \"inline-start\", ...props }: InputGroupAddonProps) {\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: chỉ là lối tắt bằng chuột, bàn phím đã Tab thẳng tới control\n <div\n role=\"group\"\n data-slot=\"input-group-addon\"\n data-align={align}\n className={cn(inputGroupAddonVariants({ align }), className)}\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button\")) {\n return;\n }\n event.currentTarget.parentElement?.querySelector(\"input\")?.focus();\n }}\n {...props}\n />\n );\n}\n\nconst inputGroupButtonVariants = cva(\"text-sm shadow-none flex gap-2 items-center\", {\n variants: {\n size: {\n xs: \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n sm: \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n \"icon-xs\": \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\"\n }\n },\n defaultVariants: {\n size: \"xs\"\n }\n});\n\nexport type InputGroupButtonProps = Omit<ButtonProps, \"size\"> & VariantProps<typeof inputGroupButtonVariants>;\n\nfunction InputGroupButton({\n className,\n type = \"button\",\n variant = \"ghost\",\n size = \"xs\",\n ...props\n}: InputGroupButtonProps) {\n return (\n <Button\n type={type}\n data-size={size}\n variant={variant}\n className={cn(inputGroupButtonVariants({ size }), className)}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextProps = React.ComponentProps<\"span\">;\n\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n return (\n <span\n className={cn(\n \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupInputProps = React.ComponentProps<\"input\">;\n\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n return (\n <Input\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n return (\n <Textarea\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n InputGroupText,\n InputGroupTextarea,\n inputGroupAddonVariants,\n inputGroupButtonVariants\n};\n"],"mappings":";AAuBI;AArBJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AACnB,SAAS,cAAgC;AACzC,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAczB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAGA;AAAA;AAAA,QAGA;AAAA,QAEA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,eACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAWA,SAAS,gBAAgB,EAAE,WAAW,QAAQ,gBAAgB,GAAG,MAAM,GAAyB;AAC9F;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,cAAY;AAAA,QACZ,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,GAAG,SAAS;AAAA,QAC3D,SAAS,CAAC,UAAU;AAClB,cAAK,MAAM,OAAuB,QAAQ,QAAQ,GAAG;AACnD;AAAA,UACF;AACA,gBAAM,cAAc,eAAe,cAAc,OAAO,GAAG,MAAM;AAAA,QACnE;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA;AAEJ;AAEA,MAAM,2BAA2B,IAAI,+CAA+C;AAAA,EAClF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAID,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAW;AAAA,MACX;AAAA,MACA,WAAW,GAAG,yBAAyB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MAC1D,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/input-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { Textarea } from \"./textarea.js\";\n\n/*\n * ─ LỚP TÔ Ở DARK: `dark:bg-input-fill`, KHÔNG PHẢI `bg-input/30` (cùng lý do với Textarea) ─\n * Ở bảng mặc định lớp tô là thứ khoanh vùng nhóm ở dark; ở bảng tương phản cao nó trong suốt vì\n * `--input` bên đó là #7E8290 và tô 30% sẽ kéo chữ xuống dưới ngưỡng.\n *\n * Riêng ở đây cái sai còn kín hơn Textarea nếu ai đó viết thẳng `bg-input/30`: lớp tô nằm trên\n * thẻ CHA, còn chữ `--muted-foreground` nằm ở `InputGroupAddon` và ở placeholder của\n * `Input`/`Textarea` con — tức KHÁC chuỗi lớp, nên `scripts/check-contrast.mjs` không thấy được\n * cặp này (giới hạn §8.1). Con số nếu viết thẳng: 3,81 · 3,54 · 3,22:1 ✗ ở dark.\n */\nexport type InputGroupProps = React.ComponentProps<\"div\">;\n\nfunction InputGroup({ className, ...props }: InputGroupProps) {\n return (\n <div\n data-slot=\"input-group\"\n role=\"group\"\n className={cn(\n \"group/input-group border-input dark:bg-input-fill relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none\",\n \"h-9 has-[>textarea]:h-auto\",\n\n // Biến thể theo vị trí addon.\n \"has-[>[data-align=inline-start]]:[&>input]:pl-2\",\n \"has-[>[data-align=inline-end]]:[&>input]:pr-2\",\n \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3\",\n \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3\",\n\n // Trạng thái focus. KHÔNG có `ring-offset-*`: quầng focus phải DÍNH SÁT viền, đúng như\n // `Input`, `Textarea` và `SelectTrigger` (cả ba đều `focus:ring-2 focus:ring-ring/50`,\n // không offset). Bản trước có `ring-offset-2 ring-offset-background` nên ô nhập trong\n // nhóm — điển hình là ô mật khẩu có nút hiện/ẩn — vẽ ra 2px nền TRANG chen giữa viền và\n // quầng: trên nền `--card` (khác `--background` ở theme tối) đó là một vòng tối lộ rõ, và\n // cạnh một ô nhập thường trong CÙNG một biểu mẫu thì thành hai kiểu focus khác nhau.\n // `ring-offset-*` là quy ước của control KHÔNG phải ô nhập chữ (Button, Checkbox, Switch,\n // Tabs…) — đừng kéo sang họ ô nhập.\n \"has-[[data-slot=input-group-control]:focus-visible]:border-primary-ink has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2\",\n\n // Trạng thái lỗi — HAI đường, vì `aria-invalid` rơi vào hai chỗ khác nhau tuỳ cách dùng.\n //\n // 1. Tự lắp: `<InputGroup><InputGroupInput aria-invalid /></InputGroup>` → cờ nằm trên\n // ô nhập CON, khớp bằng `has-[…]`.\n // 2. Qua `<FormControl>` của `@comitor/ui/form`: Radix `Slot` dán `aria-invalid` lên\n // CHÍNH thẻ `InputGroup` (nó là con trực tiếp của `FormControl`), không xuống tới ô\n // nhập bên trong. `has-[…]` chỉ soi con cháu nên không bao giờ khớp — hệ quả là ô\n // mật khẩu trong một `Form` KHÔNG BAO GIỜ đỏ viền khi validate hỏng, trong khi ô\n // email ngay trên nó thì đỏ. Cùng họ với cạm bẫy `Combobox` đã ghi trong tài liệu.\n //\n // Giữ cả hai để không nhánh nào phải nhớ nhánh kia.\n \"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive-ink dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n \"aria-invalid:ring-destructive/20 aria-invalid:border-destructive-ink dark:aria-invalid:ring-destructive/40\",\n\n className\n )}\n {...props}\n />\n );\n}\n\nconst inputGroupAddonVariants = cva(\n \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n {\n variants: {\n align: {\n \"inline-start\": \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n \"inline-end\": \"order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]\",\n \"block-start\":\n \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n \"block-end\": \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\"\n }\n },\n defaultVariants: {\n align: \"inline-start\"\n }\n }\n);\n\nexport interface InputGroupAddonProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof inputGroupAddonVariants> {}\n\n/*\n * Bấm vào vùng addon thì dồn focus sang ô nhập bên trong (trừ khi bấm trúng một nút).\n * Đây là tiện ích chuột thuần tuý — bàn phím vẫn Tab thẳng vào input/button nên không\n * cần onKeyDown; addon không phải phần tử tương tác.\n */\nfunction InputGroupAddon({ className, align = \"inline-start\", ...props }: InputGroupAddonProps) {\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: chỉ là lối tắt bằng chuột, bàn phím đã Tab thẳng tới control\n <div\n role=\"group\"\n data-slot=\"input-group-addon\"\n data-align={align}\n className={cn(inputGroupAddonVariants({ align }), className)}\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button\")) {\n return;\n }\n event.currentTarget.parentElement?.querySelector(\"input\")?.focus();\n }}\n {...props}\n />\n );\n}\n\nconst inputGroupButtonVariants = cva(\"text-sm shadow-none flex gap-2 items-center\", {\n variants: {\n size: {\n xs: \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n sm: \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n \"icon-xs\": \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\"\n }\n },\n defaultVariants: {\n size: \"xs\"\n }\n});\n\nexport type InputGroupButtonProps = Omit<ButtonProps, \"size\"> & VariantProps<typeof inputGroupButtonVariants>;\n\nfunction InputGroupButton({\n className,\n type = \"button\",\n variant = \"ghost\",\n size = \"xs\",\n ...props\n}: InputGroupButtonProps) {\n return (\n <Button\n type={type}\n data-size={size}\n variant={variant}\n className={cn(inputGroupButtonVariants({ size }), className)}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextProps = React.ComponentProps<\"span\">;\n\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n return (\n <span\n className={cn(\n \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupInputProps = React.ComponentProps<\"input\">;\n\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n return (\n <Input\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n return (\n <Textarea\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n InputGroupText,\n InputGroupTextarea,\n inputGroupAddonVariants,\n inputGroupButtonVariants\n};\n"],"mappings":";AAuBI;AArBJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AACnB,SAAS,cAAgC;AACzC,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAczB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAUA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAaA;AAAA,QACA;AAAA,QAEA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,eACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAWA,SAAS,gBAAgB,EAAE,WAAW,QAAQ,gBAAgB,GAAG,MAAM,GAAyB;AAC9F;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,cAAY;AAAA,QACZ,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,GAAG,SAAS;AAAA,QAC3D,SAAS,CAAC,UAAU;AAClB,cAAK,MAAM,OAAuB,QAAQ,QAAQ,GAAG;AACnD;AAAA,UACF;AACA,gBAAM,cAAc,eAAe,cAAc,OAAO,GAAG,MAAM;AAAA,QACnE;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA;AAEJ;AAEA,MAAM,2BAA2B,IAAI,+CAA+C;AAAA,EAClF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAID,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAW;AAAA,MACX;AAAA,MACA,WAAW,GAAG,yBAAyB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MAC1D,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/item.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Separator, type SeparatorProps } from \"./separator.js\";\n\nexport type ItemGroupProps = React.ComponentProps<\"div\">;\nexport type ItemSeparatorProps = SeparatorProps;\n\nfunction ItemGroup({ className, ...props }: ItemGroupProps) {\n return (\n <div role=\"list\" data-slot=\"item-group\" className={cn(\"group/item-group flex flex-col\", className)} {...props} />\n );\n}\n\nfunction ItemSeparator({ className, ...props }: ItemSeparatorProps) {\n return <Separator data-slot=\"item-separator\" orientation=\"horizontal\" className={cn(\"my-0\", className)} {...props} />;\n}\n\nconst itemVariants = cva(\n \"group/item flex items-center border border-transparent text-sm rounded-md transition-colors duration-150 ease-out [a&]:hover:bg-accent/50 [a&]:transition-colors flex-wrap outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n {\n variants: {\n variant: {\n default: \"bg-transparent\",\n outline: \"border-border\",\n muted: \"bg-muted/50\"\n },\n size: {\n default: \"p-4 gap-4\",\n sm: \"py-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/item.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Separator, type SeparatorProps } from \"./separator.js\";\n\nexport type ItemGroupProps = React.ComponentProps<\"div\">;\nexport type ItemSeparatorProps = SeparatorProps;\n\nfunction ItemGroup({ className, ...props }: ItemGroupProps) {\n return (\n <div role=\"list\" data-slot=\"item-group\" className={cn(\"group/item-group flex flex-col\", className)} {...props} />\n );\n}\n\nfunction ItemSeparator({ className, ...props }: ItemSeparatorProps) {\n return <Separator data-slot=\"item-separator\" orientation=\"horizontal\" className={cn(\"my-0\", className)} {...props} />;\n}\n\nconst itemVariants = cva(\n \"group/item flex items-center border border-transparent text-sm rounded-md transition-colors duration-150 ease-out [a&]:hover:bg-accent/50 [a&]:transition-colors flex-wrap outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n {\n variants: {\n variant: {\n default: \"bg-transparent\",\n outline: \"border-border\",\n muted: \"bg-muted/50\"\n },\n size: {\n default: \"p-4 gap-4\",\n sm: \"py-(--list-item-py) px-4 gap-2.5\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\"\n }\n }\n);\n\nexport interface ItemProps extends React.ComponentProps<\"div\">, VariantProps<typeof itemVariants> {\n /** Render ra chính phần tử con (Radix Slot) — dùng khi cần <a> hoặc <button>. */\n asChild?: boolean;\n}\n\nfunction Item({ className, variant = \"default\", size = \"default\", asChild = false, ...props }: ItemProps) {\n const Comp = asChild ? Slot : \"div\";\n return (\n <Comp\n data-slot=\"item\"\n data-variant={variant}\n data-size={size}\n className={cn(itemVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nconst itemMediaVariants = cva(\n \"flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5\",\n {\n variants: {\n variant: {\n default: \"bg-transparent\",\n icon: \"size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4\",\n image: \"size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover\"\n }\n },\n defaultVariants: {\n variant: \"default\"\n }\n }\n);\n\nexport interface ItemMediaProps extends React.ComponentProps<\"div\">, VariantProps<typeof itemMediaVariants> {}\nexport type ItemContentProps = React.ComponentProps<\"div\">;\nexport type ItemTitleProps = React.ComponentProps<\"div\">;\nexport type ItemDescriptionProps = React.ComponentProps<\"p\">;\nexport type ItemActionsProps = React.ComponentProps<\"div\">;\nexport type ItemHeaderProps = React.ComponentProps<\"div\">;\nexport type ItemFooterProps = React.ComponentProps<\"div\">;\n\nfunction ItemMedia({ className, variant = \"default\", ...props }: ItemMediaProps) {\n return (\n <div\n data-slot=\"item-media\"\n data-variant={variant}\n className={cn(itemMediaVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nfunction ItemContent({ className, ...props }: ItemContentProps) {\n return (\n <div\n data-slot=\"item-content\"\n className={cn(\"flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none\", className)}\n {...props}\n />\n );\n}\n\nfunction ItemTitle({ className, ...props }: ItemTitleProps) {\n return (\n <div\n data-slot=\"item-title\"\n className={cn(\"flex w-fit items-center gap-2 text-sm leading-snug font-medium\", className)}\n {...props}\n />\n );\n}\n\nfunction ItemDescription({ className, ...props }: ItemDescriptionProps) {\n return (\n <p\n data-slot=\"item-description\"\n className={cn(\n \"text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance\",\n \"[&>a:hover]:text-link-ink-hover [&>a]:underline [&>a]:underline-offset-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction ItemActions({ className, ...props }: ItemActionsProps) {\n return <div data-slot=\"item-actions\" className={cn(\"flex items-center gap-2\", className)} {...props} />;\n}\n\nfunction ItemHeader({ className, ...props }: ItemHeaderProps) {\n return (\n <div\n data-slot=\"item-header\"\n className={cn(\"flex basis-full items-center justify-between gap-2\", className)}\n {...props}\n />\n );\n}\n\nfunction ItemFooter({ className, ...props }: ItemFooterProps) {\n return (\n <div\n data-slot=\"item-footer\"\n className={cn(\"flex basis-full items-center justify-between gap-2\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Item,\n ItemActions,\n ItemContent,\n ItemDescription,\n ItemFooter,\n ItemGroup,\n ItemHeader,\n ItemMedia,\n ItemSeparator,\n ItemTitle,\n itemMediaVariants,\n itemVariants\n};\n"],"mappings":"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","names":[]}
|
|
@@ -68,7 +68,7 @@ function MenubarItem({ className, inset, variant = "default", ...props }) {
|
|
|
68
68
|
"data-inset": inset,
|
|
69
69
|
"data-variant": variant,
|
|
70
70
|
className: cn(
|
|
71
|
-
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-
|
|
71
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
72
72
|
className
|
|
73
73
|
),
|
|
74
74
|
...props
|
|
@@ -81,7 +81,7 @@ function MenubarCheckboxItem({ className, children, checked, ...props }) {
|
|
|
81
81
|
{
|
|
82
82
|
"data-slot": "menubar-checkbox-item",
|
|
83
83
|
className: cn(
|
|
84
|
-
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-
|
|
84
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-(--menu-item-py) pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
85
85
|
className
|
|
86
86
|
),
|
|
87
87
|
checked,
|
|
@@ -99,7 +99,7 @@ function MenubarRadioItem({ className, children, ...props }) {
|
|
|
99
99
|
{
|
|
100
100
|
"data-slot": "menubar-radio-item",
|
|
101
101
|
className: cn(
|
|
102
|
-
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-
|
|
102
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-(--menu-item-py) pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
103
103
|
className
|
|
104
104
|
),
|
|
105
105
|
...props,
|
|
@@ -151,7 +151,7 @@ function MenubarSubTrigger({ className, inset, children, ...props }) {
|
|
|
151
151
|
"data-slot": "menubar-sub-trigger",
|
|
152
152
|
"data-inset": inset,
|
|
153
153
|
className: cn(
|
|
154
|
-
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center mt-1 first:mt-0 rounded-sm px-2 py-
|
|
154
|
+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center mt-1 first:mt-0 rounded-sm px-2 py-(--menu-item-py) text-sm outline-none select-none data-[inset]:pl-8",
|
|
155
155
|
className
|
|
156
156
|
),
|
|
157
157
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/menubar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as MenubarPrimitive from \"@radix-ui/react-menubar\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Menubar — thanh menu ngang kiểu ứng dụng desktop (Radix Menubar).\n *\n * Dùng cho màn hình soạn thảo/bảng tính (Docs, Sheets nội bộ) — nơi cần nhóm lệnh\n * cố định. KHÔNG dùng làm điều hướng chính của app: điều hướng nằm ở Sidebar/AppHeader.\n */\n\nexport type MenubarProps = React.ComponentProps<typeof MenubarPrimitive.Root>;\n\nfunction Menubar({ className, ...props }: MenubarProps) {\n return (\n <MenubarPrimitive.Root\n data-slot=\"menubar\"\n className={cn(\"bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarMenuProps = React.ComponentProps<typeof MenubarPrimitive.Menu>;\n\nfunction MenubarMenu({ ...props }: MenubarMenuProps) {\n return <MenubarPrimitive.Menu data-slot=\"menubar-menu\" {...props} />;\n}\n\nexport type MenubarGroupProps = React.ComponentProps<typeof MenubarPrimitive.Group>;\n\nfunction MenubarGroup({ ...props }: MenubarGroupProps) {\n return <MenubarPrimitive.Group data-slot=\"menubar-group\" {...props} />;\n}\n\nexport type MenubarPortalProps = React.ComponentProps<typeof MenubarPrimitive.Portal>;\n\nfunction MenubarPortal({ ...props }: MenubarPortalProps) {\n return <MenubarPrimitive.Portal data-slot=\"menubar-portal\" {...props} />;\n}\n\nexport type MenubarRadioGroupProps = React.ComponentProps<typeof MenubarPrimitive.RadioGroup>;\n\nfunction MenubarRadioGroup({ ...props }: MenubarRadioGroupProps) {\n return <MenubarPrimitive.RadioGroup data-slot=\"menubar-radio-group\" {...props} />;\n}\n\nexport type MenubarTriggerProps = React.ComponentProps<typeof MenubarPrimitive.Trigger>;\n\nfunction MenubarTrigger({ className, ...props }: MenubarTriggerProps) {\n return (\n <MenubarPrimitive.Trigger\n data-slot=\"menubar-trigger\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type MenubarContentProps = React.ComponentProps<typeof MenubarPrimitive.Content>;\n\nfunction MenubarContent({\n className,\n align = \"start\",\n alignOffset = -4,\n sideOffset = 8,\n ...props\n}: MenubarContentProps) {\n return (\n <MenubarPortal>\n <MenubarPrimitive.Content\n data-slot=\"menubar-content\"\n align={align}\n alignOffset={alignOffset}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </MenubarPortal>\n );\n}\n\nexport type MenubarItemVariant = \"default\" | \"destructive\";\n\nexport type MenubarItemProps = React.ComponentProps<typeof MenubarPrimitive.Item> & {\n inset?: boolean;\n variant?: MenubarItemVariant;\n};\n\nfunction MenubarItem({ className, inset, variant = \"default\", ...props }: MenubarItemProps) {\n return (\n <MenubarPrimitive.Item\n data-slot=\"menubar-item\"\n data-inset={inset}\n data-variant={variant}\n // `text-destructive-ink` chứ KHÔNG `text-destructive`: `--destructive` là màu NỀN (nút xoá),\n // ghim một giá trị cho cả hai theme nên làm CHỮ trên nền popover tối chỉ được 3,53:1 ✗ —\n // và tệ thêm khi focus vì nền chuyển sang `bg-destructive/20` (2,98:1 ✗). Bậc ink đạt cả\n // hai theme, cả hai nền: 7,15:1 / 6,18:1 (light, nghỉ / focus) · 6,42:1 / 5,42:1 (dark).\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type MenubarCheckboxItemProps = React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>;\n\nfunction MenubarCheckboxItem({ className, children, checked, ...props }: MenubarCheckboxItemProps) {\n return (\n <MenubarPrimitive.CheckboxItem\n data-slot=\"menubar-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenubarPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </MenubarPrimitive.ItemIndicator>\n </span>\n {children}\n </MenubarPrimitive.CheckboxItem>\n );\n}\n\nexport type MenubarRadioItemProps = React.ComponentProps<typeof MenubarPrimitive.RadioItem>;\n\nfunction MenubarRadioItem({ className, children, ...props }: MenubarRadioItemProps) {\n return (\n <MenubarPrimitive.RadioItem\n data-slot=\"menubar-radio-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenubarPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </MenubarPrimitive.ItemIndicator>\n </span>\n {children}\n </MenubarPrimitive.RadioItem>\n );\n}\n\nexport type MenubarLabelProps = React.ComponentProps<typeof MenubarPrimitive.Label> & {\n inset?: boolean;\n};\n\nfunction MenubarLabel({ className, inset, ...props }: MenubarLabelProps) {\n return (\n <MenubarPrimitive.Label\n data-slot=\"menubar-label\"\n data-inset={inset}\n className={cn(\"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarSeparatorProps = React.ComponentProps<typeof MenubarPrimitive.Separator>;\n\nfunction MenubarSeparator({ className, ...props }: MenubarSeparatorProps) {\n return (\n <MenubarPrimitive.Separator\n data-slot=\"menubar-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarShortcutProps = React.ComponentProps<\"span\">;\n\nfunction MenubarShortcut({ className, ...props }: MenubarShortcutProps) {\n return (\n <span\n data-slot=\"menubar-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarSubProps = React.ComponentProps<typeof MenubarPrimitive.Sub>;\n\nfunction MenubarSub({ ...props }: MenubarSubProps) {\n return <MenubarPrimitive.Sub data-slot=\"menubar-sub\" {...props} />;\n}\n\nexport type MenubarSubTriggerProps = React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {\n inset?: boolean;\n};\n\nfunction MenubarSubTrigger({ className, inset, children, ...props }: MenubarSubTriggerProps) {\n return (\n <MenubarPrimitive.SubTrigger\n data-slot=\"menubar-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center mt-1 first:mt-0 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto h-4 w-4\" />\n </MenubarPrimitive.SubTrigger>\n );\n}\n\n/*\n * ─ VÌ SAO `SubContent` PHẢI BỌC `Portal` ─\n *\n * Không bọc thì Radix render nó (kèm lớp bọc định vị `position: fixed`) làm CON của\n * `MenubarContent`, mà `MenubarContent` có `overflow-x-hidden overflow-y-auto`. `position: fixed`\n * bình thường thoát được `overflow` — TRỪ KHI có tổ tiên tạo containing block cho nó, và\n * `MenubarContent` tạo đúng thứ đó: các lớp `data-[state=open]:animate-in` / `zoom-in-95` /\n * `slide-in-from-*` animate `transform`, với `animation-fill-mode: both` nên transform CÒN LẠI\n * sau khi hiệu ứng chạy xong (`scale(1)` vẫn là transform, không phải `none`).\n *\n * Hệ quả: submenu mở sang BÊN CẠNH menu cha, tức hoàn toàn nằm ngoài hộp của cha theo chiều\n * ngang, và bị `overflow-x-hidden` cắt sạch. Người dùng thấy mục có mũi tên ▸ mà bấm/rê vào\n * không ra gì — không lỗi, không cảnh báo.\n *\n * Đây là hồi quy đã biết của shadcn/ui: nó xuất hiện đúng lúc `MenubarContent` được thêm\n * `max-h-… overflow-y-auto`. Bọc `Portal` đưa submenu ra thẳng `document.body`, thoát cả\n * `overflow` lẫn containing block.\n *\n * ⚠ Đừng \"sửa\" bằng cách bỏ `overflow-y-auto` ở `MenubarContent`: lớp đó có việc của nó (menu\n * dài hơn màn hình thì phải cuộn được).\n */\nexport type MenubarSubContentProps = React.ComponentProps<typeof MenubarPrimitive.SubContent>;\n\nfunction MenubarSubContent({ className, ...props }: MenubarSubContentProps) {\n return (\n <MenubarPrimitive.Portal>\n <MenubarPrimitive.SubContent\n data-slot=\"menubar-sub-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </MenubarPrimitive.Portal>\n );\n}\n\nexport {\n Menubar,\n MenubarCheckboxItem,\n MenubarContent,\n MenubarGroup,\n MenubarItem,\n MenubarLabel,\n MenubarMenu,\n MenubarPortal,\n MenubarRadioGroup,\n MenubarRadioItem,\n MenubarSeparator,\n MenubarShortcut,\n MenubarSub,\n MenubarSubContent,\n MenubarSubTrigger,\n MenubarTrigger\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/menubar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as MenubarPrimitive from \"@radix-ui/react-menubar\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Menubar — thanh menu ngang kiểu ứng dụng desktop (Radix Menubar).\n *\n * Dùng cho màn hình soạn thảo/bảng tính (Docs, Sheets nội bộ) — nơi cần nhóm lệnh\n * cố định. KHÔNG dùng làm điều hướng chính của app: điều hướng nằm ở Sidebar/AppHeader.\n */\n\nexport type MenubarProps = React.ComponentProps<typeof MenubarPrimitive.Root>;\n\nfunction Menubar({ className, ...props }: MenubarProps) {\n return (\n <MenubarPrimitive.Root\n data-slot=\"menubar\"\n className={cn(\"bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarMenuProps = React.ComponentProps<typeof MenubarPrimitive.Menu>;\n\nfunction MenubarMenu({ ...props }: MenubarMenuProps) {\n return <MenubarPrimitive.Menu data-slot=\"menubar-menu\" {...props} />;\n}\n\nexport type MenubarGroupProps = React.ComponentProps<typeof MenubarPrimitive.Group>;\n\nfunction MenubarGroup({ ...props }: MenubarGroupProps) {\n return <MenubarPrimitive.Group data-slot=\"menubar-group\" {...props} />;\n}\n\nexport type MenubarPortalProps = React.ComponentProps<typeof MenubarPrimitive.Portal>;\n\nfunction MenubarPortal({ ...props }: MenubarPortalProps) {\n return <MenubarPrimitive.Portal data-slot=\"menubar-portal\" {...props} />;\n}\n\nexport type MenubarRadioGroupProps = React.ComponentProps<typeof MenubarPrimitive.RadioGroup>;\n\nfunction MenubarRadioGroup({ ...props }: MenubarRadioGroupProps) {\n return <MenubarPrimitive.RadioGroup data-slot=\"menubar-radio-group\" {...props} />;\n}\n\nexport type MenubarTriggerProps = React.ComponentProps<typeof MenubarPrimitive.Trigger>;\n\nfunction MenubarTrigger({ className, ...props }: MenubarTriggerProps) {\n return (\n <MenubarPrimitive.Trigger\n data-slot=\"menubar-trigger\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type MenubarContentProps = React.ComponentProps<typeof MenubarPrimitive.Content>;\n\nfunction MenubarContent({\n className,\n align = \"start\",\n alignOffset = -4,\n sideOffset = 8,\n ...props\n}: MenubarContentProps) {\n return (\n <MenubarPortal>\n <MenubarPrimitive.Content\n data-slot=\"menubar-content\"\n align={align}\n alignOffset={alignOffset}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </MenubarPortal>\n );\n}\n\nexport type MenubarItemVariant = \"default\" | \"destructive\";\n\nexport type MenubarItemProps = React.ComponentProps<typeof MenubarPrimitive.Item> & {\n inset?: boolean;\n variant?: MenubarItemVariant;\n};\n\nfunction MenubarItem({ className, inset, variant = \"default\", ...props }: MenubarItemProps) {\n return (\n <MenubarPrimitive.Item\n data-slot=\"menubar-item\"\n data-inset={inset}\n data-variant={variant}\n // `text-destructive-ink` chứ KHÔNG `text-destructive`: `--destructive` là màu NỀN (nút xoá),\n // ghim một giá trị cho cả hai theme nên làm CHỮ trên nền popover tối chỉ được 3,53:1 ✗ —\n // và tệ thêm khi focus vì nền chuyển sang `bg-destructive/20` (2,98:1 ✗). Bậc ink đạt cả\n // hai theme, cả hai nền: 7,15:1 / 6,18:1 (light, nghỉ / focus) · 6,42:1 / 5,42:1 (dark).\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type MenubarCheckboxItemProps = React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>;\n\nfunction MenubarCheckboxItem({ className, children, checked, ...props }: MenubarCheckboxItemProps) {\n return (\n <MenubarPrimitive.CheckboxItem\n data-slot=\"menubar-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-(--menu-item-py) pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenubarPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </MenubarPrimitive.ItemIndicator>\n </span>\n {children}\n </MenubarPrimitive.CheckboxItem>\n );\n}\n\nexport type MenubarRadioItemProps = React.ComponentProps<typeof MenubarPrimitive.RadioItem>;\n\nfunction MenubarRadioItem({ className, children, ...props }: MenubarRadioItemProps) {\n return (\n <MenubarPrimitive.RadioItem\n data-slot=\"menubar-radio-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 mt-1 first:mt-0 rounded-xs py-(--menu-item-py) pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenubarPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </MenubarPrimitive.ItemIndicator>\n </span>\n {children}\n </MenubarPrimitive.RadioItem>\n );\n}\n\nexport type MenubarLabelProps = React.ComponentProps<typeof MenubarPrimitive.Label> & {\n inset?: boolean;\n};\n\nfunction MenubarLabel({ className, inset, ...props }: MenubarLabelProps) {\n return (\n <MenubarPrimitive.Label\n data-slot=\"menubar-label\"\n data-inset={inset}\n className={cn(\"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarSeparatorProps = React.ComponentProps<typeof MenubarPrimitive.Separator>;\n\nfunction MenubarSeparator({ className, ...props }: MenubarSeparatorProps) {\n return (\n <MenubarPrimitive.Separator\n data-slot=\"menubar-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarShortcutProps = React.ComponentProps<\"span\">;\n\nfunction MenubarShortcut({ className, ...props }: MenubarShortcutProps) {\n return (\n <span\n data-slot=\"menubar-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport type MenubarSubProps = React.ComponentProps<typeof MenubarPrimitive.Sub>;\n\nfunction MenubarSub({ ...props }: MenubarSubProps) {\n return <MenubarPrimitive.Sub data-slot=\"menubar-sub\" {...props} />;\n}\n\nexport type MenubarSubTriggerProps = React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {\n inset?: boolean;\n};\n\nfunction MenubarSubTrigger({ className, inset, children, ...props }: MenubarSubTriggerProps) {\n return (\n <MenubarPrimitive.SubTrigger\n data-slot=\"menubar-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center mt-1 first:mt-0 rounded-sm px-2 py-(--menu-item-py) text-sm outline-none select-none data-[inset]:pl-8\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto h-4 w-4\" />\n </MenubarPrimitive.SubTrigger>\n );\n}\n\n/*\n * ─ VÌ SAO `SubContent` PHẢI BỌC `Portal` ─\n *\n * Không bọc thì Radix render nó (kèm lớp bọc định vị `position: fixed`) làm CON của\n * `MenubarContent`, mà `MenubarContent` có `overflow-x-hidden overflow-y-auto`. `position: fixed`\n * bình thường thoát được `overflow` — TRỪ KHI có tổ tiên tạo containing block cho nó, và\n * `MenubarContent` tạo đúng thứ đó: các lớp `data-[state=open]:animate-in` / `zoom-in-95` /\n * `slide-in-from-*` animate `transform`, với `animation-fill-mode: both` nên transform CÒN LẠI\n * sau khi hiệu ứng chạy xong (`scale(1)` vẫn là transform, không phải `none`).\n *\n * Hệ quả: submenu mở sang BÊN CẠNH menu cha, tức hoàn toàn nằm ngoài hộp của cha theo chiều\n * ngang, và bị `overflow-x-hidden` cắt sạch. Người dùng thấy mục có mũi tên ▸ mà bấm/rê vào\n * không ra gì — không lỗi, không cảnh báo.\n *\n * Đây là hồi quy đã biết của shadcn/ui: nó xuất hiện đúng lúc `MenubarContent` được thêm\n * `max-h-… overflow-y-auto`. Bọc `Portal` đưa submenu ra thẳng `document.body`, thoát cả\n * `overflow` lẫn containing block.\n *\n * ⚠ Đừng \"sửa\" bằng cách bỏ `overflow-y-auto` ở `MenubarContent`: lớp đó có việc của nó (menu\n * dài hơn màn hình thì phải cuộn được).\n */\nexport type MenubarSubContentProps = React.ComponentProps<typeof MenubarPrimitive.SubContent>;\n\nfunction MenubarSubContent({ className, ...props }: MenubarSubContentProps) {\n return (\n <MenubarPrimitive.Portal>\n <MenubarPrimitive.SubContent\n data-slot=\"menubar-sub-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </MenubarPrimitive.Portal>\n );\n}\n\nexport {\n Menubar,\n MenubarCheckboxItem,\n MenubarContent,\n MenubarGroup,\n MenubarItem,\n MenubarLabel,\n MenubarMenu,\n MenubarPortal,\n MenubarRadioGroup,\n MenubarRadioItem,\n MenubarSeparator,\n MenubarShortcut,\n MenubarSub,\n MenubarSubContent,\n MenubarSubTrigger,\n MenubarTrigger\n};\n"],"mappings":";AAkBI,cAuGA,YAvGA;AAhBJ,YAAY,sBAAsB;AAClC,SAAS,WAAW,kBAAkB,kBAAkB;AAExD,SAAS,UAAU;AAWnB,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAiB;AACtD,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,6EAA6E,SAAS;AAAA,MACnG,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,YAAY,EAAE,GAAG,MAAM,GAAqB;AACnD,SAAO,oBAAC,iBAAiB,MAAjB,EAAsB,aAAU,gBAAgB,GAAG,OAAO;AACpE;AAIA,SAAS,aAAa,EAAE,GAAG,MAAM,GAAsB;AACrD,SAAO,oBAAC,iBAAiB,OAAjB,EAAuB,aAAU,iBAAiB,GAAG,OAAO;AACtE;AAIA,SAAS,cAAc,EAAE,GAAG,MAAM,GAAuB;AACvD,SAAO,oBAAC,iBAAiB,QAAjB,EAAwB,aAAU,kBAAkB,GAAG,OAAO;AACxE;AAIA,SAAS,kBAAkB,EAAE,GAAG,MAAM,GAA2B;AAC/D,SAAO,oBAAC,iBAAiB,YAAjB,EAA4B,aAAU,uBAAuB,GAAG,OAAO;AACjF;AAIA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,aAAa;AAAA,EACb,GAAG;AACL,GAAwB;AACtB,SACE,oBAAC,iBACC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AASA,SAAS,YAAY,EAAE,WAAW,OAAO,UAAU,WAAW,GAAG,MAAM,GAAqB;AAC1F,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MAKd,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,oBAAoB,EAAE,WAAW,UAAU,SAAS,GAAG,MAAM,GAA6B;AACjG,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,4BAAC,UAAK,WAAU,iFACd,8BAAC,iBAAiB,eAAjB,EACC,8BAAC,aAAU,WAAU,UAAS,GAChC,GACF;AAAA,QACC;AAAA;AAAA;AAAA,EACH;AAEJ;AAIA,SAAS,iBAAiB,EAAE,WAAW,UAAU,GAAG,MAAM,GAA0B;AAClF,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,4BAAC,UAAK,WAAU,iFACd,8BAAC,iBAAiB,eAAjB,EACC,8BAAC,cAAW,WAAU,uBAAsB,GAC9C,GACF;AAAA,QACC;AAAA;AAAA;AAAA,EACH;AAEJ;AAMA,SAAS,aAAa,EAAE,WAAW,OAAO,GAAG,MAAM,GAAsB;AACvE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW,GAAG,qDAAqD,SAAS;AAAA,MAC3E,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,iBAAiB,EAAE,WAAW,GAAG,MAAM,GAA0B;AACxE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,6BAA6B,SAAS;AAAA,MACnD,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,yDAAyD,SAAS;AAAA,MAC/E,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,WAAW,EAAE,GAAG,MAAM,GAAoB;AACjD,SAAO,oBAAC,iBAAiB,KAAjB,EAAqB,aAAU,eAAe,GAAG,OAAO;AAClE;AAMA,SAAS,kBAAkB,EAAE,WAAW,OAAO,UAAU,GAAG,MAAM,GAA2B;AAC3F,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD,oBAAC,oBAAiB,WAAU,mBAAkB;AAAA;AAAA;AAAA,EAChD;AAEJ;AAyBA,SAAS,kBAAkB,EAAE,WAAW,GAAG,MAAM,GAA2B;AAC1E,SACE,oBAAC,iBAAiB,QAAjB,EACC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;","names":[]}
|
|
@@ -75,7 +75,7 @@ function SelectItem({ className, children, ...props }) {
|
|
|
75
75
|
{
|
|
76
76
|
"data-slot": "select-item",
|
|
77
77
|
className: cn(
|
|
78
|
-
"focus:bg-accent data-[state=checked]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-
|
|
78
|
+
"focus:bg-accent data-[state=checked]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-(--menu-item-py) pr-8 pl-2 text-sm outline-hidden select-none transition-colors duration-150 ease-out data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
79
79
|
className
|
|
80
80
|
),
|
|
81
81
|
...props,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Select (Radix) theo style new-york + brand Comitor.\n * Trigger có 2 cỡ (`sm` = h-8, `default` = h-9); mục đang chọn tô nền gold nhạt.\n * Lưu ý: Radix KHÔNG nhận `value=\"\"` cho SelectItem — dùng sentinel (vd `__empty__`).\n *\n * ─ VIỀN TRIGGER: BA TRẠNG THÁI, BA TOKEN ─\n * Trigger nền trong suốt nên VIỀN là thứ duy nhất khoanh vùng control → ngưỡng 3:1 ở mọi\n * trạng thái, và hover/focus phải ĐẬM HƠN lúc nghỉ chứ không nhạt đi:\n *\n * nghỉ `border-input` (= --control-edge) : 3,83:1 (light) · 4,94:1 (dark) ✓\n * hover `border-control-edge-strong` : 10,55:1 (light) · 11,65:1 (dark) ✓\n * focus `border-primary-ink` : 6,51:1 (light) · 12,67:1 (dark) ✓\n * lỗi `border-destructive-ink` : 7,15:1 (light) · 6,84:1 (dark) ✓\n *\n * Bản trước đi SAI CHIỀU ở light: `hover:border-ash-300` chỉ **1,62:1** và\n * `focus:border-gold-300` chỉ **1,86:1** — rê chuột hoặc focus vào là trigger biến mất, đúng\n * cái bẫy mà docs/hop-dong-mau.md §3 nêu. Viền lỗi cũ `border-red` (#D64545) chỉ 4,38:1 và là\n * token TÔ NỀN đem làm viền; bậc `-ink` mới là bậc đứng trên nền trang.\n * (`ring-*` giữ nguyên bậc `--destructive`/`--ring`: quầng ring là vai trò TÔ, đúng hợp đồng.)\n */\n\nexport type SelectProps = React.ComponentProps<typeof SelectPrimitive.Root>;\nexport type SelectGroupProps = React.ComponentProps<typeof SelectPrimitive.Group>;\nexport type SelectValueProps = React.ComponentProps<typeof SelectPrimitive.Value>;\nexport type SelectTriggerProps = React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n /** Chiều cao trigger: `sm` = 32px, `default` = 36px. */\n size?: \"sm\" | \"default\";\n};\nexport type SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Content>;\nexport type SelectLabelProps = React.ComponentProps<typeof SelectPrimitive.Label>;\nexport type SelectItemProps = React.ComponentProps<typeof SelectPrimitive.Item>;\nexport type SelectSeparatorProps = React.ComponentProps<typeof SelectPrimitive.Separator>;\nexport type SelectScrollUpButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>;\nexport type SelectScrollDownButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>;\n\nfunction Select({ ...props }: SelectProps) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />;\n}\n\nfunction SelectGroup({ ...props }: SelectGroupProps) {\n return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />;\n}\n\nfunction SelectValue({ ...props }: SelectValueProps) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />;\n}\n\nfunction SelectTrigger({ className, size = \"default\", children, ...props }: SelectTriggerProps) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"border-input bg-background text-foreground data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground hover:border-control-edge-strong focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink aria-invalid:border-destructive-ink aria-invalid:ring-destructive/30 flex w-fit items-center justify-between gap-2 rounded-md border px-3 text-sm whitespace-nowrap transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-muted data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon className=\"size-3.5 text-muted-foreground\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n}\n\nfunction SelectContent({ className, children, position = \"popper\", ...props }: SelectContentProps) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border shadow-overlay\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n}\n\nfunction SelectLabel({ className, ...props }: SelectLabelProps) {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(\"text-muted-foreground px-2 py-1.5 text-xs\", className)}\n {...props}\n />\n );\n}\n\n/**\n * SelectItem — mục trong danh sách chọn.\n *\n * ─ KHOẢNG CÁCH GIỮA CÁC MỤC: `mt-1 first:mt-0` ─\n * Đặt trên CHÍNH mục chứ không phải `gap` của vùng chứa, và cả họ menu của gói đều theo đúng lối\n * này (`CommandItem`, `DropdownMenuItem`/CheckboxItem/RadioItem/SubTrigger, `ContextMenu*`,\n * `Menubar*` — 14 công thức, cùng một class).\n *\n * Vì sao không `gap` ở vùng chứa: mục có thể nằm TRỰC TIẾP trong viewport hoặc lồng trong một\n * `Group`, nên `gap` phải khai ở hai chỗ và sẽ lệch nhau ngay lần sửa đầu tiên. `:first-child`\n * thì tính theo cha thật của mục, nên một class chạy đúng cho cả hai bố cục.\n *\n * Và vì sao không đổi viewport sang `display:flex`: viewport của Radix mang sẵn\n * `h-[var(--radix-select-trigger-height)]` — hôm nay không ăn thua vì nó là flex item của Content,\n * nhưng đổi kiểu bố cục ra là đánh cược rằng nó vẫn im. Margin không đụng tới chuyện đó.\n *\n * ⚠ `Menubar`Trigger KHÔNG có class này: nó nằm trên thanh NGANG, không phải trong danh sách dọc.\n *\n * Mục ĐANG RÊ/ĐANG FOCUS và mục ĐÃ CHỌN dùng CÙNG một tint `--accent`, và tint đó quá nhạt để\n * TỰ mang thông tin: ~1,0:1 với nền của popover. Nó chỉ là lớp nhận biết mềm.\n *\n * `bg-accent` chứ không phải `bg-gold-50 dark:bg-gold-500/10` như bản trước: `--accent` LÀ nhóm\n * token mà hợp đồng màu cấp riêng cho tint trạng thái nhạt, và mọi menu khác của gói\n * (`DropdownMenu`, `ContextMenu`, `Menubar`) đều dùng đúng nó — `Select` là chỗ duy nhất còn tô\n * bằng màu thang thô. Ở light hai bên là CÙNG một hex `#FBF5DC` nên không đổi gì trên màn hình;\n * ở dark tint đổi từ warm `#191814` sang trung tính `#1E2128`, tức khớp lại với các menu kia.\n *\n * ⚠ Vậy nên thứ NÓI \"đã chọn\" phải là DẤU CHECK bên phải, không phải nền: nó là một hình dạng\n * (WCAG 1.4.1 — không truyền tin chỉ bằng màu) và được nhuộm `text-app-accent-ink`, đo TRÊN CHÍNH\n * tint gold mà nó đứng lên: 5,96:1 (light) · 11,15:1 (dark), tệ nhất 10,04:1 khi popover nằm trên\n * `--muted`. Bỏ dấu check đi là mục đã chọn chỉ còn khác nhau ở một lớp tint 1,09:1 — lúc đó mới\n * là lỗi thật.\n *\n * Trước đây còn một vạch `border-l-2 border-l-app-accent-ink` làm dấu hiệu THỨ HAI. Đã bỏ: dấu\n * check một mình đã đủ cho cả 1.4.1 lẫn 1.4.11, `Combobox`/`DropdownMenu` xưa nay cũng chỉ có dấu\n * check, và mẫu Select ở comitor-ds không có vạch nào. Bỏ luôn `border-l-transparent` đi kèm —\n * nó chỉ tồn tại để giữ chỗ 2px cho vạch đó, giờ `pl-2` khớp đúng bản duyệt.\n *\n * Vì sao dấu check là `-ink` chứ không `--app-accent` trần: gold `#E8B824` chỉ 1,86:1 với nền\n * trắng — đúng cái bẫy \"token tô NỀN đem vẽ DẤU\" mà hợp đồng màu §1 cấm.\n */\nfunction SelectItem({ className, children, ...props }: SelectItemProps) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n \"focus:bg-accent data-[state=checked]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none transition-colors duration-150 ease-out data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2\",\n className\n )}\n {...props}\n >\n <span className=\"absolute right-2 flex size-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4 text-app-accent-ink\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n );\n}\n\nfunction SelectSeparator({ className, ...props }: SelectSeparatorProps) {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(\"bg-border pointer-events-none -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nfunction SelectScrollUpButton({ className, ...props }: SelectScrollUpButtonProps) {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronUpIcon className=\"size-4\" />\n </SelectPrimitive.ScrollUpButton>\n );\n}\n\nfunction SelectScrollDownButton({ className, ...props }: SelectScrollDownButtonProps) {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronDownIcon className=\"size-4\" />\n </SelectPrimitive.ScrollDownButton>\n );\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { CheckIcon, ChevronDownIcon, ChevronUpIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Select (Radix) theo style new-york + brand Comitor.\n * Trigger có 2 cỡ (`sm` = h-8, `default` = h-9); mục đang chọn tô nền gold nhạt.\n * Lưu ý: Radix KHÔNG nhận `value=\"\"` cho SelectItem — dùng sentinel (vd `__empty__`).\n *\n * ─ VIỀN TRIGGER: BA TRẠNG THÁI, BA TOKEN ─\n * Trigger nền trong suốt nên VIỀN là thứ duy nhất khoanh vùng control → ngưỡng 3:1 ở mọi\n * trạng thái, và hover/focus phải ĐẬM HƠN lúc nghỉ chứ không nhạt đi:\n *\n * nghỉ `border-input` (= --control-edge) : 3,83:1 (light) · 4,94:1 (dark) ✓\n * hover `border-control-edge-strong` : 10,55:1 (light) · 11,65:1 (dark) ✓\n * focus `border-primary-ink` : 6,51:1 (light) · 12,67:1 (dark) ✓\n * lỗi `border-destructive-ink` : 7,15:1 (light) · 6,84:1 (dark) ✓\n *\n * Bản trước đi SAI CHIỀU ở light: `hover:border-ash-300` chỉ **1,62:1** và\n * `focus:border-gold-300` chỉ **1,86:1** — rê chuột hoặc focus vào là trigger biến mất, đúng\n * cái bẫy mà docs/hop-dong-mau.md §3 nêu. Viền lỗi cũ `border-red` (#D64545) chỉ 4,38:1 và là\n * token TÔ NỀN đem làm viền; bậc `-ink` mới là bậc đứng trên nền trang.\n * (`ring-*` giữ nguyên bậc `--destructive`/`--ring`: quầng ring là vai trò TÔ, đúng hợp đồng.)\n */\n\nexport type SelectProps = React.ComponentProps<typeof SelectPrimitive.Root>;\nexport type SelectGroupProps = React.ComponentProps<typeof SelectPrimitive.Group>;\nexport type SelectValueProps = React.ComponentProps<typeof SelectPrimitive.Value>;\nexport type SelectTriggerProps = React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n /** Chiều cao trigger: `sm` = 32px, `default` = 36px. */\n size?: \"sm\" | \"default\";\n};\nexport type SelectContentProps = React.ComponentProps<typeof SelectPrimitive.Content>;\nexport type SelectLabelProps = React.ComponentProps<typeof SelectPrimitive.Label>;\nexport type SelectItemProps = React.ComponentProps<typeof SelectPrimitive.Item>;\nexport type SelectSeparatorProps = React.ComponentProps<typeof SelectPrimitive.Separator>;\nexport type SelectScrollUpButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>;\nexport type SelectScrollDownButtonProps = React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>;\n\nfunction Select({ ...props }: SelectProps) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />;\n}\n\nfunction SelectGroup({ ...props }: SelectGroupProps) {\n return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />;\n}\n\nfunction SelectValue({ ...props }: SelectValueProps) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />;\n}\n\nfunction SelectTrigger({ className, size = \"default\", children, ...props }: SelectTriggerProps) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"border-input bg-background text-foreground data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground hover:border-control-edge-strong focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink aria-invalid:border-destructive-ink aria-invalid:ring-destructive/30 flex w-fit items-center justify-between gap-2 rounded-md border px-3 text-sm whitespace-nowrap transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-muted data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon className=\"size-3.5 text-muted-foreground\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n}\n\nfunction SelectContent({ className, children, position = \"popper\", ...props }: SelectContentProps) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border border-border shadow-overlay\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n}\n\nfunction SelectLabel({ className, ...props }: SelectLabelProps) {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(\"text-muted-foreground px-2 py-1.5 text-xs\", className)}\n {...props}\n />\n );\n}\n\n/**\n * SelectItem — mục trong danh sách chọn.\n *\n * ─ KHOẢNG CÁCH GIỮA CÁC MỤC: `mt-1 first:mt-0` ─\n * Đặt trên CHÍNH mục chứ không phải `gap` của vùng chứa, và cả họ menu của gói đều theo đúng lối\n * này (`CommandItem`, `DropdownMenuItem`/CheckboxItem/RadioItem/SubTrigger, `ContextMenu*`,\n * `Menubar*` — 14 công thức, cùng một class).\n *\n * Vì sao không `gap` ở vùng chứa: mục có thể nằm TRỰC TIẾP trong viewport hoặc lồng trong một\n * `Group`, nên `gap` phải khai ở hai chỗ và sẽ lệch nhau ngay lần sửa đầu tiên. `:first-child`\n * thì tính theo cha thật của mục, nên một class chạy đúng cho cả hai bố cục.\n *\n * Và vì sao không đổi viewport sang `display:flex`: viewport của Radix mang sẵn\n * `h-[var(--radix-select-trigger-height)]` — hôm nay không ăn thua vì nó là flex item của Content,\n * nhưng đổi kiểu bố cục ra là đánh cược rằng nó vẫn im. Margin không đụng tới chuyện đó.\n *\n * ⚠ `Menubar`Trigger KHÔNG có class này: nó nằm trên thanh NGANG, không phải trong danh sách dọc.\n *\n * Mục ĐANG RÊ/ĐANG FOCUS và mục ĐÃ CHỌN dùng CÙNG một tint `--accent`, và tint đó quá nhạt để\n * TỰ mang thông tin: ~1,0:1 với nền của popover. Nó chỉ là lớp nhận biết mềm.\n *\n * `bg-accent` chứ không phải `bg-gold-50 dark:bg-gold-500/10` như bản trước: `--accent` LÀ nhóm\n * token mà hợp đồng màu cấp riêng cho tint trạng thái nhạt, và mọi menu khác của gói\n * (`DropdownMenu`, `ContextMenu`, `Menubar`) đều dùng đúng nó — `Select` là chỗ duy nhất còn tô\n * bằng màu thang thô. Ở light hai bên là CÙNG một hex `#FBF5DC` nên không đổi gì trên màn hình;\n * ở dark tint đổi từ warm `#191814` sang trung tính `#1E2128`, tức khớp lại với các menu kia.\n *\n * ⚠ Vậy nên thứ NÓI \"đã chọn\" phải là DẤU CHECK bên phải, không phải nền: nó là một hình dạng\n * (WCAG 1.4.1 — không truyền tin chỉ bằng màu) và được nhuộm `text-app-accent-ink`, đo TRÊN CHÍNH\n * tint gold mà nó đứng lên: 5,96:1 (light) · 11,15:1 (dark), tệ nhất 10,04:1 khi popover nằm trên\n * `--muted`. Bỏ dấu check đi là mục đã chọn chỉ còn khác nhau ở một lớp tint 1,09:1 — lúc đó mới\n * là lỗi thật.\n *\n * Trước đây còn một vạch `border-l-2 border-l-app-accent-ink` làm dấu hiệu THỨ HAI. Đã bỏ: dấu\n * check một mình đã đủ cho cả 1.4.1 lẫn 1.4.11, `Combobox`/`DropdownMenu` xưa nay cũng chỉ có dấu\n * check, và mẫu Select ở comitor-ds không có vạch nào. Bỏ luôn `border-l-transparent` đi kèm —\n * nó chỉ tồn tại để giữ chỗ 2px cho vạch đó, giờ `pl-2` khớp đúng bản duyệt.\n *\n * Vì sao dấu check là `-ink` chứ không `--app-accent` trần: gold `#E8B824` chỉ 1,86:1 với nền\n * trắng — đúng cái bẫy \"token tô NỀN đem vẽ DẤU\" mà hợp đồng màu §1 cấm.\n */\nfunction SelectItem({ className, children, ...props }: SelectItemProps) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n \"focus:bg-accent data-[state=checked]:bg-accent [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 mt-1 first:mt-0 rounded-sm py-(--menu-item-py) pr-8 pl-2 text-sm outline-hidden select-none transition-colors duration-150 ease-out data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2\",\n className\n )}\n {...props}\n >\n <span className=\"absolute right-2 flex size-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4 text-app-accent-ink\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n );\n}\n\nfunction SelectSeparator({ className, ...props }: SelectSeparatorProps) {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(\"bg-border pointer-events-none -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nfunction SelectScrollUpButton({ className, ...props }: SelectScrollUpButtonProps) {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronUpIcon className=\"size-4\" />\n </SelectPrimitive.ScrollUpButton>\n );\n}\n\nfunction SelectScrollDownButton({ className, ...props }: SelectScrollDownButtonProps) {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}\n >\n <ChevronDownIcon className=\"size-4\" />\n </SelectPrimitive.ScrollDownButton>\n );\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue\n};\n"],"mappings":"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","names":[]}
|
|
@@ -51,7 +51,7 @@ function TableHead({ className, ...props }) {
|
|
|
51
51
|
{
|
|
52
52
|
"data-slot": "table-head",
|
|
53
53
|
className: cn(
|
|
54
|
-
"h-
|
|
54
|
+
"h-(--table-head-h) px-4 text-left align-middle text-xs font-bold tracking-[0.08em] uppercase text-muted-foreground whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
55
55
|
className
|
|
56
56
|
),
|
|
57
57
|
...props
|
|
@@ -64,7 +64,7 @@ function TableCell({ className, ...props }) {
|
|
|
64
64
|
{
|
|
65
65
|
"data-slot": "table-cell",
|
|
66
66
|
className: cn(
|
|
67
|
-
"px-4 py-
|
|
67
|
+
"px-4 py-(--row-py) align-middle text-foreground whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
68
68
|
className
|
|
69
69
|
),
|
|
70
70
|
...props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/table.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Table — bảng cơ bản, đã bọc sẵn khung cuộn ngang + viền bo `rounded-xl`.\n * `variant=\"striped\"` (hoặc `striped`) tô nền xen kẽ cho hàng chẵn.\n *\n * Đây là primitive tĩnh: sort / chọn dòng / ghim cột nằm ở composite `DataTable`.\n */\n\nexport type TableProps = React.ComponentProps<\"table\"> & {\n variant?: \"default\" | \"striped\";\n /** Alias tiện dụng của `variant=\"striped\"`. */\n striped?: boolean;\n};\nexport type TableHeaderProps = React.ComponentProps<\"thead\">;\nexport type TableBodyProps = React.ComponentProps<\"tbody\">;\nexport type TableFooterProps = React.ComponentProps<\"tfoot\">;\nexport type TableRowProps = React.ComponentProps<\"tr\">;\nexport type TableHeadProps = React.ComponentProps<\"th\">;\nexport type TableCellProps = React.ComponentProps<\"td\">;\nexport type TableCaptionProps = React.ComponentProps<\"caption\">;\n\nfunction Table({ className, variant = \"default\", striped, ...props }: TableProps) {\n const isStriped = striped || variant === \"striped\";\n return (\n <div data-slot=\"table-container\" className=\"w-full overflow-auto rounded-xl border border-border\">\n <table\n data-slot=\"table\"\n className={cn(\n \"w-full caption-bottom text-sm\",\n isStriped &&\n \"[&_tbody_tr:nth-child(even)]:bg-table-row-stripe [&_tbody_tr:nth-child(even):hover]:bg-table-row-hover\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction TableHeader({ className, ...props }: TableHeaderProps) {\n return <thead data-slot=\"table-header\" className={cn(\"bg-muted/60 border-b border-border\", className)} {...props} />;\n}\n\nfunction TableBody({ className, ...props }: TableBodyProps) {\n return <tbody data-slot=\"table-body\" className={cn(\"[&_tr:last-child]:border-0\", className)} {...props} />;\n}\n\nfunction TableFooter({ className, ...props }: TableFooterProps) {\n return (\n <tfoot\n data-slot=\"table-footer\"\n className={cn(\"bg-muted/40 border-t border-border font-medium [&>tr]:last:border-b-0\", className)}\n {...props}\n />\n );\n}\n\n/**\n * TableRow — dòng bảng. Trạng thái ĐÃ CHỌN (`data-state=\"selected\"`) mang HAI dấu hiệu:\n *\n * 1. nền `bg-muted` — chỉ là tint nhận biết, cố ý nhạt để chữ trong dòng vẫn đọc tốt:\n * 1,07:1 với `--card` ở light · 1,10:1 ở dark. Một mình nó KHÔNG đủ 3:1 của WCAG 1.4.11,\n * và không có tint nào vừa nhạt vừa đủ 3:1 — muốn 3:1 với nền trắng thì tint phải tối\n * tới mức thành mảng đặc (xem lý lẽ y hệt ở đầu `progress.tsx`).\n * 2. vạch `border-b-app-accent-ink` — ĐÂY mới là dấu mang thông tin, và nó đạt ngưỡng:\n * 6,51:1 với `--card`/`--background`, tệ nhất 6,09:1 khi dòng nằm trên `--muted` (light);\n * 12,67:1 / 10,80:1 (dark). Dùng bậc `-ink` chứ không `--app-accent` trần: gold `#E8B824`\n * chỉ 1,86:1 với nền trắng — đúng cái bẫy \"token tô NỀN đem vẽ DẤU\".\n *\n * Vạch nằm ở CẠNH DƯỚI chứ không phải cạnh trái vì `<tr>` cuộn ngang cùng bảng: mép trái\n * trôi khỏi khung cuộn (và bị ô ghim `position:sticky` của `DataTable` đè lên), còn cạnh dưới\n * trải hết bề ngang nên luôn còn thấy. Bề rộng viền không đổi theo trạng thái ⇒ không xô layout.\n */\nfunction TableRow({ className, ...props }: TableRowProps) {\n return (\n <tr\n data-slot=\"table-row\"\n className={cn(\n \"border-b border-border transition-colors hover:bg-table-row-hover data-[state=selected]:bg-muted data-[state=selected]:border-b-app-accent-ink\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction TableHead({ className, ...props }: TableHeadProps) {\n return (\n <th\n data-slot=\"table-head\"\n className={cn(\n \"h-
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/table.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Table — bảng cơ bản, đã bọc sẵn khung cuộn ngang + viền bo `rounded-xl`.\n * `variant=\"striped\"` (hoặc `striped`) tô nền xen kẽ cho hàng chẵn.\n *\n * Đây là primitive tĩnh: sort / chọn dòng / ghim cột nằm ở composite `DataTable`.\n */\n\nexport type TableProps = React.ComponentProps<\"table\"> & {\n variant?: \"default\" | \"striped\";\n /** Alias tiện dụng của `variant=\"striped\"`. */\n striped?: boolean;\n};\nexport type TableHeaderProps = React.ComponentProps<\"thead\">;\nexport type TableBodyProps = React.ComponentProps<\"tbody\">;\nexport type TableFooterProps = React.ComponentProps<\"tfoot\">;\nexport type TableRowProps = React.ComponentProps<\"tr\">;\nexport type TableHeadProps = React.ComponentProps<\"th\">;\nexport type TableCellProps = React.ComponentProps<\"td\">;\nexport type TableCaptionProps = React.ComponentProps<\"caption\">;\n\nfunction Table({ className, variant = \"default\", striped, ...props }: TableProps) {\n const isStriped = striped || variant === \"striped\";\n return (\n <div data-slot=\"table-container\" className=\"w-full overflow-auto rounded-xl border border-border\">\n <table\n data-slot=\"table\"\n className={cn(\n \"w-full caption-bottom text-sm\",\n isStriped &&\n \"[&_tbody_tr:nth-child(even)]:bg-table-row-stripe [&_tbody_tr:nth-child(even):hover]:bg-table-row-hover\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction TableHeader({ className, ...props }: TableHeaderProps) {\n return <thead data-slot=\"table-header\" className={cn(\"bg-muted/60 border-b border-border\", className)} {...props} />;\n}\n\nfunction TableBody({ className, ...props }: TableBodyProps) {\n return <tbody data-slot=\"table-body\" className={cn(\"[&_tr:last-child]:border-0\", className)} {...props} />;\n}\n\nfunction TableFooter({ className, ...props }: TableFooterProps) {\n return (\n <tfoot\n data-slot=\"table-footer\"\n className={cn(\"bg-muted/40 border-t border-border font-medium [&>tr]:last:border-b-0\", className)}\n {...props}\n />\n );\n}\n\n/**\n * TableRow — dòng bảng. Trạng thái ĐÃ CHỌN (`data-state=\"selected\"`) mang HAI dấu hiệu:\n *\n * 1. nền `bg-muted` — chỉ là tint nhận biết, cố ý nhạt để chữ trong dòng vẫn đọc tốt:\n * 1,07:1 với `--card` ở light · 1,10:1 ở dark. Một mình nó KHÔNG đủ 3:1 của WCAG 1.4.11,\n * và không có tint nào vừa nhạt vừa đủ 3:1 — muốn 3:1 với nền trắng thì tint phải tối\n * tới mức thành mảng đặc (xem lý lẽ y hệt ở đầu `progress.tsx`).\n * 2. vạch `border-b-app-accent-ink` — ĐÂY mới là dấu mang thông tin, và nó đạt ngưỡng:\n * 6,51:1 với `--card`/`--background`, tệ nhất 6,09:1 khi dòng nằm trên `--muted` (light);\n * 12,67:1 / 10,80:1 (dark). Dùng bậc `-ink` chứ không `--app-accent` trần: gold `#E8B824`\n * chỉ 1,86:1 với nền trắng — đúng cái bẫy \"token tô NỀN đem vẽ DẤU\".\n *\n * Vạch nằm ở CẠNH DƯỚI chứ không phải cạnh trái vì `<tr>` cuộn ngang cùng bảng: mép trái\n * trôi khỏi khung cuộn (và bị ô ghim `position:sticky` của `DataTable` đè lên), còn cạnh dưới\n * trải hết bề ngang nên luôn còn thấy. Bề rộng viền không đổi theo trạng thái ⇒ không xô layout.\n */\nfunction TableRow({ className, ...props }: TableRowProps) {\n return (\n <tr\n data-slot=\"table-row\"\n className={cn(\n \"border-b border-border transition-colors hover:bg-table-row-hover data-[state=selected]:bg-muted data-[state=selected]:border-b-app-accent-ink\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction TableHead({ className, ...props }: TableHeadProps) {\n return (\n <th\n data-slot=\"table-head\"\n className={cn(\n \"h-(--table-head-h) px-4 text-left align-middle text-xs font-bold tracking-[0.08em] uppercase text-muted-foreground whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction TableCell({ className, ...props }: TableCellProps) {\n return (\n <td\n data-slot=\"table-cell\"\n className={cn(\n \"px-4 py-(--row-py) align-middle text-foreground whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction TableCaption({ className, ...props }: TableCaptionProps) {\n return (\n <caption data-slot=\"table-caption\" className={cn(\"mt-4 text-sm text-muted-foreground\", className)} {...props} />\n );\n}\n\nexport { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow };\n"],"mappings":";AA6BM;AA1BN,SAAS,UAAU;AAsBnB,SAAS,MAAM,EAAE,WAAW,UAAU,WAAW,SAAS,GAAG,MAAM,GAAe;AAChF,QAAM,YAAY,WAAW,YAAY;AACzC,SACE,oBAAC,SAAI,aAAU,mBAAkB,WAAU,wDACzC;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA,aACE;AAAA,QACF;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SAAO,oBAAC,WAAM,aAAU,gBAAe,WAAW,GAAG,sCAAsC,SAAS,GAAI,GAAG,OAAO;AACpH;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AAC1D,SAAO,oBAAC,WAAM,aAAU,cAAa,WAAW,GAAG,8BAA8B,SAAS,GAAI,GAAG,OAAO;AAC1G;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,yEAAyE,SAAS;AAAA,MAC/F,GAAG;AAAA;AAAA,EACN;AAEJ;AAkBA,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAkB;AACxD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AAC1D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AAC1D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE,oBAAC,aAAQ,aAAU,iBAAgB,WAAW,GAAG,sCAAsC,SAAS,GAAI,GAAG,OAAO;AAElH;","names":[]}
|