@comitor/ui 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +303 -43
- package/dist/chart/chart.d.ts +35 -3
- package/dist/chart/chart.js.map +1 -1
- package/dist/chart/comitor-chart.d.ts +16 -3
- package/dist/chart/comitor-chart.js.map +1 -1
- package/dist/chart/index.d.ts +2 -2
- package/dist/components/composite/avatar-group.js +1 -1
- package/dist/components/composite/avatar-group.js.map +1 -1
- package/dist/components/composite/combobox.d.ts +9 -2
- package/dist/components/composite/combobox.js +9 -2
- package/dist/components/composite/combobox.js.map +1 -1
- package/dist/components/composite/confirm-dialog.js +1 -1
- package/dist/components/composite/confirm-dialog.js.map +1 -1
- package/dist/components/composite/copy-button.js +1 -1
- package/dist/components/composite/copy-button.js.map +1 -1
- package/dist/components/composite/data-table.d.ts +17 -8
- package/dist/components/composite/data-table.js +8 -8
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/filter-chips.js +6 -5
- package/dist/components/composite/filter-chips.js.map +1 -1
- package/dist/components/composite/form-field.js +3 -1
- package/dist/components/composite/form-field.js.map +1 -1
- package/dist/components/composite/form-section.js +5 -5
- package/dist/components/composite/form-section.js.map +1 -1
- package/dist/components/composite/inline-edit.js +1 -1
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/keyboard-hint.d.ts +16 -0
- package/dist/components/composite/keyboard-hint.js +8 -6
- package/dist/components/composite/keyboard-hint.js.map +1 -1
- package/dist/components/composite/letter-avatar.d.ts +10 -18
- package/dist/components/composite/letter-avatar.js +6 -17
- package/dist/components/composite/letter-avatar.js.map +1 -1
- package/dist/components/composite/search-filter-bar.d.ts +41 -3
- package/dist/components/composite/search-filter-bar.js +27 -15
- package/dist/components/composite/search-filter-bar.js.map +1 -1
- package/dist/components/composite/stat-card.d.ts +16 -1
- package/dist/components/composite/stat-card.js +16 -9
- package/dist/components/composite/stat-card.js.map +1 -1
- package/dist/components/composite/status-config.d.ts +69 -16
- package/dist/components/composite/status-config.js +10 -10
- package/dist/components/composite/status-config.js.map +1 -1
- package/dist/components/composite/status-pill.js +5 -1
- package/dist/components/composite/status-pill.js.map +1 -1
- package/dist/components/composite/table-pagination.d.ts +46 -4
- package/dist/components/composite/table-pagination.js +27 -24
- package/dist/components/composite/table-pagination.js.map +1 -1
- package/dist/components/composite/text-utils.d.ts +12 -11
- package/dist/components/composite/text-utils.js +7 -16
- package/dist/components/composite/text-utils.js.map +1 -1
- package/dist/components/composite/toast.js +16 -6
- package/dist/components/composite/toast.js.map +1 -1
- package/dist/components/ui/alert.js +1 -1
- package/dist/components/ui/alert.js.map +1 -1
- package/dist/components/ui/badge.js +4 -4
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/button.js +3 -3
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/calendar.d.ts +41 -0
- package/dist/components/ui/calendar.js +1 -1
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/card.d.ts +35 -2
- package/dist/components/ui/card.js +17 -3
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/checkbox.js +3 -3
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/command.js +1 -1
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/context-menu.js +1 -1
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/drawer.js +1 -1
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/dropdown-menu.js +1 -1
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/empty.js +1 -1
- package/dist/components/ui/empty.js.map +1 -1
- package/dist/components/ui/field.js +4 -4
- package/dist/components/ui/field.js.map +1 -1
- package/dist/components/ui/form.js +8 -2
- package/dist/components/ui/form.js.map +1 -1
- package/dist/components/ui/input-group.js +3 -3
- package/dist/components/ui/input-group.js.map +1 -1
- package/dist/components/ui/input-otp.js +1 -1
- package/dist/components/ui/input-otp.js.map +1 -1
- package/dist/components/ui/input.js +3 -3
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/item.js +1 -1
- package/dist/components/ui/item.js.map +1 -1
- package/dist/components/ui/menubar.js +1 -1
- package/dist/components/ui/menubar.js.map +1 -1
- package/dist/components/ui/navigation-menu.js +1 -1
- package/dist/components/ui/navigation-menu.js.map +1 -1
- package/dist/components/ui/pagination.js +1 -1
- package/dist/components/ui/pagination.js.map +1 -1
- package/dist/components/ui/progress.d.ts +26 -0
- package/dist/components/ui/progress.js +5 -2
- package/dist/components/ui/progress.js.map +1 -1
- package/dist/components/ui/radio-group.js +2 -2
- package/dist/components/ui/radio-group.js.map +1 -1
- package/dist/components/ui/resizable.d.ts +14 -0
- package/dist/components/ui/resizable.js +2 -2
- package/dist/components/ui/resizable.js.map +1 -1
- package/dist/components/ui/scroll-area.js +1 -1
- package/dist/components/ui/scroll-area.js.map +1 -1
- package/dist/components/ui/select.d.ts +33 -0
- package/dist/components/ui/select.js +3 -3
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/slider.d.ts +19 -0
- package/dist/components/ui/slider.js +3 -3
- package/dist/components/ui/slider.js.map +1 -1
- package/dist/components/ui/sonner.d.ts +21 -3
- package/dist/components/ui/sonner.js +1 -2
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/components/ui/switch.js +2 -2
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/components/ui/table.d.ts +16 -0
- package/dist/components/ui/table.js +1 -1
- package/dist/components/ui/table.js.map +1 -1
- package/dist/components/ui/tabs.js +1 -1
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/components/ui/textarea.js +1 -1
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/components/ui/toggle.js +1 -1
- package/dist/components/ui/toggle.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/avatar-tone.d.ts +126 -0
- package/dist/lib/avatar-tone.js +68 -0
- package/dist/lib/avatar-tone.js.map +1 -0
- package/dist/shell/app-header.js +12 -2
- package/dist/shell/app-header.js.map +1 -1
- package/dist/shell/app-launcher.js +2 -2
- package/dist/shell/app-launcher.js.map +1 -1
- package/dist/shell/app-shell.d.ts +2 -0
- package/dist/shell/index.d.ts +3 -3
- package/dist/shell/shell-context.d.ts +29 -8
- package/dist/shell/shell-context.js +6 -20
- package/dist/shell/shell-context.js.map +1 -1
- package/dist/shell/sidebar-group.js +5 -3
- package/dist/shell/sidebar-group.js.map +1 -1
- package/dist/shell/sidebar-menu-item.js +15 -7
- package/dist/shell/sidebar-menu-item.js.map +1 -1
- package/dist/shell/types.d.ts +17 -1
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/user-menu.js +3 -2
- package/dist/shell/user-menu.js.map +1 -1
- package/dist/shell/utils.d.ts +36 -24
- package/dist/shell/utils.js +55 -20
- package/dist/shell/utils.js.map +1 -1
- package/dist/shell/workspace-switcher.js +23 -8
- package/dist/shell/workspace-switcher.js.map +1 -1
- package/dist/tokens.d.ts +165 -17
- package/dist/tokens.js +122 -25
- package/dist/tokens.js.map +1 -1
- package/package.json +8 -5
- package/styles.css +312 -22
|
@@ -5,14 +5,14 @@ import { cva } from "class-variance-authority";
|
|
|
5
5
|
import { Loader2 } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/cn.js";
|
|
7
7
|
const buttonVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center font-medium select-none whitespace-nowrap transition-colors duration-150 ease-out focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
8
|
+
"inline-flex items-center justify-center font-medium select-none whitespace-nowrap transition-colors duration-150 ease-out focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 aria-invalid:border-destructive-ink aria-invalid:hover:border-destructive-ink aria-invalid:focus-visible:outline-destructive-ink disabled:pointer-events-none disabled:opacity-50",
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
12
12
|
default: "bg-primary text-primary-foreground hover:bg-[color:color-mix(in_srgb,var(--primary),var(--ink)_15%)] border border-transparent shadow-control",
|
|
13
13
|
primary: "bg-primary text-primary-foreground hover:bg-[color:color-mix(in_srgb,var(--primary),var(--ink)_15%)] border border-transparent shadow-control",
|
|
14
|
-
secondary: "bg-secondary text-secondary-foreground hover:bg-warm dark:hover:bg-muted border border-
|
|
15
|
-
outline: "bg-transparent text-foreground border border-
|
|
14
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-warm dark:hover:bg-muted border border-control-edge",
|
|
15
|
+
outline: "bg-transparent text-foreground border border-control-edge hover:border-primary-ink hover:bg-gold-50 dark:hover:bg-gold-500/10",
|
|
16
16
|
ghost: "bg-transparent text-foreground hover:bg-muted border border-transparent",
|
|
17
17
|
destructive: "bg-destructive text-destructive-foreground hover:bg-[color:color-mix(in_srgb,var(--destructive),var(--ink)_15%)] border border-transparent",
|
|
18
18
|
link: "bg-transparent text-gold-500 hover:text-gold-600 dark:text-gold-200 dark:hover:text-gold-100 underline-offset-4 hover:underline border border-transparent p-0 h-auto"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/button.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Loader2 } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * Style port từ comitor-ds để giao diện khớp bản v0.\n * API giữ tương thích shadcn (cva + asChild + data-slot); `primary` là alias của `default`\n * — đã là API công khai trong docs comitor-ds nên không bỏ.\n *\n * ─ VÌ SAO NỀN NÚT LÀ TOKEN SEMANTIC, KHÔNG PHẢI THANG BRAND THÔ ─\n *\n * 1. `default`/`primary` trước dùng `bg-gold-300 text-ink` — cùng giá trị ở cả hai theme,\n * nên bảng màu dark của chính package (`.dark { --primary: #F5D060 }`) không bao giờ được\n * áp dụng, và app nào set lại `--primary` cho sản phẩm của mình cũng không đổi được nút.\n * Nay dùng `bg-primary text-primary-foreground`: light 10,19:1 (không đổi), dark 12,67:1\n * (trước 10,19:1) — đều vượt AA.\n *\n * 2. `destructive` trước dùng `bg-red text-white`. `--red` là màu phụ được NÂNG SÁNG ở dark\n * (#F87171) để làm CHỮ trên nền tối, dùng làm NỀN nút thì chữ trắng chỉ còn 2,77:1 ✗.\n * `--destructive` cố ý ghim
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/button.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Loader2 } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * Style port từ comitor-ds để giao diện khớp bản v0.\n * API giữ tương thích shadcn (cva + asChild + data-slot); `primary` là alias của `default`\n * — đã là API công khai trong docs comitor-ds nên không bỏ.\n *\n * ─ VÌ SAO NỀN NÚT LÀ TOKEN SEMANTIC, KHÔNG PHẢI THANG BRAND THÔ ─\n *\n * 1. `default`/`primary` trước dùng `bg-gold-300 text-ink` — cùng giá trị ở cả hai theme,\n * nên bảng màu dark của chính package (`.dark { --primary: #F5D060 }`) không bao giờ được\n * áp dụng, và app nào set lại `--primary` cho sản phẩm của mình cũng không đổi được nút.\n * Nay dùng `bg-primary text-primary-foreground`: light 10,19:1 (không đổi), dark 12,67:1\n * (trước 10,19:1) — đều vượt AA.\n *\n * 2. `destructive` trước dùng `bg-red text-white`. `--red` là màu phụ được NÂNG SÁNG ở dark\n * (#F87171) để làm CHỮ trên nền tối, dùng làm NỀN nút thì chữ trắng chỉ còn 2,77:1 ✗.\n * `--destructive` cố ý ghim một giá trị ở cả hai theme đúng cho việc này. Bản #D64545 mới\n * đạt 4,38:1 — thiếu 0,12 so với AA 4,5:1 — nên token đã được đẩy đậm thêm một bậc trong\n * `styles.css`: **#C93B3B → 5,03:1** ở cả hai theme. Đó là sửa ở TẦNG TOKEN, đúng chỗ; đừng\n * \"chữa\" lại trong component bằng cách chôn hex hay đổi sang `bg-red`.\n *\n * ─ VÌ SAO HOVER TRỘN `--ink` THAY VÌ `/90` ─\n *\n * `bg-primary/90` là \"nhạt dần về phía nền\" nên ở light mode nút SÁNG lên khi rê chuột (ngược\n * với bản v0 gold-300 → gold-400) và với destructive thì tương phản chữ trắng tụt còn 3,81:1 ✗.\n * Trộn 15% `--ink` thì tối đi ở CẢ hai theme, giữ đúng ngữ nghĩa \"nhấn xuống\" và tương phản chỉ\n * tăng: destructive 6,29:1 ✓, primary 7,58:1 (light) / 9,33:1 (dark) ✓.\n *\n * ─ VIỀN CỦA `outline` VÀ `secondary`: `--control-edge`, KHÔNG PHẢI `--border` ─\n *\n * `--border` là bậc TRANG TRÍ (vạch chia, khung Card lúc nghỉ) và cố ý nhạt — 1,16:1 ở light,\n * 1,17:1 ở dark. Nút `outline` để nền trong suốt nên viền là thứ DUY NHẤT khoanh vùng nó, và\n * nút `secondary` thì viền tách khối khỏi nền trang: cả hai là \"thông tin cần để nhận ra thành\n * phần\" theo WCAG 1.4.11 → ngưỡng 3:1. Số sau khi đổi sang `--control-edge`:\n *\n * outline viền ⇄ nền trang : 3,83:1 (light) · 4,94:1 (dark) ✓\n * secondary viền ⇄ nền nút `--secondary`: 3,13:1 (light) · 4,21:1 (dark) ✓\n *\n * Hover của `outline` cũng phải ĐẬM HƠN lúc nghỉ: `hover:border-gold-300` cũ chỉ **1,86:1** ở\n * light, tức rê chuột vào là nút mất viền. `--primary-ink` giữ đúng sắc gold mà đạt 6,51:1 với\n * nền trang và 5,96:1 với chính nền hover `gold-50` (light); 12,67:1 / 11,91:1 (dark).\n *\n * ─ `link` ─\n * `text-gold-400` (#B88A00) trên nền trắng chỉ 3,15:1 ✗ — chữ link buộc phải đạt 4,5:1.\n * Đổi sang bậc tối/sáng theo theme: gold-500 6,51:1 (light) và gold-200 12,67:1 (dark).\n * Vẫn là thang gold vì --primary ở light (#E8B824) trên nền trắng chỉ 1,86:1, không dùng\n * làm chữ được; xem docs/quyet-dinh-tich-hop.md.\n *\n * ─ `aria-invalid`: VIỀN ĐỎ, KHÔNG CHỈ CHỮ ĐỎ ─\n *\n * Control dựng trên `<Button>` mà mang giá trị form — `Combobox`/`MultiCombobox` render\n * trigger bằng `<Button role=\"combobox\" aria-invalid>` — trước đây chỉ có chữ lỗi đỏ ở dưới,\n * còn chính ô thì giữ nguyên viền `--control-edge`: người dùng thấy thông báo lỗi mà không\n * thấy ô nào đang lỗi. `Input` đã có nhánh này từ đầu (`border-destructive-ink`); đặt ở đây\n * để MỌI control dựng trên Button được nhất quán, thay vì mỗi composite tự chép lại.\n *\n * Token là bậc `-ink`, đúng như `Input`: `--destructive-ink` #9A3537 (light) / #F87171 (dark)\n * đạt 7,15:1 (light) / 6,84:1 (dark) trên `--background`, và ca TỆ NHẤT trong 5 nền trang là\n * 6,68:1 (light, trên `--muted`) / 5,82:1 (dark, trên `--muted`) — đều thừa ngưỡng 3:1 của\n * viền control (§3 hợp đồng màu). KHÔNG dùng `--destructive` (nền nút xoá, 3,76:1 ở dark —\n * sát ngưỡng) và càng không dùng `--red` (token TÔ NỀN).\n *\n * Nhánh này ghi sau các biến thể trong chuỗi lớp? Không cần: `aria-invalid:` sinh selector\n * `[aria-invalid=\"true\"]` nên đặc tả cao hơn `border-transparent`/`border-control-edge` trần,\n * và `tailwind-merge` giữ cả hai vì tập biến thể khác nhau.\n */\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center font-medium select-none whitespace-nowrap transition-colors duration-150 ease-out focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 aria-invalid:border-destructive-ink aria-invalid:hover:border-destructive-ink aria-invalid:focus-visible:outline-destructive-ink disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground hover:bg-[color:color-mix(in_srgb,var(--primary),var(--ink)_15%)] border border-transparent shadow-control\",\n primary:\n \"bg-primary text-primary-foreground hover:bg-[color:color-mix(in_srgb,var(--primary),var(--ink)_15%)] border border-transparent shadow-control\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-warm dark:hover:bg-muted border border-control-edge\",\n outline:\n \"bg-transparent text-foreground border border-control-edge hover:border-primary-ink hover:bg-gold-50 dark:hover:bg-gold-500/10\",\n ghost: \"bg-transparent text-foreground hover:bg-muted border border-transparent\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-[color:color-mix(in_srgb,var(--destructive),var(--ink)_15%)] border border-transparent\",\n link: \"bg-transparent text-gold-500 hover:text-gold-600 dark:text-gold-200 dark:hover:text-gold-100 underline-offset-4 hover:underline border border-transparent p-0 h-auto\"\n },\n size: {\n default: \"h-9 px-4 text-sm rounded-md gap-2\",\n md: \"h-9 px-4 text-sm rounded-md gap-2\",\n sm: \"h-8 px-3 text-xs rounded-md gap-1.5\",\n lg: \"h-11 px-6 text-base rounded-md gap-2\",\n icon: \"h-9 w-9 rounded-md\",\n \"icon-sm\": \"h-8 w-8 rounded-md\",\n \"icon-lg\": \"h-11 w-11 rounded-md\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\"\n }\n }\n);\n\nexport interface ButtonProps extends React.ComponentProps<\"button\">, VariantProps<typeof buttonVariants> {\n /** Render ra chính phần tử con (Radix Slot) thay vì thẻ <button>. */\n asChild?: boolean;\n /** Hiện spinner và khoá nút. */\n loading?: boolean;\n /** Icon đặt trước nội dung; bị spinner thay thế khi `loading`. */\n leftIcon?: React.ReactNode;\n /** Icon đặt sau nội dung; ẩn khi `loading`. */\n rightIcon?: React.ReactNode;\n}\n\nfunction Button({\n className,\n variant,\n size,\n asChild = false,\n loading = false,\n leftIcon,\n rightIcon,\n disabled,\n children,\n ...props\n}: ButtonProps) {\n // Slot chỉ nhận đúng MỘT phần tử con → bỏ qua lớp bọc icon/loading khi asChild.\n if (asChild) {\n return (\n <Slot data-slot=\"button\" className={cn(buttonVariants({ variant, size, className }))} {...props}>\n {children}\n </Slot>\n );\n }\n\n return (\n <button\n data-slot=\"button\"\n disabled={disabled || loading}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n >\n {loading ? (\n <Loader2 className=\"size-3.5 shrink-0 animate-spin\" aria-hidden />\n ) : leftIcon ? (\n <span className=\"shrink-0\">{leftIcon}</span>\n ) : null}\n {children}\n {!loading && rightIcon ? <span className=\"shrink-0\">{rightIcon}</span> : null}\n </button>\n );\n}\n\nexport { Button, buttonVariants };\n"],"mappings":";AAsIM,cAOF,YAPE;AApIN,SAAS,YAAY;AACrB,SAAS,WAA8B;AACvC,SAAS,eAAe;AAExB,SAAS,UAAU;AAmEnB,MAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SACE;AAAA,QACF,SACE;AAAA,QACF,WACE;AAAA,QACF,SACE;AAAA,QACF,OAAO;AAAA,QACP,aACE;AAAA,QACF,MAAM;AAAA,MACR;AAAA,MACA,MAAM;AAAA,QACJ,SAAS;AAAA,QACT,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WAAW;AAAA,QACX,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAaA,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgB;AAEd,MAAI,SAAS;AACX,WACE,oBAAC,QAAK,aAAU,UAAS,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAU,CAAC,CAAC,GAAI,GAAG,OACvF,UACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,UAAU,YAAY;AAAA,MACtB,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAU,CAAC,CAAC;AAAA,MACzD,GAAG;AAAA,MAEH;AAAA,kBACC,oBAAC,WAAQ,WAAU,kCAAiC,eAAW,MAAC,IAC9D,WACF,oBAAC,UAAK,WAAU,YAAY,oBAAS,IACnC;AAAA,QACH;AAAA,QACA,CAAC,WAAW,YAAY,oBAAC,UAAK,WAAU,YAAY,qBAAU,IAAU;AAAA;AAAA;AAAA,EAC3E;AAEJ;","names":[]}
|
|
@@ -14,6 +14,23 @@ import 'class-variance-authority';
|
|
|
14
14
|
* Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `locale.code`
|
|
15
15
|
* (mặc định `vi-VN`) thay vì `"default"` — `"default"` phụ thuộc locale của môi
|
|
16
16
|
* trường chạy nên server và trình duyệt có thể ra chuỗi khác nhau → hydration mismatch.
|
|
17
|
+
*
|
|
18
|
+
* ─ Ô NGÀY ĐANG CHỌN: `bg-primary-ink` + `text-background` ─
|
|
19
|
+
* Chữ của ô ngày KHÔNG đổi màu khi được chọn (nghỉ và chọn đều là bậc mực), nên "đang chọn"
|
|
20
|
+
* chỉ còn được kể bằng NỀN ô — thông tin thị giác, ngưỡng 3:1. `bg-primary` gold #E8B824 trên
|
|
21
|
+
* nền trắng chỉ **1,86:1** ✗. Đổi sang bậc ink của brand thì đạt cả hai phía, cả hai theme:
|
|
22
|
+
*
|
|
23
|
+
* nền ô ⇄ nền lịch : 6,51:1 (light) · 12,67:1 (dark) ✓
|
|
24
|
+
* chữ `text-background` trên nền ô : 6,51:1 (light) · 12,67:1 (dark) ✓
|
|
25
|
+
*
|
|
26
|
+
* Chữ phải là `text-background` (trắng ở light) chứ KHÔNG `text-primary-foreground` (mực ở cả
|
|
27
|
+
* hai theme): mực trên `--primary-ink` #7A5800 chỉ 1,86:1 ✗ — cùng lý do với dấu tick
|
|
28
|
+
* `Checkbox` và núm `Switch`.
|
|
29
|
+
*
|
|
30
|
+
* ⚠ CÒN TỒN, cố ý chưa động: `today` và `range_middle` dùng `bg-accent` — 1,09:1 (light) /
|
|
31
|
+
* 1,17:1 (dark) so với nền lịch. Đưa dải giữa lên 3:1 thì cả tuần biến thành một khối đặc,
|
|
32
|
+
* nên đây là quyết định THIẾT KẾ (thêm viền/gạch chân cho "hôm nay"?) chứ không sửa được bằng
|
|
33
|
+
* đổi token — để dành cho vòng riêng.
|
|
17
34
|
*/
|
|
18
35
|
type CalendarProps = React.ComponentProps<typeof DayPicker> & {
|
|
19
36
|
/** Biến thể Button dùng cho nút điều hướng tháng. */
|
|
@@ -21,6 +38,30 @@ type CalendarProps = React.ComponentProps<typeof DayPicker> & {
|
|
|
21
38
|
};
|
|
22
39
|
type CalendarDayButtonProps = React.ComponentProps<typeof DayButton>;
|
|
23
40
|
declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, formatters, components, locale, ...props }: CalendarProps): React.JSX.Element;
|
|
41
|
+
/**
|
|
42
|
+
* Ô ngày.
|
|
43
|
+
*
|
|
44
|
+
* ─ `[&>span]:opacity-80` — 80% LÀ MỨC MỜ TỐI ĐA CÒN ĐẠT AA ─
|
|
45
|
+
* `<span>` là nội dung phụ app truyền vào ô ngày (số việc, chấm sự kiện…), cỡ `text-xs` = 12px
|
|
46
|
+
* nên chịu ngưỡng CHỮ 4,5:1. Nút ngày VẪN bấm được và vẫn nhận focus → `opacity-*` chồng lên nó
|
|
47
|
+
* KHÔNG được miễn trừ theo 1.4.3. Ca tệ nhất là ngày ĐÃ CHỌN ở theme sáng (`text-background`
|
|
48
|
+
* trắng trên `bg-primary-ink` #7A5800), đo theo từng mức mờ:
|
|
49
|
+
*
|
|
50
|
+
* opacity-70 → 4,12:1 (light) · 5,90:1 (dark) ✗ ← dưới ngưỡng ở light
|
|
51
|
+
* opacity-75 → 4,46:1 · 6,82:1 ✗
|
|
52
|
+
* opacity-80 → 4,85:1 · 7,97:1 ✓ ← đang dùng
|
|
53
|
+
*
|
|
54
|
+
* Ba con số light ở trên là của nền ô MỚI. Đừng quy chúng cho `0.1.0`: bản đó tô ô chọn bằng
|
|
55
|
+
* `bg-primary` (gold #E8B824) với chữ `text-primary-foreground` (mực), nên `opacity-70` khi ấy
|
|
56
|
+
* đo được 5,26:1 (light) · 5,90:1 (dark) — tức mức mờ 70% VẪN đạt. Khuyết tật thật của `0.1.0`
|
|
57
|
+
* nằm ở chỗ khác: nền ô chọn ⇄ nền lịch chỉ 1,86:1 (light), tức 1.4.11 mới là điều bị vi phạm.
|
|
58
|
+
* Chính việc hạ nền xuống #7A5800 + chữ trắng đã làm ca tệ nhất chặt lại và kéo 70% xuống dưới
|
|
59
|
+
* ngưỡng. (Ở dark hai bản trùng số vì `--primary-ink: var(--primary)`.)
|
|
60
|
+
*
|
|
61
|
+
* Các trạng thái còn lại thoải mái ở 80%: giữa khoảng & hôm nay 9,70:1 (light) · 10,05:1 (dark),
|
|
62
|
+
* ngày thường 10,26:1 · 11,41:1. Hạ dưới 80% là hỏng đúng một ca (chọn + theme sáng) — đúng kiểu
|
|
63
|
+
* lỗi chỉ lộ ở một trạng thái mà ba vòng trước đều sót.
|
|
64
|
+
*/
|
|
24
65
|
declare function CalendarDayButton({ className, day, modifiers, ...props }: CalendarDayButtonProps): React.JSX.Element;
|
|
25
66
|
|
|
26
67
|
export { Calendar, CalendarDayButton, type CalendarDayButtonProps, type CalendarProps };
|
|
@@ -133,7 +133,7 @@ function CalendarDayButton({ className, day, modifiers, ...props }) {
|
|
|
133
133
|
"data-range-end": modifiers.range_end,
|
|
134
134
|
"data-range-middle": modifiers.range_middle,
|
|
135
135
|
className: cn(
|
|
136
|
-
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-
|
|
136
|
+
"data-[selected-single=true]:bg-primary-ink data-[selected-single=true]:text-background data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary-ink data-[range-start=true]:text-background data-[range-end=true]:bg-primary-ink data-[range-end=true]:text-background group-data-[focused=true]/day:outline-none group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-offset-2 group-data-[focused=true]/day:ring-offset-background dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-2 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-80",
|
|
137
137
|
defaultClassNames.day,
|
|
138
138
|
className
|
|
139
139
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/calendar.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { type DayButton, DayPicker, getDefaultClassNames } from \"react-day-picker\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps, buttonVariants } from \"./button.js\";\n\n/**\n * Calendar (react-day-picker v9) — lịch trần, đã map class sang token Comitor.\n * Bản có ô nhập `dd/MM/yyyy` + preset (\"Hôm nay\", \"7 ngày qua\"…) là composite\n * `DatePicker` / `DateRangePicker` ở Tier 2.\n *\n * Ngôn ngữ: truyền `locale={vi}` (date-fns/locale) để đổi tên thứ/tháng.\n *\n * Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `locale.code`\n * (mặc định `vi-VN`) thay vì `\"default\"` — `\"default\"` phụ thuộc locale của môi\n * trường chạy nên server và trình duyệt có thể ra chuỗi khác nhau → hydration mismatch.\n */\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker> & {\n /** Biến thể Button dùng cho nút điều hướng tháng. */\n buttonVariant?: ButtonProps[\"variant\"];\n};\nexport type CalendarDayButtonProps = React.ComponentProps<typeof DayButton>;\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"label\",\n buttonVariant = \"ghost\",\n formatters,\n components,\n locale,\n ...props\n}: CalendarProps) {\n const defaultClassNames = getDefaultClassNames();\n const localeCode = locale?.code ?? \"vi-VN\";\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n locale={locale}\n className={cn(\n \"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent\",\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className\n )}\n captionLayout={captionLayout}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(localeCode, { month: \"short\" }),\n ...formatters\n }}\n classNames={{\n root: cn(\"w-fit\", defaultClassNames.root),\n months: cn(\"flex gap-4 flex-col md:flex-row relative\", defaultClassNames.months),\n month: cn(\"flex flex-col w-full gap-4\", defaultClassNames.month),\n nav: cn(\"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between\", defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_previous\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_next\n ),\n month_caption: cn(\n \"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)\",\n defaultClassNames.month_caption\n ),\n dropdowns: cn(\n \"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5\",\n defaultClassNames.dropdowns\n ),\n dropdown_root: cn(\n \"relative has-focus:outline-none border border-input shadow-xs has-focus:ring-2 has-focus:ring-ring/50 has-focus:ring-offset-2 has-focus:ring-offset-background rounded-md\",\n defaultClassNames.dropdown_root\n ),\n dropdown: cn(\"absolute bg-popover inset-0 opacity-0\", defaultClassNames.dropdown),\n caption_label: cn(\n \"select-none font-medium\",\n captionLayout === \"label\"\n ? \"text-sm\"\n : \"rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5\",\n defaultClassNames.caption_label\n ),\n table: \"w-full border-collapse\",\n weekdays: cn(\"flex\", defaultClassNames.weekdays),\n weekday: cn(\n \"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none\",\n defaultClassNames.weekday\n ),\n week: cn(\"flex w-full mt-2\", defaultClassNames.week),\n week_number_header: cn(\"select-none w-(--cell-size)\", defaultClassNames.week_number_header),\n week_number: cn(\"text-[0.8rem] select-none text-muted-foreground\", defaultClassNames.week_number),\n day: cn(\n \"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none\",\n defaultClassNames.day\n ),\n range_start: cn(\"rounded-l-md bg-accent\", defaultClassNames.range_start),\n range_middle: cn(\"rounded-none\", defaultClassNames.range_middle),\n range_end: cn(\"rounded-r-md bg-accent\", defaultClassNames.range_end),\n today: cn(\n \"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none\",\n defaultClassNames.today\n ),\n outside: cn(\"text-muted-foreground aria-selected:text-muted-foreground\", defaultClassNames.outside),\n disabled: cn(\"text-muted-foreground opacity-50\", defaultClassNames.disabled),\n hidden: cn(\"invisible\", defaultClassNames.hidden),\n ...classNames\n }}\n components={{\n Root: ({ className: rootClassName, rootRef, ...rootProps }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(rootClassName)} {...rootProps} />;\n },\n Chevron: ({ className: chevronClassName, orientation, ...chevronProps }) => {\n if (orientation === \"left\") {\n return <ChevronLeftIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n if (orientation === \"right\") {\n return <ChevronRightIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n return <ChevronDownIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n },\n DayButton: CalendarDayButton,\n WeekNumber: ({ children, week: _week, ...weekProps }) => {\n return (\n <td {...weekProps}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components\n }}\n {...props}\n />\n );\n}\n\nfunction CalendarDayButton({ className, day, modifiers, ...props }: CalendarDayButtonProps) {\n const defaultClassNames = getDefaultClassNames();\n\n const ref = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString()}\n data-selected-single={\n modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle\n }\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n \"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:outline-none group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-offset-2 group-data-[focused=true]/day:ring-offset-background dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-2 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70\",\n defaultClassNames.day,\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Calendar, CalendarDayButton };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/calendar.tsx"],"sourcesContent":["\"use client\";\n\nimport { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { type DayButton, DayPicker, getDefaultClassNames } from \"react-day-picker\";\nimport { cn } from \"../../lib/cn.js\";\nimport { Button, type ButtonProps, buttonVariants } from \"./button.js\";\n\n/**\n * Calendar (react-day-picker v9) — lịch trần, đã map class sang token Comitor.\n * Bản có ô nhập `dd/MM/yyyy` + preset (\"Hôm nay\", \"7 ngày qua\"…) là composite\n * `DatePicker` / `DateRangePicker` ở Tier 2.\n *\n * Ngôn ngữ: truyền `locale={vi}` (date-fns/locale) để đổi tên thứ/tháng.\n *\n * Khác bản comitor-ds: tên tháng rút gọn trong dropdown lấy theo `locale.code`\n * (mặc định `vi-VN`) thay vì `\"default\"` — `\"default\"` phụ thuộc locale của môi\n * trường chạy nên server và trình duyệt có thể ra chuỗi khác nhau → hydration mismatch.\n *\n * ─ Ô NGÀY ĐANG CHỌN: `bg-primary-ink` + `text-background` ─\n * Chữ của ô ngày KHÔNG đổi màu khi được chọn (nghỉ và chọn đều là bậc mực), nên \"đang chọn\"\n * chỉ còn được kể bằng NỀN ô — thông tin thị giác, ngưỡng 3:1. `bg-primary` gold #E8B824 trên\n * nền trắng chỉ **1,86:1** ✗. Đổi sang bậc ink của brand thì đạt cả hai phía, cả hai theme:\n *\n * nền ô ⇄ nền lịch : 6,51:1 (light) · 12,67:1 (dark) ✓\n * chữ `text-background` trên nền ô : 6,51:1 (light) · 12,67:1 (dark) ✓\n *\n * Chữ phải là `text-background` (trắng ở light) chứ KHÔNG `text-primary-foreground` (mực ở cả\n * hai theme): mực trên `--primary-ink` #7A5800 chỉ 1,86:1 ✗ — cùng lý do với dấu tick\n * `Checkbox` và núm `Switch`.\n *\n * ⚠ CÒN TỒN, cố ý chưa động: `today` và `range_middle` dùng `bg-accent` — 1,09:1 (light) /\n * 1,17:1 (dark) so với nền lịch. Đưa dải giữa lên 3:1 thì cả tuần biến thành một khối đặc,\n * nên đây là quyết định THIẾT KẾ (thêm viền/gạch chân cho \"hôm nay\"?) chứ không sửa được bằng\n * đổi token — để dành cho vòng riêng.\n */\n\nexport type CalendarProps = React.ComponentProps<typeof DayPicker> & {\n /** Biến thể Button dùng cho nút điều hướng tháng. */\n buttonVariant?: ButtonProps[\"variant\"];\n};\nexport type CalendarDayButtonProps = React.ComponentProps<typeof DayButton>;\n\nfunction Calendar({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = \"label\",\n buttonVariant = \"ghost\",\n formatters,\n components,\n locale,\n ...props\n}: CalendarProps) {\n const defaultClassNames = getDefaultClassNames();\n const localeCode = locale?.code ?? \"vi-VN\";\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n locale={locale}\n className={cn(\n \"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent\",\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className\n )}\n captionLayout={captionLayout}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(localeCode, { month: \"short\" }),\n ...formatters\n }}\n classNames={{\n root: cn(\"w-fit\", defaultClassNames.root),\n months: cn(\"flex gap-4 flex-col md:flex-row relative\", defaultClassNames.months),\n month: cn(\"flex flex-col w-full gap-4\", defaultClassNames.month),\n nav: cn(\"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between\", defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_previous\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n \"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none\",\n defaultClassNames.button_next\n ),\n month_caption: cn(\n \"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)\",\n defaultClassNames.month_caption\n ),\n dropdowns: cn(\n \"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5\",\n defaultClassNames.dropdowns\n ),\n dropdown_root: cn(\n \"relative has-focus:outline-none border border-input shadow-xs has-focus:ring-2 has-focus:ring-ring/50 has-focus:ring-offset-2 has-focus:ring-offset-background rounded-md\",\n defaultClassNames.dropdown_root\n ),\n dropdown: cn(\"absolute bg-popover inset-0 opacity-0\", defaultClassNames.dropdown),\n caption_label: cn(\n \"select-none font-medium\",\n captionLayout === \"label\"\n ? \"text-sm\"\n : \"rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5\",\n defaultClassNames.caption_label\n ),\n table: \"w-full border-collapse\",\n weekdays: cn(\"flex\", defaultClassNames.weekdays),\n weekday: cn(\n \"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none\",\n defaultClassNames.weekday\n ),\n week: cn(\"flex w-full mt-2\", defaultClassNames.week),\n week_number_header: cn(\"select-none w-(--cell-size)\", defaultClassNames.week_number_header),\n week_number: cn(\"text-[0.8rem] select-none text-muted-foreground\", defaultClassNames.week_number),\n day: cn(\n \"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none\",\n defaultClassNames.day\n ),\n range_start: cn(\"rounded-l-md bg-accent\", defaultClassNames.range_start),\n range_middle: cn(\"rounded-none\", defaultClassNames.range_middle),\n range_end: cn(\"rounded-r-md bg-accent\", defaultClassNames.range_end),\n today: cn(\n \"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none\",\n defaultClassNames.today\n ),\n outside: cn(\"text-muted-foreground aria-selected:text-muted-foreground\", defaultClassNames.outside),\n disabled: cn(\"text-muted-foreground opacity-50\", defaultClassNames.disabled),\n hidden: cn(\"invisible\", defaultClassNames.hidden),\n ...classNames\n }}\n components={{\n Root: ({ className: rootClassName, rootRef, ...rootProps }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(rootClassName)} {...rootProps} />;\n },\n Chevron: ({ className: chevronClassName, orientation, ...chevronProps }) => {\n if (orientation === \"left\") {\n return <ChevronLeftIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n if (orientation === \"right\") {\n return <ChevronRightIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n }\n\n return <ChevronDownIcon className={cn(\"size-4\", chevronClassName)} {...chevronProps} />;\n },\n DayButton: CalendarDayButton,\n WeekNumber: ({ children, week: _week, ...weekProps }) => {\n return (\n <td {...weekProps}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components\n }}\n {...props}\n />\n );\n}\n\n/**\n * Ô ngày.\n *\n * ─ `[&>span]:opacity-80` — 80% LÀ MỨC MỜ TỐI ĐA CÒN ĐẠT AA ─\n * `<span>` là nội dung phụ app truyền vào ô ngày (số việc, chấm sự kiện…), cỡ `text-xs` = 12px\n * nên chịu ngưỡng CHỮ 4,5:1. Nút ngày VẪN bấm được và vẫn nhận focus → `opacity-*` chồng lên nó\n * KHÔNG được miễn trừ theo 1.4.3. Ca tệ nhất là ngày ĐÃ CHỌN ở theme sáng (`text-background`\n * trắng trên `bg-primary-ink` #7A5800), đo theo từng mức mờ:\n *\n * opacity-70 → 4,12:1 (light) · 5,90:1 (dark) ✗ ← dưới ngưỡng ở light\n * opacity-75 → 4,46:1 · 6,82:1 ✗\n * opacity-80 → 4,85:1 · 7,97:1 ✓ ← đang dùng\n *\n * Ba con số light ở trên là của nền ô MỚI. Đừng quy chúng cho `0.1.0`: bản đó tô ô chọn bằng\n * `bg-primary` (gold #E8B824) với chữ `text-primary-foreground` (mực), nên `opacity-70` khi ấy\n * đo được 5,26:1 (light) · 5,90:1 (dark) — tức mức mờ 70% VẪN đạt. Khuyết tật thật của `0.1.0`\n * nằm ở chỗ khác: nền ô chọn ⇄ nền lịch chỉ 1,86:1 (light), tức 1.4.11 mới là điều bị vi phạm.\n * Chính việc hạ nền xuống #7A5800 + chữ trắng đã làm ca tệ nhất chặt lại và kéo 70% xuống dưới\n * ngưỡng. (Ở dark hai bản trùng số vì `--primary-ink: var(--primary)`.)\n *\n * Các trạng thái còn lại thoải mái ở 80%: giữa khoảng & hôm nay 9,70:1 (light) · 10,05:1 (dark),\n * ngày thường 10,26:1 · 11,41:1. Hạ dưới 80% là hỏng đúng một ca (chọn + theme sáng) — đúng kiểu\n * lỗi chỉ lộ ở một trạng thái mà ba vòng trước đều sót.\n */\nfunction CalendarDayButton({ className, day, modifiers, ...props }: CalendarDayButtonProps) {\n const defaultClassNames = getDefaultClassNames();\n\n const ref = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) ref.current?.focus();\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={ref}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString()}\n data-selected-single={\n modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle\n }\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n \"data-[selected-single=true]:bg-primary-ink data-[selected-single=true]:text-background data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary-ink data-[range-start=true]:text-background data-[range-end=true]:bg-primary-ink data-[range-end=true]:text-background group-data-[focused=true]/day:outline-none group-data-[focused=true]/day:ring-ring/50 group-data-[focused=true]/day:ring-offset-2 group-data-[focused=true]/day:ring-offset-background dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-2 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-80\",\n defaultClassNames.day,\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Calendar, CalendarDayButton };\n"],"mappings":"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","names":[]}
|
|
@@ -8,14 +8,47 @@ declare const cardVariants: (props?: ({
|
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
interface CardProps extends React.ComponentProps<"div">, VariantProps<typeof cardVariants> {
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
interface CardHeaderProps extends React.ComponentProps<"div"> {
|
|
12
|
+
/**
|
|
13
|
+
* Nút/menu đặt ở GÓC PHẢI của header, ngang hàng với tiêu đề.
|
|
14
|
+
*
|
|
15
|
+
* Có nó thì header tự chuyển sang hàng ngang; bỏ trống thì bố cục dọc mặc định giữ
|
|
16
|
+
* nguyên (xem chú thích ở `CardHeader`).
|
|
17
|
+
*/
|
|
18
|
+
actions?: React.ReactNode;
|
|
19
|
+
/** className riêng cho cụm `actions` (vd đổi `gap`, cho phép xuống dòng). */
|
|
20
|
+
actionsClassName?: string;
|
|
21
|
+
}
|
|
12
22
|
type CardTitleProps = React.ComponentProps<"h3">;
|
|
13
23
|
type CardDescriptionProps = React.ComponentProps<"p">;
|
|
14
24
|
type CardActionProps = React.ComponentProps<"div">;
|
|
15
25
|
type CardContentProps = React.ComponentProps<"div">;
|
|
16
26
|
type CardFooterProps = React.ComponentProps<"div">;
|
|
17
27
|
declare function CardBase({ className, variant, padding, ...props }: CardProps): React.JSX.Element;
|
|
18
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Đầu thẻ. Mặc định XẾP DỌC (`flex-col space-y-1.5`) — tiêu đề trên, mô tả dưới.
|
|
30
|
+
*
|
|
31
|
+
* ─ VÌ SAO CÓ `actions` ─
|
|
32
|
+
* Thẻ có nút ở góc phải là mẫu lặp lại ở mọi app, mà bố cục dọc mặc định thì chặn nó: mỗi
|
|
33
|
+
* chỗ dùng đều phải ghi đè đúng một chuỗi bốn lớp `"flex-row items-start justify-between
|
|
34
|
+
* gap-3 space-y-0"` — chép tay, dễ sót `space-y-0`, và không có gì bảo đảm hai app ghi đè
|
|
35
|
+
* giống nhau. `actions` gói đúng chuỗi đó lại thành một prop.
|
|
36
|
+
*
|
|
37
|
+
* Mặc định KHÔNG đổi: không truyền `actions` thì lớp và cây DOM y hệt `0.2.0` (thêm một
|
|
38
|
+
* `<div>` bọc chỉ xảy ra ở nhánh có actions), nên bản vá này không phá bố cục nào đang chạy.
|
|
39
|
+
*
|
|
40
|
+
* Quan hệ với `CardAction`: `CardAction` là ô `ml-auto` để tự lắp khi bạn ĐÃ tự đặt header
|
|
41
|
+
* thành hàng ngang. `actions` làm cả hai việc — đổi bố cục *và* đặt chỗ — nên là đường đi
|
|
42
|
+
* mặc định; `CardAction` giữ nguyên cho ai đã dùng.
|
|
43
|
+
*
|
|
44
|
+
* ```tsx
|
|
45
|
+
* <CardHeader actions={<Button size="sm" variant="ghost">Sửa</Button>}>
|
|
46
|
+
* <CardTitle>Thông tin đơn vị</CardTitle>
|
|
47
|
+
* <CardDescription>Cập nhật lần cuối hôm qua</CardDescription>
|
|
48
|
+
* </CardHeader>
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare function CardHeader({ className, actions, actionsClassName, children, ...props }: CardHeaderProps): React.JSX.Element;
|
|
19
52
|
declare function CardTitle({ className, ...props }: CardTitleProps): React.JSX.Element;
|
|
20
53
|
declare function CardDescription({ className, ...props }: CardDescriptionProps): React.JSX.Element;
|
|
21
54
|
declare function CardAction({ className, ...props }: CardActionProps): React.JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from "class-variance-authority";
|
|
3
3
|
import { cn } from "../../lib/cn.js";
|
|
4
4
|
const cardVariants = cva("rounded-xl", {
|
|
@@ -24,8 +24,22 @@ const cardVariants = cva("rounded-xl", {
|
|
|
24
24
|
function CardBase({ className, variant, padding, ...props }) {
|
|
25
25
|
return /* @__PURE__ */ jsx("div", { "data-slot": "card", className: cn(cardVariants({ variant, padding }), className), ...props });
|
|
26
26
|
}
|
|
27
|
-
function CardHeader({ className, ...props }) {
|
|
28
|
-
|
|
27
|
+
function CardHeader({ className, actions, actionsClassName, children, ...props }) {
|
|
28
|
+
if (actions === void 0 || actions === null || actions === false) {
|
|
29
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "card-header", className: cn("flex flex-col space-y-1.5", className), ...props, children });
|
|
30
|
+
}
|
|
31
|
+
return /* @__PURE__ */ jsxs(
|
|
32
|
+
"div",
|
|
33
|
+
{
|
|
34
|
+
"data-slot": "card-header",
|
|
35
|
+
className: cn("flex flex-row items-start justify-between gap-3 space-y-0", className),
|
|
36
|
+
...props,
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx("div", { "data-slot": "card-header-text", className: "flex min-w-0 flex-col space-y-1.5", children }),
|
|
39
|
+
/* @__PURE__ */ jsx("div", { "data-slot": "card-header-actions", className: cn("flex shrink-0 items-center gap-2", actionsClassName), children: actions })
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
);
|
|
29
43
|
}
|
|
30
44
|
function CardTitle({ className, ...props }) {
|
|
31
45
|
return /* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/card.tsx"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * Cấu trúc + style port nguyên trạng từ comitor-ds. Tên export giữ tương thích shadcn\n * (Card / CardHeader / CardTitle / CardDescription / CardContent / CardFooter), thêm\n * CardAction (bổ sung, không có trong shadcn gốc).\n */\nconst cardVariants = cva(\"rounded-xl\", {\n variants: {\n variant: {\n default: \"bg-card border border-border shadow-surface\",\n elevated: \"bg-card border border-border shadow-card\",\n outline: \"bg-transparent border border-border\",\n ghost: \"bg-transparent border-none\"\n },\n padding: {\n none: \"\",\n sm: \"p-4\",\n md: \"p-6\",\n lg: \"p-8\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n padding: \"md\"\n }\n});\n\nexport interface CardProps extends React.ComponentProps<\"div\">, VariantProps<typeof cardVariants> {}\nexport
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/card.tsx"],"sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * Cấu trúc + style port nguyên trạng từ comitor-ds. Tên export giữ tương thích shadcn\n * (Card / CardHeader / CardTitle / CardDescription / CardContent / CardFooter), thêm\n * CardAction (bổ sung, không có trong shadcn gốc).\n */\nconst cardVariants = cva(\"rounded-xl\", {\n variants: {\n variant: {\n default: \"bg-card border border-border shadow-surface\",\n elevated: \"bg-card border border-border shadow-card\",\n outline: \"bg-transparent border border-border\",\n ghost: \"bg-transparent border-none\"\n },\n padding: {\n none: \"\",\n sm: \"p-4\",\n md: \"p-6\",\n lg: \"p-8\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n padding: \"md\"\n }\n});\n\nexport interface CardProps extends React.ComponentProps<\"div\">, VariantProps<typeof cardVariants> {}\nexport interface CardHeaderProps extends React.ComponentProps<\"div\"> {\n /**\n * Nút/menu đặt ở GÓC PHẢI của header, ngang hàng với tiêu đề.\n *\n * Có nó thì header tự chuyển sang hàng ngang; bỏ trống thì bố cục dọc mặc định giữ\n * nguyên (xem chú thích ở `CardHeader`).\n */\n actions?: React.ReactNode;\n /** className riêng cho cụm `actions` (vd đổi `gap`, cho phép xuống dòng). */\n actionsClassName?: string;\n}\nexport type CardTitleProps = React.ComponentProps<\"h3\">;\nexport type CardDescriptionProps = React.ComponentProps<\"p\">;\nexport type CardActionProps = React.ComponentProps<\"div\">;\nexport type CardContentProps = React.ComponentProps<\"div\">;\nexport type CardFooterProps = React.ComponentProps<\"div\">;\n\nfunction CardBase({ className, variant, padding, ...props }: CardProps) {\n return <div data-slot=\"card\" className={cn(cardVariants({ variant, padding }), className)} {...props} />;\n}\n\n/**\n * Đầu thẻ. Mặc định XẾP DỌC (`flex-col space-y-1.5`) — tiêu đề trên, mô tả dưới.\n *\n * ─ VÌ SAO CÓ `actions` ─\n * Thẻ có nút ở góc phải là mẫu lặp lại ở mọi app, mà bố cục dọc mặc định thì chặn nó: mỗi\n * chỗ dùng đều phải ghi đè đúng một chuỗi bốn lớp `\"flex-row items-start justify-between\n * gap-3 space-y-0\"` — chép tay, dễ sót `space-y-0`, và không có gì bảo đảm hai app ghi đè\n * giống nhau. `actions` gói đúng chuỗi đó lại thành một prop.\n *\n * Mặc định KHÔNG đổi: không truyền `actions` thì lớp và cây DOM y hệt `0.2.0` (thêm một\n * `<div>` bọc chỉ xảy ra ở nhánh có actions), nên bản vá này không phá bố cục nào đang chạy.\n *\n * Quan hệ với `CardAction`: `CardAction` là ô `ml-auto` để tự lắp khi bạn ĐÃ tự đặt header\n * thành hàng ngang. `actions` làm cả hai việc — đổi bố cục *và* đặt chỗ — nên là đường đi\n * mặc định; `CardAction` giữ nguyên cho ai đã dùng.\n *\n * ```tsx\n * <CardHeader actions={<Button size=\"sm\" variant=\"ghost\">Sửa</Button>}>\n * <CardTitle>Thông tin đơn vị</CardTitle>\n * <CardDescription>Cập nhật lần cuối hôm qua</CardDescription>\n * </CardHeader>\n * ```\n */\nfunction CardHeader({ className, actions, actionsClassName, children, ...props }: CardHeaderProps) {\n if (actions === undefined || actions === null || actions === false) {\n return (\n <div data-slot=\"card-header\" className={cn(\"flex flex-col space-y-1.5\", className)} {...props}>\n {children}\n </div>\n );\n }\n\n return (\n <div\n data-slot=\"card-header\"\n className={cn(\"flex flex-row items-start justify-between gap-3 space-y-0\", className)}\n {...props}\n >\n {/* `min-w-0` để tiêu đề dài `truncate` được thay vì đẩy cụm nút ra khỏi thẻ. */}\n <div data-slot=\"card-header-text\" className=\"flex min-w-0 flex-col space-y-1.5\">\n {children}\n </div>\n <div data-slot=\"card-header-actions\" className={cn(\"flex shrink-0 items-center gap-2\", actionsClassName)}>\n {actions}\n </div>\n </div>\n );\n}\n\nfunction CardTitle({ className, ...props }: CardTitleProps) {\n return (\n <h3\n data-slot=\"card-title\"\n className={cn(\"text-lg font-semibold text-foreground leading-none tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nfunction CardDescription({ className, ...props }: CardDescriptionProps) {\n return <p data-slot=\"card-description\" className={cn(\"text-sm text-muted-foreground\", className)} {...props} />;\n}\n\nfunction CardAction({ className, ...props }: CardActionProps) {\n return <div data-slot=\"card-action\" className={cn(\"ml-auto\", className)} {...props} />;\n}\n\nfunction CardContent({ className, ...props }: CardContentProps) {\n return <div data-slot=\"card-content\" className={cn(\"pt-4\", className)} {...props} />;\n}\n\nfunction CardFooter({ className, ...props }: CardFooterProps) {\n return <div data-slot=\"card-footer\" className={cn(\"flex items-center pt-4\", className)} {...props} />;\n}\n\n/*\n * `Card` mở cả hai lối dùng: API tên rời (Card + CardHeader/CardTitle/…) và API gộp\n * (Card.Header / Card.Title / Card.Description / Card.Content / Card.Footer / Card.Action).\n */\nconst Card = Object.assign(CardBase, {\n Header: CardHeader,\n Title: CardTitle,\n Description: CardDescription,\n Content: CardContent,\n Footer: CardFooter,\n Action: CardAction\n});\n\nexport { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, cardVariants };\n"],"mappings":"AAiDS,cAoCL,YApCK;AAjDT,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAOnB,MAAM,eAAe,IAAI,cAAc;AAAA,EACrC,UAAU;AAAA,IACR,SAAS;AAAA,MACP,SAAS;AAAA,MACT,UAAU;AAAA,MACV,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,IACN;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA,IACf,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACF,CAAC;AAoBD,SAAS,SAAS,EAAE,WAAW,SAAS,SAAS,GAAG,MAAM,GAAc;AACtE,SAAO,oBAAC,SAAI,aAAU,QAAO,WAAW,GAAG,aAAa,EAAE,SAAS,QAAQ,CAAC,GAAG,SAAS,GAAI,GAAG,OAAO;AACxG;AAyBA,SAAS,WAAW,EAAE,WAAW,SAAS,kBAAkB,UAAU,GAAG,MAAM,GAAoB;AACjG,MAAI,YAAY,UAAa,YAAY,QAAQ,YAAY,OAAO;AAClE,WACE,oBAAC,SAAI,aAAU,eAAc,WAAW,GAAG,6BAA6B,SAAS,GAAI,GAAG,OACrF,UACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,6DAA6D,SAAS;AAAA,MACnF,GAAG;AAAA,MAGJ;AAAA,4BAAC,SAAI,aAAU,oBAAmB,WAAU,qCACzC,UACH;AAAA,QACA,oBAAC,SAAI,aAAU,uBAAsB,WAAW,GAAG,oCAAoC,gBAAgB,GACpG,mBACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AAC1D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,qEAAqE,SAAS;AAAA,MAC3F,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,EAAE,WAAW,GAAG,MAAM,GAAyB;AACtE,SAAO,oBAAC,OAAE,aAAU,oBAAmB,WAAW,GAAG,iCAAiC,SAAS,GAAI,GAAG,OAAO;AAC/G;AAEA,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SAAO,oBAAC,SAAI,aAAU,eAAc,WAAW,GAAG,WAAW,SAAS,GAAI,GAAG,OAAO;AACtF;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SAAO,oBAAC,SAAI,aAAU,gBAAe,WAAW,GAAG,QAAQ,SAAS,GAAI,GAAG,OAAO;AACpF;AAEA,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SAAO,oBAAC,SAAI,aAAU,eAAc,WAAW,GAAG,0BAA0B,SAAS,GAAI,GAAG,OAAO;AACrG;AAMA,MAAM,OAAO,OAAO,OAAO,UAAU;AAAA,EACnC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AACV,CAAC;","names":[]}
|
|
@@ -16,10 +16,10 @@ 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-
|
|
20
|
-
"data-[state=indeterminate]:bg-
|
|
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",
|
|
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
|
-
"hover:border-
|
|
22
|
+
"hover:border-control-edge-strong",
|
|
23
23
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
24
24
|
className
|
|
25
25
|
),
|
|
@@ -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
|
|
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":[]}
|
|
@@ -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: "size-4 shrink-0
|
|
38
|
+
/* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4 shrink-0" }),
|
|
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 <SearchIcon className=\"size-4 shrink-0
|
|
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":[]}
|
|
@@ -73,7 +73,7 @@ function ContextMenuItem({ className, inset, variant = "default", ...props }) {
|
|
|
73
73
|
"data-inset": inset,
|
|
74
74
|
"data-variant": variant,
|
|
75
75
|
className: cn(
|
|
76
|
-
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_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]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
76
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
77
77
|
className
|
|
78
78
|
),
|
|
79
79
|
...props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/context-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as ContextMenuPrimitive from \"@radix-ui/react-context-menu\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * ContextMenu — menu chuột phải (Radix ContextMenu).\n *\n * Dùng cho thao tác nhanh trên thẻ task, dòng bảng, mục sidebar. Cấu trúc mục\n * giống `DropdownMenu` (cùng biến thể `destructive`, `inset`) để hai menu nhìn như một.\n * Vì chuột phải không có trên thiết bị cảm ứng, mọi thao tác ở đây phải có đường\n * thay thế (nút \"…\" mở DropdownMenu).\n */\n\nexport type ContextMenuProps = React.ComponentProps<typeof ContextMenuPrimitive.Root>;\n\nfunction ContextMenu({ ...props }: ContextMenuProps) {\n return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />;\n}\n\nexport type ContextMenuTriggerProps = React.ComponentProps<typeof ContextMenuPrimitive.Trigger>;\n\nfunction ContextMenuTrigger({ ...props }: ContextMenuTriggerProps) {\n return <ContextMenuPrimitive.Trigger data-slot=\"context-menu-trigger\" {...props} />;\n}\n\nexport type ContextMenuGroupProps = React.ComponentProps<typeof ContextMenuPrimitive.Group>;\n\nfunction ContextMenuGroup({ ...props }: ContextMenuGroupProps) {\n return <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />;\n}\n\nexport type ContextMenuPortalProps = React.ComponentProps<typeof ContextMenuPrimitive.Portal>;\n\nfunction ContextMenuPortal({ ...props }: ContextMenuPortalProps) {\n return <ContextMenuPrimitive.Portal data-slot=\"context-menu-portal\" {...props} />;\n}\n\nexport type ContextMenuSubProps = React.ComponentProps<typeof ContextMenuPrimitive.Sub>;\n\nfunction ContextMenuSub({ ...props }: ContextMenuSubProps) {\n return <ContextMenuPrimitive.Sub data-slot=\"context-menu-sub\" {...props} />;\n}\n\nexport type ContextMenuRadioGroupProps = React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>;\n\nfunction ContextMenuRadioGroup({ ...props }: ContextMenuRadioGroupProps) {\n return <ContextMenuPrimitive.RadioGroup data-slot=\"context-menu-radio-group\" {...props} />;\n}\n\nexport type ContextMenuSubTriggerProps = React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n};\n\nfunction ContextMenuSubTrigger({ className, inset, children, ...props }: ContextMenuSubTriggerProps) {\n return (\n <ContextMenuPrimitive.SubTrigger\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto\" />\n </ContextMenuPrimitive.SubTrigger>\n );\n}\n\nexport type ContextMenuSubContentProps = React.ComponentProps<typeof ContextMenuPrimitive.SubContent>;\n\nfunction ContextMenuSubContent({ className, ...props }: ContextMenuSubContentProps) {\n return (\n <ContextMenuPrimitive.SubContent\n data-slot=\"context-menu-sub-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type ContextMenuContentProps = React.ComponentProps<typeof ContextMenuPrimitive.Content>;\n\nfunction ContextMenuContent({ className, ...props }: ContextMenuContentProps) {\n return (\n <ContextMenuPrimitive.Portal>\n <ContextMenuPrimitive.Content\n data-slot=\"context-menu-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </ContextMenuPrimitive.Portal>\n );\n}\n\nexport type ContextMenuItemVariant = \"default\" | \"destructive\";\n\nexport type ContextMenuItemProps = React.ComponentProps<typeof ContextMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: ContextMenuItemVariant;\n};\n\nfunction ContextMenuItem({ className, inset, variant = \"default\", ...props }: ContextMenuItemProps) {\n return (\n <ContextMenuPrimitive.Item\n data-slot=\"context-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_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]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type ContextMenuCheckboxItemProps = React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>;\n\nfunction ContextMenuCheckboxItem({ className, children, checked, ...props }: ContextMenuCheckboxItemProps) {\n return (\n <ContextMenuPrimitive.CheckboxItem\n data-slot=\"context-menu-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.CheckboxItem>\n );\n}\n\nexport type ContextMenuRadioItemProps = React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>;\n\nfunction ContextMenuRadioItem({ className, children, ...props }: ContextMenuRadioItemProps) {\n return (\n <ContextMenuPrimitive.RadioItem\n data-slot=\"context-menu-radio-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.RadioItem>\n );\n}\n\nexport type ContextMenuLabelProps = React.ComponentProps<typeof ContextMenuPrimitive.Label> & {\n inset?: boolean;\n};\n\nfunction ContextMenuLabel({ className, inset, ...props }: ContextMenuLabelProps) {\n return (\n <ContextMenuPrimitive.Label\n data-slot=\"context-menu-label\"\n data-inset={inset}\n className={cn(\"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nexport type ContextMenuSeparatorProps = React.ComponentProps<typeof ContextMenuPrimitive.Separator>;\n\nfunction ContextMenuSeparator({ className, ...props }: ContextMenuSeparatorProps) {\n return (\n <ContextMenuPrimitive.Separator\n data-slot=\"context-menu-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type ContextMenuShortcutProps = React.ComponentProps<\"span\">;\n\nfunction ContextMenuShortcut({ className, ...props }: ContextMenuShortcutProps) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuGroup,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuPortal,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/context-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as ContextMenuPrimitive from \"@radix-ui/react-context-menu\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * ContextMenu — menu chuột phải (Radix ContextMenu).\n *\n * Dùng cho thao tác nhanh trên thẻ task, dòng bảng, mục sidebar. Cấu trúc mục\n * giống `DropdownMenu` (cùng biến thể `destructive`, `inset`) để hai menu nhìn như một.\n * Vì chuột phải không có trên thiết bị cảm ứng, mọi thao tác ở đây phải có đường\n * thay thế (nút \"…\" mở DropdownMenu).\n */\n\nexport type ContextMenuProps = React.ComponentProps<typeof ContextMenuPrimitive.Root>;\n\nfunction ContextMenu({ ...props }: ContextMenuProps) {\n return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />;\n}\n\nexport type ContextMenuTriggerProps = React.ComponentProps<typeof ContextMenuPrimitive.Trigger>;\n\nfunction ContextMenuTrigger({ ...props }: ContextMenuTriggerProps) {\n return <ContextMenuPrimitive.Trigger data-slot=\"context-menu-trigger\" {...props} />;\n}\n\nexport type ContextMenuGroupProps = React.ComponentProps<typeof ContextMenuPrimitive.Group>;\n\nfunction ContextMenuGroup({ ...props }: ContextMenuGroupProps) {\n return <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />;\n}\n\nexport type ContextMenuPortalProps = React.ComponentProps<typeof ContextMenuPrimitive.Portal>;\n\nfunction ContextMenuPortal({ ...props }: ContextMenuPortalProps) {\n return <ContextMenuPrimitive.Portal data-slot=\"context-menu-portal\" {...props} />;\n}\n\nexport type ContextMenuSubProps = React.ComponentProps<typeof ContextMenuPrimitive.Sub>;\n\nfunction ContextMenuSub({ ...props }: ContextMenuSubProps) {\n return <ContextMenuPrimitive.Sub data-slot=\"context-menu-sub\" {...props} />;\n}\n\nexport type ContextMenuRadioGroupProps = React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>;\n\nfunction ContextMenuRadioGroup({ ...props }: ContextMenuRadioGroupProps) {\n return <ContextMenuPrimitive.RadioGroup data-slot=\"context-menu-radio-group\" {...props} />;\n}\n\nexport type ContextMenuSubTriggerProps = React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n};\n\nfunction ContextMenuSubTrigger({ className, inset, children, ...props }: ContextMenuSubTriggerProps) {\n return (\n <ContextMenuPrimitive.SubTrigger\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto\" />\n </ContextMenuPrimitive.SubTrigger>\n );\n}\n\nexport type ContextMenuSubContentProps = React.ComponentProps<typeof ContextMenuPrimitive.SubContent>;\n\nfunction ContextMenuSubContent({ className, ...props }: ContextMenuSubContentProps) {\n return (\n <ContextMenuPrimitive.SubContent\n data-slot=\"context-menu-sub-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type ContextMenuContentProps = React.ComponentProps<typeof ContextMenuPrimitive.Content>;\n\nfunction ContextMenuContent({ className, ...props }: ContextMenuContentProps) {\n return (\n <ContextMenuPrimitive.Portal>\n <ContextMenuPrimitive.Content\n data-slot=\"context-menu-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-overlay\",\n className\n )}\n {...props}\n />\n </ContextMenuPrimitive.Portal>\n );\n}\n\nexport type ContextMenuItemVariant = \"default\" | \"destructive\";\n\nexport type ContextMenuItemProps = React.ComponentProps<typeof ContextMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: ContextMenuItemVariant;\n};\n\nfunction ContextMenuItem({ className, inset, variant = \"default\", ...props }: ContextMenuItemProps) {\n return (\n <ContextMenuPrimitive.Item\n data-slot=\"context-menu-item\"\n data-inset={inset}\n data-variant={variant}\n // `text-destructive-ink` chứ KHÔNG `text-destructive`: `--destructive` là màu NỀN (nút xoá),\n // ghim một giá trị cho cả hai theme nên làm CHỮ trên nền popover tối chỉ được 3,53:1 ✗ —\n // và tệ thêm khi focus vì nền chuyển sang `bg-destructive/20` (2,98:1 ✗). Bậc ink đạt cả\n // hai theme, cả hai nền: 7,15:1 / 6,18:1 (light, nghỉ / focus) · 6,42:1 / 5,42:1 (dark).\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type ContextMenuCheckboxItemProps = React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>;\n\nfunction ContextMenuCheckboxItem({ className, children, checked, ...props }: ContextMenuCheckboxItemProps) {\n return (\n <ContextMenuPrimitive.CheckboxItem\n data-slot=\"context-menu-checkbox-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.CheckboxItem>\n );\n}\n\nexport type ContextMenuRadioItemProps = React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>;\n\nfunction ContextMenuRadioItem({ className, children, ...props }: ContextMenuRadioItemProps) {\n return (\n <ContextMenuPrimitive.RadioItem\n data-slot=\"context-menu-radio-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.RadioItem>\n );\n}\n\nexport type ContextMenuLabelProps = React.ComponentProps<typeof ContextMenuPrimitive.Label> & {\n inset?: boolean;\n};\n\nfunction ContextMenuLabel({ className, inset, ...props }: ContextMenuLabelProps) {\n return (\n <ContextMenuPrimitive.Label\n data-slot=\"context-menu-label\"\n data-inset={inset}\n className={cn(\"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nexport type ContextMenuSeparatorProps = React.ComponentProps<typeof ContextMenuPrimitive.Separator>;\n\nfunction ContextMenuSeparator({ className, ...props }: ContextMenuSeparatorProps) {\n return (\n <ContextMenuPrimitive.Separator\n data-slot=\"context-menu-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nexport type ContextMenuShortcutProps = React.ComponentProps<\"span\">;\n\nfunction ContextMenuShortcut({ className, ...props }: ContextMenuShortcutProps) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn(\"text-muted-foreground ml-auto text-xs tracking-widest\", className)}\n {...props}\n />\n );\n}\n\nexport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuGroup,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuPortal,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger\n};\n"],"mappings":"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","names":[]}
|
|
@@ -44,7 +44,7 @@ function DrawerContent({ className, children, ...props }) {
|
|
|
44
44
|
),
|
|
45
45
|
...props,
|
|
46
46
|
children: [
|
|
47
|
-
/* @__PURE__ */ jsx("div", { className: "bg-
|
|
47
|
+
/* @__PURE__ */ jsx("div", { className: "bg-control-edge mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
|
|
48
48
|
children
|
|
49
49
|
]
|
|
50
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/drawer.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Drawer — panel kéo-thả (vaul), có quán tính và tay cầm kéo như app di động.\n *\n * Khác `Sheet` (Radix Dialog, trượt cứng): Drawer cho phép người dùng vuốt để đóng,\n * hợp với màn hình cảm ứng. Hướng đặt bằng prop `direction` của vaul —\n * style bám theo thuộc tính `data-vaul-drawer-direction` mà vaul tự gắn.\n */\n\nexport type DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root>;\n\nfunction Drawer({ ...props }: DrawerProps) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />;\n}\n\nexport type DrawerTriggerProps = React.ComponentProps<typeof DrawerPrimitive.Trigger>;\n\nfunction DrawerTrigger({ ...props }: DrawerTriggerProps) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\n}\n\nexport type DrawerPortalProps = React.ComponentProps<typeof DrawerPrimitive.Portal>;\n\nfunction DrawerPortal({ ...props }: DrawerPortalProps) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\n}\n\nexport type DrawerCloseProps = React.ComponentProps<typeof DrawerPrimitive.Close>;\n\nfunction DrawerClose({ ...props }: DrawerCloseProps) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\n}\n\nexport type DrawerOverlayProps = React.ComponentProps<typeof DrawerPrimitive.Overlay>;\n\nfunction DrawerOverlay({ className, ...props }: DrawerOverlayProps) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-ink/60 dark:bg-ink/80 backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerContentProps = React.ComponentProps<typeof DrawerPrimitive.Content>;\n\nfunction DrawerContent({ className, children, ...props }: DrawerContentProps) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n \"group/drawer-content bg-background fixed z-50 flex h-auto flex-col\",\n \"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b\",\n \"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t\",\n \"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n \"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n {
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/drawer.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Drawer — panel kéo-thả (vaul), có quán tính và tay cầm kéo như app di động.\n *\n * Khác `Sheet` (Radix Dialog, trượt cứng): Drawer cho phép người dùng vuốt để đóng,\n * hợp với màn hình cảm ứng. Hướng đặt bằng prop `direction` của vaul —\n * style bám theo thuộc tính `data-vaul-drawer-direction` mà vaul tự gắn.\n */\n\nexport type DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root>;\n\nfunction Drawer({ ...props }: DrawerProps) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />;\n}\n\nexport type DrawerTriggerProps = React.ComponentProps<typeof DrawerPrimitive.Trigger>;\n\nfunction DrawerTrigger({ ...props }: DrawerTriggerProps) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />;\n}\n\nexport type DrawerPortalProps = React.ComponentProps<typeof DrawerPrimitive.Portal>;\n\nfunction DrawerPortal({ ...props }: DrawerPortalProps) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />;\n}\n\nexport type DrawerCloseProps = React.ComponentProps<typeof DrawerPrimitive.Close>;\n\nfunction DrawerClose({ ...props }: DrawerCloseProps) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />;\n}\n\nexport type DrawerOverlayProps = React.ComponentProps<typeof DrawerPrimitive.Overlay>;\n\nfunction DrawerOverlay({ className, ...props }: DrawerOverlayProps) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-ink/60 dark:bg-ink/80 backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerContentProps = React.ComponentProps<typeof DrawerPrimitive.Content>;\n\nfunction DrawerContent({ className, children, ...props }: DrawerContentProps) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n \"group/drawer-content bg-background fixed z-50 flex h-auto flex-col\",\n \"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b\",\n \"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t\",\n \"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n \"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n {/*\n Tay cầm kéo — chỉ hiện khi drawer trượt từ đáy lên.\n Màu `--control-edge`, KHÔNG phải `--muted`: mảng `bg-*` này CHÍNH LÀ hình dạng của dấu\n duy nhất nói \"kéo ở đây\", nên nó chịu ngưỡng 3:1 (WCAG 1.4.11) chứ không phải là một\n bề mặt. `bg-muted` trên `bg-background` của DrawerContent chỉ 1,07:1 (light) · 1,17:1\n (dark) — coi như không hiện. `bg-control-edge`: 3,83:1 (light) · 4,94:1 (dark) ✓\n */}\n <div className=\"bg-control-edge mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n}\n\nexport type DrawerHeaderProps = React.ComponentProps<\"div\">;\n\nfunction DrawerHeader({ className, ...props }: DrawerHeaderProps) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\n \"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport type DrawerFooterProps = React.ComponentProps<\"div\">;\n\nfunction DrawerFooter({ className, ...props }: DrawerFooterProps) {\n return <div data-slot=\"drawer-footer\" className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)} {...props} />;\n}\n\nexport type DrawerTitleProps = React.ComponentProps<typeof DrawerPrimitive.Title>;\n\nfunction DrawerTitle({ className, ...props }: DrawerTitleProps) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\"text-foreground font-semibold\", className)}\n {...props}\n />\n );\n}\n\nexport type DrawerDescriptionProps = React.ComponentProps<typeof DrawerPrimitive.Description>;\n\nfunction DrawerDescription({ className, ...props }: DrawerDescriptionProps) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger\n};\n"],"mappings":";AAiBS,cA0CH,YA1CG;AAdT,SAAS,UAAU,uBAAuB;AAC1C,SAAS,UAAU;AAYnB,SAAS,OAAO,EAAE,GAAG,MAAM,GAAgB;AACzC,SAAO,oBAAC,gBAAgB,MAAhB,EAAqB,aAAU,UAAU,GAAG,OAAO;AAC7D;AAIA,SAAS,cAAc,EAAE,GAAG,MAAM,GAAuB;AACvD,SAAO,oBAAC,gBAAgB,SAAhB,EAAwB,aAAU,kBAAkB,GAAG,OAAO;AACxE;AAIA,SAAS,aAAa,EAAE,GAAG,MAAM,GAAsB;AACrD,SAAO,oBAAC,gBAAgB,QAAhB,EAAuB,aAAU,iBAAiB,GAAG,OAAO;AACtE;AAIA,SAAS,YAAY,EAAE,GAAG,MAAM,GAAqB;AACnD,SAAO,oBAAC,gBAAgB,OAAhB,EAAsB,aAAU,gBAAgB,GAAG,OAAO;AACpE;AAIA,SAAS,cAAc,EAAE,WAAW,GAAG,MAAM,GAAuB;AAClE,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,cAAc,EAAE,WAAW,UAAU,GAAG,MAAM,GAAuB;AAC5E,SACE,qBAAC,gBAAa,aAAU,iBACtB;AAAA,wBAAC,iBAAc;AAAA,IACf;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QASJ;AAAA,8BAAC,SAAI,WAAU,0IAAyI;AAAA,UACvJ;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAIA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,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,aAAa,EAAE,WAAW,GAAG,MAAM,GAAsB;AAChE,SAAO,oBAAC,SAAI,aAAU,iBAAgB,WAAW,GAAG,mCAAmC,SAAS,GAAI,GAAG,OAAO;AAChH;AAIA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAqB;AAC9D,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iCAAiC,SAAS;AAAA,MACvD,GAAG;AAAA;AAAA,EACN;AAEJ;AAIA,SAAS,kBAAkB,EAAE,WAAW,GAAG,MAAM,GAA2B;AAC1E,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,iCAAiC,SAAS;AAAA,MACvD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -37,7 +37,7 @@ function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
|
|
|
37
37
|
"data-inset": inset,
|
|
38
38
|
"data-variant": variant,
|
|
39
39
|
className: cn(
|
|
40
|
-
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_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]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
40
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-ink data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive-ink data-[variant=destructive]:*:[svg]:!text-destructive-ink [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
41
41
|
className
|
|
42
42
|
),
|
|
43
43
|
...props
|