@comitor/ui 0.2.1 → 0.3.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 (90) hide show
  1. package/README.md +236 -30
  2. package/dist/chart/chart-colors.d.ts +56 -0
  3. package/dist/chart/chart-colors.js +21 -0
  4. package/dist/chart/chart-colors.js.map +1 -0
  5. package/dist/chart/chart.d.ts +40 -0
  6. package/dist/chart/chart.js.map +1 -1
  7. package/dist/chart/comitor-chart.d.ts +21 -43
  8. package/dist/chart/comitor-chart.js +13 -24
  9. package/dist/chart/comitor-chart.js.map +1 -1
  10. package/dist/chart/index.d.ts +2 -1
  11. package/dist/chart/index.js +1 -0
  12. package/dist/chart/index.js.map +1 -1
  13. package/dist/components/composite/data-table-constants.d.ts +40 -0
  14. package/dist/components/composite/data-table-constants.js +13 -0
  15. package/dist/components/composite/data-table-constants.js.map +1 -0
  16. package/dist/components/composite/data-table.d.ts +2 -27
  17. package/dist/components/composite/data-table.js +1 -10
  18. package/dist/components/composite/data-table.js.map +1 -1
  19. package/dist/components/composite/date-picker-constants.d.ts +21 -0
  20. package/dist/components/composite/date-picker-constants.js +39 -0
  21. package/dist/components/composite/date-picker-constants.js.map +1 -0
  22. package/dist/components/composite/date-picker.d.ts +2 -18
  23. package/dist/components/composite/date-picker.js +6 -48
  24. package/dist/components/composite/date-picker.js.map +1 -1
  25. package/dist/components/composite/form-field-constants.d.ts +26 -0
  26. package/dist/components/composite/form-field-constants.js +13 -0
  27. package/dist/components/composite/form-field-constants.js.map +1 -0
  28. package/dist/components/composite/form-field.d.ts +1 -13
  29. package/dist/components/composite/form-field.js +1 -11
  30. package/dist/components/composite/form-field.js.map +1 -1
  31. package/dist/components/composite/search-filter-bar-constants.d.ts +52 -0
  32. package/dist/components/composite/search-filter-bar-constants.js +18 -0
  33. package/dist/components/composite/search-filter-bar-constants.js.map +1 -0
  34. package/dist/components/composite/search-filter-bar.d.ts +3 -37
  35. package/dist/components/composite/search-filter-bar.js +2 -15
  36. package/dist/components/composite/search-filter-bar.js.map +1 -1
  37. package/dist/components/composite/table-pagination-constants.d.ts +56 -0
  38. package/dist/components/composite/table-pagination-constants.js +18 -0
  39. package/dist/components/composite/table-pagination-constants.js.map +1 -0
  40. package/dist/components/composite/table-pagination.d.ts +2 -43
  41. package/dist/components/composite/table-pagination.js +4 -15
  42. package/dist/components/composite/table-pagination.js.map +1 -1
  43. package/dist/components/ui/badge.js +3 -3
  44. package/dist/components/ui/badge.js.map +1 -1
  45. package/dist/components/ui/button.js +4 -4
  46. package/dist/components/ui/button.js.map +1 -1
  47. package/dist/components/ui/calendar.js +1 -1
  48. package/dist/components/ui/calendar.js.map +1 -1
  49. package/dist/components/ui/checkbox.js +2 -2
  50. package/dist/components/ui/checkbox.js.map +1 -1
  51. package/dist/components/ui/command.js +1 -1
  52. package/dist/components/ui/command.js.map +1 -1
  53. package/dist/components/ui/input-group.js +1 -1
  54. package/dist/components/ui/input-group.js.map +1 -1
  55. package/dist/components/ui/input-otp.js +1 -1
  56. package/dist/components/ui/input-otp.js.map +1 -1
  57. package/dist/components/ui/navigation-menu.js +1 -1
  58. package/dist/components/ui/navigation-menu.js.map +1 -1
  59. package/dist/components/ui/radio-group.js +1 -1
  60. package/dist/components/ui/radio-group.js.map +1 -1
  61. package/dist/components/ui/resizable.js +1 -1
  62. package/dist/components/ui/resizable.js.map +1 -1
  63. package/dist/components/ui/slider.js +1 -1
  64. package/dist/components/ui/slider.js.map +1 -1
  65. package/dist/components/ui/switch.js +1 -1
  66. package/dist/components/ui/switch.js.map +1 -1
  67. package/dist/components/ui/textarea.js +1 -1
  68. package/dist/components/ui/textarea.js.map +1 -1
  69. package/dist/index.d.ts +11 -6
  70. package/dist/index.js +5 -0
  71. package/dist/index.js.map +1 -1
  72. package/dist/shell/contrast-constants.d.ts +30 -0
  73. package/dist/shell/contrast-constants.js +16 -0
  74. package/dist/shell/contrast-constants.js.map +1 -0
  75. package/dist/shell/contrast-provider.d.ts +60 -0
  76. package/dist/shell/contrast-provider.js +61 -0
  77. package/dist/shell/contrast-provider.js.map +1 -0
  78. package/dist/shell/contrast-toggle.d.ts +24 -0
  79. package/dist/shell/contrast-toggle.js +29 -0
  80. package/dist/shell/contrast-toggle.js.map +1 -0
  81. package/dist/shell/index.d.ts +3 -0
  82. package/dist/shell/index.js +3 -0
  83. package/dist/shell/index.js.map +1 -1
  84. package/dist/shell/shell-context.js +3 -2
  85. package/dist/shell/shell-context.js.map +1 -1
  86. package/dist/tokens.d.ts +170 -70
  87. package/dist/tokens.js +124 -47
  88. package/dist/tokens.js.map +1 -1
  89. package/package.json +3 -2
  90. package/styles.css +255 -120
@@ -16,8 +16,8 @@ function Checkbox({ className, label, description, indeterminate, checked, id, .
16
16
  className: cn(
17
17
  "peer size-4 shrink-0 rounded border border-input bg-background",
18
18
  "transition-colors duration-150 ease-out",
19
- "data-[state=checked]:bg-control-on data-[state=checked]:border-control-on data-[state=checked]:text-control-knob",
20
- "data-[state=indeterminate]:bg-control-on data-[state=indeterminate]:border-control-on data-[state=indeterminate]:text-control-knob",
19
+ "data-[state=checked]:bg-control-on data-[state=checked]:border-control-on data-[state=checked]:text-control-check",
20
+ "data-[state=indeterminate]:bg-control-on data-[state=indeterminate]:border-control-on data-[state=indeterminate]:text-control-check",
21
21
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
22
22
  "hover:border-control-edge-strong",
23
23
  "disabled:cursor-not-allowed disabled:opacity-50",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport { CheckIcon, MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\nexport interface CheckboxProps extends React.ComponentProps<typeof CheckboxPrimitive.Root> {\n /** Nhãn đặt cạnh ô đánh dấu; có nhãn thì component tự bọc thêm lớp bố cục. */\n label?: string;\n /** Mô tả phụ dưới nhãn. */\n description?: string;\n /**\n * Đường tắt cho trạng thái \"chọn một phần\" — đè lên `checked`.\n * Tương đương `checked=\"indeterminate\"`; giữ lại cho tương thích ngược.\n */\n indeterminate?: boolean;\n}\n\n/*\n * Style port từ comitor-ds (đổi gray-* → ash-* theo namespace của package).\n *\n * ─ ICON BÁM THEO STATE THẬT, KHÔNG THEO PROP ─\n * Bản cũ chọn icon bằng `indeterminate ? <Minus/> : <Check/>`, tức chỉ nhìn PROP. Ai truyền\n * `checked=\"indeterminate\"` thẳng (Radix cho phép, và `composite/data-table.tsx` đang làm đúng\n * như vậy cho ô \"chọn tất cả\") thì nền chuyển sang trạng thái một phần nhưng icon vẫn là dấu\n * tick → mới chọn vài dòng mà đầu bảng trông y hệt đã chọn hết. Trường hợp không kiểm soát\n * (`defaultChecked=\"indeterminate\"`) còn sai cả sau khi người dùng bấm.\n * Nay cả hai icon cùng được render và bật/tắt bằng `data-state` của chính Radix Indicator\n * (Radix phát `data-state=\"checked\" | \"indeterminate\"` trên Indicator) → luôn khớp state thật,\n * kể cả ở nhánh không kiểm soát.\n *\n * ─ MÀU: BA PHÉP ĐO, GIỐNG `Switch` ─\n *\n * Ô đánh dấu là thành phần phi văn bản (WCAG 1.4.11, ngưỡng 3:1) và phải nhận ra được ở cả hai\n * trạng thái:\n *\n * (1) viền/ô ⇄ nền trang → có nhìn thấy ô không\n * (2) dấu tick ⇄ nền ô → nó đang được chọn hay không\n *\n * `bg-primary` (gold #E8B824 ở light) chỉ đạt **1,86:1** với nền trắng, nên ô ĐÃ CHỌN mất hẳn\n * ranh giới ở light — chỉ dấu tick còn nổi. Vai trò \"nền control khi BẬT/ĐÃ CHỌN\" có token\n * riêng là `--control-on` (= `--primary-ink`: gold-500 ở light, = `--primary` ở dark), đạt cả\n * hai phía ở cả hai theme:\n *\n * ô ⇄ nền trang : 6,51:1 (light) · 12,67:1 (dark) ✓\n * tick ⇄ nền ô : 6,51:1 (light) · 12,67:1 (dark) ✓\n *\n * Dấu tick là `text-control-knob` (= `--background`: trắng ở light, #0E1116 ở dark) chứ không\n * phải `text-primary-foreground` (mực ở CẢ hai theme): mực trên gold-500 chỉ 1,86:1 ✗. Cùng một\n * lối với núm của `Switch` — phần chỉ báo mang màu NỀN TRANG nên luôn tương phản đúng chiều.\n *\n * ─ VIỀN LÚC NGHỈ VÀ LÚC HOVER ─\n * `border-input` nay trỏ vào `--control-edge` #7E8290 (3,83:1 light · 4,94:1 dark ✓) — trước đó\n * là #EDEEF1, chỉ 1,16:1, tức ô chưa chọn gần như tàng hình. Hover phải ĐẬM HƠN lúc nghỉ, không\n * nhạt đi: `border-ash-300` cũ ở light chỉ 1,62:1 ✗. Nay đi qua `--control-edge-strong` (thang\n * ash được token hoá đúng vai trò này): light 10,55:1 · dark 11,65:1 trên nền trang, 10,94:1\n * trên `--card` ✓ — dùng token thay vì `ash-700`/`ash-300` thô để app đổi thang xám không vỡ.\n */\nfunction Checkbox({ className, label, description, indeterminate, checked, id, ...props }: CheckboxProps) {\n const generatedId = React.useId();\n const checkboxId = id ?? generatedId;\n\n const control = (\n <CheckboxPrimitive.Root\n id={checkboxId}\n data-slot=\"checkbox\"\n checked={indeterminate ? \"indeterminate\" : checked}\n className={cn(\n \"peer size-4 shrink-0 rounded border border-input bg-background\",\n \"transition-colors duration-150 ease-out\",\n \"data-[state=checked]:bg-control-on data-[state=checked]:border-control-on data-[state=checked]:text-control-knob\",\n \"data-[state=indeterminate]:bg-control-on data-[state=indeterminate]:border-control-on data-[state=indeterminate]:text-control-knob\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"hover:border-control-edge-strong\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"group/checkbox-indicator flex items-center justify-center text-current transition-none\"\n >\n <MinusIcon\n className=\"hidden size-2.5 group-data-[state=indeterminate]/checkbox-indicator:block\"\n strokeWidth={3}\n />\n <CheckIcon className=\"hidden size-2.5 group-data-[state=checked]/checkbox-indicator:block\" strokeWidth={3} />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n\n if (!label && !description) {\n return control;\n }\n\n return (\n <div className=\"flex items-start gap-2.5\">\n {control}\n <div className=\"flex flex-col gap-0.5\">\n {label && (\n <label\n htmlFor={checkboxId}\n className=\"text-sm font-medium text-foreground cursor-pointer select-none leading-none\"\n >\n {label}\n </label>\n )}\n {description && <p className=\"text-xs text-muted-foreground leading-relaxed\">{description}</p>}\n </div>\n </div>\n );\n}\n\nexport { Checkbox };\n"],"mappings":";AAgFM,SAIE,KAJF;AA9EN,YAAY,uBAAuB;AACnC,SAAS,WAAW,iBAAiB;AACrC,YAAY,WAAW;AACvB,SAAS,UAAU;AAsDnB,SAAS,SAAS,EAAE,WAAW,OAAO,aAAa,eAAe,SAAS,IAAI,GAAG,MAAM,GAAkB;AACxG,QAAM,cAAc,MAAM,MAAM;AAChC,QAAM,aAAa,MAAM;AAEzB,QAAM,UACJ;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,IAAI;AAAA,MACJ,aAAU;AAAA,MACV,SAAS,gBAAgB,kBAAkB;AAAA,MAC3C,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,kBAAkB;AAAA,QAAlB;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAEV;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,aAAa;AAAA;AAAA,YACf;AAAA,YACA,oBAAC,aAAU,WAAU,uEAAsE,aAAa,GAAG;AAAA;AAAA;AAAA,MAC7G;AAAA;AAAA,EACF;AAGF,MAAI,CAAC,SAAS,CAAC,aAAa;AAC1B,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,SAAI,WAAU,4BACZ;AAAA;AAAA,IACD,qBAAC,SAAI,WAAU,yBACZ;AAAA,eACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAED,eAAe,oBAAC,OAAE,WAAU,iDAAiD,uBAAY;AAAA,OAC5F;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/checkbox.tsx"],"sourcesContent":["\"use client\";\n\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport { CheckIcon, MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\nexport interface CheckboxProps extends React.ComponentProps<typeof CheckboxPrimitive.Root> {\n /** Nhãn đặt cạnh ô đánh dấu; có nhãn thì component tự bọc thêm lớp bố cục. */\n label?: string;\n /** Mô tả phụ dưới nhãn. */\n description?: string;\n /**\n * Đường tắt cho trạng thái \"chọn một phần\" — đè lên `checked`.\n * Tương đương `checked=\"indeterminate\"`; giữ lại cho tương thích ngược.\n */\n indeterminate?: boolean;\n}\n\n/*\n * Style port từ comitor-ds (đổi gray-* → ash-* theo namespace của package).\n *\n * ─ ICON BÁM THEO STATE THẬT, KHÔNG THEO PROP ─\n * Bản cũ chọn icon bằng `indeterminate ? <Minus/> : <Check/>`, tức chỉ nhìn PROP. Ai truyền\n * `checked=\"indeterminate\"` thẳng (Radix cho phép, và `composite/data-table.tsx` đang làm đúng\n * như vậy cho ô \"chọn tất cả\") thì nền chuyển sang trạng thái một phần nhưng icon vẫn là dấu\n * tick → mới chọn vài dòng mà đầu bảng trông y hệt đã chọn hết. Trường hợp không kiểm soát\n * (`defaultChecked=\"indeterminate\"`) còn sai cả sau khi người dùng bấm.\n * Nay cả hai icon cùng được render và bật/tắt bằng `data-state` của chính Radix Indicator\n * (Radix phát `data-state=\"checked\" | \"indeterminate\"` trên Indicator) → luôn khớp state thật,\n * kể cả ở nhánh không kiểm soát.\n *\n * ─ MÀU: ĐI QUA TOKEN VAI TRÒ, GIỐNG `Switch` ─\n *\n * Ô đánh dấu là thành phần phi văn bản (WCAG 1.4.11, ngưỡng 3:1) và phải nhận ra được ở cả hai\n * trạng thái: (1) viền/ô ⇄ nền trang, (2) dấu tick ⇄ nền ô.\n *\n * ô ĐÃ CHỌN `data-[state=checked]:bg-control-on` (comitor-ds: bg-gold-300)\n * dấu tick `data-[state=checked]:text-control-check` (comitor-ds: text-ink)\n * viền nghỉ `border-input` · hover `border-control-edge-strong`\n *\n * ⚠ Dấu tick dùng `--control-check`, KHÔNG phải `--control-knob` (núm Switch) và cũng không phải\n * `text-ink` cứng: hai bảng màu lật ngược nhau ở đúng chỗ này. Bảng mặc định tô nền ô bằng gold\n * SÁNG nên tick phải là MỰC (10,19:1); bảng tương phản cao tô gold-500 TỐI nên tick phải là màu\n * nền trang (6,51:1). Núm Switch thì trắng ở bảng duyệt nên nó ở lại `--control-knob` — đó là lý\n * do hai vai trò tách thành hai token.\n *\n * Số đo hai bảng (light):\n *\n * bảng MẶC ĐỊNH bảng TƯƠNG PHẢN CAO\n * ô ⇄ nền trang 1,86:1 ✗ (đã chấp nhận) 6,51:1 ✓\n * tick ⇄ nền ô 10,19:1 ✓ 6,51:1 ✓\n * viền nghỉ ⇄ nền 1,16:1 ✗ (đã chấp nhận) 3,83:1 ✓\n *\n * Hover phải ĐẬM HƠN lúc nghỉ, không nhạt đi — `--control-edge-strong` là ash-300 ở light và\n * ash-500 ở dark, đúng cặp `hover:border-gray-300 dark:hover:border-gray-500` của comitor-ds.\n */\nfunction Checkbox({ className, label, description, indeterminate, checked, id, ...props }: CheckboxProps) {\n const generatedId = React.useId();\n const checkboxId = id ?? generatedId;\n\n const control = (\n <CheckboxPrimitive.Root\n id={checkboxId}\n data-slot=\"checkbox\"\n checked={indeterminate ? \"indeterminate\" : checked}\n className={cn(\n \"peer size-4 shrink-0 rounded border border-input bg-background\",\n \"transition-colors duration-150 ease-out\",\n \"data-[state=checked]:bg-control-on data-[state=checked]:border-control-on data-[state=checked]:text-control-check\",\n \"data-[state=indeterminate]:bg-control-on data-[state=indeterminate]:border-control-on data-[state=indeterminate]:text-control-check\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"hover:border-control-edge-strong\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"group/checkbox-indicator flex items-center justify-center text-current transition-none\"\n >\n <MinusIcon\n className=\"hidden size-2.5 group-data-[state=indeterminate]/checkbox-indicator:block\"\n strokeWidth={3}\n />\n <CheckIcon className=\"hidden size-2.5 group-data-[state=checked]/checkbox-indicator:block\" strokeWidth={3} />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n );\n\n if (!label && !description) {\n return control;\n }\n\n return (\n <div className=\"flex items-start gap-2.5\">\n {control}\n <div className=\"flex flex-col gap-0.5\">\n {label && (\n <label\n htmlFor={checkboxId}\n className=\"text-sm font-medium text-foreground cursor-pointer select-none leading-none\"\n >\n {label}\n </label>\n )}\n {description && <p className=\"text-xs text-muted-foreground leading-relaxed\">{description}</p>}\n </div>\n </div>\n );\n}\n\nexport { Checkbox };\n"],"mappings":";AA8EM,SAIE,KAJF;AA5EN,YAAY,uBAAuB;AACnC,SAAS,WAAW,iBAAiB;AACrC,YAAY,WAAW;AACvB,SAAS,UAAU;AAoDnB,SAAS,SAAS,EAAE,WAAW,OAAO,aAAa,eAAe,SAAS,IAAI,GAAG,MAAM,GAAkB;AACxG,QAAM,cAAc,MAAM,MAAM;AAChC,QAAM,aAAa,MAAM;AAEzB,QAAM,UACJ;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,IAAI;AAAA,MACJ,aAAU;AAAA,MACV,SAAS,gBAAgB,kBAAkB;AAAA,MAC3C,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,kBAAkB;AAAA,QAAlB;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAEV;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,aAAa;AAAA;AAAA,YACf;AAAA,YACA,oBAAC,aAAU,WAAU,uEAAsE,aAAa,GAAG;AAAA;AAAA;AAAA,MAC7G;AAAA;AAAA,EACF;AAGF,MAAI,CAAC,SAAS,CAAC,aAAa;AAC1B,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,SAAI,WAAU,4BACZ;AAAA;AAAA,IACD,qBAAC,SAAI,WAAU,yBACZ;AAAA,eACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAED,eAAe,oBAAC,OAAE,WAAU,iDAAiD,uBAAY;AAAA,OAC5F;AAAA,KACF;AAEJ;","names":[]}
@@ -35,7 +35,7 @@ function CommandDialog({
35
35
  }
36
36
  function CommandInput({ className, ...props }) {
37
37
  return /* @__PURE__ */ jsxs("div", { "data-slot": "command-input-wrapper", className: "flex h-9 items-center gap-2 border-b px-3", children: [
38
- /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4 shrink-0" }),
38
+ /* @__PURE__ */ jsx(SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
39
39
  /* @__PURE__ */ jsx(
40
40
  CommandPrimitive.Input,
41
41
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/command.tsx"],"sourcesContent":["\"use client\";\n\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { SearchIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"./dialog.js\";\n\n/**\n * Command — danh sách lệnh có lọc theo phím gõ (cmdk).\n *\n * Đây là primitive trần: composite `CommandPalette` và `Combobox` (Tier 2) dựng lên\n * từ đây. `CommandDialog` bọc sẵn trong `Dialog` cho bảng lệnh Cmd/Ctrl+K.\n *\n * Lưu ý a11y: `CommandDialog` bắt buộc có tiêu đề + mô tả cho trình đọc màn hình\n * (đặt trong `DialogHeader` ẩn `sr-only`) — đổi bằng prop `title`/`description`.\n */\n\nexport type CommandProps = React.ComponentProps<typeof CommandPrimitive>;\n\nfunction Command({ className, ...props }: CommandProps) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type CommandDialogProps = React.ComponentProps<typeof Dialog> & {\n /** Tiêu đề cho trình đọc màn hình. Mặc định \"Bảng lệnh\". */\n title?: string;\n /** Mô tả cho trình đọc màn hình. Mặc định \"Tìm và chạy một lệnh…\". */\n description?: string;\n className?: string;\n showCloseButton?: boolean;\n};\n\nfunction CommandDialog({\n title = \"Bảng lệnh\",\n description = \"Tìm và chạy một lệnh…\",\n children,\n className,\n showCloseButton = true,\n ...props\n}: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogContent className={cn(\"overflow-hidden p-0\", className)} showCloseButton={showCloseButton}>\n <Command className=\"[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport type CommandInputProps = React.ComponentProps<typeof CommandPrimitive.Input>;\n\nfunction CommandInput({ className, ...props }: CommandInputProps) {\n return (\n <div data-slot=\"command-input-wrapper\" className=\"flex h-9 items-center gap-2 border-b px-3\">\n {/* Icon kính lúp là icon MANG NGHĨA (nói \"đây là ô tìm kiếm\") → ngưỡng 3:1.\n `opacity-50` cũ hạ màu kế thừa xuống 3,56:1 (light) / 4,99:1 (dark) — đạt nhưng sát,\n và là con số không token nào bảo vệ. `--muted-foreground` là bậc dành đúng cho vai trò\n này: 5,23:1 (light) · 5,35:1 (dark) trên nền popover. */}\n <SearchIcon className=\"text-muted-foreground size-4 shrink-0\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nexport type CommandListProps = React.ComponentProps<typeof CommandPrimitive.List>;\n\nfunction CommandList({ className, ...props }: CommandListProps) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto\", className)}\n {...props}\n />\n );\n}\n\nexport type CommandEmptyProps = React.ComponentProps<typeof CommandPrimitive.Empty>;\n\nfunction CommandEmpty({ ...props }: CommandEmptyProps) {\n return <CommandPrimitive.Empty data-slot=\"command-empty\" className=\"py-6 text-center text-sm\" {...props} />;\n}\n\nexport type CommandGroupProps = React.ComponentProps<typeof CommandPrimitive.Group>;\n\nfunction CommandGroup({ className, ...props }: CommandGroupProps) {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n \"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type CommandSeparatorProps = React.ComponentProps<typeof CommandPrimitive.Separator>;\n\nfunction CommandSeparator({ className, ...props }: CommandSeparatorProps) {\n return (\n <CommandPrimitive.Separator\n data-slot=\"command-separator\"\n className={cn(\"bg-border -mx-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type CommandItemProps = React.ComponentProps<typeof CommandPrimitive.Item>;\n\nfunction CommandItem({ className, ...props }: CommandItemProps) {\n return (\n <CommandPrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type CommandShortcutProps = React.ComponentProps<\"span\">;\n\nfunction CommandShortcut({ className, ...props }: CommandShortcutProps) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut\n};\n"],"mappings":";AAsBI,cA8BE,YA9BF;AApBJ,SAAS,WAAW,wBAAwB;AAC5C,SAAS,kBAAkB;AAE3B,SAAS,UAAU;AACnB,SAAS,QAAQ,eAAe,mBAAmB,cAAc,mBAAmB;AAcpF,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAiB;AACtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAWA,SAAS,cAAc;AAAA,EACrB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB,GAAG;AACL,GAAuB;AACrB,SACE,qBAAC,UAAQ,GAAG,OACV;AAAA,yBAAC,gBAAa,WAAU,WACtB;AAAA,0BAAC,eAAa,iBAAM;AAAA,MACpB,oBAAC,qBAAmB,uBAAY;AAAA,OAClC;AAAA,IACA,oBAAC,iBAAc,WAAW,GAAG,uBAAuB,SAAS,GAAG,iBAC9D,8BAAC,WAAQ,WAAU,yZAChB,UACH,GACF;AAAA,KACF;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE,qBAAC,SAAI,aAAU,yBAAwB,WAAU,6CAK/C;AAAA,wBAAC,cAAW,WAAU,yCAAwC;AAAA,IAC9D;AAAA,MAAC,iBAAiB;AAAA,MAAjB;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,KACF;AAEJ;AAIA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,+DAA+D,SAAS;AAAA,MACrF,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,aAAa,EAAE,GAAG,MAAM,GAAsB;AACrD,SAAO,oBAAC,iBAAiB,OAAjB,EAAuB,aAAU,iBAAgB,WAAU,4BAA4B,GAAG,OAAO;AAC3G;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,iBAAiB,EAAE,WAAW,GAAG,MAAM,GAA0B;AACxE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,wBAAwB,SAAS;AAAA,MAC9C,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,yDAAyD,SAAS;AAAA,MAC/E,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/command.tsx"],"sourcesContent":["\"use client\";\n\nimport { Command as CommandPrimitive } from \"cmdk\";\nimport { SearchIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"./dialog.js\";\n\n/**\n * Command — danh sách lệnh có lọc theo phím gõ (cmdk).\n *\n * Đây là primitive trần: composite `CommandPalette` và `Combobox` (Tier 2) dựng lên\n * từ đây. `CommandDialog` bọc sẵn trong `Dialog` cho bảng lệnh Cmd/Ctrl+K.\n *\n * Lưu ý a11y: `CommandDialog` bắt buộc có tiêu đề + mô tả cho trình đọc màn hình\n * (đặt trong `DialogHeader` ẩn `sr-only`) — đổi bằng prop `title`/`description`.\n */\n\nexport type CommandProps = React.ComponentProps<typeof CommandPrimitive>;\n\nfunction Command({ className, ...props }: CommandProps) {\n return (\n <CommandPrimitive\n data-slot=\"command\"\n className={cn(\n \"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type CommandDialogProps = React.ComponentProps<typeof Dialog> & {\n /** Tiêu đề cho trình đọc màn hình. Mặc định \"Bảng lệnh\". */\n title?: string;\n /** Mô tả cho trình đọc màn hình. Mặc định \"Tìm và chạy một lệnh…\". */\n description?: string;\n className?: string;\n showCloseButton?: boolean;\n};\n\nfunction CommandDialog({\n title = \"Bảng lệnh\",\n description = \"Tìm và chạy một lệnh…\",\n children,\n className,\n showCloseButton = true,\n ...props\n}: CommandDialogProps) {\n return (\n <Dialog {...props}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogContent className={cn(\"overflow-hidden p-0\", className)} showCloseButton={showCloseButton}>\n <Command className=\"[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport type CommandInputProps = React.ComponentProps<typeof CommandPrimitive.Input>;\n\nfunction CommandInput({ className, ...props }: CommandInputProps) {\n return (\n <div data-slot=\"command-input-wrapper\" className=\"flex h-9 items-center gap-2 border-b px-3\">\n {/* Icon kính lúp là icon MANG NGHĨA (nói \"đây là ô tìm kiếm\") → ngưỡng 3:1.\n `opacity-50` cũ hạ màu kế thừa xuống 3,56:1 (light) / 4,99:1 (dark) — đạt nhưng sát,\n và là con số không token nào bảo vệ. `--muted-foreground` là bậc dành đúng cho vai trò\n này: 5,23:1 (light) · 5,35:1 (dark) trên nền popover. */}\n <SearchIcon className=\"size-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n data-slot=\"command-input\"\n className={cn(\n \"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nexport type CommandListProps = React.ComponentProps<typeof CommandPrimitive.List>;\n\nfunction CommandList({ className, ...props }: CommandListProps) {\n return (\n <CommandPrimitive.List\n data-slot=\"command-list\"\n className={cn(\"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto\", className)}\n {...props}\n />\n );\n}\n\nexport type CommandEmptyProps = React.ComponentProps<typeof CommandPrimitive.Empty>;\n\nfunction CommandEmpty({ ...props }: CommandEmptyProps) {\n return <CommandPrimitive.Empty data-slot=\"command-empty\" className=\"py-6 text-center text-sm\" {...props} />;\n}\n\nexport type CommandGroupProps = React.ComponentProps<typeof CommandPrimitive.Group>;\n\nfunction CommandGroup({ className, ...props }: CommandGroupProps) {\n return (\n <CommandPrimitive.Group\n data-slot=\"command-group\"\n className={cn(\n \"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type CommandSeparatorProps = React.ComponentProps<typeof CommandPrimitive.Separator>;\n\nfunction CommandSeparator({ className, ...props }: CommandSeparatorProps) {\n return (\n <CommandPrimitive.Separator\n data-slot=\"command-separator\"\n className={cn(\"bg-border -mx-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type CommandItemProps = React.ComponentProps<typeof CommandPrimitive.Item>;\n\nfunction CommandItem({ className, ...props }: CommandItemProps) {\n return (\n <CommandPrimitive.Item\n data-slot=\"command-item\"\n className={cn(\n \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type CommandShortcutProps = React.ComponentProps<\"span\">;\n\nfunction CommandShortcut({ className, ...props }: CommandShortcutProps) {\n return (\n <span\n data-slot=\"command-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Command,\n CommandDialog,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut\n};\n"],"mappings":";AAsBI,cA8BE,YA9BF;AApBJ,SAAS,WAAW,wBAAwB;AAC5C,SAAS,kBAAkB;AAE3B,SAAS,UAAU;AACnB,SAAS,QAAQ,eAAe,mBAAmB,cAAc,mBAAmB;AAcpF,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAiB;AACtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAWA,SAAS,cAAc;AAAA,EACrB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB,GAAG;AACL,GAAuB;AACrB,SACE,qBAAC,UAAQ,GAAG,OACV;AAAA,yBAAC,gBAAa,WAAU,WACtB;AAAA,0BAAC,eAAa,iBAAM;AAAA,MACpB,oBAAC,qBAAmB,uBAAY;AAAA,OAClC;AAAA,IACA,oBAAC,iBAAc,WAAW,GAAG,uBAAuB,SAAS,GAAG,iBAC9D,8BAAC,WAAQ,WAAU,yZAChB,UACH,GACF;AAAA,KACF;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE,qBAAC,SAAI,aAAU,yBAAwB,WAAU,6CAK/C;AAAA,wBAAC,cAAW,WAAU,8BAA6B;AAAA,IACnD;AAAA,MAAC,iBAAiB;AAAA,MAAjB;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA,KACF;AAEJ;AAIA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,+DAA+D,SAAS;AAAA,MACrF,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,aAAa,EAAE,GAAG,MAAM,GAAsB;AACrD,SAAO,oBAAC,iBAAiB,OAAjB,EAAuB,aAAU,iBAAgB,WAAU,4BAA4B,GAAG,OAAO;AAC3G;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,iBAAiB,EAAE,WAAW,GAAG,MAAM,GAA0B;AACxE,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,wBAAwB,SAAS;AAAA,MAC9C,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,yDAAyD,SAAS;AAAA,MAC/E,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -12,7 +12,7 @@ function InputGroup({ className, ...props }) {
12
12
  "data-slot": "input-group",
13
13
  role: "group",
14
14
  className: cn(
15
- "group/input-group border-input relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none",
15
+ "group/input-group border-input dark:bg-input-fill relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none",
16
16
  "h-9 has-[>textarea]:h-auto",
17
17
  // Biến thể theo vị trí addon.
18
18
  "has-[>[data-align=inline-start]]:[&>input]:pl-2",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/input-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { Textarea } from \"./textarea.js\";\n\n/*\n * ─ KHÔNG CÓ `dark:bg-input/30` (cùng lý do với Textarea) ─\n * `--input` = `--control-edge` là token VIỀN control (hợp đồng màu §3), không phải bậc tô nền.\n * Riêng ở đây cái sai còn kín hơn: lớp tô nằm trên thẻ CHA, còn chữ `--muted-foreground` nằm ở\n * `InputGroupAddon` và ở placeholder của `Input`/`Textarea` con — tức KHÁC chuỗi lớp, nên\n * `scripts/check-contrast.mjs` không thấy được cặp này (giới hạn §8.1). Số vẫn hỏng y hệt\n * Textarea: 3,81 · 3,54 · 3,22:1 ✗ ở dark. Bỏ lớp tô → addon và placeholder về đo với nền TRANG,\n * 5,70 · 5,35 · 4,86:1 ✓; khung nhóm vẫn do `border-input` khoanh (4,21–4,94:1 ở dark).\n */\nexport type InputGroupProps = React.ComponentProps<\"div\">;\n\nfunction InputGroup({ className, ...props }: InputGroupProps) {\n return (\n <div\n data-slot=\"input-group\"\n role=\"group\"\n className={cn(\n \"group/input-group border-input relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none\",\n \"h-9 has-[>textarea]:h-auto\",\n\n // Biến thể theo vị trí addon.\n \"has-[>[data-align=inline-start]]:[&>input]:pl-2\",\n \"has-[>[data-align=inline-end]]:[&>input]:pr-2\",\n \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3\",\n \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3\",\n\n // Trạng thái focus.\n \"has-[[data-slot=input-group-control]:focus-visible]:border-primary-ink has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-background\",\n\n // Trạng thái lỗi.\n \"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive-ink dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n\n className\n )}\n {...props}\n />\n );\n}\n\nconst inputGroupAddonVariants = cva(\n \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n {\n variants: {\n align: {\n \"inline-start\": \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n \"inline-end\": \"order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]\",\n \"block-start\":\n \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n \"block-end\": \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\"\n }\n },\n defaultVariants: {\n align: \"inline-start\"\n }\n }\n);\n\nexport interface InputGroupAddonProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof inputGroupAddonVariants> {}\n\n/*\n * Bấm vào vùng addon thì dồn focus sang ô nhập bên trong (trừ khi bấm trúng một nút).\n * Đây là tiện ích chuột thuần tuý — bàn phím vẫn Tab thẳng vào input/button nên không\n * cần onKeyDown; addon không phải phần tử tương tác.\n */\nfunction InputGroupAddon({ className, align = \"inline-start\", ...props }: InputGroupAddonProps) {\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: chỉ là lối tắt bằng chuột, bàn phím đã Tab thẳng tới control\n <div\n role=\"group\"\n data-slot=\"input-group-addon\"\n data-align={align}\n className={cn(inputGroupAddonVariants({ align }), className)}\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button\")) {\n return;\n }\n event.currentTarget.parentElement?.querySelector(\"input\")?.focus();\n }}\n {...props}\n />\n );\n}\n\nconst inputGroupButtonVariants = cva(\"text-sm shadow-none flex gap-2 items-center\", {\n variants: {\n size: {\n xs: \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n sm: \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n \"icon-xs\": \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\"\n }\n },\n defaultVariants: {\n size: \"xs\"\n }\n});\n\nexport type InputGroupButtonProps = Omit<ButtonProps, \"size\"> & VariantProps<typeof inputGroupButtonVariants>;\n\nfunction InputGroupButton({\n className,\n type = \"button\",\n variant = \"ghost\",\n size = \"xs\",\n ...props\n}: InputGroupButtonProps) {\n return (\n <Button\n type={type}\n data-size={size}\n variant={variant}\n className={cn(inputGroupButtonVariants({ size }), className)}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextProps = React.ComponentProps<\"span\">;\n\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n return (\n <span\n className={cn(\n \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupInputProps = React.ComponentProps<\"input\">;\n\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n return (\n <Input\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n return (\n <Textarea\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n InputGroupText,\n InputGroupTextarea,\n inputGroupAddonVariants,\n inputGroupButtonVariants\n};\n"],"mappings":";AAsBI;AApBJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AACnB,SAAS,cAAgC;AACzC,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAazB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAGA;AAAA;AAAA,QAGA;AAAA,QAEA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,eACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAWA,SAAS,gBAAgB,EAAE,WAAW,QAAQ,gBAAgB,GAAG,MAAM,GAAyB;AAC9F;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,cAAY;AAAA,QACZ,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,GAAG,SAAS;AAAA,QAC3D,SAAS,CAAC,UAAU;AAClB,cAAK,MAAM,OAAuB,QAAQ,QAAQ,GAAG;AACnD;AAAA,UACF;AACA,gBAAM,cAAc,eAAe,cAAc,OAAO,GAAG,MAAM;AAAA,QACnE;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA;AAEJ;AAEA,MAAM,2BAA2B,IAAI,+CAA+C;AAAA,EAClF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAID,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAW;AAAA,MACX;AAAA,MACA,WAAW,GAAG,yBAAyB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MAC1D,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/input-group.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps } from \"./button.js\";\nimport { Input } from \"./input.js\";\nimport { Textarea } from \"./textarea.js\";\n\n/*\n * ─ LỚP TÔ Ở DARK: `dark:bg-input-fill`, KHÔNG PHẢI `bg-input/30` (cùng lý do với Textarea) ─\n * Ở bảng mặc định lớp tô là thứ khoanh vùng nhóm ở dark; ở bảng tương phản cao nó trong suốt vì\n * `--input` bên đó là #7E8290 và tô 30% sẽ kéo chữ xuống dưới ngưỡng.\n *\n * Riêng ở đây cái sai còn kín hơn Textarea nếu ai đó viết thẳng `bg-input/30`: lớp tô nằm trên\n * thẻ CHA, còn chữ `--muted-foreground` nằm ở `InputGroupAddon` và ở placeholder của\n * `Input`/`Textarea` con — tức KHÁC chuỗi lớp, nên `scripts/check-contrast.mjs` không thấy được\n * cặp này (giới hạn §8.1). Con số nếu viết thẳng: 3,81 · 3,54 · 3,22:1 ✗ ở dark.\n */\nexport type InputGroupProps = React.ComponentProps<\"div\">;\n\nfunction InputGroup({ className, ...props }: InputGroupProps) {\n return (\n <div\n data-slot=\"input-group\"\n role=\"group\"\n className={cn(\n \"group/input-group border-input dark:bg-input-fill relative flex w-full items-center rounded-md border shadow-xs transition-colors duration-150 ease-out outline-none\",\n \"h-9 has-[>textarea]:h-auto\",\n\n // Biến thể theo vị trí addon.\n \"has-[>[data-align=inline-start]]:[&>input]:pl-2\",\n \"has-[>[data-align=inline-end]]:[&>input]:pr-2\",\n \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3\",\n \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3\",\n\n // Trạng thái focus.\n \"has-[[data-slot=input-group-control]:focus-visible]:border-primary-ink has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-2 has-[[data-slot=input-group-control]:focus-visible]:ring-offset-background\",\n\n // Trạng thái lỗi.\n \"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive-ink dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n\n className\n )}\n {...props}\n />\n );\n}\n\nconst inputGroupAddonVariants = cva(\n \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50\",\n {\n variants: {\n align: {\n \"inline-start\": \"order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]\",\n \"inline-end\": \"order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]\",\n \"block-start\":\n \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5\",\n \"block-end\": \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5\"\n }\n },\n defaultVariants: {\n align: \"inline-start\"\n }\n }\n);\n\nexport interface InputGroupAddonProps\n extends React.ComponentProps<\"div\">,\n VariantProps<typeof inputGroupAddonVariants> {}\n\n/*\n * Bấm vào vùng addon thì dồn focus sang ô nhập bên trong (trừ khi bấm trúng một nút).\n * Đây là tiện ích chuột thuần tuý — bàn phím vẫn Tab thẳng vào input/button nên không\n * cần onKeyDown; addon không phải phần tử tương tác.\n */\nfunction InputGroupAddon({ className, align = \"inline-start\", ...props }: InputGroupAddonProps) {\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: chỉ là lối tắt bằng chuột, bàn phím đã Tab thẳng tới control\n <div\n role=\"group\"\n data-slot=\"input-group-addon\"\n data-align={align}\n className={cn(inputGroupAddonVariants({ align }), className)}\n onClick={(event) => {\n if ((event.target as HTMLElement).closest(\"button\")) {\n return;\n }\n event.currentTarget.parentElement?.querySelector(\"input\")?.focus();\n }}\n {...props}\n />\n );\n}\n\nconst inputGroupButtonVariants = cva(\"text-sm shadow-none flex gap-2 items-center\", {\n variants: {\n size: {\n xs: \"h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2\",\n sm: \"h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5\",\n \"icon-xs\": \"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0\",\n \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\"\n }\n },\n defaultVariants: {\n size: \"xs\"\n }\n});\n\nexport type InputGroupButtonProps = Omit<ButtonProps, \"size\"> & VariantProps<typeof inputGroupButtonVariants>;\n\nfunction InputGroupButton({\n className,\n type = \"button\",\n variant = \"ghost\",\n size = \"xs\",\n ...props\n}: InputGroupButtonProps) {\n return (\n <Button\n type={type}\n data-size={size}\n variant={variant}\n className={cn(inputGroupButtonVariants({ size }), className)}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextProps = React.ComponentProps<\"span\">;\n\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n return (\n <span\n className={cn(\n \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupInputProps = React.ComponentProps<\"input\">;\n\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n return (\n <Input\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type InputGroupTextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n return (\n <Textarea\n data-slot=\"input-group-control\"\n className={cn(\n \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n InputGroupText,\n InputGroupTextarea,\n inputGroupAddonVariants,\n inputGroupButtonVariants\n};\n"],"mappings":";AAuBI;AArBJ,SAAS,WAA8B;AAEvC,SAAS,UAAU;AACnB,SAAS,cAAgC;AACzC,SAAS,aAAa;AACtB,SAAS,gBAAgB;AAczB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA;AAAA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QAGA;AAAA;AAAA,QAGA;AAAA,QAEA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,0BAA0B;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,cAAc;AAAA,QACd,eACE;AAAA,QACF,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAWA,SAAS,gBAAgB,EAAE,WAAW,QAAQ,gBAAgB,GAAG,MAAM,GAAyB;AAC9F;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,cAAY;AAAA,QACZ,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,GAAG,SAAS;AAAA,QAC3D,SAAS,CAAC,UAAU;AAClB,cAAK,MAAM,OAAuB,QAAQ,QAAQ,GAAG;AACnD;AAAA,UACF;AACA,gBAAM,cAAc,eAAe,cAAc,OAAO,GAAG,MAAM;AAAA,QACnE;AAAA,QACC,GAAG;AAAA;AAAA,IACN;AAAA;AAEJ;AAEA,MAAM,2BAA2B,IAAI,+CAA+C;AAAA,EAClF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM;AAAA,EACR;AACF,CAAC;AAID,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,GAAG;AACL,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAW;AAAA,MACX;AAAA,MACA,WAAW,GAAG,yBAAyB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MAC1D,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -27,7 +27,7 @@ function InputOTPSlot({ index, className, ...props }) {
27
27
  "data-slot": "input-otp-slot",
28
28
  "data-active": slot?.isActive,
29
29
  className: cn(
30
- "data-[active=true]:border-primary-ink data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink data-[active=true]:aria-invalid:border-destructive-ink border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-colors duration-150 ease-out outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
30
+ "data-[active=true]:border-primary-ink data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink data-[active=true]:aria-invalid:border-destructive-ink dark:bg-input-fill border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-colors duration-150 ease-out outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
31
31
  className
32
32
  ),
33
33
  ...props,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/input-otp.tsx"],"sourcesContent":["\"use client\";\n\nimport { OTPInput, OTPInputContext } from \"input-otp\";\nimport { MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * InputOTP — ô nhập mã OTP (input-otp). Dùng cho luồng 2FA / xác thực email.\n *\n * Khác bản comitor-ds: đọc slot bằng optional chaining thay vì `?? {}` — tsconfig\n * bật `noUncheckedIndexedAccess` nên `slots[index]` là `Slot | undefined`.\n */\n\nexport type InputOTPProps = React.ComponentProps<typeof OTPInput> & {\n containerClassName?: string;\n};\nexport type InputOTPGroupProps = React.ComponentProps<\"div\">;\nexport type InputOTPSlotProps = React.ComponentProps<\"div\"> & { index: number };\nexport type InputOTPSeparatorProps = React.ComponentProps<\"div\">;\n\nfunction InputOTP({ className, containerClassName, ...props }: InputOTPProps) {\n return (\n <OTPInput\n data-slot=\"input-otp\"\n containerClassName={cn(\"flex items-center gap-2 has-disabled:opacity-50\", containerClassName)}\n className={cn(\"disabled:cursor-not-allowed\", className)}\n {...props}\n />\n );\n}\n\nfunction InputOTPGroup({ className, ...props }: InputOTPGroupProps) {\n return <div data-slot=\"input-otp-group\" className={cn(\"flex items-center\", className)} {...props} />;\n}\n\nfunction InputOTPSlot({ index, className, ...props }: InputOTPSlotProps) {\n const inputOTPContext = React.useContext(OTPInputContext);\n const slot = inputOTPContext?.slots[index];\n\n return (\n <div\n data-slot=\"input-otp-slot\"\n data-active={slot?.isActive}\n className={cn(\n \"data-[active=true]:border-primary-ink data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink data-[active=true]:aria-invalid:border-destructive-ink border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-colors duration-150 ease-out outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]\",\n className\n )}\n {...props}\n >\n {slot?.char}\n {slot?.hasFakeCaret && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"animate-caret-blink bg-foreground h-4 w-px duration-1000\" />\n </div>\n )}\n </div>\n );\n}\n\nfunction InputOTPSeparator({ ...props }: InputOTPSeparatorProps) {\n return (\n // biome-ignore lint/a11y/useFocusableInteractive: dấu phân cách tĩnh giữa các nhóm ô, không phải splitter kéo được nên không cần focus\n // biome-ignore lint/a11y/useAriaPropsForRole: separator tĩnh không có giá trị → không cần aria-valuenow\n <div data-slot=\"input-otp-separator\" role=\"separator\" {...props}>\n <MinusIcon />\n </div>\n );\n}\n\nexport { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };\n"],"mappings":";AAuBI,cAkBA,YAlBA;AArBJ,SAAS,UAAU,uBAAuB;AAC1C,SAAS,iBAAiB;AAC1B,YAAY,WAAW;AACvB,SAAS,UAAU;AAgBnB,SAAS,SAAS,EAAE,WAAW,oBAAoB,GAAG,MAAM,GAAkB;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,oBAAoB,GAAG,mDAAmD,kBAAkB;AAAA,MAC5F,WAAW,GAAG,+BAA+B,SAAS;AAAA,MACrD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AAClE,SAAO,oBAAC,SAAI,aAAU,mBAAkB,WAAW,GAAG,qBAAqB,SAAS,GAAI,GAAG,OAAO;AACpG;AAEA,SAAS,aAAa,EAAE,OAAO,WAAW,GAAG,MAAM,GAAsB;AACvE,QAAM,kBAAkB,MAAM,WAAW,eAAe;AACxD,QAAM,OAAO,iBAAiB,MAAM,KAAK;AAEzC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAa,MAAM;AAAA,MACnB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,cAAM;AAAA,QACN,MAAM,gBACL,oBAAC,SAAI,WAAU,yEACb,8BAAC,SAAI,WAAU,4DAA2D,GAC5E;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,kBAAkB,EAAE,GAAG,MAAM,GAA2B;AAC/D;AAAA;AAAA;AAAA,IAGE,oBAAC,SAAI,aAAU,uBAAsB,MAAK,aAAa,GAAG,OACxD,8BAAC,aAAU,GACb;AAAA;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/input-otp.tsx"],"sourcesContent":["\"use client\";\n\nimport { OTPInput, OTPInputContext } from \"input-otp\";\nimport { MinusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * InputOTP — ô nhập mã OTP (input-otp). Dùng cho luồng 2FA / xác thực email.\n *\n * Khác bản comitor-ds: đọc slot bằng optional chaining thay vì `?? {}` — tsconfig\n * bật `noUncheckedIndexedAccess` nên `slots[index]` là `Slot | undefined`.\n */\n\nexport type InputOTPProps = React.ComponentProps<typeof OTPInput> & {\n containerClassName?: string;\n};\nexport type InputOTPGroupProps = React.ComponentProps<\"div\">;\nexport type InputOTPSlotProps = React.ComponentProps<\"div\"> & { index: number };\nexport type InputOTPSeparatorProps = React.ComponentProps<\"div\">;\n\nfunction InputOTP({ className, containerClassName, ...props }: InputOTPProps) {\n return (\n <OTPInput\n data-slot=\"input-otp\"\n containerClassName={cn(\"flex items-center gap-2 has-disabled:opacity-50\", containerClassName)}\n className={cn(\"disabled:cursor-not-allowed\", className)}\n {...props}\n />\n );\n}\n\nfunction InputOTPGroup({ className, ...props }: InputOTPGroupProps) {\n return <div data-slot=\"input-otp-group\" className={cn(\"flex items-center\", className)} {...props} />;\n}\n\nfunction InputOTPSlot({ index, className, ...props }: InputOTPSlotProps) {\n const inputOTPContext = React.useContext(OTPInputContext);\n const slot = inputOTPContext?.slots[index];\n\n return (\n <div\n data-slot=\"input-otp-slot\"\n data-active={slot?.isActive}\n className={cn(\n \"data-[active=true]:border-primary-ink data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink data-[active=true]:aria-invalid:border-destructive-ink dark:bg-input-fill border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-colors duration-150 ease-out outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]\",\n className\n )}\n {...props}\n >\n {slot?.char}\n {slot?.hasFakeCaret && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <div className=\"animate-caret-blink bg-foreground h-4 w-px duration-1000\" />\n </div>\n )}\n </div>\n );\n}\n\nfunction InputOTPSeparator({ ...props }: InputOTPSeparatorProps) {\n return (\n // biome-ignore lint/a11y/useFocusableInteractive: dấu phân cách tĩnh giữa các nhóm ô, không phải splitter kéo được nên không cần focus\n // biome-ignore lint/a11y/useAriaPropsForRole: separator tĩnh không có giá trị → không cần aria-valuenow\n <div data-slot=\"input-otp-separator\" role=\"separator\" {...props}>\n <MinusIcon />\n </div>\n );\n}\n\nexport { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };\n"],"mappings":";AAuBI,cAkBA,YAlBA;AArBJ,SAAS,UAAU,uBAAuB;AAC1C,SAAS,iBAAiB;AAC1B,YAAY,WAAW;AACvB,SAAS,UAAU;AAgBnB,SAAS,SAAS,EAAE,WAAW,oBAAoB,GAAG,MAAM,GAAkB;AAC5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,oBAAoB,GAAG,mDAAmD,kBAAkB;AAAA,MAC5F,WAAW,GAAG,+BAA+B,SAAS;AAAA,MACrD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AAClE,SAAO,oBAAC,SAAI,aAAU,mBAAkB,WAAW,GAAG,qBAAqB,SAAS,GAAI,GAAG,OAAO;AACpG;AAEA,SAAS,aAAa,EAAE,OAAO,WAAW,GAAG,MAAM,GAAsB;AACvE,QAAM,kBAAkB,MAAM,WAAW,eAAe;AACxD,QAAM,OAAO,iBAAiB,MAAM,KAAK;AAEzC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,eAAa,MAAM;AAAA,MACnB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA,cAAM;AAAA,QACN,MAAM,gBACL,oBAAC,SAAI,WAAU,yEACb,8BAAC,SAAI,WAAU,4DAA2D,GAC5E;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,kBAAkB,EAAE,GAAG,MAAM,GAA2B;AAC/D;AAAA;AAAA;AAAA,IAGE,oBAAC,SAAI,aAAU,uBAAsB,MAAK,aAAa,GAAG,OACxD,8BAAC,aAAU,GACb;AAAA;AAEJ;","names":[]}
@@ -105,7 +105,7 @@ function NavigationMenuIndicator({ className, ...props }) {
105
105
  className
106
106
  ),
107
107
  ...props,
108
- children: /* @__PURE__ */ jsx("div", { className: "bg-primary-ink relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" })
108
+ children: /* @__PURE__ */ jsx("div", { className: "bg-control-edge relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" })
109
109
  }
110
110
  );
111
111
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/navigation-menu.tsx"],"sourcesContent":["import * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\";\nimport { cva } from \"class-variance-authority\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * NavigationMenu — thanh điều hướng có menu bung (Radix NavigationMenu).\n *\n * Dùng cho site marketing / trang công khai. Trong app đã đăng nhập thì điều hướng\n * là Sidebar + AppLauncher, không dùng component này.\n *\n * KHÔNG có directive \"use client\": file chỉ khai báo bố cục, phần state nằm trong\n * gói Radix (bản thân gói đã đánh dấu client) → dùng được trực tiếp trong RSC.\n *\n * `viewport` (mặc định true): nội dung menu render vào một khung chung phía dưới\n * thanh, có hiệu ứng đổi kích thước. Đặt false khi muốn mỗi mục tự bung popover\n * riêng — style tương ứng bám theo `group-data-[viewport=false]`.\n */\n\nexport type NavigationMenuProps = React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {\n viewport?: boolean;\n};\n\nfunction NavigationMenu({ className, children, viewport = true, ...props }: NavigationMenuProps) {\n return (\n <NavigationMenuPrimitive.Root\n data-slot=\"navigation-menu\"\n data-viewport={viewport}\n className={cn(\"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center\", className)}\n {...props}\n >\n {children}\n {viewport && <NavigationMenuViewport />}\n </NavigationMenuPrimitive.Root>\n );\n}\n\nexport type NavigationMenuListProps = React.ComponentProps<typeof NavigationMenuPrimitive.List>;\n\nfunction NavigationMenuList({ className, ...props }: NavigationMenuListProps) {\n return (\n <NavigationMenuPrimitive.List\n data-slot=\"navigation-menu-list\"\n className={cn(\"group flex flex-1 list-none items-center justify-center gap-1\", className)}\n {...props}\n />\n );\n}\n\nexport type NavigationMenuItemProps = React.ComponentProps<typeof NavigationMenuPrimitive.Item>;\n\nfunction NavigationMenuItem({ className, ...props }: NavigationMenuItemProps) {\n return (\n <NavigationMenuPrimitive.Item data-slot=\"navigation-menu-item\" className={cn(\"relative\", className)} {...props} />\n );\n}\n\n/** Style dùng lại cho mục điều hướng không có menu con (vd bọc thẻ <a>). */\nconst navigationMenuTriggerStyle = cva(\n \"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 outline-none transition-colors duration-150 ease-out 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\nexport type NavigationMenuTriggerProps = React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>;\n\nfunction NavigationMenuTrigger({ className, children, ...props }: NavigationMenuTriggerProps) {\n return (\n <NavigationMenuPrimitive.Trigger\n data-slot=\"navigation-menu-trigger\"\n className={cn(navigationMenuTriggerStyle(), \"group\", className)}\n {...props}\n >\n {children}{\" \"}\n <ChevronDownIcon\n className=\"relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180\"\n aria-hidden=\"true\"\n />\n </NavigationMenuPrimitive.Trigger>\n );\n}\n\nexport type NavigationMenuContentProps = React.ComponentProps<typeof NavigationMenuPrimitive.Content>;\n\nfunction NavigationMenuContent({ className, ...props }: NavigationMenuContentProps) {\n return (\n <NavigationMenuPrimitive.Content\n data-slot=\"navigation-menu-content\"\n className={cn(\n \"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto\",\n \"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow-overlay group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type NavigationMenuViewportProps = React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>;\n\nfunction NavigationMenuViewport({ className, ...props }: NavigationMenuViewportProps) {\n return (\n <div className=\"absolute top-full left-0 isolate z-50 flex justify-center\">\n <NavigationMenuPrimitive.Viewport\n data-slot=\"navigation-menu-viewport\"\n className={cn(\n \"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-overlay md:w-[var(--radix-navigation-menu-viewport-width)]\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nexport type NavigationMenuLinkProps = React.ComponentProps<typeof NavigationMenuPrimitive.Link>;\n\nfunction NavigationMenuLink({ className, ...props }: NavigationMenuLinkProps) {\n return (\n <NavigationMenuPrimitive.Link\n data-slot=\"navigation-menu-link\"\n className={cn(\n \"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-colors duration-150 ease-out 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 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type NavigationMenuIndicatorProps = React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>;\n\nfunction NavigationMenuIndicator({ className, ...props }: NavigationMenuIndicatorProps) {\n return (\n <NavigationMenuPrimitive.Indicator\n data-slot=\"navigation-menu-indicator\"\n className={cn(\n \"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden\",\n className\n )}\n {...props}\n >\n {/*\n Hình vuông xoay 45° chỉ vào trigger ĐANG MỞ — theo Radix đây là dấu \"highlight the\n currently active trigger\", tức thông tin trạng thái, ngưỡng 3:1 (WCAG 1.4.11).\n `bg-border` là bậc TRANG TRÍ (1,16:1 light · 1,17:1 dark với --background) nên dấu này\n vốn không hiện ở cả hai theme. Dùng `--primary-ink` — cùng token mà `Tabs` dùng cho vạch\n active (`data-[state=active]:border-primary-ink`): 6,51:1 (light) · 12,67:1 (dark) trên\n --background, 6,09:1 · 10,80:1 trên --muted ✓\n */}\n <div className=\"bg-primary-ink relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\" />\n </NavigationMenuPrimitive.Indicator>\n );\n}\n\nexport {\n NavigationMenu,\n NavigationMenuContent,\n NavigationMenuIndicator,\n NavigationMenuItem,\n NavigationMenuLink,\n NavigationMenuList,\n NavigationMenuTrigger,\n NavigationMenuViewport,\n navigationMenuTriggerStyle\n};\n"],"mappings":"AA0BI,SAOe,KAPf;AA1BJ,YAAY,6BAA6B;AACzC,SAAS,WAAW;AACpB,SAAS,uBAAuB;AAEhC,SAAS,UAAU;AAoBnB,SAAS,eAAe,EAAE,WAAW,UAAU,WAAW,MAAM,GAAG,MAAM,GAAwB;AAC/F,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,iBAAe;AAAA,MACf,WAAW,GAAG,oFAAoF,SAAS;AAAA,MAC1G,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,YAAY,oBAAC,0BAAuB;AAAA;AAAA;AAAA,EACvC;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iEAAiE,SAAS;AAAA,MACvF,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE,oBAAC,wBAAwB,MAAxB,EAA6B,aAAU,wBAAuB,WAAW,GAAG,YAAY,SAAS,GAAI,GAAG,OAAO;AAEpH;AAGA,MAAM,6BAA6B;AAAA,EACjC;AACF;AAIA,SAAS,sBAAsB,EAAE,WAAW,UAAU,GAAG,MAAM,GAA+B;AAC5F,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,2BAA2B,GAAG,SAAS,SAAS;AAAA,MAC7D,GAAG;AAAA,MAEH;AAAA;AAAA,QAAU;AAAA,QACX;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAY;AAAA;AAAA,QACd;AAAA;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,sBAAsB,EAAE,WAAW,GAAG,MAAM,GAA+B;AAClF,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,uBAAuB,EAAE,WAAW,GAAG,MAAM,GAAgC;AACpF,SACE,oBAAC,SAAI,WAAU,6DACb;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,wBAAwB,EAAE,WAAW,GAAG,MAAM,GAAiC;AACtF,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAUJ,8BAAC,SAAI,WAAU,+EAA8E;AAAA;AAAA,EAC/F;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/navigation-menu.tsx"],"sourcesContent":["import * as NavigationMenuPrimitive from \"@radix-ui/react-navigation-menu\";\nimport { cva } from \"class-variance-authority\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * NavigationMenu — thanh điều hướng có menu bung (Radix NavigationMenu).\n *\n * Dùng cho site marketing / trang công khai. Trong app đã đăng nhập thì điều hướng\n * là Sidebar + AppLauncher, không dùng component này.\n *\n * KHÔNG có directive \"use client\": file chỉ khai báo bố cục, phần state nằm trong\n * gói Radix (bản thân gói đã đánh dấu client) → dùng được trực tiếp trong RSC.\n *\n * `viewport` (mặc định true): nội dung menu render vào một khung chung phía dưới\n * thanh, có hiệu ứng đổi kích thước. Đặt false khi muốn mỗi mục tự bung popover\n * riêng — style tương ứng bám theo `group-data-[viewport=false]`.\n */\n\nexport type NavigationMenuProps = React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {\n viewport?: boolean;\n};\n\nfunction NavigationMenu({ className, children, viewport = true, ...props }: NavigationMenuProps) {\n return (\n <NavigationMenuPrimitive.Root\n data-slot=\"navigation-menu\"\n data-viewport={viewport}\n className={cn(\"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center\", className)}\n {...props}\n >\n {children}\n {viewport && <NavigationMenuViewport />}\n </NavigationMenuPrimitive.Root>\n );\n}\n\nexport type NavigationMenuListProps = React.ComponentProps<typeof NavigationMenuPrimitive.List>;\n\nfunction NavigationMenuList({ className, ...props }: NavigationMenuListProps) {\n return (\n <NavigationMenuPrimitive.List\n data-slot=\"navigation-menu-list\"\n className={cn(\"group flex flex-1 list-none items-center justify-center gap-1\", className)}\n {...props}\n />\n );\n}\n\nexport type NavigationMenuItemProps = React.ComponentProps<typeof NavigationMenuPrimitive.Item>;\n\nfunction NavigationMenuItem({ className, ...props }: NavigationMenuItemProps) {\n return (\n <NavigationMenuPrimitive.Item data-slot=\"navigation-menu-item\" className={cn(\"relative\", className)} {...props} />\n );\n}\n\n/** Style dùng lại cho mục điều hướng không có menu con (vd bọc thẻ <a>). */\nconst navigationMenuTriggerStyle = cva(\n \"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 outline-none transition-colors duration-150 ease-out 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\nexport type NavigationMenuTriggerProps = React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>;\n\nfunction NavigationMenuTrigger({ className, children, ...props }: NavigationMenuTriggerProps) {\n return (\n <NavigationMenuPrimitive.Trigger\n data-slot=\"navigation-menu-trigger\"\n className={cn(navigationMenuTriggerStyle(), \"group\", className)}\n {...props}\n >\n {children}{\" \"}\n <ChevronDownIcon\n className=\"relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180\"\n aria-hidden=\"true\"\n />\n </NavigationMenuPrimitive.Trigger>\n );\n}\n\nexport type NavigationMenuContentProps = React.ComponentProps<typeof NavigationMenuPrimitive.Content>;\n\nfunction NavigationMenuContent({ className, ...props }: NavigationMenuContentProps) {\n return (\n <NavigationMenuPrimitive.Content\n data-slot=\"navigation-menu-content\"\n className={cn(\n \"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto\",\n \"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow-overlay group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type NavigationMenuViewportProps = React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>;\n\nfunction NavigationMenuViewport({ className, ...props }: NavigationMenuViewportProps) {\n return (\n <div className=\"absolute top-full left-0 isolate z-50 flex justify-center\">\n <NavigationMenuPrimitive.Viewport\n data-slot=\"navigation-menu-viewport\"\n className={cn(\n \"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow-overlay md:w-[var(--radix-navigation-menu-viewport-width)]\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nexport type NavigationMenuLinkProps = React.ComponentProps<typeof NavigationMenuPrimitive.Link>;\n\nfunction NavigationMenuLink({ className, ...props }: NavigationMenuLinkProps) {\n return (\n <NavigationMenuPrimitive.Link\n data-slot=\"navigation-menu-link\"\n className={cn(\n \"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-colors duration-150 ease-out 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 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type NavigationMenuIndicatorProps = React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>;\n\nfunction NavigationMenuIndicator({ className, ...props }: NavigationMenuIndicatorProps) {\n return (\n <NavigationMenuPrimitive.Indicator\n data-slot=\"navigation-menu-indicator\"\n className={cn(\n \"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden\",\n className\n )}\n {...props}\n >\n {/*\n Hình vuông xoay 45° chỉ vào trigger ĐANG MỞ — theo Radix đây là dấu \"highlight the\n currently active trigger\", tức thông tin trạng thái, ngưỡng 3:1 (WCAG 1.4.11).\n `bg-border` là bậc TRANG TRÍ (1,16:1 light · 1,17:1 dark với --background) nên dấu này\n vốn không hiện ở cả hai theme. Dùng `--primary-ink` — cùng token mà `Tabs` dùng cho vạch\n active (`data-[state=active]:border-primary-ink`): 6,51:1 (light) · 12,67:1 (dark) trên\n --background, 6,09:1 · 10,80:1 trên --muted ✓\n */}\n <div className=\"bg-control-edge relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md\" />\n </NavigationMenuPrimitive.Indicator>\n );\n}\n\nexport {\n NavigationMenu,\n NavigationMenuContent,\n NavigationMenuIndicator,\n NavigationMenuItem,\n NavigationMenuLink,\n NavigationMenuList,\n NavigationMenuTrigger,\n NavigationMenuViewport,\n navigationMenuTriggerStyle\n};\n"],"mappings":"AA0BI,SAOe,KAPf;AA1BJ,YAAY,6BAA6B;AACzC,SAAS,WAAW;AACpB,SAAS,uBAAuB;AAEhC,SAAS,UAAU;AAoBnB,SAAS,eAAe,EAAE,WAAW,UAAU,WAAW,MAAM,GAAG,MAAM,GAAwB;AAC/F,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,iBAAe;AAAA,MACf,WAAW,GAAG,oFAAoF,SAAS;AAAA,MAC1G,GAAG;AAAA,MAEH;AAAA;AAAA,QACA,YAAY,oBAAC,0BAAuB;AAAA;AAAA;AAAA,EACvC;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iEAAiE,SAAS;AAAA,MACvF,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE,oBAAC,wBAAwB,MAAxB,EAA6B,aAAU,wBAAuB,WAAW,GAAG,YAAY,SAAS,GAAI,GAAG,OAAO;AAEpH;AAGA,MAAM,6BAA6B;AAAA,EACjC;AACF;AAIA,SAAS,sBAAsB,EAAE,WAAW,UAAU,GAAG,MAAM,GAA+B;AAC5F,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,2BAA2B,GAAG,SAAS,SAAS;AAAA,MAC7D,GAAG;AAAA,MAEH;AAAA;AAAA,QAAU;AAAA,QACX;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAY;AAAA;AAAA,QACd;AAAA;AAAA;AAAA,EACF;AAEJ;AAIA,SAAS,sBAAsB,EAAE,WAAW,GAAG,MAAM,GAA+B;AAClF,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,uBAAuB,EAAE,WAAW,GAAG,MAAM,GAAgC;AACpF,SACE,oBAAC,SAAI,WAAU,6DACb;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAIA,SAAS,mBAAmB,EAAE,WAAW,GAAG,MAAM,GAA4B;AAC5E,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,wBAAwB,EAAE,WAAW,GAAG,MAAM,GAAiC;AACtF,SACE;AAAA,IAAC,wBAAwB;AAAA,IAAxB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAUJ,8BAAC,SAAI,WAAU,gFAA+E;AAAA;AAAA,EAChG;AAEJ;","names":[]}
@@ -12,7 +12,7 @@ function RadioGroupItem({ className, ...props }) {
12
12
  {
13
13
  "data-slot": "radio-group-item",
14
14
  className: cn(
15
- "border-input text-control-on focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50",
15
+ "border-input text-control-on focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink dark:bg-input-fill aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50",
16
16
  className
17
17
  ),
18
18
  ...props,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport { CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ─ CHẤM ĐÃ CHỌN: `--control-on`, KHÔNG PHẢI `--primary` ─\n *\n * Ô radio để nền trong suốt, nên \"đã chọn hay chưa\" chỉ còn được kể bằng cái CHẤM ở giữa —\n * thông tin thị giác đúng nghĩa của WCAG 1.4.11, ngưỡng 3:1. `fill-primary` (gold #E8B824)\n * trên nền trắng chỉ **1,86:1** ✗: ở light người dùng nhìn ô đã chọn gần như y hệt ô chưa chọn.\n * (Ở dark thì lọt lưới nên lỗi này chỉ lộ ở một nửa số theme.)\n * `--control-on` (= `--primary-ink`) là token của vai trò \"control ở trạng thái BẬT/ĐÃ CHỌN\".\n * Đo với nền TRANG, lấy ca tệ nhất trong 5 nền (`--muted`) và ca thường gặp (`--background`):\n *\n * chấm ⇄ nền trang : 6,09–6,51:1 (light) · 10,80–12,67:1 (dark) ✓\n * viền ô `border-input` ⇄ nền trang : 3,83:1 (light) · 4,21–4,94:1 (dark) ✓\n *\n * ─ VÌ SAO BỎ `dark:bg-input/30` ─\n * `--input` là alias của `--control-edge` — token VIỀN control (hợp đồng màu §3), không phải bậc\n * tô nền. Đem nó tô ruột ở dark vừa sai vai trò, vừa nâng mặt nền LÊN SÁNG hơn mọi nền trang, kéo\n * `--muted-foreground` (token chữ, hiệu chỉnh theo nền TRANG — §6) xuống dưới ngưỡng ở mọi nền:\n * 3,81 · 3,54 · 3,22:1 ✗. Bỏ lớp tô thì ô radio vẫn được khoanh vùng bằng chính viền `border-input`\n * (4,21–4,94:1 ở dark, thừa ngưỡng 3:1), và chấm đã chọn còn nổi hơn trước (8,47 → 10,80–12,67:1).\n *\n * `text-control-on` trên Item giữ cho `fill-current`/icon do app truyền vào cũng đúng màu.\n * Viền lỗi dùng `--destructive-ink` (7,15:1 / 6,84:1) thay `--destructive` (5,03:1 nhưng chỉ\n * 3,76:1 ở dark) — xem hợp đồng màu §1: bậc đi lên nền TRANG luôn là `-ink`.\n */\nexport type RadioGroupProps = React.ComponentProps<typeof RadioGroupPrimitive.Root>;\nexport type RadioGroupItemProps = React.ComponentProps<typeof RadioGroupPrimitive.Item>;\n\nfunction RadioGroup({ className, ...props }: RadioGroupProps) {\n return <RadioGroupPrimitive.Root data-slot=\"radio-group\" className={cn(\"grid gap-3\", className)} {...props} />;\n}\n\nfunction RadioGroupItem({ className, ...props }: RadioGroupItemProps) {\n return (\n <RadioGroupPrimitive.Item\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-input text-control-on focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"relative flex items-center justify-center\"\n >\n <CircleIcon className=\"fill-control-on absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport { RadioGroup, RadioGroupItem };\n"],"mappings":";AAmCS;AAjCT,YAAY,yBAAyB;AACrC,SAAS,kBAAkB;AAE3B,SAAS,UAAU;AA6BnB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SAAO,oBAAC,oBAAoB,MAApB,EAAyB,aAAU,eAAc,WAAW,GAAG,cAAc,SAAS,GAAI,GAAG,OAAO;AAC9G;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,oBAAoB;AAAA,QAApB;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAEV,8BAAC,cAAW,WAAU,sFAAqF;AAAA;AAAA,MAC7G;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport { CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ─ CHẤM ĐÃ CHỌN: `--control-on`, KHÔNG PHẢI `--primary` ─\n *\n * Ô radio để nền trong suốt, nên \"đã chọn hay chưa\" chỉ còn được kể bằng cái CHẤM ở giữa —\n * thông tin thị giác đúng nghĩa của WCAG 1.4.11, ngưỡng 3:1. `fill-primary` (gold #E8B824)\n * trên nền trắng chỉ **1,86:1** ✗: ở light người dùng nhìn ô đã chọn gần như y hệt ô chưa chọn.\n * (Ở dark thì lọt lưới nên lỗi này chỉ lộ ở một nửa số theme.)\n * `--control-on` là token của vai trò \"control ở trạng thái BẬT/ĐÃ CHỌN\"; giá trị của nó đổi theo\n * bảng màu (styles.css khối 5b), nên ĐỪNG viết thẳng `fill-primary` / `bg-gold-300` ở đây.\n * Đo với nền TRANG, lấy ca tệ nhất trong 5 nền (`--muted`) và ca thường gặp (`--background`):\n *\n * bảng MẶC ĐỊNH chấm ⇄ nền trang : 1,73–1,86:1 ✗ (bản duyệt, đã chấp nhận)\n * bảng TƯƠNG PHẢN CAO chấm ⇄ nền trang : 6,09–6,51:1 (light) · 10,80–12,67:1 (dark) ✓\n *\n * ─ LỚP TÔ Ở DARK: `dark:bg-input-fill` ─\n * comitor-ds viết `dark:bg-input/30`, và ở bảng mặc định lớp tô đó là thứ DUY NHẤT còn khoanh\n * vùng ô radio: viền `--input` (#1E2128) trên `--card` (#16181D) chỉ 1,10:1. Nhưng viết thẳng\n * `bg-input/30` thì ở bảng tương phản cao (`--input` = #7E8290) lớp tô sáng hơn mọi nền trang và\n * kéo `--muted-foreground` xuống 3,81 · 3,54 · 3,22:1 ✗ — nên nó đi qua `--input-fill`, biến này\n * trong suốt ở bảng tương phản cao (bên đó viền tự đủ đậm: 4,94:1).\n *\n * `text-control-on` trên Item giữ cho `fill-current`/icon do app truyền vào cũng đúng màu.\n * Viền lỗi dùng `--destructive-ink`: ở bảng mặc định nó bằng `--destructive` (đúng `border-red`\n * của comitor-ds), ở bảng tương phản cao nó là bậc tối riêng (7,15:1 / 6,84:1) — xem hợp đồng\n * màu §1: bậc đi lên nền TRANG luôn là `-ink`.\n */\nexport type RadioGroupProps = React.ComponentProps<typeof RadioGroupPrimitive.Root>;\nexport type RadioGroupItemProps = React.ComponentProps<typeof RadioGroupPrimitive.Item>;\n\nfunction RadioGroup({ className, ...props }: RadioGroupProps) {\n return <RadioGroupPrimitive.Root data-slot=\"radio-group\" className={cn(\"grid gap-3\", className)} {...props} />;\n}\n\nfunction RadioGroupItem({ className, ...props }: RadioGroupItemProps) {\n return (\n <RadioGroupPrimitive.Item\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-input text-control-on focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink dark:bg-input-fill aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"relative flex items-center justify-center\"\n >\n <CircleIcon className=\"fill-control-on absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport { RadioGroup, RadioGroupItem };\n"],"mappings":";AAqCS;AAnCT,YAAY,yBAAyB;AACrC,SAAS,kBAAkB;AAE3B,SAAS,UAAU;AA+BnB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SAAO,oBAAC,oBAAoB,MAApB,EAAyB,aAAU,eAAc,WAAW,GAAG,cAAc,SAAS,GAAI,GAAG,OAAO;AAC9G;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,oBAAoB;AAAA,QAApB;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAEV,8BAAC,cAAW,WAAU,sFAAqF;AAAA;AAAA,MAC7G;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -26,7 +26,7 @@ function ResizableHandle({ withHandle, className, ...props }) {
26
26
  className
27
27
  ),
28
28
  ...props,
29
- children: withHandle && /* @__PURE__ */ jsx("div", { className: "bg-control-edge z-10 flex h-4 w-3 items-center justify-center rounded-xs [&_svg]:text-control-knob", children: /* @__PURE__ */ jsx(GripVerticalIcon, { className: "size-2.5" }) })
29
+ children: withHandle && /* @__PURE__ */ jsx("div", { className: "bg-control-edge z-10 flex h-4 w-3 items-center justify-center rounded-xs border", children: /* @__PURE__ */ jsx(GripVerticalIcon, { className: "size-2.5" }) })
30
30
  }
31
31
  );
32
32
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/resizable.tsx"],"sourcesContent":["\"use client\";\n\nimport { GripVerticalIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport * as ResizablePrimitive from \"react-resizable-panels\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Resizable (react-resizable-panels) — bố cục nhiều panel kéo giãn được.\n * Dùng cho layout 3 cột kiểu Linear/Lark (danh sách ▸ chi tiết ▸ panel phụ).\n * Muốn nhớ kích thước: đặt `autoSaveId` trên `ResizablePanelGroup`.\n *\n * ─ MÀU: `--control-edge`, KHÔNG PHẢI `--border` ─\n * Thanh chia này là CONTROL THẬT chứ không phải đường kẻ trang trí: react-resizable-panels\n * render nó với `role=\"separator\"` + `tabIndex=0`, kéo được bằng chuột lẫn bàn phím. Nên nó\n * chịu ngưỡng 3:1 của WCAG 1.4.11, và theo hợp đồng ở styles.css §RANH GIỚI thì ranh giới\n * control phải đi qua `--control-edge` — `--border` là bậc TRANG TRÍ, cố ý nhạt:\n *\n * `bg-border` ⇄ --background 1,16:1 (light) · 1,17:1 (dark) ✗\n * ⇄ --card 1,16:1 · 1,10:1 ✗ · ⇄ --muted 1,08:1 · 1,00:1 ✗\n * `bg-control-edge` ⇄ --background 3,83:1 · 4,94:1 ✓ · ⇄ --card 3,83:1 · 4,64:1 ✓\n * ⇄ --muted 3,58:1 · 4,21:1 ✓\n *\n * Núm `withHandle` cũng vậy: tô đặc `bg-control-edge` (tự khoanh vùng, không cần `border`),\n * vạch grip bên trong lấy `--control-knob` (= nền trang) — 3,83:1 / 4,94:1 trên núm ✓.\n */\n\nexport type ResizablePanelGroupProps = React.ComponentProps<typeof ResizablePrimitive.PanelGroup>;\nexport type ResizablePanelProps = React.ComponentProps<typeof ResizablePrimitive.Panel>;\nexport type ResizableHandleProps = React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {\n /** Hiện tay nắm có gạch dọc ở giữa thanh chia. */\n withHandle?: boolean;\n};\n\nfunction ResizablePanelGroup({ className, ...props }: ResizablePanelGroupProps) {\n return (\n <ResizablePrimitive.PanelGroup\n data-slot=\"resizable-panel-group\"\n className={cn(\"flex h-full w-full data-[panel-group-direction=vertical]:flex-col\", className)}\n {...props}\n />\n );\n}\n\nfunction ResizablePanel({ ...props }: ResizablePanelProps) {\n return <ResizablePrimitive.Panel data-slot=\"resizable-panel\" {...props} />;\n}\n\nfunction ResizableHandle({ withHandle, className, ...props }: ResizableHandleProps) {\n return (\n <ResizablePrimitive.PanelResizeHandle\n data-slot=\"resizable-handle\"\n className={cn(\n \"bg-control-edge relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90\",\n className\n )}\n {...props}\n >\n {withHandle && (\n <div className=\"bg-control-edge z-10 flex h-4 w-3 items-center justify-center rounded-xs [&_svg]:text-control-knob\">\n <GripVerticalIcon className=\"size-2.5\" />\n </div>\n )}\n </ResizablePrimitive.PanelResizeHandle>\n );\n}\n\nexport { ResizableHandle, ResizablePanel, ResizablePanelGroup };\n"],"mappings":";AAoCI;AAlCJ,SAAS,wBAAwB;AAEjC,YAAY,wBAAwB;AACpC,SAAS,UAAU;AA6BnB,SAAS,oBAAoB,EAAE,WAAW,GAAG,MAAM,GAA6B;AAC9E,SACE;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,qEAAqE,SAAS;AAAA,MAC3F,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,GAAG,MAAM,GAAwB;AACzD,SAAO,oBAAC,mBAAmB,OAAnB,EAAyB,aAAU,mBAAmB,GAAG,OAAO;AAC1E;AAEA,SAAS,gBAAgB,EAAE,YAAY,WAAW,GAAG,MAAM,GAAyB;AAClF,SACE;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH,wBACC,oBAAC,SAAI,WAAU,sGACb,8BAAC,oBAAiB,WAAU,YAAW,GACzC;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/resizable.tsx"],"sourcesContent":["\"use client\";\n\nimport { GripVerticalIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport * as ResizablePrimitive from \"react-resizable-panels\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Resizable (react-resizable-panels) — bố cục nhiều panel kéo giãn được.\n * Dùng cho layout 3 cột kiểu Linear/Lark (danh sách ▸ chi tiết ▸ panel phụ).\n * Muốn nhớ kích thước: đặt `autoSaveId` trên `ResizablePanelGroup`.\n *\n * ─ MÀU: `--control-edge`, KHÔNG PHẢI `--border` ─\n * Thanh chia này là CONTROL THẬT chứ không phải đường kẻ trang trí: react-resizable-panels\n * render nó với `role=\"separator\"` + `tabIndex=0`, kéo được bằng chuột lẫn bàn phím. Nên nó\n * chịu ngưỡng 3:1 của WCAG 1.4.11, và theo hợp đồng ở styles.css §RANH GIỚI thì ranh giới\n * control phải đi qua `--control-edge` — `--border` là bậc TRANG TRÍ, cố ý nhạt:\n *\n * `bg-border` ⇄ --background 1,16:1 (light) · 1,17:1 (dark) ✗\n * ⇄ --card 1,16:1 · 1,10:1 ✗ · ⇄ --muted 1,08:1 · 1,00:1 ✗\n * `bg-control-edge` ⇄ --background 3,83:1 · 4,94:1 ✓ · ⇄ --card 3,83:1 · 4,64:1 ✓\n * ⇄ --muted 3,58:1 · 4,21:1 ✓\n *\n * Núm `withHandle` cũng vậy: tô đặc `bg-control-edge` (tự khoanh vùng, không cần `border`),\n * vạch grip bên trong lấy `--control-knob` (= nền trang) — 3,83:1 / 4,94:1 trên núm ✓.\n */\n\nexport type ResizablePanelGroupProps = React.ComponentProps<typeof ResizablePrimitive.PanelGroup>;\nexport type ResizablePanelProps = React.ComponentProps<typeof ResizablePrimitive.Panel>;\nexport type ResizableHandleProps = React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {\n /** Hiện tay nắm có gạch dọc ở giữa thanh chia. */\n withHandle?: boolean;\n};\n\nfunction ResizablePanelGroup({ className, ...props }: ResizablePanelGroupProps) {\n return (\n <ResizablePrimitive.PanelGroup\n data-slot=\"resizable-panel-group\"\n className={cn(\"flex h-full w-full data-[panel-group-direction=vertical]:flex-col\", className)}\n {...props}\n />\n );\n}\n\nfunction ResizablePanel({ ...props }: ResizablePanelProps) {\n return <ResizablePrimitive.Panel data-slot=\"resizable-panel\" {...props} />;\n}\n\nfunction ResizableHandle({ withHandle, className, ...props }: ResizableHandleProps) {\n return (\n <ResizablePrimitive.PanelResizeHandle\n data-slot=\"resizable-handle\"\n className={cn(\n \"bg-control-edge relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90\",\n className\n )}\n {...props}\n >\n {withHandle && (\n <div className=\"bg-control-edge z-10 flex h-4 w-3 items-center justify-center rounded-xs border\">\n <GripVerticalIcon className=\"size-2.5\" />\n </div>\n )}\n </ResizablePrimitive.PanelResizeHandle>\n );\n}\n\nexport { ResizableHandle, ResizablePanel, ResizablePanelGroup };\n"],"mappings":";AAoCI;AAlCJ,SAAS,wBAAwB;AAEjC,YAAY,wBAAwB;AACpC,SAAS,UAAU;AA6BnB,SAAS,oBAAoB,EAAE,WAAW,GAAG,MAAM,GAA6B;AAC9E,SACE;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,qEAAqE,SAAS;AAAA,MAC3F,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,eAAe,EAAE,GAAG,MAAM,GAAwB;AACzD,SAAO,oBAAC,mBAAmB,OAAnB,EAAyB,aAAU,mBAAmB,GAAG,OAAO;AAC1E;AAEA,SAAS,gBAAgB,EAAE,YAAY,WAAW,GAAG,MAAM,GAAyB;AAClF,SACE;AAAA,IAAC,mBAAmB;AAAA,IAAnB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH,wBACC,oBAAC,SAAI,WAAU,mFACb,8BAAC,oBAAiB,WAAU,YAAW,GACzC;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
@@ -26,7 +26,7 @@ function Slider({ className, defaultValue, value, min = 0, max = 100, ...props }
26
26
  SliderPrimitive.Track,
27
27
  {
28
28
  "data-slot": "slider-track",
29
- className: "bg-control-edge relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5",
29
+ className: "bg-slider-track relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5",
30
30
  children: /* @__PURE__ */ jsx(
31
31
  SliderPrimitive.Range,
32
32
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/slider.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Slider (Radix) — hỗ trợ 1 hoặc nhiều thumb (range) và cả hướng dọc.\n * Số thumb suy ra từ `value`/`defaultValue`; không truyền gì thì mặc định 2 thumb\n * ứng với [min, max].\n *\n * ─ MÀU: CÙNG BỘ TOKEN VỚI `Switch`/`Checkbox` ─\n *\n * Thanh trượt là thành phần phi văn bản (WCAG 1.4.11, ngưỡng 3:1). Bản trước hỏng cả ba phía\n * ở light: rãnh `bg-muted` chỉ **1,07:1** với nền trắng (không nhìn thấy cái slider), đoạn đã\n * chọn `bg-primary` **1,86:1** với nền và **1,73:1** với rãnh, núm `bg-white` **1,86:1** với\n * đoạn đã chọn — và `bg-white` cứng thì ở dark núm ăn 1,49:1 với đoạn gold sáng.\n *\n * Nay đúng bộ ba của hợp đồng màu (docs/hop-dong-mau.md §3–§4):\n *\n * rãnh `bg-control-edge` : 3,83:1 (light) · 4,94:1 (dark) với nền trang ✓\n * đã chọn `bg-control-on` : 6,51:1 (light) · 12,67:1 (dark) với nền trang ✓\n * núm `bg-control-knob` : 6,51 / 12,67:1 với đoạn đã chọn · 3,83 / 4,94:1 với rãnh ✓\n * viền núm `border-control-on` : 6,51:1 (light) · 12,67:1 (dark) với nền trang ✓\n *\n * ⚠ Đoạn ĐÃ CHỌN ⇄ rãnh chỉ 1,70:1 (light) / 2,57:1 (dark) — giống hệt cặp rãnh BẬT ⇄ rãnh TẮT\n * của `Switch`, và chấp nhận vì cùng lý do: VỊ TRÍ NÚM mới là thứ mang giá trị, còn hai màu kia\n * đều tự đạt ≥3:1 với nền trang và với núm. Với nền trang trắng thì không tồn tại cặp màu nào\n * vừa ≥3:1 với nhau vừa ≥3:1 với nền.\n */\n\nexport type SliderProps = React.ComponentProps<typeof SliderPrimitive.Root>;\n\nfunction Slider({ className, defaultValue, value, min = 0, max = 100, ...props }: SliderProps) {\n const _values = React.useMemo(\n () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]),\n [value, defaultValue, min, max]\n );\n\n return (\n <SliderPrimitive.Root\n data-slot=\"slider\"\n defaultValue={defaultValue}\n value={value}\n min={min}\n max={max}\n className={cn(\n \"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col\",\n className\n )}\n {...props}\n >\n <SliderPrimitive.Track\n data-slot=\"slider-track\"\n className=\"bg-control-edge relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5\"\n >\n <SliderPrimitive.Range\n data-slot=\"slider-range\"\n className=\"bg-control-on absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\"\n />\n </SliderPrimitive.Track>\n {Array.from({ length: _values.length }, (_, index) => (\n <SliderPrimitive.Thumb\n data-slot=\"slider-thumb\"\n // biome-ignore lint/suspicious/noArrayIndexKey: thumb chỉ phân biệt bằng vị trí, không có id\n key={index}\n className=\"border-control-on ring-ring/50 block size-4 shrink-0 rounded-full border bg-control-knob shadow-sm transition-colors duration-150 ease-out hover:ring-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\"\n />\n ))}\n </SliderPrimitive.Root>\n );\n}\n\nexport { Slider };\n"],"mappings":";AAwCI,SAgBI,KAhBJ;AAtCJ,YAAY,qBAAqB;AACjC,YAAY,WAAW;AACvB,SAAS,UAAU;AA6BnB,SAAS,OAAO,EAAE,WAAW,cAAc,OAAO,MAAM,GAAG,MAAM,KAAK,GAAG,MAAM,GAAgB;AAC7F,QAAM,UAAU,MAAM;AAAA,IACpB,MAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,KAAK,GAAG;AAAA,IAC5F,CAAC,OAAO,cAAc,KAAK,GAAG;AAAA,EAChC;AAEA,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC,gBAAgB;AAAA,UAAhB;AAAA,YACC,aAAU;AAAA,YACV,WAAU;AAAA,YAEV;AAAA,cAAC,gBAAgB;AAAA,cAAhB;AAAA,gBACC,aAAU;AAAA,gBACV,WAAU;AAAA;AAAA,YACZ;AAAA;AAAA,QACF;AAAA,QACC,MAAM,KAAK,EAAE,QAAQ,QAAQ,OAAO,GAAG,CAAC,GAAG,UAC1C;AAAA,UAAC,gBAAgB;AAAA,UAAhB;AAAA,YACC,aAAU;AAAA,YAGV,WAAU;AAAA;AAAA,UADL;AAAA,QAEP,CACD;AAAA;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/slider.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Slider (Radix) — hỗ trợ 1 hoặc nhiều thumb (range) và cả hướng dọc.\n * Số thumb suy ra từ `value`/`defaultValue`; không truyền gì thì mặc định 2 thumb\n * ứng với [min, max].\n *\n * ─ MÀU: CÙNG BỘ TOKEN VỚI `Switch`/`Checkbox` ─\n *\n * Thanh trượt là thành phần phi văn bản (WCAG 1.4.11, ngưỡng 3:1). Bản trước hỏng cả ba phía\n * ở light: rãnh `bg-muted` chỉ **1,07:1** với nền trắng (không nhìn thấy cái slider), đoạn đã\n * chọn `bg-primary` **1,86:1** với nền và **1,73:1** với rãnh, núm `bg-white` **1,86:1** với\n * đoạn đã chọn — và `bg-white` cứng thì ở dark núm ăn 1,49:1 với đoạn gold sáng.\n *\n * Nay đúng bộ ba của hợp đồng màu (docs/hop-dong-mau.md §3–§4):\n *\n * rãnh `bg-control-edge` : 3,83:1 (light) · 4,94:1 (dark) với nền trang ✓\n * đã chọn `bg-control-on` : 6,51:1 (light) · 12,67:1 (dark) với nền trang ✓\n * núm `bg-control-knob` : 6,51 / 12,67:1 với đoạn đã chọn · 3,83 / 4,94:1 với rãnh ✓\n * viền núm `border-control-on` : 6,51:1 (light) · 12,67:1 (dark) với nền trang ✓\n *\n * ⚠ Đoạn ĐÃ CHỌN ⇄ rãnh chỉ 1,70:1 (light) / 2,57:1 (dark) — giống hệt cặp rãnh BẬT ⇄ rãnh TẮT\n * của `Switch`, và chấp nhận vì cùng lý do: VỊ TRÍ NÚM mới là thứ mang giá trị, còn hai màu kia\n * đều tự đạt ≥3:1 với nền trang và với núm. Với nền trang trắng thì không tồn tại cặp màu nào\n * vừa ≥3:1 với nhau vừa ≥3:1 với nền.\n */\n\nexport type SliderProps = React.ComponentProps<typeof SliderPrimitive.Root>;\n\nfunction Slider({ className, defaultValue, value, min = 0, max = 100, ...props }: SliderProps) {\n const _values = React.useMemo(\n () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]),\n [value, defaultValue, min, max]\n );\n\n return (\n <SliderPrimitive.Root\n data-slot=\"slider\"\n defaultValue={defaultValue}\n value={value}\n min={min}\n max={max}\n className={cn(\n \"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col\",\n className\n )}\n {...props}\n >\n <SliderPrimitive.Track\n data-slot=\"slider-track\"\n className=\"bg-slider-track relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5\"\n >\n <SliderPrimitive.Range\n data-slot=\"slider-range\"\n className=\"bg-control-on absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full\"\n />\n </SliderPrimitive.Track>\n {Array.from({ length: _values.length }, (_, index) => (\n <SliderPrimitive.Thumb\n data-slot=\"slider-thumb\"\n // biome-ignore lint/suspicious/noArrayIndexKey: thumb chỉ phân biệt bằng vị trí, không có id\n key={index}\n className=\"border-control-on ring-ring/50 block size-4 shrink-0 rounded-full border bg-control-knob shadow-sm transition-colors duration-150 ease-out hover:ring-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50\"\n />\n ))}\n </SliderPrimitive.Root>\n );\n}\n\nexport { Slider };\n"],"mappings":";AAwCI,SAgBI,KAhBJ;AAtCJ,YAAY,qBAAqB;AACjC,YAAY,WAAW;AACvB,SAAS,UAAU;AA6BnB,SAAS,OAAO,EAAE,WAAW,cAAc,OAAO,MAAM,GAAG,MAAM,KAAK,GAAG,MAAM,GAAgB;AAC7F,QAAM,UAAU,MAAM;AAAA,IACpB,MAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,KAAK,GAAG;AAAA,IAC5F,CAAC,OAAO,cAAc,KAAK,GAAG;AAAA,EAChC;AAEA,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC,gBAAgB;AAAA,UAAhB;AAAA,YACC,aAAU;AAAA,YACV,WAAU;AAAA,YAEV;AAAA,cAAC,gBAAgB;AAAA,cAAhB;AAAA,gBACC,aAAU;AAAA,gBACV,WAAU;AAAA;AAAA,YACZ;AAAA;AAAA,QACF;AAAA,QACC,MAAM,KAAK,EAAE,QAAQ,QAAQ,OAAO,GAAG,CAAC,GAAG,UAC1C;AAAA,UAAC,gBAAgB;AAAA,UAAhB;AAAA,YACC,aAAU;AAAA,YAGV,WAAU;AAAA;AAAA,UADL;AAAA,QAEP,CACD;AAAA;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -20,7 +20,7 @@ function Switch({ className, size = "md", label, description, id, ...props }) {
20
20
  "data-slot": "switch",
21
21
  id: switchId,
22
22
  className: cn(
23
- "peer inline-flex shrink-0 items-center rounded-full bg-control-edge data-[state=checked]:bg-control-on",
23
+ "peer inline-flex shrink-0 items-center rounded-full bg-switch-track data-[state=checked]:bg-control-on",
24
24
  "transition-colors duration-150 ease-out",
25
25
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
26
26
  "disabled:cursor-not-allowed disabled:opacity-50",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/switch.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SwitchPrimitive from \"@radix-ui/react-switch\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/* Bảng cỡ tách ra ngoài component để không dựng lại object mỗi lần render. */\nconst trackSizes = {\n sm: \"h-4 w-7\",\n md: \"h-5 w-9\"\n} as const;\n\nconst thumbSizes = {\n sm: \"size-3 data-[state=checked]:translate-x-3\",\n md: \"size-4 data-[state=checked]:translate-x-4\"\n} as const;\n\nexport interface SwitchProps extends React.ComponentProps<typeof SwitchPrimitive.Root> {\n /** Cỡ công tắc. Mặc định `md`. */\n size?: \"sm\" | \"md\";\n /** Nhãn đặt cạnh công tắc; có nhãn thì component tự bọc thêm lớp bố cục. */\n label?: string;\n /** Mô tả phụ dưới nhãn. */\n description?: string;\n}\n\n/*\n * Style port từ comitor-ds (đổi gray-* → ash-* theo namespace của package).\n *\n * ─ CÔNG TẮC LÀ THÀNH PHẦN PHI VĂN BẢN: BA PHÉP ĐO, KHÔNG PHẢI MỘT ─\n *\n * WCAG 1.4.11 đòi 3:1 cho \"thông tin thị giác cần để nhận ra thành phần và TRẠNG THÁI của nó\".\n * Với công tắc, đó là ba cặp — hỏng cặp nào cũng là hỏng:\n *\n * (1) rãnh ⇄ nền trang → có nhìn thấy cái công tắc không\n * (2) núm ⇄ rãnh → núm đang nằm bên nào\n * (3) rãnh BẬT ⇄ rãnh TẮT → hai trạng thái có khác nhau không (ở đây khác cả màu lẫn vị trí)\n *\n * ─ RÃNH KHI TẮT: `--control-edge` ─\n * Bản đầu dùng `bg-ash-300 dark:bg-ash-700`, hỏng cả (1) lẫn (2): 1,62:1 ở light và 1,79:1 ở dark.\n * Bản kế dùng `bg-muted-foreground` — đúng số nhưng SAI token: `--muted-foreground` là bậc CHỮ,\n * nó bị kéo tối/sáng theo nhu cầu đọc chữ (và đã đổi một lần: #7E8290 → #686C7A / #888D9B), tức\n * ràng buộc \"núm trắng còn nổi\" không có gì bảo vệ. `--control-edge` #7E8290 là token sinh ra\n * đúng cho vai trò này và ghim MỘT giá trị ở cả hai theme nên không cần biến thể `dark:`:\n *\n * rãnh ⇄ nền : 3,83:1 (light, nền trắng) · 4,94:1 (dark, #0E1116) ✓\n * núm ⇄ rãnh : 3,83:1 (light) · 4,94:1 (dark) ✓\n *\n * ─ RÃNH KHI BẬT: `--control-on`, KHÔNG PHẢI `--primary` ─\n * `--primary` (gold #E8B824 ở light) là màu TÔ NỀN cho khối lớn; làm rãnh công tắc thì hỏng cả\n * (1) lẫn (2) ở light — **1,86:1** so với nền trắng và **1,86:1** so với núm trắng — nên trạng\n * thái BẬT chỉ còn phân biệt được bằng VỊ TRÍ núm. Ở dark rãnh #F5D060 nổi trên nền, nhưng núm\n * trắng lại chìm vào rãnh (**1,49:1** ✗). `--control-on` (= `--primary-ink`: gold-500 ở light,\n * = `--primary` ở dark) là token dành đúng cho vai trò \"nền control khi BẬT\", đạt cả hai phía\n * ở cả hai theme:\n *\n * rãnh ⇄ nền : 6,51:1 (light) · 12,67:1 (dark) ✓\n * núm ⇄ rãnh : 6,51:1 (light) · 12,67:1 (dark) ✓\n *\n * ⚠ (3) rãnh BẬT ⇄ rãnh TẮT chỉ 1,70:1 (light) / 2,57:1 (dark) — cố ý và chấp nhận được: hai\n * trạng thái còn khác nhau bằng VỊ TRÍ núm, và cả hai rãnh đều tự đạt ≥3:1 với nền trang lẫn\n * với núm. Không có cặp màu nào vừa đạt (1) vừa đạt (3) khi nền trang là trắng.\n *\n * ─ NÚM: `bg-control-knob`, KHÔNG PHẢI `bg-white` ─\n * Núm mang màu NỀN TRANG (`--control-knob` = `--background`: trắng ở light, #0E1116 ở dark) nên\n * nó luôn tương phản đúng chiều với rãnh — bốn tổ hợp trạng thái × theme ở trên đều là số đo của\n * chính lựa chọn này. `bg-white` cứng chỉ đúng ở light: ở dark nó ăn 1,49:1 với rãnh BẬT.\n *\n * Đúng luật \"chọn ĐỌC ĐƯỢC hơn ĐÚNG BRAND\": rãnh BẬT ở light nay là gold sẫm chứ không phải gold\n * rực của brand book. Đổi lại người dùng nhìn thấy trạng thái. Xem docs/quyet-dinh-tich-hop.md §30.\n * ⚠ App ghi đè `--primary` phải ghi đè kèm `--primary-ink`, nếu không công tắc giữ nguyên gold\n * mặc định — sai màu mà không sai chuẩn, đúng thứ tự ưu tiên của repo.\n */\nfunction Switch({ className, size = \"md\", label, description, id, ...props }: SwitchProps) {\n const generatedId = React.useId();\n const switchId = id ?? generatedId;\n\n const switchElement = (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n id={switchId}\n className={cn(\n \"peer inline-flex shrink-0 items-center rounded-full bg-control-edge data-[state=checked]:bg-control-on\",\n \"transition-colors duration-150 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n trackSizes[size],\n className\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block translate-x-0.5 rounded-full bg-control-knob shadow-sm ring-0\",\n \"transition-transform duration-150 ease-out\",\n thumbSizes[size]\n )}\n />\n </SwitchPrimitive.Root>\n );\n\n if (!label && !description) {\n return switchElement;\n }\n\n return (\n <div className=\"flex items-start gap-2.5\">\n {switchElement}\n <div className=\"flex flex-col gap-0.5\">\n {label && (\n <label\n htmlFor={switchId}\n className=\"text-sm font-medium text-foreground cursor-pointer select-none leading-none\"\n >\n {label}\n </label>\n )}\n {description && <p className=\"text-xs text-muted-foreground leading-relaxed\">{description}</p>}\n </div>\n </div>\n );\n}\n\nexport { Switch };\n"],"mappings":";AA2FM,cAkBA,YAlBA;AAzFN,YAAY,qBAAqB;AACjC,YAAY,WAAW;AACvB,SAAS,UAAU;AAGnB,MAAM,aAAa;AAAA,EACjB,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,aAAa;AAAA,EACjB,IAAI;AAAA,EACJ,IAAI;AACN;AA0DA,SAAS,OAAO,EAAE,WAAW,OAAO,MAAM,OAAO,aAAa,IAAI,GAAG,MAAM,GAAgB;AACzF,QAAM,cAAc,MAAM,MAAM;AAChC,QAAM,WAAW,MAAM;AAEvB,QAAM,gBACJ;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,IAAI;AAAA,MACJ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,IAAI;AAAA,QACf;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA,WAAW,IAAI;AAAA,UACjB;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAGF,MAAI,CAAC,SAAS,CAAC,aAAa;AAC1B,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,SAAI,WAAU,4BACZ;AAAA;AAAA,IACD,qBAAC,SAAI,WAAU,yBACZ;AAAA,eACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAED,eAAe,oBAAC,OAAE,WAAU,iDAAiD,uBAAY;AAAA,OAC5F;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/switch.tsx"],"sourcesContent":["\"use client\";\n\nimport * as SwitchPrimitive from \"@radix-ui/react-switch\";\nimport * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/* Bảng cỡ tách ra ngoài component để không dựng lại object mỗi lần render. */\nconst trackSizes = {\n sm: \"h-4 w-7\",\n md: \"h-5 w-9\"\n} as const;\n\nconst thumbSizes = {\n sm: \"size-3 data-[state=checked]:translate-x-3\",\n md: \"size-4 data-[state=checked]:translate-x-4\"\n} as const;\n\nexport interface SwitchProps extends React.ComponentProps<typeof SwitchPrimitive.Root> {\n /** Cỡ công tắc. Mặc định `md`. */\n size?: \"sm\" | \"md\";\n /** Nhãn đặt cạnh công tắc; có nhãn thì component tự bọc thêm lớp bố cục. */\n label?: string;\n /** Mô tả phụ dưới nhãn. */\n description?: string;\n}\n\n/*\n * Style port từ comitor-ds (đổi gray-* → ash-* theo namespace của package).\n *\n * ─ BA LỚP MÀU, BA TOKEN VAI TRÒ — KHÔNG viết thẳng thang màu ─\n *\n * rãnh TẮT `bg-switch-track` (comitor-ds: bg-gray-300 / dark:bg-gray-700)\n * rãnh BẬT `data-[state=checked]:bg-control-on` (comitor-ds: bg-gold-300)\n * núm `bg-control-knob` (comitor-ds: bg-white)\n *\n * Ba token này là chỗ DUY NHẤT phân biệt hai bảng màu của gói (styles.css khối 5b). Viết thẳng\n * `bg-ash-300` / `bg-gold-300` / `bg-white` là khoá cứng bảng mặc định và bảng tương phản cao\n * lặng lẽ không đổi được công tắc — không có gì báo đỏ.\n *\n * ─ CÔNG TẮC LÀ THÀNH PHẦN PHI VĂN BẢN: BA PHÉP ĐO, KHÔNG PHẢI MỘT ─\n *\n * WCAG 1.4.11 đòi 3:1 cho \"thông tin thị giác cần để nhận ra thành phần và TRẠNG THÁI của nó\".\n * Với công tắc, đó là ba cặp:\n *\n * (1) rãnh ⇄ nền trang → có nhìn thấy cái công tắc không\n * (2) núm ⇄ rãnh → núm đang nằm bên nào\n * (3) rãnh BẬT ⇄ rãnh TẮT → hai trạng thái có khác nhau không\n *\n * Số đo của hai bảng — bảng mặc định là bản duyệt comitor-ds, nó KHÔNG đạt (1) và (2), và đó là\n * quyết định đã chấp nhận (xem khối NGUỒN SỰ THẬT đầu styles.css):\n *\n * bảng MẶC ĐỊNH (light) bảng TƯƠNG PHẢN CAO (light · dark)\n * rãnh TẮT ⇄ nền 1,52:1 ✗ 3,83:1 · 4,94:1 ✓\n * rãnh BẬT ⇄ nền 1,86:1 ✗ 6,51:1 · 12,67:1 ✓\n * núm ⇄ rãnh BẬT 1,86:1 ✗ 6,51:1 · 12,67:1 ✓\n *\n * ⚠ Ở bảng tương phản cao, (3) rãnh BẬT ⇄ rãnh TẮT chỉ 1,70:1 (light) / 2,57:1 (dark) — cố ý:\n * hai trạng thái còn khác nhau bằng VỊ TRÍ núm, và cả hai rãnh đều tự đạt ≥3:1 với nền trang lẫn\n * với núm. Không có cặp màu nào vừa đạt (1) vừa đạt (3) khi nền trang là trắng.\n *\n * ⚠ ĐỪNG dùng `--muted-foreground` cho rãnh TẮT dù số có vẻ vừa: đó là bậc CHỮ, nó bị kéo\n * tối/sáng theo nhu cầu đọc chữ (đã đổi một lần #7E8290 → #686C7A / #888D9B), tức ràng buộc\n * \"núm còn nổi trên rãnh\" không có gì bảo vệ.\n */\nfunction Switch({ className, size = \"md\", label, description, id, ...props }: SwitchProps) {\n const generatedId = React.useId();\n const switchId = id ?? generatedId;\n\n const switchElement = (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n id={switchId}\n className={cn(\n \"peer inline-flex shrink-0 items-center rounded-full bg-switch-track data-[state=checked]:bg-control-on\",\n \"transition-colors duration-150 ease-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n trackSizes[size],\n className\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block translate-x-0.5 rounded-full bg-control-knob shadow-sm ring-0\",\n \"transition-transform duration-150 ease-out\",\n thumbSizes[size]\n )}\n />\n </SwitchPrimitive.Root>\n );\n\n if (!label && !description) {\n return switchElement;\n }\n\n return (\n <div className=\"flex items-start gap-2.5\">\n {switchElement}\n <div className=\"flex flex-col gap-0.5\">\n {label && (\n <label\n htmlFor={switchId}\n className=\"text-sm font-medium text-foreground cursor-pointer select-none leading-none\"\n >\n {label}\n </label>\n )}\n {description && <p className=\"text-xs text-muted-foreground leading-relaxed\">{description}</p>}\n </div>\n </div>\n );\n}\n\nexport { Switch };\n"],"mappings":";AAkFM,cAkBA,YAlBA;AAhFN,YAAY,qBAAqB;AACjC,YAAY,WAAW;AACvB,SAAS,UAAU;AAGnB,MAAM,aAAa;AAAA,EACjB,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,aAAa;AAAA,EACjB,IAAI;AAAA,EACJ,IAAI;AACN;AAiDA,SAAS,OAAO,EAAE,WAAW,OAAO,MAAM,OAAO,aAAa,IAAI,GAAG,MAAM,GAAgB;AACzF,QAAM,cAAc,MAAM,MAAM;AAChC,QAAM,WAAW,MAAM;AAEvB,QAAM,gBACJ;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,IAAI;AAAA,MACJ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,IAAI;AAAA,QACf;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UACC,aAAU;AAAA,UACV,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA,WAAW,IAAI;AAAA,UACjB;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAGF,MAAI,CAAC,SAAS,CAAC,aAAa;AAC1B,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,SAAI,WAAU,4BACZ;AAAA;AAAA,IACD,qBAAC,SAAI,WAAU,yBACZ;AAAA,eACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MAED,eAAe,oBAAC,OAAE,WAAU,iDAAiD,uBAAY;AAAA,OAC5F;AAAA,KACF;AAEJ;","names":[]}
@@ -6,7 +6,7 @@ function Textarea({ className, ...props }) {
6
6
  {
7
7
  "data-slot": "textarea",
8
8
  className: cn(
9
- "border-input placeholder:text-muted-foreground aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-colors duration-150 ease-out focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
9
+ "border-input placeholder:text-muted-foreground aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink dark:bg-input-fill flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-colors duration-150 ease-out focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
10
10
  className
11
11
  ),
12
12
  ...props
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/textarea.tsx"],"sourcesContent":["import type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ─ KHÔNG CÓ `dark:bg-input/30` ─\n * `--input` là alias của `--control-edge`, tức token VIỀN control (hợp đồng màu §3) — không phải\n * bậc tô nền. Bản trước mượn nó tô ruột ô ở dark, và lớp tô ấy SÁNG hơn mọi nền trang nên kéo\n * placeholder `--muted-foreground` (token chữ, hiệu chỉnh theo nền TRANG — §6) xuống dưới ngưỡng\n * 4,5:1 ở TẤT CẢ nền dark, không riêng ca tệ nhất:\n *\n * cũ #888D9B trên `input/30` hợp lên nền: 3,81 (background) · 3,54 (card) · 3,22:1 (muted) ✗\n * nay #888D9B trên chính nền trang : 5,70 (background) · 5,35 (card) · 4,86:1 (muted) ✓\n *\n * Ô vẫn được khoanh vùng bằng `border-input` — 3,83:1 (light) · 4,21–4,94:1 (dark), thừa ngưỡng\n * 3:1 của thành phần phi văn bản, đúng vai trò mà §3 giao cho viền control.\n */\nexport type TextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction Textarea({ className, ...props }: TextareaProps) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"border-input placeholder:text-muted-foreground aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-colors duration-150 ease-out focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n"],"mappings":"AAoBI;AAnBJ,SAAS,UAAU;AAiBnB,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;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/textarea.tsx"],"sourcesContent":["import type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ─ LỚP TÔ Ở DARK ĐI QUA `--input-fill`, KHÔNG PHẢI `bg-input/30` ─\n *\n * comitor-ds viết `dark:bg-input/30` cho ô nhập, và ở bảng MẶC ĐỊNH lớp tô đó cần thiết thật:\n * viền `--input` (#1E2128) trên `--card` (#16181D) chỉ 1,10:1, nên nó là thứ gần như duy nhất\n * còn khoanh vùng ô ở dark.\n *\n * Nhưng ở bảng TƯƠNG PHẢN CAO, `--input` là #7E8290 — tô 30% ra một mảng xám SÁNG hơn mọi nền\n * trang và kéo placeholder `--muted-foreground` xuống dưới AA:\n *\n * bg-input/30 ở bảng tương phản cao: 3,81 (background) · 3,54 (card) · 3,22:1 (muted) ✗\n * --input-fill trong suốt : 5,70 (background) · 5,35 (card) · 4,86:1 (muted) ✓\n *\n * Nên lớp tô là token riêng: `--input-fill` = `color-mix(--input 30%, transparent)` ở bảng mặc\n * định và `transparent` ở bảng tương phản cao (bên đó viền đã tự đủ đậm để khoanh vùng).\n */\nexport type TextareaProps = React.ComponentProps<\"textarea\">;\n\nfunction Textarea({ className, ...props }: TextareaProps) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"border-input placeholder:text-muted-foreground aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink dark:bg-input-fill flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-colors duration-150 ease-out focus:outline-none focus:ring-2 focus:ring-ring/50 focus:border-primary-ink disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n"],"mappings":"AAuBI;AAtBJ,SAAS,UAAU;AAoBnB,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;","names":[]}
package/dist/index.d.ts CHANGED
@@ -3,11 +3,14 @@ export { Combobox, ComboboxOption, ComboboxProps, MultiCombobox, MultiComboboxPr
3
3
  export { CommandPalette, CommandPaletteGroup, CommandPaletteItem, CommandPaletteProps } from './components/composite/command-palette.js';
4
4
  export { ConfirmDialog, ConfirmDialogProps } from './components/composite/confirm-dialog.js';
5
5
  export { CopyButton, CopyButtonProps } from './components/composite/copy-button.js';
6
- export { DEFAULT_DATA_TABLE_LABELS, DataTable, DataTableAlign, DataTableColumn, DataTableColumnToggle, DataTableColumnToggleProps, DataTableLabels, DataTableProps, DataTableSort, SortDirection } from './components/composite/data-table.js';
7
- export { DEFAULT_DATE_PRESETS, DEFAULT_DATE_RANGE_PRESETS, DatePicker, DatePickerProps, DatePreset, DateRangePicker, DateRangePickerProps, DateRangePreset, VI_DATE_FORMAT } from './components/composite/date-picker.js';
6
+ export { DataTable, DataTableAlign, DataTableColumn, DataTableColumnToggle, DataTableColumnToggleProps, DataTableProps, DataTableSort, SortDirection } from './components/composite/data-table.js';
7
+ export { DEFAULT_DATA_TABLE_LABELS, DataTableLabels } from './components/composite/data-table-constants.js';
8
+ export { DatePicker, DatePickerProps, DateRangePicker, DateRangePickerProps } from './components/composite/date-picker.js';
9
+ export { DEFAULT_DATE_PRESETS, DEFAULT_DATE_RANGE_PRESETS, DatePreset, DateRangePreset, VI_DATE_FORMAT } from './components/composite/date-picker-constants.js';
8
10
  export { EmptyState, EmptyStateProps, IconComponent } from './components/composite/empty-state.js';
9
11
  export { FilterChipItem, FilterChips, FilterChipsProps } from './components/composite/filter-chips.js';
10
- export { FormField, FormFieldColSpan, FormFieldControlProps, FormFieldProps, SELECT_EMPTY_VALUE, fromSelectValue, toSelectValue } from './components/composite/form-field.js';
12
+ export { FormField, FormFieldColSpan, FormFieldControlProps, FormFieldProps } from './components/composite/form-field.js';
13
+ export { SELECT_EMPTY_VALUE, fromSelectValue, toSelectValue } from './components/composite/form-field-constants.js';
11
14
  export { FormSection, FormSectionAccent, FormSectionProps } from './components/composite/form-section.js';
12
15
  export { InlineEdit, InlineEditProps } from './components/composite/inline-edit.js';
13
16
  export { KeyboardHint, KeyboardHintProps, useIsMac } from './components/composite/keyboard-hint.js';
@@ -15,11 +18,13 @@ export { LetterAvatar, LetterAvatarProps, LetterAvatarSize, getAvatarToneClasses
15
18
  export { PageContainer, PageContainerProps, PageContainerWidth } from './components/composite/page-container.js';
16
19
  export { PageHeader, PageHeaderBreadcrumbItem, PageHeaderLinkProps, PageHeaderProps } from './components/composite/page-header.js';
17
20
  export { RelativeTime, RelativeTimeProps } from './components/composite/relative-time.js';
18
- export { DEFAULT_SEARCH_FILTER_BAR_LABELS, SearchFilterBar, SearchFilterBarLabels, SearchFilterBarProps } from './components/composite/search-filter-bar.js';
21
+ export { SearchFilterBar, SearchFilterBarProps } from './components/composite/search-filter-bar.js';
22
+ export { DEFAULT_SEARCH_FILTER_BAR_LABELS, SearchFilterBarLabels } from './components/composite/search-filter-bar-constants.js';
19
23
  export { StatCard, StatCardDelta, StatCardDeltaDirection, StatCardProps, StatCardTone, statCardIconVariants } from './components/composite/stat-card.js';
20
24
  export { STATUS_TONES, StatusConfig, StatusTone, StatusToneName, getStatusClasses, getStatusConfig, getStatusLabel, sortByStatusPriority } from './components/composite/status-config.js';
21
25
  export { StatusPill, StatusPillProps, StatusPillVariant } from './components/composite/status-pill.js';
22
- export { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_TABLE_PAGINATION_LABELS, TablePagination, TablePaginationLabels, TablePaginationProps } from './components/composite/table-pagination.js';
26
+ export { TablePagination, TablePaginationProps } from './components/composite/table-pagination.js';
27
+ export { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_TABLE_PAGINATION_LABELS, TablePaginationLabels } from './components/composite/table-pagination-constants.js';
23
28
  export { getVietnameseInitials, matchesSearch, normalizeVi, removeVietnameseTones, truncate } from './components/composite/text-utils.js';
24
29
  export { ToastOptions, ToastVariant, Toaster, ToasterProps, dismissToast, toast, toastPromise } from './components/composite/toast.js';
25
30
  export { Accordion, AccordionContent, AccordionContentProps, AccordionItem, AccordionItemProps, AccordionProps, AccordionTrigger, AccordionTriggerProps } from './components/ui/accordion.js';
@@ -72,7 +77,7 @@ export { Toggle, ToggleProps, ToggleVariantProps, toggleVariants } from './compo
72
77
  export { ToggleGroup, ToggleGroupItem, ToggleGroupItemProps, ToggleGroupProps, ToggleGroupVariantProps } from './components/ui/toggle-group.js';
73
78
  export { Tooltip, TooltipContent, TooltipContentProps, TooltipProps, TooltipProvider, TooltipProviderProps, TooltipTrigger, TooltipTriggerProps } from './components/ui/tooltip.js';
74
79
  export { cn } from './lib/cn.js';
75
- export { ThemeMode, accent, accentForeground, accentInk, ash, brandNeutral, chart, getAvatarToneColors, gold, layout, semantic, status, typography } from './tokens.js';
80
+ export { ThemeMode, accent, accentForeground, accentInk, ash, brandNeutral, chart, getAvatarToneColors, gold, highContrast, layout, semantic, status, typography } from './tokens.js';
76
81
  export { AvatarToneColors, getAvatarToneIndex } from './lib/avatar-tone.js';
77
82
  export { DateRange } from 'react-day-picker';
78
83
  import 'react';
package/dist/index.js CHANGED
@@ -4,10 +4,13 @@ export * from "./components/composite/command-palette.js";
4
4
  export * from "./components/composite/confirm-dialog.js";
5
5
  export * from "./components/composite/copy-button.js";
6
6
  export * from "./components/composite/data-table.js";
7
+ export * from "./components/composite/data-table-constants.js";
7
8
  export * from "./components/composite/date-picker.js";
9
+ export * from "./components/composite/date-picker-constants.js";
8
10
  export * from "./components/composite/empty-state.js";
9
11
  export * from "./components/composite/filter-chips.js";
10
12
  export * from "./components/composite/form-field.js";
13
+ export * from "./components/composite/form-field-constants.js";
11
14
  export * from "./components/composite/form-section.js";
12
15
  export * from "./components/composite/inline-edit.js";
13
16
  export * from "./components/composite/keyboard-hint.js";
@@ -16,10 +19,12 @@ export * from "./components/composite/page-container.js";
16
19
  export * from "./components/composite/page-header.js";
17
20
  export * from "./components/composite/relative-time.js";
18
21
  export * from "./components/composite/search-filter-bar.js";
22
+ export * from "./components/composite/search-filter-bar-constants.js";
19
23
  export * from "./components/composite/stat-card.js";
20
24
  export * from "./components/composite/status-config.js";
21
25
  export * from "./components/composite/status-pill.js";
22
26
  export * from "./components/composite/table-pagination.js";
27
+ export * from "./components/composite/table-pagination-constants.js";
23
28
  import {
24
29
  getVietnameseInitials,
25
30
  matchesSearch,