@comitor/ui 3.3.1 → 3.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/composite/command-palette.d.ts +1 -1
- package/dist/components/composite/command-palette.js.map +1 -1
- package/dist/components/composite/data-table.d.ts +10 -5
- package/dist/components/composite/data-table.js +2 -2
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/filter-chips.d.ts +1 -1
- package/dist/components/composite/filter-chips.js.map +1 -1
- package/dist/components/composite/inline-edit.d.ts +2 -2
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/route-tabs.d.ts +8 -6
- package/dist/components/composite/route-tabs.js.map +1 -1
- package/dist/components/composite/status-config.d.ts +1 -1
- package/dist/components/composite/status-config.js.map +1 -1
- package/dist/components/ui/progress.d.ts +14 -2
- package/dist/components/ui/progress.js +12 -3
- package/dist/components/ui/progress.js.map +1 -1
- package/dist/components/ui/resizable.d.ts +1 -1
- package/dist/components/ui/resizable.js.map +1 -1
- package/dist/data-grid/data-grid-context-menu.js +3 -2
- package/dist/data-grid/data-grid-context-menu.js.map +1 -1
- package/dist/data-grid/data-grid-row-height-menu.js +2 -2
- package/dist/data-grid/data-grid-row-height-menu.js.map +1 -1
- package/dist/data-grid/types.d.ts +7 -2
- package/dist/data-grid/use-data-grid.d.ts +14 -4
- package/dist/data-grid/use-data-grid.js +5 -3
- package/dist/data-grid/use-data-grid.js.map +1 -1
- package/dist/data-grid/utils.js +0 -1
- package/dist/data-grid/utils.js.map +1 -1
- package/dist/i18n/namespaces/combobox.js +2 -2
- package/dist/i18n/namespaces/combobox.js.map +1 -1
- package/dist/i18n/namespaces/confirm-dialog.js +1 -1
- package/dist/i18n/namespaces/confirm-dialog.js.map +1 -1
- package/dist/i18n/namespaces/date-picker.js +2 -2
- package/dist/i18n/namespaces/date-picker.js.map +1 -1
- package/dist/i18n/namespaces/inline-edit.js +1 -1
- package/dist/i18n/namespaces/inline-edit.js.map +1 -1
- package/dist/i18n/namespaces/search-filter-bar.js +2 -2
- package/dist/i18n/namespaces/search-filter-bar.js.map +1 -1
- package/dist/i18n/namespaces/time-picker.js +1 -1
- package/dist/i18n/namespaces/time-picker.js.map +1 -1
- package/dist/shell/app-launcher.d.ts +2 -2
- package/dist/shell/app-launcher.js.map +1 -1
- package/dist/shell/app-shell.d.ts +1 -1
- package/dist/shell/app-shell.js.map +1 -1
- package/dist/shell/comitor-logo.d.ts +1 -1
- package/dist/shell/comitor-logo.js.map +1 -1
- package/dist/shell/shell-context.d.ts +3 -2
- package/dist/shell/shell-context.js.map +1 -1
- package/dist/shell/sidebar-group.d.ts +1 -1
- package/dist/shell/sidebar-group.js.map +1 -1
- package/dist/shell/types.d.ts +1 -1
- package/dist/shell/types.js.map +1 -1
- package/dist/shell/utils.d.ts +2 -2
- package/dist/shell/utils.js.map +1 -1
- package/dist/tokens.d.ts +23 -9
- package/dist/tokens.js +17 -9
- package/dist/tokens.js.map +1 -1
- package/dist/uploader/image-upload-field.d.ts +2 -2
- package/dist/uploader/image-upload-field.js.map +1 -1
- package/package.json +1 -1
package/dist/tokens.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * Design token dạng TS object — dành cho nơi KHÔNG có Tailwind: canvas, biểu đồ tuỳ biến,\n * email HTML, xuất PDF, (sau này) React Native.\n *\n * ⚠ Nguồn sự thật của giá trị là `styles.css` (CSS variable). File này chỉ MIRROR hằng số.\n * Sửa màu ở styles.css thì phải sửa kèm ở đây — và ngược lại.\n *\n * Trong môi trường có Tailwind, LUÔN dùng class token (`bg-primary`, `text-ash-500`),\n * đừng đọc hex từ đây rồi nhét vào style inline: làm vậy là mất dark mode.\n *\n * Hợp đồng ba vai trò của màu (giống hệt `styles.css`; bảng đầy đủ ở docs/hop-dong-mau.md),\n * đọc tên là biết dùng ở đâu:\n * `x` → tô NỀN (fill đặc hoặc tint /5–/20 — trần chung an toàn cho mọi tone)\n * `xForeground` → chữ/icon NẰM TRÊN nền `x` ĐẶC — đo với `x`, ngưỡng 4,5:1\n * `xInk` → chính màu đó khi ĐI LÊN nền trang hoặc lên tint của chính nó (chữ, icon\n * rời, viền, vạch active) — đo với nền đó\n *\n * Cố ý KHÔNG mirror (xem docs/quyet-dinh-tich-hop.md §21): `--elevation-*` (chuỗi box-shadow chỉ\n * có nghĩa trong CSS), `--app-accent*` (giá trị thuộc về APP, không phải gói), `--font-*`.\n */\n\nimport { AVATAR_TONES, type AvatarToneColors, type AvatarTonePalette, getAvatarToneIndex } from \"./lib/avatar-tone.js\";\n\n/** Thang GOLD 7 bậc — trục màu chính của brand Comitor. */\nexport const gold = {\n 50: \"#FBF5DC\",\n 100: \"#F8E89D\",\n 200: \"#F5D060\",\n /** primary ở light mode */\n 300: \"#E8B824\",\n 400: \"#B88A00\",\n 500: \"#7A5800\",\n 600: \"#5C4006\"\n} as const;\n\n/** Trung tính brand (khác thang ash — đây là màu \"giấy/mực\" của brand book). */\nexport const brandNeutral = {\n ink: \"#0E1116\",\n inkSoft: \"#3A3F4A\",\n paper: \"#FBF7EE\",\n warm: \"#EDE8D8\"\n} as const;\n\n/**\n * Thang xám Comitor — namespace `ash`, KHÔNG phải `gray`.\n * Chỉ có 6 bậc; các bậc khuyết (200/400/600/800) cố ý không khai để không lẫn với\n * thang gray dựng sẵn của Tailwind (xem chú thích trong styles.css).\n */\nexport const ash = {\n 50: \"#F7F7F8\",\n 100: \"#EDEEF1\",\n 300: \"#C8CBD3\",\n 500: \"#7E8290\",\n 700: \"#3A3F4A\",\n 900: \"#16181D\"\n} as const;\n\n/**\n * Màu phụ — vai trò TÔ NỀN (fill đặc hoặc tint). Bản `dark` được nâng sáng vì trên nền #0E1116\n * bản `light` chìm nghỉm.\n *\n * ⚠ ĐỪNG dùng bảng này làm màu CHỮ ở light: nó được chọn để tô nền nên quá nhạt — dùng\n * `accentInk` ngay dưới. `accent.dark.navy` giữ nguyên #0F1E3D chỉ để mirror đúng `styles.css`\n * (khối `.dark` không override `--navy`); vai trò navy ở dark do `accentInk.dark.navy` gánh.\n */\nexport const accent = {\n light: {\n navy: \"#0F1E3D\",\n teal: \"#1F8A8B\",\n red: \"#D64545\",\n green: \"#2EA56A\",\n yellow: \"#F2C94C\",\n /** Gốc của tone `severe`. Giữ 3:1 trên mọi nền trang (3,56:1 trắng · 3,32:1 `muted`) — vì nó\n * TÔ chấm của `StatusPill variant=\"dot\"`; bậc sáng hơn (#F97316) chỉ 2,80:1. */\n orange: \"#EA580C\"\n },\n /** Bảng MẶC ĐỊNH (comitor-ds) không khai lại màu phụ ở dark — cùng hex với light. Bản sáng\n * dành cho nền tối nằm ở `highContrast.dark`. Trừ `orange`: comitor-ds không có cam, và cùng\n * hex thì chữ cam trên tint /15 của chính nó ở dark chỉ 4,21:1 — nên bảng mặc định có bản\n * dark riêng. */\n dark: {\n navy: \"#0F1E3D\",\n teal: \"#1F8A8B\",\n red: \"#D64545\",\n green: \"#2EA56A\",\n yellow: \"#F2C94C\",\n orange: \"#FB923C\"\n }\n} as const;\n\n/**\n * Bậc INK của màu phụ — mirror `--teal-ink` / `--green-ink` / `--red-ink` / `--yellow-ink` /\n * `--orange-ink` / `--navy-ink`. Đây là màu phụ khi nó làm **CHỮ/ICON/VIỀN trên nền trang**\n * (hoặc trên tint /5–/20 của chính nó), tức vai trò NGƯỢC với `accent`.\n *\n * Hợp đồng đặt tên của cả gói: `--x` tô nền · `--x-foreground` là chữ NẰM TRÊN nền `--x` ·\n * `--x-ink` là chính màu đó ĐI LÊN nền trang.\n *\n * Bản `light` là bậc tối đã đóng băng (kết quả của `color-mix(in srgb, --<màu>, --ink 30/40%)`\n * mà trước đây mỗi component tự tính lấy); bản `dark` chính là `accent.dark`. Đo trên tint 15%\n * của chính nó — nền thật mà chữ initials avatar và badge tonal đứng lên: teal 5,54 · green 5,60\n * · red 5,83:1 ở light (bản chưa có ink: 3,44 / 2,69 / 3,57:1 ✗).\n *\n * ⚠ Ở bảng MẶC ĐỊNH, `accent.dark` bằng hex của light nên bậc ink ở dark cũng vậy — đúng thứ\n * comitor-ds render. Bậc sáng cho nền tối nằm ở `highContrast.dark`.\n */\nexport const accentInk = {\n light: {\n navy: accent.light.navy,\n teal: \"#1A6668\",\n red: \"#9A3537\",\n green: \"#216A48\",\n /** = gold[500]: vàng không có bậc tối riêng, mượn thang gold cùng sắc. */\n yellow: gold[500],\n /** = color-mix(srgb, orange 72%, ink 28%) — bậc SÁNG nhất còn giữ trần tint /20: 5,85:1 trắng ·\n * 4,87:1 trên orange/15 · 4,58:1 trên orange/20. Bảng tương phản cao tối hơn: `highContrast`. */\n orange: \"#AC440F\"\n },\n dark: {\n /** navy nguyên bản chìm vào nền dark (1,15:1) → bậc sáng riêng; cũng là `--chart-3` ở dark. */\n navy: \"#7EA2E8\",\n teal: accent.dark.teal,\n red: accent.dark.red,\n green: accent.dark.green,\n yellow: accent.dark.yellow,\n orange: accent.dark.orange\n }\n} as const;\n\n/**\n * Bậc FOREGROUND của màu phụ — mirror `--teal-foreground` / `--green-foreground` /\n * `--yellow-foreground` / `--orange-foreground` / `--navy-foreground`. Đây là chữ/icon nằm\n * **TRÊN nền `accent.*` ĐẶC**, tức vai trò thứ ba, khác hẳn `accentInk` (màu đó đi lên nền TRANG).\n *\n * Cả bốn sắc teal/green/yellow/orange đều sáng ở light và còn sáng hơn ở dark → chữ là MỰC ở cả\n * hai theme, một giá trị đủ dùng: teal 4,56:1 (light) / 10,16:1 (dark) · green 6,04 / 10,85 ·\n * yellow 11,92 / 12,35 · orange 5,31 / 8,36. Navy thì ngược lại — chữ TRẮNG, 16,49:1 ở cả hai theme.\n *\n * ⚠ CỐ Ý KHÔNG có `red`: nền đỏ đặc không mang được chữ tốt ở light — trắng trên #D64545 được\n * 4,38:1 và mực 4,32:1, cả hai đều dưới AA. Nền đỏ đặc có chữ phải dùng `status.*.destructive`;\n * ở bảng tương phản cao token đó là #C93B3B và đạt 5,03:1. `accent.*.red` chỉ để tô TINT và làm\n * màu chart. (Cam thì mực cứu được — 5,31:1 — nên `orange` có mặt.)\n */\nexport const accentForeground = {\n light: {\n navy: \"#FFFFFF\",\n teal: \"#0E1116\",\n green: \"#0E1116\",\n yellow: \"#0E1116\",\n orange: \"#0E1116\"\n },\n dark: {\n navy: \"#FFFFFF\",\n teal: \"#0E1116\",\n green: \"#0E1116\",\n yellow: \"#0E1116\",\n orange: \"#0E1116\"\n }\n} as const;\n\n/** Semantic token của shadcn — mirror khối `:root` / `.dark` trong styles.css. */\nexport const semantic = {\n light: {\n background: \"#FFFFFF\",\n foreground: \"#0E1116\",\n card: \"#FFFFFF\",\n cardForeground: \"#0E1116\",\n popover: \"#FFFFFF\",\n popoverForeground: \"#0E1116\",\n primary: \"#E8B824\",\n primaryForeground: \"#0E1116\",\n /** Bậc INK của brand. Ở bảng MẶC ĐỊNH nó BẰNG `primary`: comitor-ds tô gold cho cả những vai\n * trò mà 1.4.11 đòi 3:1 (rãnh Switch BẬT, gạch active của Tabs) — 1,86:1, đã biết và chấp\n * nhận. Bậc tối #7A5800 nằm ở `highContrast.light.primaryInk`. */\n primaryInk: \"#E8B824\",\n secondary: \"#EDE8D8\",\n secondaryForeground: \"#0E1116\",\n muted: \"#F7F7F8\",\n /** Bậc chữ phụ — giá trị của comitor-ds, bằng đúng `ash[500]`. 3,83:1 trên nền trắng và\n * 3,58:1 trên `muted`, tức dưới AA 4,5:1: đã biết và chấp nhận, bản đạt chuẩn nằm ở\n * `highContrast.light.mutedForeground`. */\n mutedForeground: ash[500],\n accent: \"#FBF5DC\",\n accentForeground: \"#0E1116\",\n /** Nền nút xoá. Chữ trắng trên nó được 4,38:1 — thiếu 0,12 so với AA, đã biết và chấp nhận. */\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n /** Nền nút xoá lúc HOVER = `destructive` ở 90% trên nền trắng, đúng thứ `hover:bg-red/90`\n * của comitor-ds render. Ghim hex vì `/90` phụ thuộc nền phía sau. */\n destructiveHover: \"#DA5A5A\",\n /** Chữ báo lỗi trên nền trang. comitor-ds viết `text-destructive` cho vai trò này, tức cùng\n * hex với nền — 4,38:1 trên nền trắng. */\n destructiveInk: \"#D64545\",\n /** RANH GIỚI TRANG TRÍ (vạch chia, viền thẻ nghỉ) — 1,16:1, cố ý: 1.4.11 không đòi tương\n * phản cho thứ không mang thông tin. Ranh giới của CONTROL đi qua `controlEdge`. */\n border: \"#EDEEF1\",\n /** Viền ô nhập — comitor-ds cho nó cùng bậc với `border`. 1,16:1, đã biết và chấp nhận. */\n input: \"#EDEEF1\",\n ring: \"#E8B824\",\n /** Ranh giới của control lúc NGHỈ — ở bảng mặc định bằng đúng `input`. */\n controlEdge: \"#EDEEF1\",\n /** Cùng vai trò nhưng ở HOVER/ACTIVE. comitor-ds dùng `hover:border-gray-300` = `ash[300]`. */\n controlEdgeStrong: ash[300],\n /** Nền control khi BẬT/ĐÃ CHỌN (rãnh Switch bật, ô Checkbox đã chọn, chấm Radio). comitor-ds\n * tô gold: 1,86:1 với nền trang và với núm — đã biết và chấp nhận. */\n controlOn: \"#E8B824\",\n /** Núm Switch / chấm Radio — TRẮNG ở cả hai theme, đúng `bg-white` của comitor-ds. */\n controlKnob: \"#FFFFFF\",\n /** DẤU CHECK của Checkbox và chữ trên ngày ĐANG CHỌN của Calendar — tách khỏi `controlKnob`\n * vì trên nền gold SÁNG thì dấu phải là MỰC (10,19:1), còn ở bảng tương phản cao nền là\n * gold-500 TỐI nên dấu lật thành màu nền trang. */\n controlCheck: brandNeutral.ink,\n /** Rãnh Switch khi TẮT — comitor-ds dùng `bg-gray-300` / `dark:bg-gray-700`. */\n switchTrack: ash[300],\n /** Rãnh Slider — comitor-ds dùng `bg-muted`, khác bậc với rãnh Switch. */\n sliderTrack: \"#F7F7F8\",\n /** Button variant `link`. comitor-ds dùng `gold[400]` — 3,15:1, dưới AA, đã biết và chấp nhận. */\n linkInk: gold[400],\n linkInkHover: gold[300],\n sidebar: \"#FAFAF9\",\n sidebarForeground: \"#0E1116\",\n sidebarBorder: \"#EDEEF1\",\n sidebarAccent: \"#FBF5DC\",\n sidebarAccentForeground: \"#0E1116\"\n },\n dark: {\n background: \"#0E1116\",\n foreground: \"#F7F7F8\",\n card: \"#16181D\",\n cardForeground: \"#F7F7F8\",\n popover: \"#16181D\",\n popoverForeground: \"#F7F7F8\",\n primary: \"#F5D060\",\n primaryForeground: \"#0E1116\",\n /** Ở dark `primary` vốn đã là bậc sáng (12,67:1 trên nền `background`) nên ink = chính nó. */\n primaryInk: \"#F5D060\",\n secondary: \"#1E2128\",\n secondaryForeground: \"#F7F7F8\",\n muted: \"#1E2128\",\n /** comitor-ds không khai lại token này ở dark — cùng hex với light. 4,21:1 trên `muted`,\n * dưới AA: đã biết và chấp nhận. */\n mutedForeground: ash[500],\n accent: \"#1E2128\",\n accentForeground: \"#F7F7F8\",\n /** Ghim đúng giá trị của light — comitor-ds dùng chung một hex cho cả hai theme. */\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n destructiveHover: \"#DA5A5A\",\n /** 4,06:1 trên `card` — cùng hex với nền, đúng `text-destructive` của comitor-ds. */\n destructiveInk: \"#D64545\",\n /** TRANG TRÍ, 1,17:1 trên `background` và 1,10:1 trên `card` — cố ý, xem nhánh light. */\n border: \"#1E2128\",\n /** Viền ô nhập bản dark của comitor-ds — cùng bậc với `border`. */\n input: \"#1E2128\",\n ring: \"#F5D060\",\n controlEdge: \"#1E2128\",\n /** comitor-ds dùng `dark:hover:border-gray-500` = `ash[500]` (lật chiều so với light). */\n controlEdgeStrong: ash[500],\n /** comitor-ds tô `dark:data-[state=checked]:bg-gold-300`, nhưng token `primary` của chính nó\n * ở dark là #F5D060; gói theo token, nên control BẬT ở dark là bậc sáng. */\n controlOn: \"#F5D060\",\n /** TRẮNG ở cả hai theme, đúng `bg-white` của comitor-ds. */\n controlKnob: \"#FFFFFF\",\n /** comitor-ds vẽ dấu check bằng `text-ink` ở CẢ hai theme. */\n controlCheck: brandNeutral.ink,\n switchTrack: ash[700],\n sliderTrack: \"#1E2128\",\n linkInk: gold[400],\n linkInkHover: gold[300],\n sidebar: \"#16181D\",\n sidebarForeground: \"#F7F7F8\",\n sidebarBorder: \"#1E2128\",\n sidebarAccent: \"#1E2128\",\n sidebarAccentForeground: \"#F7F7F8\"\n }\n} as const;\n\n/**\n * BẢNG MÀU THỨ HAI — TƯƠNG PHẢN CAO. Mirror khối `[data-contrast=\"high\"]` /\n * `.dark[data-contrast=\"high\"]` trong styles.css.\n *\n * Chỉ chứa những token KHÁC bảng mặc định, đúng như CSS: hợp lên trên để có bảng đầy đủ.\n *\n * ```ts\n * const mau = { ...semantic.light, ...highContrast.light };\n * ```\n *\n * Bảng mặc định là comitor-ds (bản duyệt, có những cặp dưới ngưỡng — đã liệt kê ở từng token).\n * Bảng này là bảng đã đo, do NGƯỜI DÙNG bật qua `<ContrastProvider>` + `<ContrastToggle>`.\n * `accent` là màu phụ bản sáng dành cho nền tối — bảng mặc định không có, xem `accent.dark`.\n */\nexport const highContrast = {\n light: {\n /** 5,23:1 trên `background` · 4,89:1 trên `muted` (bảng mặc định: 3,83 / 3,58) */\n mutedForeground: \"#686C7A\",\n /** Chữ trắng trên nền này được 5,03:1 (bảng mặc định #D64545 chỉ 4,38:1) */\n destructive: \"#C93B3B\",\n /** = color-mix(destructive, ink 15%) — ĐẬM thêm khi hover, ngược chiều bảng mặc định. */\n destructiveHover: \"#AD3535\",\n /** 7,15:1 trên nền trắng (bảng mặc định dùng chính #D64545, 4,38:1) */\n destructiveInk: accentInk.light.red,\n /** 3,83:1 trên nền trắng (bảng mặc định #EDEEF1 chỉ 1,16:1) */\n input: \"#7E8290\",\n controlEdge: \"#7E8290\",\n controlEdgeStrong: ash[700],\n /** Rãnh ⇄ nền 6,51:1 và rãnh ⇄ núm 6,51:1 (bảng mặc định: 1,86 / 1,86) */\n controlOn: gold[500],\n /** = `background`, nên luôn tương phản đúng chiều với cả rãnh BẬT lẫn rãnh TẮT. */\n controlKnob: \"#FFFFFF\",\n controlCheck: \"#FFFFFF\",\n switchTrack: \"#7E8290\",\n sliderTrack: \"#7E8290\",\n primaryInk: gold[500],\n linkInk: gold[500],\n linkInkHover: gold[600],\n /** = `--orange-ink` của khối này — bậc ink màu phụ DUY NHẤT mà bảng này đổi ở light:\n * color-mix(srgb, orange 65%, ink 35%), 6,66:1 trắng · 5,55:1 trên orange/15 (bảng mặc định\n * #AC440F: 5,85 / 4,87:1). */\n severeInk: \"#9D3F10\",\n accent: accent.light\n },\n dark: {\n /** 5,70:1 trên `background` · 5,35:1 trên `card` · 4,86:1 trên `muted` */\n mutedForeground: \"#888D9B\",\n destructive: \"#C93B3B\",\n destructiveHover: \"#AD3535\",\n destructiveInk: \"#F87171\",\n /** Ghim một giá trị cho cả hai theme — 4,94:1 trên `background` */\n input: \"#7E8290\",\n controlEdge: \"#7E8290\",\n controlEdgeStrong: ash[300],\n /** 12,67:1 với nền trang VÀ với `controlKnob` (#0E1116) */\n controlOn: \"#F5D060\",\n controlKnob: \"#0E1116\",\n controlCheck: \"#0E1116\",\n switchTrack: \"#7E8290\",\n sliderTrack: \"#7E8290\",\n primaryInk: \"#F5D060\",\n linkInk: gold[200],\n linkInkHover: gold[100],\n /** Bằng bảng mặc định (8,36:1 trên `background` · 6,09:1 trên orange/15) — khai cho đủ cặp\n * với `light`, vì CSS phải huỷ bậc tối của khối light ở đây. */\n severeInk: accent.dark.orange,\n /** Màu phụ bản SÁNG cho nền tối — bảng mặc định không có bản dark cho nhóm này (trừ `orange`,\n * vốn đã sáng ở `accent.dark`). */\n accent: {\n navy: \"#0F1E3D\",\n teal: \"#2DD4BF\",\n red: \"#F87171\",\n green: \"#4ADE80\",\n yellow: \"#FACC15\",\n orange: accent.dark.orange\n }\n }\n} as const;\n\n/**\n * Alias trạng thái — success/warning/severe/info trỏ vào màu phụ, destructive giữ nguyên 2 chế độ.\n * `severe` (cam) là nấc giữa warning và destructive: ưu tiên cao, SLA sắp trễ.\n *\n * Ba lớp KHÁC nhau, đừng gộp:\n * • `destructive` — màu NỀN (nút xoá): #D64545 ở bảng mặc định (chữ trắng 4,38:1, dưới AA và đã\n * chấp nhận), #C93B3B ở bảng tương phản cao (5,03:1 ✓);\n * • `destructiveForeground` (#FFFFFF) — chữ NẰM TRÊN nền đó;\n * • `destructiveInk` — màu CHỮ đỏ trên nền trang (dòng lỗi). Bảng mặc định cho nó cùng hex với\n * nền (đúng `text-destructive` của comitor-ds); bảng tương phản cao tách thành bậc tối riêng.\n *\n * `successForeground` / `warningForeground` / `infoForeground` là MỰC ở CẢ hai theme (không phải\n * trắng ở light rồi lật ở dark như bản 0.1.0): ba nền trạng thái đều là sắc sáng.\n * success mực 6,04:1 (light) · 10,85:1 (dark) [trắng chỉ 3,13 / 1,74:1 ✗]\n * info mực 4,56:1 (light) · 10,16:1 (dark) [trắng chỉ 4,14 / 1,86:1 ✗]\n * warning mực 11,92:1 (light) · 12,35:1 (dark) [trắng chỉ 1,59 / 1,53:1 ✗]\n * severe mực 5,31:1 (light) · 8,36:1 (dark) [trắng chỉ 3,56 / 2,26:1 ✗]\n * ⚠ `info` ở light là cặp chặt nhất gói (4,56:1, dư 0,06) — đổi `accent.light.teal` là phải đo lại.\n */\nexport const status = {\n light: {\n success: accent.light.green,\n successForeground: \"#0E1116\",\n warning: accent.light.yellow,\n warningForeground: \"#0E1116\",\n severe: accent.light.orange,\n severeForeground: \"#0E1116\",\n info: accent.light.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n successInk: accentInk.light.green,\n warningInk: accentInk.light.yellow,\n severeInk: accentInk.light.orange,\n infoInk: accentInk.light.teal,\n destructiveInk: accentInk.light.red\n },\n dark: {\n success: accent.dark.green,\n successForeground: \"#0E1116\",\n warning: accent.dark.yellow,\n warningForeground: \"#0E1116\",\n severe: accent.dark.orange,\n severeForeground: \"#0E1116\",\n info: accent.dark.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n successInk: accentInk.dark.green,\n warningInk: accentInk.dark.yellow,\n severeInk: accentInk.dark.orange,\n infoInk: accentInk.dark.teal,\n destructiveInk: accentInk.dark.red\n }\n} as const;\n\n/** Bảng màu biểu đồ (chart-1…chart-5). Thứ tự = thứ tự series mặc định. */\nexport const chart = {\n light: [gold[300], accent.light.teal, accent.light.navy, accent.light.green, accent.light.red],\n dark: [gold[200], accent.dark.teal, accentInk.dark.navy, accent.dark.green, accent.dark.red]\n} as const;\n\n/**\n * Màu avatar chữ cái (hex đục) cho nơi KHÔNG có Tailwind: canvas, email HTML, PDF.\n *\n * ⚠ KHÔNG phải bảng màu thứ hai. Bảng tone DUY NHẤT của cả gói nằm ở `lib/avatar-tone.ts`, mỗi\n * tone mang cả chuỗi class (cho DOM) lẫn công thức hex tương ứng; ở đây chỉ nạp bảng màu vào công\n * thức đó. Web và email vì vậy dùng chung một thứ tự tone, một hàm băm, một cách chuẩn hoá tên —\n * không còn hai bảng để lệch nhau.\n */\nconst avatarTonePalette = {\n light: {\n gold,\n ash,\n brandNeutral,\n accent: accent.light,\n accentInk: accentInk.light,\n card: semantic.light.card\n },\n dark: {\n gold,\n ash,\n brandNeutral,\n accent: accent.dark,\n accentInk: accentInk.dark,\n card: semantic.dark.card\n }\n} satisfies Record<ThemeMode, AvatarTonePalette>;\n\n/**\n * Cặp nền/chữ (hex) của avatar cho một tên, theo theme.\n *\n * Tên hàm nói đúng thứ nó trả về: `...Colors` = hex (canvas/email/PDF), còn bản trả về chuỗi\n * class Tailwind cho DOM là `getAvatarToneClasses` ở `@comitor/ui`. Cùng một tên người luôn ra\n * cùng một tone ở cả hai đường vì chỉ số do MỘT hàm tính (`getAvatarToneIndex`).\n */\nexport function getAvatarToneColors(name: string, mode: ThemeMode = \"light\"): AvatarToneColors {\n // `noUncheckedIndexedAccess` bật → phải có nhánh lùi, dù chỉ số luôn nằm trong [0, 8).\n const tone = AVATAR_TONES[getAvatarToneIndex(name)] ?? AVATAR_TONES[0];\n return tone[mode](avatarTonePalette[mode]);\n}\n\nexport type { AvatarToneColors };\nexport { getAvatarToneIndex };\n\n/** Số đo khung ứng dụng — mirror --sidebar-width / --sidebar-width-icon / --header-height. */\nexport const layout = {\n /** px — --header-height 3.5rem */\n headerHeight: 56,\n /** px — --sidebar-width 16rem */\n sidebarWidth: 256,\n /** px — --sidebar-width-icon 3.5rem */\n sidebarCollapsedWidth: 56,\n /** px — --radius 0.375rem */\n radius: 6,\n breakpoints: { mobile: 768, tablet: 1024, desktop: 1280 },\n /** px — kích thước tối thiểu của vùng chạm trên mobile (WCAG 2.5.5) */\n minTapTarget: 44\n} as const;\n\n/** Font stack — app phải tự nạp Inter (subsets latin + vietnamese) và gán --font-inter. */\nexport const typography = {\n fontSans: '\"Inter\", system-ui, sans-serif',\n fontMono: '\"Geist Mono\", ui-monospace, monospace'\n} as const;\n\nexport type ThemeMode = \"light\" | \"dark\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/tokens.ts"],"sourcesContent":["/**\n * Design token dạng TS object — dành cho nơi KHÔNG có Tailwind: canvas, biểu đồ tuỳ biến,\n * email HTML, xuất PDF, (sau này) React Native.\n *\n * ⚠ Nguồn sự thật của giá trị là `styles.css` (CSS variable). File này chỉ MIRROR hằng số.\n * Sửa màu ở styles.css thì phải sửa kèm ở đây — và ngược lại.\n *\n * Trong môi trường có Tailwind, LUÔN dùng class token (`bg-primary`, `text-ash-500`),\n * đừng đọc hex từ đây rồi nhét vào style inline: làm vậy là mất dark mode.\n *\n * Hợp đồng ba vai trò của màu (giống hệt `styles.css`; bảng đầy đủ ở docs/hop-dong-mau.md),\n * đọc tên là biết dùng ở đâu:\n * `x` → tô NỀN (fill đặc hoặc tint /5–/20 — trần chung an toàn cho mọi tone)\n * `xForeground` → chữ/icon NẰM TRÊN nền `x` ĐẶC — đo với `x`, ngưỡng 4,5:1\n * `xInk` → chính màu đó khi ĐI LÊN nền trang hoặc lên tint của chính nó (chữ, icon\n * rời, viền, vạch active) — đo với nền đó\n *\n * Cố ý KHÔNG mirror (xem docs/quyet-dinh-tich-hop.md §21): `--elevation-*` (chuỗi box-shadow chỉ\n * có nghĩa trong CSS), `--app-accent*` (giá trị thuộc về APP, không phải gói), `--font-*`.\n */\n\nimport { AVATAR_TONES, type AvatarToneColors, type AvatarTonePalette, getAvatarToneIndex } from \"./lib/avatar-tone.js\";\n\n/** Thang GOLD 7 bậc — trục màu chính của brand Comitor. */\nexport const gold = {\n 50: \"#FBF5DC\",\n 100: \"#F8E89D\",\n 200: \"#F5D060\",\n /** primary ở light mode */\n 300: \"#E8B824\",\n 400: \"#B88A00\",\n 500: \"#7A5800\",\n 600: \"#5C4006\"\n} as const;\n\n/** Trung tính brand (khác thang ash — đây là màu \"giấy/mực\" của brand book). */\nexport const brandNeutral = {\n ink: \"#0E1116\",\n inkSoft: \"#3A3F4A\",\n paper: \"#FBF7EE\",\n warm: \"#EDE8D8\"\n} as const;\n\n/**\n * Thang xám Comitor — namespace `ash`, KHÔNG phải `gray`.\n * Chỉ có 6 bậc; các bậc khuyết (200/400/600/800) cố ý không khai để không lẫn với\n * thang gray dựng sẵn của Tailwind (xem chú thích trong styles.css).\n */\nexport const ash = {\n 50: \"#F7F7F8\",\n 100: \"#EDEEF1\",\n 300: \"#C8CBD3\",\n 500: \"#7E8290\",\n 700: \"#3A3F4A\",\n 900: \"#16181D\"\n} as const;\n\n/**\n * Màu phụ — vai trò TÔ NỀN (fill đặc hoặc tint). Bản `dark` được nâng sáng vì trên nền #0E1116\n * bản `light` chìm nghỉm.\n *\n * ⚠ ĐỪNG dùng bảng này làm màu CHỮ ở light: nó được chọn để tô nền nên quá nhạt — dùng\n * `accentInk` ngay dưới. `accent.dark.navy` giữ nguyên #0F1E3D chỉ để mirror đúng `styles.css`\n * (khối `.dark` không override `--navy`); vai trò navy ở dark do `accentInk.dark.navy` gánh.\n */\nexport const accent = {\n light: {\n navy: \"#0F1E3D\",\n teal: \"#1F8A8B\",\n red: \"#D64545\",\n green: \"#2EA56A\",\n yellow: \"#F2C94C\",\n /** Gốc của tone `severe`. Giữ 3:1 trên mọi nền trang (3,56:1 trắng · 3,32:1 `muted`) — vì nó\n * TÔ chấm của `StatusPill variant=\"dot\"`; bậc sáng hơn (#F97316) chỉ 2,80:1. */\n orange: \"#EA580C\"\n },\n /** Bảng MẶC ĐỊNH (comitor-ds) không khai lại màu phụ ở dark — cùng hex với light. Bản sáng\n * dành cho nền tối nằm ở `highContrast.dark`. Trừ `orange`: comitor-ds không có cam, và cùng\n * hex thì chữ cam trên tint /15 của chính nó ở dark chỉ 4,21:1 — nên bảng mặc định có bản\n * dark riêng. */\n dark: {\n navy: \"#0F1E3D\",\n teal: \"#1F8A8B\",\n red: \"#D64545\",\n green: \"#2EA56A\",\n yellow: \"#F2C94C\",\n orange: \"#FB923C\"\n }\n} as const;\n\n/**\n * Bậc INK của màu phụ — mirror `--teal-ink` / `--green-ink` / `--red-ink` / `--yellow-ink` /\n * `--orange-ink` / `--navy-ink`. Đây là màu phụ khi nó làm **CHỮ/ICON/VIỀN trên nền trang**\n * (hoặc trên tint /5–/20 của chính nó), tức vai trò NGƯỢC với `accent`.\n *\n * Hợp đồng đặt tên của cả gói: `--x` tô nền · `--x-foreground` là chữ NẰM TRÊN nền `--x` ·\n * `--x-ink` là chính màu đó ĐI LÊN nền trang.\n *\n * Bản `light` là bậc tối đã đóng băng (kết quả của `color-mix(in srgb, --<màu>, --ink 30/40%)`\n * mà trước đây mỗi component tự tính lấy); bản `dark` chính là `accent.dark`. Đo trên tint 15%\n * của chính nó — nền thật mà chữ initials avatar và badge tonal đứng lên: teal 5,54 · green 5,60\n * · red 5,83:1 ở light (bản chưa có ink: 3,44 / 2,69 / 3,57:1 ✗).\n *\n * ⚠ Ở bảng MẶC ĐỊNH, `accent.dark` bằng hex của light nên bậc ink ở dark cũng vậy — đúng thứ\n * comitor-ds render. Bậc sáng cho nền tối nằm ở `highContrast.dark`.\n */\nexport const accentInk = {\n light: {\n navy: accent.light.navy,\n teal: \"#1A6668\",\n red: \"#9A3537\",\n green: \"#216A48\",\n /** = gold[500]: vàng không có bậc tối riêng, mượn thang gold cùng sắc. */\n yellow: gold[500],\n /** = color-mix(srgb, orange 72%, ink 28%) — bậc SÁNG nhất còn giữ trần tint /20: 5,85:1 trắng ·\n * 4,87:1 trên orange/15 · 4,58:1 trên orange/20. Bảng tương phản cao tối hơn: `highContrast`. */\n orange: \"#AC440F\"\n },\n dark: {\n /** navy nguyên bản chìm vào nền dark (1,15:1) → bậc sáng riêng; cũng là `--chart-3` ở dark. */\n navy: \"#7EA2E8\",\n teal: accent.dark.teal,\n red: accent.dark.red,\n green: accent.dark.green,\n yellow: accent.dark.yellow,\n orange: accent.dark.orange\n }\n} as const;\n\n/**\n * Bậc FOREGROUND của màu phụ — mirror `--teal-foreground` / `--green-foreground` /\n * `--yellow-foreground` / `--orange-foreground` / `--navy-foreground`. Đây là chữ/icon nằm\n * **TRÊN nền `accent.*` ĐẶC**, tức vai trò thứ ba, khác hẳn `accentInk` (màu đó đi lên nền TRANG).\n *\n * Cả bốn sắc teal/green/yellow/orange đều sáng ở light và còn sáng hơn ở dark → chữ là MỰC ở cả\n * hai theme, một giá trị đủ dùng: teal 4,56:1 (light) / 10,16:1 (dark) · green 6,04 / 10,85 ·\n * yellow 11,92 / 12,35 · orange 5,31 / 8,36. Navy thì ngược lại — chữ TRẮNG, 16,49:1 ở cả hai theme.\n *\n * ⚠ CỐ Ý KHÔNG có `red`: nền đỏ đặc không mang được chữ tốt ở light — trắng trên #D64545 được\n * 4,38:1 và mực 4,32:1, cả hai đều dưới AA. Nền đỏ đặc có chữ phải dùng `status.*.destructive`;\n * ở bảng tương phản cao token đó là #C93B3B và đạt 5,03:1. `accent.*.red` chỉ để tô TINT và làm\n * màu chart. (Cam thì mực cứu được — 5,31:1 — nên `orange` có mặt.)\n */\nexport const accentForeground = {\n light: {\n navy: \"#FFFFFF\",\n teal: \"#0E1116\",\n green: \"#0E1116\",\n yellow: \"#0E1116\",\n orange: \"#0E1116\"\n },\n dark: {\n navy: \"#FFFFFF\",\n teal: \"#0E1116\",\n green: \"#0E1116\",\n yellow: \"#0E1116\",\n orange: \"#0E1116\"\n }\n} as const;\n\n/** Semantic token của shadcn — mirror khối `:root` / `.dark` trong styles.css. */\nexport const semantic = {\n light: {\n background: \"#FFFFFF\",\n foreground: \"#0E1116\",\n card: \"#FFFFFF\",\n cardForeground: \"#0E1116\",\n popover: \"#FFFFFF\",\n popoverForeground: \"#0E1116\",\n primary: \"#E8B824\",\n primaryForeground: \"#0E1116\",\n /** Bậc INK của brand. Ở bảng MẶC ĐỊNH nó BẰNG `primary`: comitor-ds tô gold cho cả những vai\n * trò mà 1.4.11 đòi 3:1 (rãnh Switch BẬT, gạch active của Tabs) — 1,86:1, đã biết và chấp\n * nhận. Bậc tối #7A5800 nằm ở `highContrast.light.primaryInk`. */\n primaryInk: \"#E8B824\",\n secondary: \"#EDE8D8\",\n secondaryForeground: \"#0E1116\",\n muted: \"#F7F7F8\",\n /** Bậc chữ phụ — giá trị của comitor-ds, bằng đúng `ash[500]`. 3,83:1 trên nền trắng và\n * 3,58:1 trên `muted`, tức dưới AA 4,5:1: đã biết và chấp nhận, bản đạt chuẩn nằm ở\n * `highContrast.light.mutedForeground`. */\n mutedForeground: ash[500],\n accent: \"#FBF5DC\",\n accentForeground: \"#0E1116\",\n /** Nền nút xoá. Chữ trắng trên nó được 4,38:1 — thiếu 0,12 so với AA, đã biết và chấp nhận. */\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n /** Nền nút xoá lúc HOVER = `destructive` ở 90% trên nền trắng, đúng thứ `hover:bg-red/90`\n * của comitor-ds render. Ghim hex vì `/90` phụ thuộc nền phía sau. */\n destructiveHover: \"#DA5A5A\",\n /** Chữ báo lỗi trên nền trang. comitor-ds viết `text-destructive` cho vai trò này, tức cùng\n * hex với nền — 4,38:1 trên nền trắng. */\n destructiveInk: \"#D64545\",\n /** RANH GIỚI TRANG TRÍ (vạch chia, viền thẻ nghỉ) — 1,16:1, cố ý: 1.4.11 không đòi tương\n * phản cho thứ không mang thông tin. Ranh giới của CONTROL đi qua `controlEdge`. */\n border: \"#EDEEF1\",\n /** Viền ô nhập — comitor-ds cho nó cùng bậc với `border`. 1,16:1, đã biết và chấp nhận. */\n input: \"#EDEEF1\",\n ring: \"#E8B824\",\n /** Ranh giới của control lúc NGHỈ — ở bảng mặc định bằng đúng `input`. */\n controlEdge: \"#EDEEF1\",\n /** Cùng vai trò nhưng ở HOVER/ACTIVE. comitor-ds dùng `hover:border-gray-300` = `ash[300]`. */\n controlEdgeStrong: ash[300],\n /** Nền control khi BẬT/ĐÃ CHỌN (rãnh Switch bật, ô Checkbox đã chọn, chấm Radio). comitor-ds\n * tô gold: 1,86:1 với nền trang và với núm — đã biết và chấp nhận. */\n controlOn: \"#E8B824\",\n /** Núm Switch / chấm Radio — TRẮNG ở cả hai theme, đúng `bg-white` của comitor-ds. */\n controlKnob: \"#FFFFFF\",\n /** DẤU CHECK của Checkbox và chữ trên ngày ĐANG CHỌN của Calendar — tách khỏi `controlKnob`\n * vì trên nền gold SÁNG thì dấu phải là MỰC (10,19:1), còn ở bảng tương phản cao nền là\n * gold-500 TỐI nên dấu lật thành màu nền trang. */\n controlCheck: brandNeutral.ink,\n /** Rãnh Switch khi TẮT — comitor-ds dùng `bg-gray-300` / `dark:bg-gray-700`. */\n switchTrack: ash[300],\n /** Rãnh Slider — comitor-ds dùng `bg-muted`, khác bậc với rãnh Switch. */\n sliderTrack: \"#F7F7F8\",\n /** Button variant `link`. comitor-ds dùng `gold[400]` — 3,15:1, dưới AA, đã biết và chấp nhận. */\n linkInk: gold[400],\n linkInkHover: gold[300],\n sidebar: \"#FAFAF9\",\n sidebarForeground: \"#0E1116\",\n sidebarBorder: \"#EDEEF1\",\n sidebarAccent: \"#FBF5DC\",\n sidebarAccentForeground: \"#0E1116\"\n },\n dark: {\n background: \"#0E1116\",\n foreground: \"#F7F7F8\",\n card: \"#16181D\",\n cardForeground: \"#F7F7F8\",\n popover: \"#16181D\",\n popoverForeground: \"#F7F7F8\",\n primary: \"#F5D060\",\n primaryForeground: \"#0E1116\",\n /** Ở dark `primary` vốn đã là bậc sáng (12,67:1 trên nền `background`) nên ink = chính nó. */\n primaryInk: \"#F5D060\",\n secondary: \"#1E2128\",\n secondaryForeground: \"#F7F7F8\",\n muted: \"#1E2128\",\n /** comitor-ds không khai lại token này ở dark — cùng hex với light. 4,21:1 trên `muted`,\n * dưới AA: đã biết và chấp nhận. */\n mutedForeground: ash[500],\n accent: \"#1E2128\",\n accentForeground: \"#F7F7F8\",\n /** Ghim đúng giá trị của light — comitor-ds dùng chung một hex cho cả hai theme. */\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n destructiveHover: \"#DA5A5A\",\n /** 4,06:1 trên `card` — cùng hex với nền, đúng `text-destructive` của comitor-ds. */\n destructiveInk: \"#D64545\",\n /** TRANG TRÍ, 1,17:1 trên `background` và 1,10:1 trên `card` — cố ý, xem nhánh light. */\n border: \"#1E2128\",\n /** Viền ô nhập bản dark — KHÔNG phải #1E2128 của comitor-ds (chỉ 1,10:1 trên `card`, ô nhập tan\n * vào nền). #434A5A = 2,00:1, mirror `.dark { --input }` từ `0.5.0`; trước `3.3.3` mirror này còn\n * ghi #1E2128. */\n input: \"#434A5A\",\n ring: \"#F5D060\",\n /** = `input` (`--control-edge: var(--input)`). */\n controlEdge: \"#434A5A\",\n /** comitor-ds dùng `dark:hover:border-gray-500` = `ash[500]` (lật chiều so với light). */\n controlEdgeStrong: ash[500],\n /** comitor-ds tô `dark:data-[state=checked]:bg-gold-300`, nhưng token `primary` của chính nó\n * ở dark là #F5D060; gói theo token, nên control BẬT ở dark là bậc sáng. */\n controlOn: \"#F5D060\",\n /** TRẮNG ở cả hai theme, đúng `bg-white` của comitor-ds. */\n controlKnob: \"#FFFFFF\",\n /** comitor-ds vẽ dấu check bằng `text-ink` ở CẢ hai theme. */\n controlCheck: brandNeutral.ink,\n switchTrack: ash[700],\n sliderTrack: \"#1E2128\",\n linkInk: gold[400],\n linkInkHover: gold[300],\n sidebar: \"#16181D\",\n sidebarForeground: \"#F7F7F8\",\n sidebarBorder: \"#1E2128\",\n sidebarAccent: \"#1E2128\",\n sidebarAccentForeground: \"#F7F7F8\"\n }\n} as const;\n\n/**\n * BẢNG MÀU THỨ HAI — TƯƠNG PHẢN CAO. Mirror khối `[data-contrast=\"high\"]` /\n * `.dark[data-contrast=\"high\"]` trong styles.css.\n *\n * Chỉ chứa những token KHÁC bảng mặc định, đúng như CSS: hợp lên trên để có bảng đầy đủ.\n *\n * ```ts\n * const mau = { ...semantic.light, ...highContrast.light };\n * ```\n *\n * Bảng mặc định là comitor-ds (bản duyệt, có những cặp dưới ngưỡng — đã liệt kê ở từng token).\n * Bảng này là bảng đã đo, do NGƯỜI DÙNG bật qua `<ContrastProvider>` + `<ContrastToggle>`.\n * `accent` là màu phụ bản sáng dành cho nền tối — bảng mặc định không có, xem `accent.dark`.\n */\nexport const highContrast = {\n light: {\n /** 5,23:1 trên `background` · 4,89:1 trên `muted` (bảng mặc định: 3,83 / 3,58) */\n mutedForeground: \"#686C7A\",\n /** Chữ trắng trên nền này được 5,03:1 (bảng mặc định #D64545 chỉ 4,38:1) */\n destructive: \"#C93B3B\",\n /** = color-mix(destructive, ink 15%) — ĐẬM thêm khi hover, ngược chiều bảng mặc định. */\n destructiveHover: \"#AD3535\",\n /** 7,15:1 trên nền trắng (bảng mặc định dùng chính #D64545, 4,38:1) */\n destructiveInk: accentInk.light.red,\n /** 3,83:1 trên nền trắng (bảng mặc định #EDEEF1 chỉ 1,16:1) */\n input: \"#7E8290\",\n controlEdge: \"#7E8290\",\n controlEdgeStrong: ash[700],\n /** #B48700 = gold-400 tối đi 2%: 3,28:1 trên `background`/`card` · 3,06:1 trên `muted` · ⇄ núm\n * trắng 3,28:1 (bảng mặc định: 1,86 / 1,86). Trước `3.3.3` mirror này còn ghi gold-500 — giá trị\n * `styles.css` đã bỏ. */\n controlOn: \"#B48700\",\n /** = `background`, nên luôn tương phản đúng chiều với cả rãnh BẬT lẫn rãnh TẮT. */\n controlKnob: \"#FFFFFF\",\n /** MỰC trên nền gold (5,77:1) — cùng cách vẽ với bảng mặc định; trước `3.3.3` mirror này ghi trắng. */\n controlCheck: brandNeutral.ink,\n switchTrack: \"#7E8290\",\n sliderTrack: \"#7E8290\",\n primaryInk: gold[500],\n linkInk: gold[500],\n linkInkHover: gold[600],\n /** = `--orange-ink` của khối này — bậc ink màu phụ DUY NHẤT mà bảng này đổi ở light:\n * color-mix(srgb, orange 65%, ink 35%), 6,66:1 trắng · 5,55:1 trên orange/15 (bảng mặc định\n * #AC440F: 5,85 / 4,87:1). */\n severeInk: \"#9D3F10\",\n accent: accent.light\n },\n dark: {\n /** 5,70:1 trên `background` · 5,35:1 trên `card` · 4,86:1 trên `muted` */\n mutedForeground: \"#888D9B\",\n destructive: \"#C93B3B\",\n destructiveHover: \"#AD3535\",\n destructiveInk: \"#F87171\",\n /** Ghim một giá trị cho cả hai theme — 4,94:1 trên `background` */\n input: \"#7E8290\",\n controlEdge: \"#7E8290\",\n controlEdgeStrong: ash[300],\n /** 12,67:1 với nền trang VÀ với `controlKnob` (#0E1116) */\n controlOn: \"#F5D060\",\n controlKnob: \"#0E1116\",\n controlCheck: \"#0E1116\",\n switchTrack: \"#7E8290\",\n sliderTrack: \"#7E8290\",\n primaryInk: \"#F5D060\",\n linkInk: gold[200],\n linkInkHover: gold[100],\n /** Bằng bảng mặc định (8,36:1 trên `background` · 6,09:1 trên orange/15) — khai cho đủ cặp\n * với `light`, vì CSS phải huỷ bậc tối của khối light ở đây. */\n severeInk: accent.dark.orange,\n /** Màu phụ bản SÁNG cho nền tối — bảng mặc định không có bản dark cho nhóm này (trừ `orange`,\n * vốn đã sáng ở `accent.dark`). */\n accent: {\n navy: \"#0F1E3D\",\n teal: \"#2DD4BF\",\n red: \"#F87171\",\n green: \"#4ADE80\",\n yellow: \"#FACC15\",\n orange: accent.dark.orange\n }\n }\n} as const;\n\n/**\n * Alias trạng thái — success/warning/severe/info trỏ vào màu phụ, destructive giữ nguyên 2 chế độ.\n * `severe` (cam) là nấc giữa warning và destructive: ưu tiên cao, SLA sắp trễ.\n *\n * Ba lớp KHÁC nhau, đừng gộp:\n * • `destructive` — màu NỀN (nút xoá): #D64545 ở bảng mặc định (chữ trắng 4,38:1, dưới AA và đã\n * chấp nhận), #C93B3B ở bảng tương phản cao (5,03:1 ✓);\n * • `destructiveForeground` (#FFFFFF) — chữ NẰM TRÊN nền đó;\n * • `destructiveInk` — màu CHỮ đỏ trên nền trang (dòng lỗi). Bảng mặc định cho nó cùng hex với\n * nền (đúng `text-destructive` của comitor-ds); bảng tương phản cao tách thành bậc tối riêng.\n *\n * `successForeground` / `warningForeground` / `infoForeground` là MỰC ở CẢ hai theme (không phải\n * trắng ở light rồi lật ở dark như bản 0.1.0): ba nền trạng thái đều là sắc sáng.\n * success mực 6,04:1 (light) · 10,85:1 (dark) [trắng chỉ 3,13 / 1,74:1 ✗]\n * info mực 4,56:1 (light) · 10,16:1 (dark) [trắng chỉ 4,14 / 1,86:1 ✗]\n * warning mực 11,92:1 (light) · 12,35:1 (dark) [trắng chỉ 1,59 / 1,53:1 ✗]\n * severe mực 5,31:1 (light) · 8,36:1 (dark) [trắng chỉ 3,56 / 2,26:1 ✗]\n * ⚠ `info` ở light là cặp chặt nhất gói (4,56:1, dư 0,06) — đổi `accent.light.teal` là phải đo lại.\n */\nexport const status = {\n light: {\n success: accent.light.green,\n successForeground: \"#0E1116\",\n warning: accent.light.yellow,\n warningForeground: \"#0E1116\",\n severe: accent.light.orange,\n severeForeground: \"#0E1116\",\n info: accent.light.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n successInk: accentInk.light.green,\n warningInk: accentInk.light.yellow,\n severeInk: accentInk.light.orange,\n infoInk: accentInk.light.teal,\n /** = `semantic.light.destructiveInk` (#D64545 — `--destructive-ink: var(--destructive)` ở bảng mặc\n * định). Trước `3.3.3` trỏ nhầm `accentInk.light.red` (#9A3537 — bậc của bảng TƯƠNG PHẢN CAO). */\n destructiveInk: semantic.light.destructiveInk\n },\n dark: {\n success: accent.dark.green,\n successForeground: \"#0E1116\",\n warning: accent.dark.yellow,\n warningForeground: \"#0E1116\",\n severe: accent.dark.orange,\n severeForeground: \"#0E1116\",\n info: accent.dark.teal,\n infoForeground: \"#0E1116\",\n destructive: \"#D64545\",\n destructiveForeground: \"#FFFFFF\",\n successInk: accentInk.dark.green,\n warningInk: accentInk.dark.yellow,\n severeInk: accentInk.dark.orange,\n infoInk: accentInk.dark.teal,\n destructiveInk: semantic.dark.destructiveInk\n }\n} as const;\n\n/**\n * Bảng màu biểu đồ (chart-1…chart-5). Thứ tự = thứ tự series mặc định.\n *\n * ⚠ `chart-1` ở dark là `gold[300]` như light: `styles.css` KHÔNG khai lại `--chart-1` trong `.dark`\n * (chỉ `--chart-3` tự sáng lên qua `--navy-ink`). Trước `3.3.3` mirror này ghi `gold[200]` — biểu\n * đồ canvas/email/PDF dựng từ `tokens` ra màu khác biểu đồ trên trang.\n */\nexport const chart = {\n light: [gold[300], accent.light.teal, accent.light.navy, accent.light.green, accent.light.red],\n dark: [gold[300], accent.dark.teal, accentInk.dark.navy, accent.dark.green, accent.dark.red]\n} as const;\n\n/**\n * Màu avatar chữ cái (hex đục) cho nơi KHÔNG có Tailwind: canvas, email HTML, PDF.\n *\n * ⚠ KHÔNG phải bảng màu thứ hai. Bảng tone DUY NHẤT của cả gói nằm ở `lib/avatar-tone.ts`, mỗi\n * tone mang cả chuỗi class (cho DOM) lẫn công thức hex tương ứng; ở đây chỉ nạp bảng màu vào công\n * thức đó. Web và email vì vậy dùng chung một thứ tự tone, một hàm băm, một cách chuẩn hoá tên —\n * không còn hai bảng để lệch nhau.\n */\nconst avatarTonePalette = {\n light: {\n gold,\n ash,\n brandNeutral,\n accent: accent.light,\n accentInk: accentInk.light,\n card: semantic.light.card\n },\n dark: {\n gold,\n ash,\n brandNeutral,\n accent: accent.dark,\n accentInk: accentInk.dark,\n card: semantic.dark.card\n }\n} satisfies Record<ThemeMode, AvatarTonePalette>;\n\n/**\n * Cặp nền/chữ (hex) của avatar cho một tên, theo theme.\n *\n * Tên hàm nói đúng thứ nó trả về: `...Colors` = hex (canvas/email/PDF), còn bản trả về chuỗi\n * class Tailwind cho DOM là `getAvatarToneClasses` ở `@comitor/ui`. Cùng một tên người luôn ra\n * cùng một tone ở cả hai đường vì chỉ số do MỘT hàm tính (`getAvatarToneIndex`).\n */\nexport function getAvatarToneColors(name: string, mode: ThemeMode = \"light\"): AvatarToneColors {\n // `noUncheckedIndexedAccess` bật → phải có nhánh lùi, dù chỉ số luôn nằm trong [0, 8).\n const tone = AVATAR_TONES[getAvatarToneIndex(name)] ?? AVATAR_TONES[0];\n return tone[mode](avatarTonePalette[mode]);\n}\n\nexport type { AvatarToneColors };\nexport { getAvatarToneIndex };\n\n/** Số đo khung ứng dụng — mirror --sidebar-width / --sidebar-width-icon / --header-height. */\nexport const layout = {\n /** px — --header-height 3.5rem */\n headerHeight: 56,\n /** px — --sidebar-width 16rem */\n sidebarWidth: 256,\n /** px — --sidebar-width-icon 3.5rem */\n sidebarCollapsedWidth: 56,\n /** px — --radius 0.375rem */\n radius: 6,\n breakpoints: { mobile: 768, tablet: 1024, desktop: 1280 },\n /** px — kích thước tối thiểu của vùng chạm trên mobile (WCAG 2.5.5) */\n minTapTarget: 44\n} as const;\n\n/** Font stack — app phải tự nạp Inter (subsets latin + vietnamese) và gán --font-inter. */\nexport const typography = {\n fontSans: '\"Inter\", system-ui, sans-serif',\n fontMono: '\"Geist Mono\", ui-monospace, monospace'\n} as const;\n\nexport type ThemeMode = \"light\" | \"dark\";\n"],"mappings":"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","names":[]}
|
|
@@ -12,8 +12,8 @@ import { FileUploadResult } from './use-file-upload.js';
|
|
|
12
12
|
* hơn chính thứ nó đang sửa, và nó đứng tách khỏi tấm ảnh nên người dùng phải tự ghép "hộp bên
|
|
13
13
|
* phải này là để đổi ảnh bên trái".
|
|
14
14
|
*
|
|
15
|
-
* Ở đây bố cục là: **ảnh · nút · nút · một dòng chú thích** —
|
|
16
|
-
*
|
|
15
|
+
* Ở đây bố cục là: **ảnh · nút · nút · một dòng chú thích** — khuôn quen thuộc của ô ảnh đại diện,
|
|
16
|
+
* người dùng nhận ra ngay mà không phải đọc.
|
|
17
17
|
*
|
|
18
18
|
* ── KÉO-THẢ VẪN CÒN, NHƯNG VÔ HÌNH CHO TỚI KHI CẦN ─────────────────────────────────────────
|
|
19
19
|
* Cả hàng là vùng nhận thả. Viền đứt nét CÓ MẶT ở mọi trạng thái nhưng `border-transparent` lúc
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/uploader/image-upload-field.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport { Progress } from \"../components/ui/progress.js\";\nimport { useComitorLabels } from \"../i18n/locale-provider.js\";\nimport { cn } from \"../lib/cn.js\";\nimport type { ImageUploadFieldLabels } from \"./file-upload-constants.js\";\nimport { type FileUploadResult, useFileUpload } from \"./use-file-upload.js\";\n\n/**\n * Ô tải MỘT tấm ảnh gắn với một thực thể — ảnh đại diện người dùng, logo workspace, ảnh nhân sự.\n *\n * ── VÌ SAO KHÔNG DÙNG `FileUpload` CHO CA NÀY ────────────────────────────────────────────────\n * `FileUpload` vẽ một khung kéo-thả viền đứt nét chiếm cả bề rộng thẻ. Đó là bố cục ĐÚNG khi người\n * dùng sắp thả vào đó nhiều tài liệu, và là bố cục SAI cho một tấm ảnh vuông 56px: cái khung to\n * hơn chính thứ nó đang sửa, và nó đứng tách khỏi tấm ảnh nên người dùng phải tự ghép \"hộp bên\n * phải này là để đổi ảnh bên trái\".\n *\n * Ở đây bố cục là: **ảnh · nút · nút · một dòng chú thích** —
|
|
1
|
+
{"version":3,"sources":["../../src/uploader/image-upload-field.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { Button } from \"../components/ui/button.js\";\nimport { Progress } from \"../components/ui/progress.js\";\nimport { useComitorLabels } from \"../i18n/locale-provider.js\";\nimport { cn } from \"../lib/cn.js\";\nimport type { ImageUploadFieldLabels } from \"./file-upload-constants.js\";\nimport { type FileUploadResult, useFileUpload } from \"./use-file-upload.js\";\n\n/**\n * Ô tải MỘT tấm ảnh gắn với một thực thể — ảnh đại diện người dùng, logo workspace, ảnh nhân sự.\n *\n * ── VÌ SAO KHÔNG DÙNG `FileUpload` CHO CA NÀY ────────────────────────────────────────────────\n * `FileUpload` vẽ một khung kéo-thả viền đứt nét chiếm cả bề rộng thẻ. Đó là bố cục ĐÚNG khi người\n * dùng sắp thả vào đó nhiều tài liệu, và là bố cục SAI cho một tấm ảnh vuông 56px: cái khung to\n * hơn chính thứ nó đang sửa, và nó đứng tách khỏi tấm ảnh nên người dùng phải tự ghép \"hộp bên\n * phải này là để đổi ảnh bên trái\".\n *\n * Ở đây bố cục là: **ảnh · nút · nút · một dòng chú thích** — khuôn quen thuộc của ô ảnh đại diện,\n * người dùng nhận ra ngay mà không phải đọc.\n *\n * ── KÉO-THẢ VẪN CÒN, NHƯNG VÔ HÌNH CHO TỚI KHI CẦN ─────────────────────────────────────────\n * Cả hàng là vùng nhận thả. Viền đứt nét CÓ MẶT ở mọi trạng thái nhưng `border-transparent` lúc\n * nghỉ — cùng độ dày, nên nó không chiếm một pixel nào và không có cú nhảy bố cục khi tệp được rê\n * qua. Đây là lý do `border-2` nằm ở lớp ngoài cùng chứ không được thêm vào lúc `dragging`.\n *\n * ⚠ Vì không có khung thường trực, `labels.hint` là chỗ DUY NHẤT người dùng học được rằng kéo-thả\n * chạy được. Rút gọn nó thành mỗi \"tối đa 2 MB\" là bỏ đi tính năng trong mắt người dùng.\n */\nexport interface ImageUploadFieldProps<T = unknown> {\n /** Đường dẫn endpoint nhận multipart, ví dụ `/api/account/avatar`. */\n endpoint: string;\n fieldName?: string;\n maxFileSize: number;\n allowedFileTypes: string[];\n /**\n * Ảnh xem trước — một `ReactNode`, KHÔNG phải một chuỗi `src`.\n *\n * Gói không được biết `LetterAvatar` rơi về initials kiểu gì, tone màu nào, tròn hay vuông. Nhận\n * cả node còn cho một cái nữa: ảnh bìa 16:9 cắm vừa cùng component này mà không thêm prop nào.\n */\n preview: ReactNode;\n /** Có ảnh thật hay không — quyết định nút gỡ ảnh (`labels.remove`) có bấm được không. */\n hasImage?: boolean;\n /**\n * `false` = chỉ hiện ảnh, không nút, không vùng thả.\n *\n * Dành cho người xem không có quyền sửa. Hiện một khung tải bị vô hiệu với họ là mời họ bấm vào\n * một thứ sẽ không bao giờ chạy.\n */\n editable?: boolean;\n /** Đang chạy một thao tác khác (ví dụ gỡ ảnh) — khoá cả hai nút. */\n busy?: boolean;\n onRemove?: () => void;\n onUploaded: (result: FileUploadResult<T>) => void;\n onError?: (error: { code?: string; status?: number; message: string }) => void;\n /**\n * Nhãn hiển thị. Không truyền thì theo namespace `imageUploadField` của `ComitorLocaleProvider`\n * (không có provider → tiếng Anh); truyền `Partial<>` để đổi từng chuỗi.\n */\n labels?: Partial<ImageUploadFieldLabels>;\n className?: string;\n}\n\nexport function ImageUploadField<T = unknown>({\n endpoint,\n fieldName,\n maxFileSize,\n allowedFileTypes,\n preview,\n hasImage = false,\n editable = true,\n busy = false,\n onRemove,\n onUploaded,\n onError,\n labels,\n className\n}: ImageUploadFieldProps<T>) {\n const text = useComitorLabels(\"imageUploadField\", labels);\n\n const upload = useFileUpload<T>({\n endpoint,\n ...(fieldName ? { fieldName } : {}),\n maxFileSize,\n allowedFileTypes,\n maxNumberOfFiles: 1,\n onUploaded,\n ...(onError ? { onError } : {}),\n labels: { uploadFailed: text.uploadFailed, missingDependency: text.missingDependency },\n disabled: !editable || busy\n });\n\n const blocked = !editable || busy || upload.busy;\n\n /*\n * Không sửa được thì chỉ còn tấm ảnh. Không vùng thả, không nút, không `<input>` — không có gì\n * để người dùng bấm nhầm rồi tự hỏi vì sao không có chuyện gì xảy ra.\n */\n if (!editable) return <div className={cn(\"flex items-center gap-4\", className)}>{preview}</div>;\n\n return (\n <div className={cn(\"flex flex-col gap-2\", className)}>\n <input {...upload.inputProps} className=\"sr-only\" />\n\n {/*\n ⚠ `border-2 border-dashed` có mặt ở CẢ HAI trạng thái; chỉ MÀU đổi. Thêm viền lúc `dragging`\n sẽ đẩy nội dung 2px và làm cả hàng giật đúng lúc người dùng đang nhắm chỗ thả.\n */}\n <div\n {...upload.dropProps}\n className={cn(\n \"relative flex flex-wrap items-center gap-4 rounded-lg border-2 border-dashed p-2 transition-colors\",\n upload.dragging ? \"border-primary-ink\" : \"border-transparent\"\n )}\n >\n {preview}\n\n <div className={cn(\"flex flex-wrap items-center gap-2\", blocked && \"opacity-60\")}>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" loading={upload.busy} onClick={upload.open}>\n {upload.busy ? text.uploading : text.choose}\n </Button>\n {/* Luôn render, chỉ `disabled` khi chưa có ảnh — bố cục không nhảy một nút. */}\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" disabled={!hasImage || blocked} onClick={onRemove}>\n {text.remove}\n </Button>\n </div>\n\n {/*\n Chữ `dropPrompt` phủ lên cả khối khi đang rê tệp qua. `pointer-events-none` là BẮT\n BUỘC: thiếu nó, lớp phủ tự nuốt sự kiện `drop` của khối bên dưới và việc thả im lặng\n không làm gì.\n */}\n {upload.dragging ? (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center rounded-lg bg-card/90\">\n <span className=\"font-medium text-sm\">{text.dropPrompt}</span>\n </div>\n ) : null}\n </div>\n\n <p className=\"text-muted-foreground text-xs\">{text.hint}</p>\n\n {/* Thanh tiến độ chỉ hiện khi đang tải — một thanh 0% đứng yên trông như hỏng. */}\n {upload.busy ? <Progress value={upload.percent} aria-label={text.uploading} /> : null}\n </div>\n );\n}\n"],"mappings":";AAoGwB,cAmBhB,YAnBgB;AAjGxB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,wBAAwB;AACjC,SAAS,UAAU;AAEnB,SAAgC,qBAAqB;AAyD9C,SAAS,iBAA8B;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,WAAW;AAAA,EACX,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,OAAO,iBAAiB,oBAAoB,MAAM;AAExD,QAAM,SAAS,cAAiB;AAAA,IAC9B;AAAA,IACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,IACjC;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,QAAQ,EAAE,cAAc,KAAK,cAAc,mBAAmB,KAAK,kBAAkB;AAAA,IACrF,UAAU,CAAC,YAAY;AAAA,EACzB,CAAC;AAED,QAAM,UAAU,CAAC,YAAY,QAAQ,OAAO;AAM5C,MAAI,CAAC,SAAU,QAAO,oBAAC,SAAI,WAAW,GAAG,2BAA2B,SAAS,GAAI,mBAAQ;AAEzF,SACE,qBAAC,SAAI,WAAW,GAAG,uBAAuB,SAAS,GACjD;AAAA,wBAAC,WAAO,GAAG,OAAO,YAAY,WAAU,WAAU;AAAA,IAMlD;AAAA,MAAC;AAAA;AAAA,QACE,GAAG,OAAO;AAAA,QACX,WAAW;AAAA,UACT;AAAA,UACA,OAAO,WAAW,uBAAuB;AAAA,QAC3C;AAAA,QAEC;AAAA;AAAA,UAED,qBAAC,SAAI,WAAW,GAAG,qCAAqC,WAAW,YAAY,GAC7E;AAAA,gCAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAS,OAAO,MAAM,SAAS,OAAO,MACrF,iBAAO,OAAO,KAAK,YAAY,KAAK,QACvC;AAAA,YAEA,oBAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,MAAK,MAAK,UAAU,CAAC,YAAY,SAAS,SAAS,UACtF,eAAK,QACR;AAAA,aACF;AAAA,UAOC,OAAO,WACN,oBAAC,SAAI,WAAU,+FACb,8BAAC,UAAK,WAAU,uBAAuB,eAAK,YAAW,GACzD,IACE;AAAA;AAAA;AAAA,IACN;AAAA,IAEA,oBAAC,OAAE,WAAU,iCAAiC,eAAK,MAAK;AAAA,IAGvD,OAAO,OAAO,oBAAC,YAAS,OAAO,OAAO,SAAS,cAAY,KAAK,WAAW,IAAK;AAAA,KACnF;AAEJ;","names":[]}
|
package/package.json
CHANGED