@comitor/ui 3.13.0 → 3.13.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.
@@ -20,11 +20,11 @@ import * as React from 'react';
20
20
  * `aria-valuetext`, kèm lỗi console; nay thanh vẫn đầy VÀ trình đọc màn hình đọc "100%". Nói rõ là
21
21
  * đã vượt (bao nhiêu, làm gì tiếp) vẫn là việc của chữ cạnh thanh và `tone`, không của thanh.
22
22
  *
23
- * ─ TÔNG `default`: cặp `gold` của avatar (chưa phát hành) ─
24
- * Thanh `gold-600` (dark `gold-100`), rãnh `gold-100` (dark `gold-500/30`) — cùng cặp với `StatusPill`
25
- * `primary`, `Gantt` và `CircularProgress`. Lý do: ở bảng màu MẶC ĐỊNH `--primary-ink` là vàng thương hiệu
26
- * #E8B824 (1,73:1 với nền, 1,55:1 với rãnh — xem dưới), thanh vàng nhạt gần như không thấy. Phần lý lẽ
27
- * bên dưới về `--primary-ink` là lịch sử trước đợt đổi này; ba tông còn lại vẫn đúng như mô tả.
23
+ * ─ ⛔ ĐỪNG ĐỔI TÔNG `default` SANG MÀU ĐẬM HƠN (chốt 2026-11-01) ─
24
+ * 3.13.0 đổi thanh sang `gold-600` / rãnh `gold-100` để qua tương phản ở bảng mặc định; trên giao diện thật thanh nâu
25
+ * sẫm nặng hơn mọi thứ quanh nó và bị trả về đây ở 3.13.1. Mức 1,73:1 (thanh ⇄ nền) / 1,55:1 (thanh ⇄ rãnh) ở bảng
26
+ * MẶC ĐỊNH là ĐÃ CHẤP NHẬN — bỏ qua phép đo tương phản cho phần tô này. Cần rõ hơn thì chữ cạnh thanh, `tone`, hoặc
27
+ * bảng tương phản cao (`data-contrast="high"`, nơi các số đo bên dưới đạt). Xem CLAUDE.md, mục màu.
28
28
  *
29
29
  * ─ MÀU: `--primary-ink`, KHÔNG PHẢI `--primary` ─
30
30
  * Phần đã chạy là thông tin thị giác (WCAG 1.4.11, 3:1) và phải nổi trên CẢ nền trang lẫn phần
@@ -6,7 +6,7 @@ import { cn } from "../../lib/cn.js";
6
6
  const progressTrackVariants = cva("border-control-edge relative h-2 w-full overflow-hidden rounded-full border", {
7
7
  variants: {
8
8
  tone: {
9
- default: "bg-gold-100 dark:bg-gold-500/30",
9
+ default: "bg-primary-ink/20",
10
10
  success: "bg-success-ink/20",
11
11
  warning: "bg-warning-ink/20",
12
12
  destructive: "bg-destructive-ink/20"
@@ -17,7 +17,7 @@ const progressTrackVariants = cva("border-control-edge relative h-2 w-full overf
17
17
  const progressBarVariants = cva("h-full w-full flex-1 transition-transform duration-150 ease-out", {
18
18
  variants: {
19
19
  tone: {
20
- default: "bg-gold-600 dark:bg-gold-100",
20
+ default: "bg-primary-ink",
21
21
  success: "bg-success-ink",
22
22
  // `hazard-stripes` là PHẦN BẮT BUỘC của tông này, không phải trang trí — xem chú thích đầu file.
23
23
  warning: "bg-warning-ink hazard-stripes",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/ui/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport * as ProgressPrimitive from \"@radix-ui/react-progress\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Progress (Radix) — thanh tiến trình xác định (`value` 0…`max`, `max` mặc định 100).\n * Bỏ trống `value` để về trạng thái indeterminate của Radix (thanh đứng yên ở 0);\n * animation cho indeterminate là việc của app.\n *\n * ─ THANH VẼ THEO `value / max`, VÀ THANH VỚI `aria-*` NÓI CÙNG MỘT CON SỐ ─\n * Radix đọc `max` để dựng `aria-valuemax` và `aria-valuetext` (`round(value / max · 100)%`). Trước\n * `3.3.3` phần đã chạy lại vẽ bằng `100 - value` — bỏ qua `max` —, nên `max={30} value={24}` vẽ 24%\n * trong khi trình đọc màn hình đọc 80%: hai người nhìn cùng một control nhận hai con số.\n *\n * Nay cả hai đi từ MỘT giá trị: `value` được KẸP vào `[0, max]` rồi mới giao cho Radix (`max` không\n * phải số dương → 100, đúng luật Radix). Kẹp chứ không coi là indeterminate, vì ca vượt ngưỡng hay\n * gặp nhất là CÓ Ý: hạn mức dùng 120% phải là thanh ĐẦY, không phải thanh rỗng. Trước `3.3.3` ca đó\n * tình cờ đã đầy (`translateX(--20%)` là CSS vô hiệu) nhưng Radix coi `value` sai → không có\n * `aria-valuetext`, kèm lỗi console; nay thanh vẫn đầy VÀ trình đọc màn hình đọc \"100%\". Nói rõ là\n * đã vượt (bao nhiêu, làm gì tiếp) vẫn là việc của chữ cạnh thanh và `tone`, không của thanh.\n *\n * ─ TÔNG `default`: cặp `gold` của avatar (chưa phát hành) ─\n * Thanh `gold-600` (dark `gold-100`), rãnh `gold-100` (dark `gold-500/30`) — cùng cặp với `StatusPill`\n * `primary`, `Gantt` và `CircularProgress`. Lý do: ở bảng màu MẶC ĐỊNH `--primary-ink` là vàng thương hiệu\n * #E8B824 (1,73:1 với nền, 1,55:1 với rãnh — xem dưới), thanh vàng nhạt gần như không thấy. Phần lý lẽ\n * bên dưới về `--primary-ink` là lịch sử trước đợt đổi này; ba tông còn lại vẫn đúng như mô tả.\n *\n * ─ MÀU: `--primary-ink`, KHÔNG PHẢI `--primary` ─\n * Phần đã chạy là thông tin thị giác (WCAG 1.4.11, 3:1) và phải nổi trên CẢ nền trang lẫn phần\n * rãnh còn lại. `bg-primary` gold #E8B824 ở light chỉ **1,86:1** với nền trắng và **1,64:1** với\n * rãnh `bg-primary/20` — thanh 8px vàng nhạt trên nền trắng gần như không thấy. Bậc ink của\n * brand đạt cả hai phía ở cả hai theme:\n *\n * đã chạy ⇄ nền trang : 6,51:1 (light) · 12,67:1 (dark) ✓\n * đã chạy ⇄ rãnh `/20` : 4,85:1 (light) · 7,97:1 (dark) ✓\n * (tệ nhất khi rãnh nằm trên --muted: 4,54:1 · 6,52:1 ✓)\n *\n * ─ RÃNH: TINT `/20` GIỮ NGUYÊN, NHƯNG PHẢI CÓ VIỀN `--control-edge` ─\n * Bản trước chỉ có tint: rãnh ⇄ nền trang mới **1,34:1** (light) và **1,59–1,66:1** (dark), tức\n * KHÔNG nhìn thấy mốc 100% — ở `value` nhỏ không đọc được TỈ LỆ, ở `value=0` cả control biến mất.\n * Cách sửa hiển nhiên (đổi rãnh sang `bg-control-edge` như Slider/Switch) lại hỏng chỗ khác:\n * `--primary-ink` ⇄ `--control-edge` chỉ **1,70:1** (light) / **2,57:1** (dark), mà Progress\n * KHÔNG có núm nên ranh giới đã-chạy/chưa-chạy là thứ DUY NHẤT mang giá trị — mất nó là mất\n * thông tin. Với nền trang trắng không tồn tại cặp màu nào vừa ≥3:1 với nhau vừa ≥3:1 với nền\n * (chứng minh trong chú thích của `Slider`), nên hai ràng buộc phải tách ra hai lớp:\n *\n * khoanh vùng control → `border-control-edge` ⇄ nền trang : 3,83:1 (light) · 4,94:1 (dark) ✓\n * ⇄ --muted : 3,58:1 · 4,21:1 ✓\n * đọc tỉ lệ → đã chạy ⇄ rãnh `/20` : 4,85:1 · 7,97:1 ✓\n *\n * Đây đúng vai trò hợp đồng đặt cho `--control-edge` (styles.css §RANH GIỚI): \"khi nền control\n * trong suốt/nhạt thì VIỀN là thứ duy nhất khoanh vùng\".\n *\n * ─ TÔNG: BỐN GIÁ TRỊ, VÀ `warning` KHÔNG ĐƯỢC PHÉP CHỈ LÀ MỘT MÀU KHÁC ─\n * `tone` đổi CẢ phần đã chạy lẫn rãnh sang cùng một bậc ink, nên tỉ lệ đọc-được giữ nguyên cấu\n * trúc của bản mặc định. Đo trên ba bề mặt (`--background`, `--card`, `--muted`), tệ nhất mỗi cột.\n *\n * ⚠ Cột \"light\"/\"dark\" dưới đây là **bảng TƯƠNG PHẢN CAO** — đúng bảng mà cổng màu đo, và lý do\n * ghi ở đầu `scripts/a11y/tailwind.mjs`. Ở bảng mặc định (bản duyệt comitor-ds) `--primary-ink`\n * chính là `--primary` gold #E8B824, nên tông `default` chỉ được 1,73:1 với nền và 1,55:1 với\n * rãnh: đó là con số của BẢN DUYỆT, không phải lỗi của component — đổi nó là đổi bảng màu.\n *\n * tông đã chạy ⇄ nền trang đã chạy ⇄ rãnh cùng tông /20\n * default 6,09:1 (light) 10,80 (dark) 4,54:1 (light) 6,52:1 (dark) ✓\n * success 6,09:1 9,25 4,55:1 5,88:1 ✓\n * warning 6,09:1 10,52 4,54:1 6,47:1 ✓\n * destructive 6,68:1 5,82 4,85:1 4,25:1 ✓\n *\n * ⚠ NHƯNG `warning` KHÔNG phân biệt được với `default` BẰNG MÀU, và đó là số đo chứ không phải\n * cảm tính. Đo `--warning-ink` ⇄ `--primary-ink` trên cả BỐN bảng màu của gói:\n *\n * light (mặc định) #7A5800 ⇄ #E8B824 3,51:1 ✓\n * light (tương phản cao) #7A5800 ⇄ #7A5800 1,00:1 ✗ CÙNG MỘT HEX\n * dark (mặc định) #F2C94C ⇄ #F5D060 1,06:1 ✗\n * dark (tương phản cao) #FACC15 ⇄ #F5D060 1,03:1 ✗\n *\n * Ba trên bốn bảng màu cho ra hai thanh trông y hệt nhau. Đây KHÔNG sửa được ở tầng token: brand\n * của Comitor là gold, warning là yellow — cùng một sắc. Muốn tách hai bậc ink ra 3:1 thì một\n * trong hai phải rời khỏi vùng đọc-được: với nền trắng, chữ đạt 4,5:1 đòi độ chói ≤ 0,183, mà\n * cách `--gold-500` (0,111) đúng 3:1 thì phải ≤ 0,004 (gần như đen) hoặc ≥ 0,433 (quá sáng để\n * làm chữ). Hai ràng buộc không giao nhau — không có hex nào thoả cả hai.\n *\n * Nên tông `warning` nói bằng thứ KHÔNG PHẢI MÀU: `hazard-stripes` (khai ở styles.css) khoét\n * những rãnh chéo xuyên phần đã chạy để nền rãnh lộ ra. Ranh giới vạch ⇄ rãnh chính là cặp\n * \"đã chạy ⇄ rãnh\" đã đo ở bảng trên (4,54:1 light · 6,47:1 dark ✓), nên vạch KHÔNG thêm màu nào\n * và không cần đo thêm. Đây đúng là WCAG 1.4.1: tín hiệu không được chỉ nằm ở màu — bản vạch còn\n * đọc ra ở chế độ xám, chỗ mà mọi khác biệt sắc độ đều biến mất.\n *\n * ⚠ Vì vậy: KHÔNG được \"đơn giản hoá\" `warning` xuống còn mỗi `bg-warning-ink`. Bỏ vạch đi là\n * API đổi tông mà không đổi được gì ở ba trên bốn bảng màu — chỗ gọi tưởng đã cảnh báo, người\n * dùng không thấy gì. Cùng lý do đó, ĐỪNG thêm tông thứ năm nào nữa mà chưa đo lại bảng trên.\n *\n * Vạch chỉ là cách nói \"sắp chạm ngưỡng\" trên chính cái thanh; nó KHÔNG thay được một câu chữ.\n * Chỗ nào cần nói rõ ngưỡng là bao nhiêu và làm gì tiếp thì vẫn phải có `Alert`/`StatusPill` hoặc\n * một dòng `text-warning-ink` cạnh thanh.\n */\n\n/*\n * HAI `cva`, không phải hai bảng `Record`.\n *\n * Không phải chuyện phong cách: cổng màu (`scripts/check-contrast.mjs`) chỉ bóc chuỗi lớp từ ba\n * chỗ — thuộc tính `className`, `cva(base, { variants })`, và đối số TRỰC TIẾP của `cn()`. Class\n * nằm trong một `Record` rồi đi vào `cn()` qua biến là VÔ HÌNH với cổng: nó không báo lỗi, nó\n * lặng lẽ không đo — và miễn trừ của rãnh sẽ thành \"ruỗng\", đúng thứ cổng coi là nơi giấu lỗi.\n * Dạng `cva` cho cổng thấy cả base lẫn từng lá biến thể.\n */\nconst progressTrackVariants = cva(\"border-control-edge relative h-2 w-full overflow-hidden rounded-full border\", {\n variants: {\n tone: {\n default: \"bg-gold-100 dark:bg-gold-500/30\",\n success: \"bg-success-ink/20\",\n warning: \"bg-warning-ink/20\",\n destructive: \"bg-destructive-ink/20\"\n }\n },\n defaultVariants: { tone: \"default\" }\n});\n\nconst progressBarVariants = cva(\"h-full w-full flex-1 transition-transform duration-150 ease-out\", {\n variants: {\n tone: {\n default: \"bg-gold-600 dark:bg-gold-100\",\n success: \"bg-success-ink\",\n // `hazard-stripes` là PHẦN BẮT BUỘC của tông này, không phải trang trí — xem chú thích đầu file.\n warning: \"bg-warning-ink hazard-stripes\",\n destructive: \"bg-destructive-ink\"\n }\n },\n defaultVariants: { tone: \"default\" }\n});\n\n/** Tông của thanh — xem chú thích ở trên về việc vì sao `warning` phải kèm vạch chéo. */\nexport type ProgressTone = NonNullable<VariantProps<typeof progressBarVariants>[\"tone\"]>;\n\nexport interface ProgressProps\n extends React.ComponentProps<typeof ProgressPrimitive.Root>,\n VariantProps<typeof progressBarVariants> {}\n\n/** Mặc định của Radix — `max` không hợp lệ thì Radix cũng rơi về đúng số này cho `aria-*`. */\nconst DEFAULT_MAX = 100;\n\n/**\n * Kẹp `value` vào `[0, max]` — giá trị DUY NHẤT mà cả thanh lẫn Radix (`aria-*`) đọc (xem chú thích\n * đầu file). Không phải số hữu hạn (`null`, `undefined`, `NaN`) thì giữ nguyên: Radix coi là\n * indeterminate và thanh đứng ở 0.\n */\nfunction clampValue(value: number | null | undefined, max: number): number | null | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return value;\n return Math.min(Math.max(value, 0), max);\n}\n\nfunction Progress({ className, value, max, tone = \"default\", ...props }: ProgressProps) {\n const validMax = typeof max === \"number\" && max > 0 ? max : DEFAULT_MAX;\n const clamped = clampValue(value, validMax);\n const percent = typeof clamped === \"number\" && Number.isFinite(clamped) ? (clamped / validMax) * 100 : 0;\n\n return (\n <ProgressPrimitive.Root\n data-slot=\"progress\"\n data-tone={tone}\n className={cn(progressTrackVariants({ tone }), className)}\n value={clamped}\n max={max}\n {...props}\n >\n <ProgressPrimitive.Indicator\n data-slot=\"progress-indicator\"\n className={progressBarVariants({ tone })}\n style={{ transform: `translateX(-${100 - percent}%)` }}\n />\n </ProgressPrimitive.Root>\n );\n}\n\nexport { Progress, progressBarVariants, progressTrackVariants };\n"],"mappings":";AAwKM;AAtKN,YAAY,uBAAuB;AACnC,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAwGnB,MAAM,wBAAwB,IAAI,+EAA+E;AAAA,EAC/G,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CAAC;AAED,MAAM,sBAAsB,IAAI,mEAAmE;AAAA,EACjG,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,SAAS;AAAA;AAAA,MAET,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CAAC;AAUD,MAAM,cAAc;AAOpB,SAAS,WAAW,OAAkC,KAAwC;AAC5F,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,GAAG;AACzC;AAEA,SAAS,SAAS,EAAE,WAAW,OAAO,KAAK,OAAO,WAAW,GAAG,MAAM,GAAkB;AACtF,QAAM,WAAW,OAAO,QAAQ,YAAY,MAAM,IAAI,MAAM;AAC5D,QAAM,UAAU,WAAW,OAAO,QAAQ;AAC1C,QAAM,UAAU,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,IAAK,UAAU,WAAY,MAAM;AAEvG,SACE;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,aAAU;AAAA,MACV,aAAW;AAAA,MACX,WAAW,GAAG,sBAAsB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MACxD,OAAO;AAAA,MACP;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,kBAAkB;AAAA,QAAlB;AAAA,UACC,aAAU;AAAA,UACV,WAAW,oBAAoB,EAAE,KAAK,CAAC;AAAA,UACvC,OAAO,EAAE,WAAW,eAAe,MAAM,OAAO,KAAK;AAAA;AAAA,MACvD;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/ui/progress.tsx"],"sourcesContent":["\"use client\";\n\nimport * as ProgressPrimitive from \"@radix-ui/react-progress\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/**\n * Progress (Radix) — thanh tiến trình xác định (`value` 0…`max`, `max` mặc định 100).\n * Bỏ trống `value` để về trạng thái indeterminate của Radix (thanh đứng yên ở 0);\n * animation cho indeterminate là việc của app.\n *\n * ─ THANH VẼ THEO `value / max`, VÀ THANH VỚI `aria-*` NÓI CÙNG MỘT CON SỐ ─\n * Radix đọc `max` để dựng `aria-valuemax` và `aria-valuetext` (`round(value / max · 100)%`). Trước\n * `3.3.3` phần đã chạy lại vẽ bằng `100 - value` — bỏ qua `max` —, nên `max={30} value={24}` vẽ 24%\n * trong khi trình đọc màn hình đọc 80%: hai người nhìn cùng một control nhận hai con số.\n *\n * Nay cả hai đi từ MỘT giá trị: `value` được KẸP vào `[0, max]` rồi mới giao cho Radix (`max` không\n * phải số dương → 100, đúng luật Radix). Kẹp chứ không coi là indeterminate, vì ca vượt ngưỡng hay\n * gặp nhất là CÓ Ý: hạn mức dùng 120% phải là thanh ĐẦY, không phải thanh rỗng. Trước `3.3.3` ca đó\n * tình cờ đã đầy (`translateX(--20%)` là CSS vô hiệu) nhưng Radix coi `value` sai → không có\n * `aria-valuetext`, kèm lỗi console; nay thanh vẫn đầy VÀ trình đọc màn hình đọc \"100%\". Nói rõ là\n * đã vượt (bao nhiêu, làm gì tiếp) vẫn là việc của chữ cạnh thanh và `tone`, không của thanh.\n *\n * ─ ⛔ ĐỪNG ĐỔI TÔNG `default` SANG MÀU ĐẬM HƠN (chốt 2026-11-01) ─\n * 3.13.0 đổi thanh sang `gold-600` / rãnh `gold-100` để qua tương phản ở bảng mặc định; trên giao diện thật thanh nâu\n * sẫm nặng hơn mọi thứ quanh nó và bị trả về đây ở 3.13.1. Mức 1,73:1 (thanh ⇄ nền) / 1,55:1 (thanh ⇄ rãnh) ở bảng\n * MẶC ĐỊNH là ĐÃ CHẤP NHẬN — bỏ qua phép đo tương phản cho phần tô này. Cần rõ hơn thì chữ cạnh thanh, `tone`, hoặc\n * bảng tương phản cao (`data-contrast=\"high\"`, nơi các số đo bên dưới đạt). Xem CLAUDE.md, mục màu.\n *\n * ─ MÀU: `--primary-ink`, KHÔNG PHẢI `--primary` ─\n * Phần đã chạy là thông tin thị giác (WCAG 1.4.11, 3:1) và phải nổi trên CẢ nền trang lẫn phần\n * rãnh còn lại. `bg-primary` gold #E8B824 ở light chỉ **1,86:1** với nền trắng và **1,64:1** với\n * rãnh `bg-primary/20` — thanh 8px vàng nhạt trên nền trắng gần như không thấy. Bậc ink của\n * brand đạt cả hai phía ở cả hai theme:\n *\n * đã chạy ⇄ nền trang : 6,51:1 (light) · 12,67:1 (dark) ✓\n * đã chạy ⇄ rãnh `/20` : 4,85:1 (light) · 7,97:1 (dark) ✓\n * (tệ nhất khi rãnh nằm trên --muted: 4,54:1 · 6,52:1 ✓)\n *\n * ─ RÃNH: TINT `/20` GIỮ NGUYÊN, NHƯNG PHẢI CÓ VIỀN `--control-edge` ─\n * Bản trước chỉ có tint: rãnh ⇄ nền trang mới **1,34:1** (light) và **1,59–1,66:1** (dark), tức\n * KHÔNG nhìn thấy mốc 100% — ở `value` nhỏ không đọc được TỈ LỆ, ở `value=0` cả control biến mất.\n * Cách sửa hiển nhiên (đổi rãnh sang `bg-control-edge` như Slider/Switch) lại hỏng chỗ khác:\n * `--primary-ink` ⇄ `--control-edge` chỉ **1,70:1** (light) / **2,57:1** (dark), mà Progress\n * KHÔNG có núm nên ranh giới đã-chạy/chưa-chạy là thứ DUY NHẤT mang giá trị — mất nó là mất\n * thông tin. Với nền trang trắng không tồn tại cặp màu nào vừa ≥3:1 với nhau vừa ≥3:1 với nền\n * (chứng minh trong chú thích của `Slider`), nên hai ràng buộc phải tách ra hai lớp:\n *\n * khoanh vùng control → `border-control-edge` ⇄ nền trang : 3,83:1 (light) · 4,94:1 (dark) ✓\n * ⇄ --muted : 3,58:1 · 4,21:1 ✓\n * đọc tỉ lệ → đã chạy ⇄ rãnh `/20` : 4,85:1 · 7,97:1 ✓\n *\n * Đây đúng vai trò hợp đồng đặt cho `--control-edge` (styles.css §RANH GIỚI): \"khi nền control\n * trong suốt/nhạt thì VIỀN là thứ duy nhất khoanh vùng\".\n *\n * ─ TÔNG: BỐN GIÁ TRỊ, VÀ `warning` KHÔNG ĐƯỢC PHÉP CHỈ LÀ MỘT MÀU KHÁC ─\n * `tone` đổi CẢ phần đã chạy lẫn rãnh sang cùng một bậc ink, nên tỉ lệ đọc-được giữ nguyên cấu\n * trúc của bản mặc định. Đo trên ba bề mặt (`--background`, `--card`, `--muted`), tệ nhất mỗi cột.\n *\n * ⚠ Cột \"light\"/\"dark\" dưới đây là **bảng TƯƠNG PHẢN CAO** — đúng bảng mà cổng màu đo, và lý do\n * ghi ở đầu `scripts/a11y/tailwind.mjs`. Ở bảng mặc định (bản duyệt comitor-ds) `--primary-ink`\n * chính là `--primary` gold #E8B824, nên tông `default` chỉ được 1,73:1 với nền và 1,55:1 với\n * rãnh: đó là con số của BẢN DUYỆT, không phải lỗi của component — đổi nó là đổi bảng màu.\n *\n * tông đã chạy ⇄ nền trang đã chạy ⇄ rãnh cùng tông /20\n * default 6,09:1 (light) 10,80 (dark) 4,54:1 (light) 6,52:1 (dark) ✓\n * success 6,09:1 9,25 4,55:1 5,88:1 ✓\n * warning 6,09:1 10,52 4,54:1 6,47:1 ✓\n * destructive 6,68:1 5,82 4,85:1 4,25:1 ✓\n *\n * ⚠ NHƯNG `warning` KHÔNG phân biệt được với `default` BẰNG MÀU, và đó là số đo chứ không phải\n * cảm tính. Đo `--warning-ink` ⇄ `--primary-ink` trên cả BỐN bảng màu của gói:\n *\n * light (mặc định) #7A5800 ⇄ #E8B824 3,51:1 ✓\n * light (tương phản cao) #7A5800 ⇄ #7A5800 1,00:1 ✗ CÙNG MỘT HEX\n * dark (mặc định) #F2C94C ⇄ #F5D060 1,06:1 ✗\n * dark (tương phản cao) #FACC15 ⇄ #F5D060 1,03:1 ✗\n *\n * Ba trên bốn bảng màu cho ra hai thanh trông y hệt nhau. Đây KHÔNG sửa được ở tầng token: brand\n * của Comitor là gold, warning là yellow — cùng một sắc. Muốn tách hai bậc ink ra 3:1 thì một\n * trong hai phải rời khỏi vùng đọc-được: với nền trắng, chữ đạt 4,5:1 đòi độ chói ≤ 0,183, mà\n * cách `--gold-500` (0,111) đúng 3:1 thì phải ≤ 0,004 (gần như đen) hoặc ≥ 0,433 (quá sáng để\n * làm chữ). Hai ràng buộc không giao nhau — không có hex nào thoả cả hai.\n *\n * Nên tông `warning` nói bằng thứ KHÔNG PHẢI MÀU: `hazard-stripes` (khai ở styles.css) khoét\n * những rãnh chéo xuyên phần đã chạy để nền rãnh lộ ra. Ranh giới vạch ⇄ rãnh chính là cặp\n * \"đã chạy ⇄ rãnh\" đã đo ở bảng trên (4,54:1 light · 6,47:1 dark ✓), nên vạch KHÔNG thêm màu nào\n * và không cần đo thêm. Đây đúng là WCAG 1.4.1: tín hiệu không được chỉ nằm ở màu — bản vạch còn\n * đọc ra ở chế độ xám, chỗ mà mọi khác biệt sắc độ đều biến mất.\n *\n * ⚠ Vì vậy: KHÔNG được \"đơn giản hoá\" `warning` xuống còn mỗi `bg-warning-ink`. Bỏ vạch đi là\n * API đổi tông mà không đổi được gì ở ba trên bốn bảng màu — chỗ gọi tưởng đã cảnh báo, người\n * dùng không thấy gì. Cùng lý do đó, ĐỪNG thêm tông thứ năm nào nữa mà chưa đo lại bảng trên.\n *\n * Vạch chỉ là cách nói \"sắp chạm ngưỡng\" trên chính cái thanh; nó KHÔNG thay được một câu chữ.\n * Chỗ nào cần nói rõ ngưỡng là bao nhiêu và làm gì tiếp thì vẫn phải có `Alert`/`StatusPill` hoặc\n * một dòng `text-warning-ink` cạnh thanh.\n */\n\n/*\n * HAI `cva`, không phải hai bảng `Record`.\n *\n * Không phải chuyện phong cách: cổng màu (`scripts/check-contrast.mjs`) chỉ bóc chuỗi lớp từ ba\n * chỗ — thuộc tính `className`, `cva(base, { variants })`, và đối số TRỰC TIẾP của `cn()`. Class\n * nằm trong một `Record` rồi đi vào `cn()` qua biến là VÔ HÌNH với cổng: nó không báo lỗi, nó\n * lặng lẽ không đo — và miễn trừ của rãnh sẽ thành \"ruỗng\", đúng thứ cổng coi là nơi giấu lỗi.\n * Dạng `cva` cho cổng thấy cả base lẫn từng lá biến thể.\n */\nconst progressTrackVariants = cva(\"border-control-edge relative h-2 w-full overflow-hidden rounded-full border\", {\n variants: {\n tone: {\n default: \"bg-primary-ink/20\",\n success: \"bg-success-ink/20\",\n warning: \"bg-warning-ink/20\",\n destructive: \"bg-destructive-ink/20\"\n }\n },\n defaultVariants: { tone: \"default\" }\n});\n\nconst progressBarVariants = cva(\"h-full w-full flex-1 transition-transform duration-150 ease-out\", {\n variants: {\n tone: {\n default: \"bg-primary-ink\",\n success: \"bg-success-ink\",\n // `hazard-stripes` là PHẦN BẮT BUỘC của tông này, không phải trang trí — xem chú thích đầu file.\n warning: \"bg-warning-ink hazard-stripes\",\n destructive: \"bg-destructive-ink\"\n }\n },\n defaultVariants: { tone: \"default\" }\n});\n\n/** Tông của thanh — xem chú thích ở trên về việc vì sao `warning` phải kèm vạch chéo. */\nexport type ProgressTone = NonNullable<VariantProps<typeof progressBarVariants>[\"tone\"]>;\n\nexport interface ProgressProps\n extends React.ComponentProps<typeof ProgressPrimitive.Root>,\n VariantProps<typeof progressBarVariants> {}\n\n/** Mặc định của Radix — `max` không hợp lệ thì Radix cũng rơi về đúng số này cho `aria-*`. */\nconst DEFAULT_MAX = 100;\n\n/**\n * Kẹp `value` vào `[0, max]` — giá trị DUY NHẤT mà cả thanh lẫn Radix (`aria-*`) đọc (xem chú thích\n * đầu file). Không phải số hữu hạn (`null`, `undefined`, `NaN`) thì giữ nguyên: Radix coi là\n * indeterminate và thanh đứng ở 0.\n */\nfunction clampValue(value: number | null | undefined, max: number): number | null | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return value;\n return Math.min(Math.max(value, 0), max);\n}\n\nfunction Progress({ className, value, max, tone = \"default\", ...props }: ProgressProps) {\n const validMax = typeof max === \"number\" && max > 0 ? max : DEFAULT_MAX;\n const clamped = clampValue(value, validMax);\n const percent = typeof clamped === \"number\" && Number.isFinite(clamped) ? (clamped / validMax) * 100 : 0;\n\n return (\n <ProgressPrimitive.Root\n data-slot=\"progress\"\n data-tone={tone}\n className={cn(progressTrackVariants({ tone }), className)}\n value={clamped}\n max={max}\n {...props}\n >\n <ProgressPrimitive.Indicator\n data-slot=\"progress-indicator\"\n className={progressBarVariants({ tone })}\n style={{ transform: `translateX(-${100 - percent}%)` }}\n />\n </ProgressPrimitive.Root>\n );\n}\n\nexport { Progress, progressBarVariants, progressTrackVariants };\n"],"mappings":";AAwKM;AAtKN,YAAY,uBAAuB;AACnC,SAAS,WAA8B;AAEvC,SAAS,UAAU;AAwGnB,MAAM,wBAAwB,IAAI,+EAA+E;AAAA,EAC/G,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CAAC;AAED,MAAM,sBAAsB,IAAI,mEAAmE;AAAA,EACjG,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,SAAS;AAAA;AAAA,MAET,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,iBAAiB,EAAE,MAAM,UAAU;AACrC,CAAC;AAUD,MAAM,cAAc;AAOpB,SAAS,WAAW,OAAkC,KAAwC;AAC5F,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,GAAG;AACzC;AAEA,SAAS,SAAS,EAAE,WAAW,OAAO,KAAK,OAAO,WAAW,GAAG,MAAM,GAAkB;AACtF,QAAM,WAAW,OAAO,QAAQ,YAAY,MAAM,IAAI,MAAM;AAC5D,QAAM,UAAU,WAAW,OAAO,QAAQ;AAC1C,QAAM,UAAU,OAAO,YAAY,YAAY,OAAO,SAAS,OAAO,IAAK,UAAU,WAAY,MAAM;AAEvG,SACE;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,aAAU;AAAA,MACV,aAAW;AAAA,MACX,WAAW,GAAG,sBAAsB,EAAE,KAAK,CAAC,GAAG,SAAS;AAAA,MACxD,OAAO;AAAA,MACP;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,kBAAkB;AAAA,QAAlB;AAAA,UACC,aAAU;AAAA,UACV,WAAW,oBAAoB,EAAE,KAAK,CAAC;AAAA,UACvC,OAAO,EAAE,WAAW,eAAe,MAAM,OAAO,KAAK;AAAA;AAAA,MACvD;AAAA;AAAA,EACF;AAEJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comitor/ui",
3
- "version": "3.13.0",
3
+ "version": "3.13.1",
4
4
  "description": "Comitor design system — Shadcn/UI tùy biến theo brand Comitor + component dùng chung cho hệ sinh thái SaaS (Tasks, Chat, CRM, HR…)",
5
5
  "license": "MIT",
6
6
  "type": "module",