@comitor/ui 0.5.1 → 0.6.0

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.
Files changed (37) hide show
  1. package/README.md +3 -2
  2. package/dist/components/composite/data-table.d.ts +7 -0
  3. package/dist/components/composite/data-table.js +5 -6
  4. package/dist/components/composite/data-table.js.map +1 -1
  5. package/dist/components/ui/command.js +1 -1
  6. package/dist/components/ui/command.js.map +1 -1
  7. package/dist/components/ui/context-menu.js +4 -4
  8. package/dist/components/ui/context-menu.js.map +1 -1
  9. package/dist/components/ui/dropdown-menu.js +4 -4
  10. package/dist/components/ui/dropdown-menu.js.map +1 -1
  11. package/dist/components/ui/item.js +1 -1
  12. package/dist/components/ui/item.js.map +1 -1
  13. package/dist/components/ui/menubar.js +4 -4
  14. package/dist/components/ui/menubar.js.map +1 -1
  15. package/dist/components/ui/select.js +1 -1
  16. package/dist/components/ui/select.js.map +1 -1
  17. package/dist/components/ui/table.js +2 -2
  18. package/dist/components/ui/table.js.map +1 -1
  19. package/dist/shell/display-axis-constants.d.ts +35 -0
  20. package/dist/shell/display-axis-constants.js +27 -0
  21. package/dist/shell/display-axis-constants.js.map +1 -0
  22. package/dist/shell/display-axis-controls.d.ts +51 -0
  23. package/dist/shell/display-axis-controls.js +70 -0
  24. package/dist/shell/display-axis-controls.js.map +1 -0
  25. package/dist/shell/display-axis-provider.d.ts +40 -0
  26. package/dist/shell/display-axis-provider.js +101 -0
  27. package/dist/shell/display-axis-provider.js.map +1 -0
  28. package/dist/shell/index.d.ts +3 -0
  29. package/dist/shell/index.js +3 -0
  30. package/dist/shell/index.js.map +1 -1
  31. package/dist/shell/shell-context.d.ts +9 -1
  32. package/dist/shell/shell-context.js +14 -4
  33. package/dist/shell/shell-context.js.map +1 -1
  34. package/dist/shell/sidebar-menu-item.js +1 -1
  35. package/dist/shell/sidebar-menu-item.js.map +1 -1
  36. package/package.json +1 -1
  37. package/styles.css +82 -1
@@ -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-3 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":[]}
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-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",
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-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",
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-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",
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-1.5 text-sm outline-none select-none data-[inset]:pl-8",
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":"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","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-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",
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-10 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]",
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-3 align-middle text-foreground whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
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-10 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-3 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":[]}
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":[]}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Hằng của HAI trục hiển thị thêm: MẬT ĐỘ và CỠ CHỮ.
3
+ *
4
+ * ⚠ File này KHÔNG có `"use client"`, và đó là điều kiện bắt buộc chứ không phải tuỳ chọn.
5
+ * Root layout là Server Component nhưng vẫn cần đọc tên thuộc tính / khoá lưu (vd để render sẵn
6
+ * giá trị lên `<html>`). Mọi export của một module `"use client"` khi đi tới Server Component sẽ
7
+ * biến thành client-reference proxy và đọc ra `undefined` — KHÔNG ném lỗi, không cảnh báo. Đúng
8
+ * cái bẫy mà `contrast-constants.ts` được tách ra để tránh (CHANGELOG 0.2.2 §2), và cổng
9
+ * `verify:directives` canh đúng chuyện này.
10
+ *
11
+ * Vì vậy: hằng ở đây, provider ở file khác, và `shell/index.ts` re-export THẲNG từ file này
12
+ * chứ không đi vòng qua provider.
13
+ */
14
+ /** `comfortable` là mặc định và KHÔNG ghi thuộc tính ra DOM — xem ghi chú ở cuối file. */
15
+ declare const DENSITY_MODES: readonly ["comfortable", "compact"];
16
+ type DensityMode = (typeof DENSITY_MODES)[number];
17
+ declare const DENSITY_ATTRIBUTE = "data-density";
18
+ declare const DENSITY_STORAGE_KEY = "comitor-density";
19
+ declare const DEFAULT_DENSITY: DensityMode;
20
+ declare function isDensityMode(value: unknown): value is DensityMode;
21
+ /**
22
+ * `md` là mặc định và KHÔNG ghi thuộc tính ra DOM.
23
+ *
24
+ * Giá trị là `sm`/`md`/`lg` chứ không phải `small`/`medium`/`large`: chúng đi thẳng vào
25
+ * `[data-font-size="…"]` trong CSS và vào localStorage, tức là chuỗi MÁY — theo luật tên trường,
26
+ * không theo luật nhãn. Nhãn tiếng Việt do app truyền vào control.
27
+ */
28
+ declare const FONT_SIZE_MODES: readonly ["sm", "md", "lg"];
29
+ type FontSizeMode = (typeof FONT_SIZE_MODES)[number];
30
+ declare const FONT_SIZE_ATTRIBUTE = "data-font-size";
31
+ declare const FONT_SIZE_STORAGE_KEY = "comitor-font-size";
32
+ declare const DEFAULT_FONT_SIZE: FontSizeMode;
33
+ declare function isFontSizeMode(value: unknown): value is FontSizeMode;
34
+
35
+ export { DEFAULT_DENSITY, DEFAULT_FONT_SIZE, DENSITY_ATTRIBUTE, DENSITY_MODES, DENSITY_STORAGE_KEY, type DensityMode, FONT_SIZE_ATTRIBUTE, FONT_SIZE_MODES, FONT_SIZE_STORAGE_KEY, type FontSizeMode, isDensityMode, isFontSizeMode };
@@ -0,0 +1,27 @@
1
+ const DENSITY_MODES = ["comfortable", "compact"];
2
+ const DENSITY_ATTRIBUTE = "data-density";
3
+ const DENSITY_STORAGE_KEY = "comitor-density";
4
+ const DEFAULT_DENSITY = "comfortable";
5
+ function isDensityMode(value) {
6
+ return typeof value === "string" && DENSITY_MODES.includes(value);
7
+ }
8
+ const FONT_SIZE_MODES = ["sm", "md", "lg"];
9
+ const FONT_SIZE_ATTRIBUTE = "data-font-size";
10
+ const FONT_SIZE_STORAGE_KEY = "comitor-font-size";
11
+ const DEFAULT_FONT_SIZE = "md";
12
+ function isFontSizeMode(value) {
13
+ return typeof value === "string" && FONT_SIZE_MODES.includes(value);
14
+ }
15
+ export {
16
+ DEFAULT_DENSITY,
17
+ DEFAULT_FONT_SIZE,
18
+ DENSITY_ATTRIBUTE,
19
+ DENSITY_MODES,
20
+ DENSITY_STORAGE_KEY,
21
+ FONT_SIZE_ATTRIBUTE,
22
+ FONT_SIZE_MODES,
23
+ FONT_SIZE_STORAGE_KEY,
24
+ isDensityMode,
25
+ isFontSizeMode
26
+ };
27
+ //# sourceMappingURL=display-axis-constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shell/display-axis-constants.ts"],"sourcesContent":["/**\n * Hằng của HAI trục hiển thị thêm: MẬT ĐỘ và CỠ CHỮ.\n *\n * ⚠ File này KHÔNG có `\"use client\"`, và đó là điều kiện bắt buộc chứ không phải tuỳ chọn.\n * Root layout là Server Component nhưng vẫn cần đọc tên thuộc tính / khoá lưu (vd để render sẵn\n * giá trị lên `<html>`). Mọi export của một module `\"use client\"` khi đi tới Server Component sẽ\n * biến thành client-reference proxy và đọc ra `undefined` — KHÔNG ném lỗi, không cảnh báo. Đúng\n * cái bẫy mà `contrast-constants.ts` được tách ra để tránh (CHANGELOG 0.2.2 §2), và cổng\n * `verify:directives` canh đúng chuyện này.\n *\n * Vì vậy: hằng ở đây, provider ở file khác, và `shell/index.ts` re-export THẲNG từ file này\n * chứ không đi vòng qua provider.\n */\n\n/* ────────────────────────────────────────────────────────────────────────────\n Trục 3 — MẬT ĐỘ (bố cục gọn)\n ──────────────────────────────────────────────────────────────────────────── */\n\n/** `comfortable` là mặc định và KHÔNG ghi thuộc tính ra DOM — xem ghi chú ở cuối file. */\nexport const DENSITY_MODES = [\"comfortable\", \"compact\"] as const;\nexport type DensityMode = (typeof DENSITY_MODES)[number];\n\nexport const DENSITY_ATTRIBUTE = \"data-density\";\nexport const DENSITY_STORAGE_KEY = \"comitor-density\";\nexport const DEFAULT_DENSITY: DensityMode = \"comfortable\";\n\nexport function isDensityMode(value: unknown): value is DensityMode {\n return typeof value === \"string\" && (DENSITY_MODES as readonly string[]).includes(value);\n}\n\n/* ────────────────────────────────────────────────────────────────────────────\n Trục 4 — CỠ CHỮ\n ──────────────────────────────────────────────────────────────────────────── */\n\n/**\n * `md` là mặc định và KHÔNG ghi thuộc tính ra DOM.\n *\n * Giá trị là `sm`/`md`/`lg` chứ không phải `small`/`medium`/`large`: chúng đi thẳng vào\n * `[data-font-size=\"…\"]` trong CSS và vào localStorage, tức là chuỗi MÁY — theo luật tên trường,\n * không theo luật nhãn. Nhãn tiếng Việt do app truyền vào control.\n */\nexport const FONT_SIZE_MODES = [\"sm\", \"md\", \"lg\"] as const;\nexport type FontSizeMode = (typeof FONT_SIZE_MODES)[number];\n\nexport const FONT_SIZE_ATTRIBUTE = \"data-font-size\";\nexport const FONT_SIZE_STORAGE_KEY = \"comitor-font-size\";\nexport const DEFAULT_FONT_SIZE: FontSizeMode = \"md\";\n\nexport function isFontSizeMode(value: unknown): value is FontSizeMode {\n return typeof value === \"string\" && (FONT_SIZE_MODES as readonly string[]).includes(value);\n}\n\n/**\n * ─ VÌ SAO GIÁ TRỊ MẶC ĐỊNH KHÔNG GHI THUỘC TÍNH ─\n *\n * Cả hai trục đều GỠ thuộc tính khi ở mức mặc định thay vì ghi `data-density=\"comfortable\"`.\n * Cùng lối với `ContrastProvider`, và ở trục cỡ chữ thì nó còn là chuyện TRỢ NĂNG:\n *\n * Mức `md` không được phép là `font-size: 100%` viết ra DOM, càng không được là `16px`. Người\n * dùng có thể đã tăng cỡ chữ mặc định của trình duyệt; khai một cỡ tuyệt đối ở `<html>` là XOÁ\n * lựa chọn đó — tức tự tay vi phạm WCAG 1.4.4 ngay bên trong một tính năng trợ năng. Không ghi gì\n * ở mức mặc định thì cỡ gốc của trình duyệt được giữ nguyên, còn `sm`/`lg` là PHẦN TRĂM của chính\n * cỡ đó (87,5% / 112,5%) nên vẫn tôn trọng nó.\n *\n * ⚠ Không cổng nào bắt được lỗi này: `check-contrast.mjs` chỉ có ngưỡng 4,5 và 3, không có logic\n * cỡ chữ nào cả.\n */\n"],"mappings":"AAmBO,MAAM,gBAAgB,CAAC,eAAe,SAAS;AAG/C,MAAM,oBAAoB;AAC1B,MAAM,sBAAsB;AAC5B,MAAM,kBAA+B;AAErC,SAAS,cAAc,OAAsC;AAClE,SAAO,OAAO,UAAU,YAAa,cAAoC,SAAS,KAAK;AACzF;AAaO,MAAM,kBAAkB,CAAC,MAAM,MAAM,IAAI;AAGzC,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;AAC9B,MAAM,oBAAkC;AAExC,SAAS,eAAe,OAAuC;AACpE,SAAO,OAAO,UAAU,YAAa,gBAAsC,SAAS,KAAK;AAC3F;","names":[]}
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+ import { FontSizeMode } from './display-axis-constants.js';
3
+
4
+ /**
5
+ * Hai control của trục MẬT ĐỘ và trục CỠ CHỮ.
6
+ *
7
+ * ─ VÌ SAO CẢ HAI ĐỀU CÓ `disabled={!mounted}` ─
8
+ * Giá trị thật nằm ở localStorage, mà server không đọc được. Render lần đầu ở client PHẢI khớp
9
+ * HTML server sinh ra, nếu không React báo lệch hydration — nên provider giữ giá trị mặc định cho
10
+ * tới khi effect đầu chạy. Trong khoảnh khắc đó control hiện mặc định chứ không hiện lựa chọn
11
+ * thật, và nếu người dùng kịp bấm thì họ ghi đè lên chính lựa chọn của mình. Khoá cho tới khi
12
+ * `mounted` là cách `ContrastToggle` đã dùng.
13
+ *
14
+ * (DOM thật thì không nháy: script chống nháy của provider đã đặt thuộc tính lên `<html>` trước
15
+ * lần sơn đầu tiên. Chỉ CONTROL là phải chờ.)
16
+ */
17
+ interface DensityToggleProps {
18
+ label?: string;
19
+ description?: string;
20
+ className?: string;
21
+ }
22
+ /**
23
+ * Nút gạt "bố cục gọn".
24
+ *
25
+ * ⚠ Đây KHÔNG phải một trường của biểu mẫu cài đặt. Nó có hiệu lực NGAY và tự lưu, giống
26
+ * `ThemeToggle`/`ContrastToggle` — đừng đưa nó vào luồng nháp/"Lưu thay đổi". Trộn hai kiểu thì
27
+ * thanh "Có thay đổi chưa lưu" sáng lên cho một thay đổi đã có hiệu lực, và nút "Hoàn tác" trong
28
+ * toast sẽ lùi bản nháp mà KHÔNG lùi được trục — giao diện tự mâu thuẫn với chính nó.
29
+ */
30
+ declare function DensityToggle({ label, description, className }: DensityToggleProps): React.JSX.Element;
31
+ interface FontSizeControlProps {
32
+ /** Nhãn của cả nhóm, đọc lên cho trình đọc màn hình. */
33
+ label?: string;
34
+ /** Nhãn từng mức — THỨ TỰ theo `FONT_SIZE_MODES` (`sm`, `md`, `lg`). */
35
+ optionLabels?: Record<FontSizeMode, string>;
36
+ className?: string;
37
+ }
38
+ /**
39
+ * Chọn cỡ chữ — ba mức, một hàng nút.
40
+ *
41
+ * `role="radiogroup"` + `aria-checked` chứ không phải ba nút thường: đây là chọn MỘT trong ba, và
42
+ * trình đọc màn hình cần nghe "2 trên 3" chứ không nghe ba nút rời rạc. Dùng `<button>` thật (bàn
43
+ * phím tới được bằng Tab) thay vì roving focus của Radix ToggleGroup — nhóm chỉ có ba mục nên
44
+ * roving focus không đáng cái giá thêm một phụ thuộc.
45
+ *
46
+ * Cỡ chữ của CHÍNH các nút này cố định `text-sm`: nếu chúng phóng theo lựa chọn thì bấm "Lớn" sẽ
47
+ * làm cả hàng nút nhảy chỗ ngay dưới ngón tay.
48
+ */
49
+ declare function FontSizeControl({ label, optionLabels, className }: FontSizeControlProps): React.JSX.Element;
50
+
51
+ export { DensityToggle, type DensityToggleProps, FontSizeControl, type FontSizeControlProps };
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Switch } from "../components/ui/switch.js";
4
+ import { cn } from "../lib/cn.js";
5
+ import { FONT_SIZE_MODES } from "./display-axis-constants.js";
6
+ import { useDensity, useFontSize } from "./display-axis-provider.js";
7
+ function DensityToggle({
8
+ label = "B\u1ED1 c\u1EE5c g\u1ECDn",
9
+ description = "Gi\u1EA3m kho\u1EA3ng c\xE1ch d\u1ECDc gi\u1EEFa c\xE1c d\xF2ng trong b\u1EA3ng, danh s\xE1ch v\xE0 menu.",
10
+ className
11
+ }) {
12
+ const { density, setDensity, mounted } = useDensity();
13
+ return /* @__PURE__ */ jsx(
14
+ Switch,
15
+ {
16
+ className,
17
+ checked: density === "compact",
18
+ onCheckedChange: (checked) => setDensity(checked ? "compact" : "comfortable"),
19
+ disabled: !mounted,
20
+ label,
21
+ description
22
+ }
23
+ );
24
+ }
25
+ const DEFAULT_FONT_SIZE_LABELS = {
26
+ sm: "Nh\u1ECF",
27
+ md: "V\u1EEBa",
28
+ lg: "L\u1EDBn"
29
+ };
30
+ function FontSizeControl({
31
+ label = "C\u1EE1 ch\u1EEF",
32
+ optionLabels = DEFAULT_FONT_SIZE_LABELS,
33
+ className
34
+ }) {
35
+ const { fontSize, setFontSize, mounted } = useFontSize();
36
+ return /* @__PURE__ */ jsx(
37
+ "div",
38
+ {
39
+ role: "radiogroup",
40
+ "aria-label": label,
41
+ className: cn("inline-flex items-center gap-1 rounded-md border border-input p-1 text-sm", className),
42
+ children: FONT_SIZE_MODES.map((mode) => {
43
+ const selected = fontSize === mode;
44
+ return /* @__PURE__ */ jsx(
45
+ "button",
46
+ {
47
+ type: "button",
48
+ role: "radio",
49
+ "aria-checked": selected,
50
+ disabled: !mounted,
51
+ onClick: () => setFontSize(mode),
52
+ className: cn(
53
+ "rounded-sm px-3 py-1 text-sm transition-colors duration-150 ease-out",
54
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground",
55
+ "disabled:cursor-not-allowed disabled:opacity-50",
56
+ selected ? "bg-control-on text-control-check font-medium" : "text-muted-foreground hover:bg-accent hover:text-foreground"
57
+ ),
58
+ children: optionLabels[mode]
59
+ },
60
+ mode
61
+ );
62
+ })
63
+ }
64
+ );
65
+ }
66
+ export {
67
+ DensityToggle,
68
+ FontSizeControl
69
+ };
70
+ //# sourceMappingURL=display-axis-controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/shell/display-axis-controls.tsx"],"sourcesContent":["\"use client\";\n\nimport { Switch } from \"../components/ui/switch.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { FONT_SIZE_MODES, type FontSizeMode } from \"./display-axis-constants.js\";\nimport { useDensity, useFontSize } from \"./display-axis-provider.js\";\n\n/**\n * Hai control của trục MẬT ĐỘ và trục CỠ CHỮ.\n *\n * ─ VÌ SAO CẢ HAI ĐỀU CÓ `disabled={!mounted}` ─\n * Giá trị thật nằm ở localStorage, mà server không đọc được. Render lần đầu ở client PHẢI khớp\n * HTML server sinh ra, nếu không React báo lệch hydration — nên provider giữ giá trị mặc định cho\n * tới khi effect đầu chạy. Trong khoảnh khắc đó control hiện mặc định chứ không hiện lựa chọn\n * thật, và nếu người dùng kịp bấm thì họ ghi đè lên chính lựa chọn của mình. Khoá cho tới khi\n * `mounted` là cách `ContrastToggle` đã dùng.\n *\n * (DOM thật thì không nháy: script chống nháy của provider đã đặt thuộc tính lên `<html>` trước\n * lần sơn đầu tiên. Chỉ CONTROL là phải chờ.)\n */\n\nexport interface DensityToggleProps {\n label?: string;\n description?: string;\n className?: string;\n}\n\n/**\n * Nút gạt \"bố cục gọn\".\n *\n * ⚠ Đây KHÔNG phải một trường của biểu mẫu cài đặt. Nó có hiệu lực NGAY và tự lưu, giống\n * `ThemeToggle`/`ContrastToggle` — đừng đưa nó vào luồng nháp/\"Lưu thay đổi\". Trộn hai kiểu thì\n * thanh \"Có thay đổi chưa lưu\" sáng lên cho một thay đổi đã có hiệu lực, và nút \"Hoàn tác\" trong\n * toast sẽ lùi bản nháp mà KHÔNG lùi được trục — giao diện tự mâu thuẫn với chính nó.\n */\nexport function DensityToggle({\n label = \"Bố cục gọn\",\n description = \"Giảm khoảng cách dọc giữa các dòng trong bảng, danh sách và menu.\",\n className\n}: DensityToggleProps) {\n const { density, setDensity, mounted } = useDensity();\n\n return (\n <Switch\n className={className}\n checked={density === \"compact\"}\n onCheckedChange={(checked) => setDensity(checked ? \"compact\" : \"comfortable\")}\n disabled={!mounted}\n label={label}\n description={description}\n />\n );\n}\n\nexport interface FontSizeControlProps {\n /** Nhãn của cả nhóm, đọc lên cho trình đọc màn hình. */\n label?: string;\n /** Nhãn từng mức — THỨ TỰ theo `FONT_SIZE_MODES` (`sm`, `md`, `lg`). */\n optionLabels?: Record<FontSizeMode, string>;\n className?: string;\n}\n\nconst DEFAULT_FONT_SIZE_LABELS: Record<FontSizeMode, string> = {\n sm: \"Nhỏ\",\n md: \"Vừa\",\n lg: \"Lớn\"\n};\n\n/**\n * Chọn cỡ chữ — ba mức, một hàng nút.\n *\n * `role=\"radiogroup\"` + `aria-checked` chứ không phải ba nút thường: đây là chọn MỘT trong ba, và\n * trình đọc màn hình cần nghe \"2 trên 3\" chứ không nghe ba nút rời rạc. Dùng `<button>` thật (bàn\n * phím tới được bằng Tab) thay vì roving focus của Radix ToggleGroup — nhóm chỉ có ba mục nên\n * roving focus không đáng cái giá thêm một phụ thuộc.\n *\n * Cỡ chữ của CHÍNH các nút này cố định `text-sm`: nếu chúng phóng theo lựa chọn thì bấm \"Lớn\" sẽ\n * làm cả hàng nút nhảy chỗ ngay dưới ngón tay.\n */\nexport function FontSizeControl({\n label = \"Cỡ chữ\",\n optionLabels = DEFAULT_FONT_SIZE_LABELS,\n className\n}: FontSizeControlProps) {\n const { fontSize, setFontSize, mounted } = useFontSize();\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={label}\n className={cn(\"inline-flex items-center gap-1 rounded-md border border-input p-1 text-sm\", className)}\n >\n {FONT_SIZE_MODES.map((mode) => {\n const selected = fontSize === mode;\n return (\n <button\n key={mode}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n disabled={!mounted}\n onClick={() => setFontSize(mode)}\n className={cn(\n \"rounded-sm px-3 py-1 text-sm transition-colors duration-150 ease-out\",\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n selected\n ? \"bg-control-on text-control-check font-medium\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\"\n )}\n >\n {optionLabels[mode]}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";AA2CI;AAzCJ,SAAS,cAAc;AACvB,SAAS,UAAU;AACnB,SAAS,uBAA0C;AACnD,SAAS,YAAY,mBAAmB;AA8BjC,SAAS,cAAc;AAAA,EAC5B,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AACF,GAAuB;AACrB,QAAM,EAAE,SAAS,YAAY,QAAQ,IAAI,WAAW;AAEpD,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAS,YAAY;AAAA,MACrB,iBAAiB,CAAC,YAAY,WAAW,UAAU,YAAY,aAAa;AAAA,MAC5E,UAAU,CAAC;AAAA,MACX;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;AAUA,MAAM,2BAAyD;AAAA,EAC7D,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAaO,SAAS,gBAAgB;AAAA,EAC9B,QAAQ;AAAA,EACR,eAAe;AAAA,EACf;AACF,GAAyB;AACvB,QAAM,EAAE,UAAU,aAAa,QAAQ,IAAI,YAAY;AAEvD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,WAAW,GAAG,6EAA6E,SAAS;AAAA,MAEnG,0BAAgB,IAAI,CAAC,SAAS;AAC7B,cAAM,WAAW,aAAa;AAC9B,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,gBAAc;AAAA,YACd,UAAU,CAAC;AAAA,YACX,SAAS,MAAM,YAAY,IAAI;AAAA,YAC/B,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA,WACI,iDACA;AAAA,YACN;AAAA,YAEC,uBAAa,IAAI;AAAA;AAAA,UAfb;AAAA,QAgBP;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { DensityMode, FontSizeMode } from './display-axis-constants.js';
4
+
5
+ interface DisplayAxisValue<T extends string> {
6
+ value: T;
7
+ setValue: (next: T) => void;
8
+ /** `false` cho tới khi effect đầu tiên chạy — dùng để tránh lệch hydration ở control. */
9
+ mounted: boolean;
10
+ }
11
+ declare const DensityProvider: ({ children, defaultValue, storageKey }: {
12
+ children: ReactNode;
13
+ defaultValue?: "compact" | "comfortable" | undefined;
14
+ storageKey?: string;
15
+ }) => React.JSX.Element;
16
+ declare const DensityScript: ({ storageKey }: {
17
+ storageKey?: string;
18
+ }) => React.JSX.Element;
19
+ /** Đọc/đổi mật độ bố cục. */
20
+ declare function useDensity(): {
21
+ density: DensityMode;
22
+ setDensity: (m: DensityMode) => void;
23
+ mounted: boolean;
24
+ };
25
+ declare const FontSizeProvider: ({ children, defaultValue, storageKey }: {
26
+ children: ReactNode;
27
+ defaultValue?: "sm" | "md" | "lg" | undefined;
28
+ storageKey?: string;
29
+ }) => React.JSX.Element;
30
+ declare const FontSizeScript: ({ storageKey }: {
31
+ storageKey?: string;
32
+ }) => React.JSX.Element;
33
+ /** Đọc/đổi cỡ chữ. */
34
+ declare function useFontSize(): {
35
+ fontSize: FontSizeMode;
36
+ setFontSize: (m: FontSizeMode) => void;
37
+ mounted: boolean;
38
+ };
39
+
40
+ export { DensityProvider, DensityScript, type DisplayAxisValue, FontSizeProvider, FontSizeScript, useDensity, useFontSize };