@comitor/ui 3.7.0 → 3.7.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.
|
@@ -38,13 +38,15 @@ function Badge({
|
|
|
38
38
|
children,
|
|
39
39
|
...props
|
|
40
40
|
}) {
|
|
41
|
-
const Comp = asChild && !onRemove ? Slot : "span";
|
|
42
41
|
if (onRemove && !removeLabel && typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
43
42
|
console.warn(
|
|
44
43
|
"[Badge] `onRemove` c\u1EA7n `removeLabel` \u2014 n\xFAt \u2715 ch\u1EC9 c\xF3 icon th\xEC t\xEAn tr\u1EE3 n\u0103ng l\xE0 th\u1EE9 duy nh\u1EA5t n\xF3i n\xF3 g\u1EE1 g\xEC."
|
|
45
44
|
);
|
|
46
45
|
}
|
|
47
|
-
|
|
46
|
+
if (asChild && !onRemove) {
|
|
47
|
+
return /* @__PURE__ */ jsx(Slot, { "data-slot": "badge", className: cn(badgeVariants({ variant, size }), className), ...props, children });
|
|
48
|
+
}
|
|
49
|
+
return /* @__PURE__ */ jsxs("span", { "data-slot": "badge", className: cn(badgeVariants({ variant, size }), onRemove && "pr-1", className), ...props, children: [
|
|
48
50
|
children,
|
|
49
51
|
onRemove ? (
|
|
50
52
|
/*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * Style port từ comitor-ds (biến thể tonal + 2 cỡ sm/md).\n * API giữ tương thích shadcn (cva + asChild + data-slot).\n *\n * ─ BIẾN THỂ TONAL: NỀN LÀ `--x`, CHỮ LÀ `--x-ink` ─\n *\n * Badge in chữ 10–12px nên phải đạt AA 4,5:1 trên chính cái tint mà nó đứng lên. Hợp đồng\n * ba vai trò (docs/hop-dong-mau.md §1) đã có sẵn bậc chữ cho việc này: nền là `--success` /\n * `--warning` / `--destructive`, chữ là bậc `-ink` tương ứng. Đo trên tint hợp lên `--card`:\n *\n * success-ink #216A48 trên bg-success/15 #E0F2E9 = 5,60:1 ✓ (light)\n * #4ADE80 trên #1E362C = 7,44:1 ✓ (dark)\n * warning-ink #7A5800 trên bg-warning/20 #FCF4DB = 5,93:1 ✓ (light)\n * #FACC15 trên #443C1B = 7,19:1 ✓ (dark)\n * destructive-ink #9A3537 trên bg-destructive/15 #F7E2E2 = 5,76:1 ✓ (light)\n * #F87171 trên #311D22 = 5,71:1 ✓ (dark)\n *\n * Bản trước dẫn xuất bậc tối tại chỗ bằng `color-mix(--x, --ink N%)` cho light rồi lật về\n * `dark:text-success` / `dark:text-warning` / `dark:text-red` cho dark. Số thì đạt, nhưng\n * NHÁNH DARK vi phạm hợp đồng: `--success`/`--warning` là token TÔ NỀN, không phải màu chữ —\n * app nào đổi `--success` cho hợp brand mình là badge hỏng chữ mà không có gì báo. Bậc `-ink`\n * mới là biến mang ràng buộc tương phản, và nó tự lật theo theme (styles.css khai `-ink` riêng\n * cho `.dark`) nên không cần biến thể `dark:` nào ở đây nữa.\n *\n * ⚠ Trần tint: `/20` là mức đậm nhất an toàn cho MỌI tone ở cả hai theme. `warning` chịu được\n * `/20` thoải mái (trần của vàng là `/60` light · `/36` dark) nhưng đỏ ở dark chỉ tới `/22` —\n * muốn đậm hơn `/15` phải tra bảng trần tint ở docs/hop-dong-mau.md §2.\n *\n * ─ VIỀN CỦA BIẾN THỂ `outline` ─\n * `border-control-edge` chứ không `border-border`: nền badge trong suốt nên VIỀN là thứ duy\n * nhất khoanh vùng nó → chịu ngưỡng 3:1 (WCAG 1.4.11). `--border` là bậc TRANG TRÍ, chỉ\n * 1,16:1 (light) / 1,17:1 (dark) — badge gần như tàng hình. `--control-edge`: 3,83:1 / 4,94:1 ✓ ở bảng\n * TƯƠNG PHẢN CAO; ở bảng MẶC ĐỊNH nó cũng chỉ 1,16:1 (light) / 2,00:1 (dark) trên `--card` (đo trên 3.3.4) —\n * khoản dưới ngưỡng bảng mặc định đã chấp nhận cho viền control, người cần tương phản bật bảng tương phản cao.\n *\n * ─ BIẾN THỂ `default`/`primary` ─\n * Vẫn là thang gold thô (`gold-100`/`gold-600` ở light, `gold-500/20`+`gold-200` ở dark) vì\n * cặp này đã đo và dư biên: 7,77:1 (light) · 10,23:1 (dark).\n */\nconst badgeVariants = cva(\n \"inline-flex items-center font-medium rounded-md whitespace-nowrap gap-1 [&>svg]:size-3 [&>svg]:pointer-events-none\",\n {\n variants: {\n variant: {\n default: \"bg-gold-100 text-gold-600 dark:bg-gold-500/20 dark:text-gold-200\",\n primary: \"bg-gold-100 text-gold-600 dark:bg-gold-500/20 dark:text-gold-200\",\n secondary: \"bg-secondary text-secondary-foreground\",\n outline: \"bg-transparent border border-control-edge text-foreground\",\n success: \"bg-green/10 dark:bg-green/20 text-green-ink\",\n warning: \"bg-yellow/10 dark:bg-yellow/20 text-yellow-ink\",\n destructive: \"bg-red/10 dark:bg-red/15 text-destructive-ink\"\n },\n size: {\n sm: \"px-1.5 py-0.5 text-[10px]\",\n md: \"px-2 py-0.5 text-xs\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n size: \"md\"\n }\n }\n);\n\nexport interface BadgeProps extends React.ComponentProps<\"span\">, VariantProps<typeof badgeVariants> {\n /** Render ra chính phần tử con (Radix Slot) thay vì thẻ <span>. Bị bỏ qua khi có `onRemove`. */\n asChild?: boolean;\n /**\n * Chip GỠ ĐƯỢC (từ 3.7.0): vẽ một nút ✕ ở cuối badge (người theo dõi, nhãn đã gắn…). Đi cùng\n * `removeLabel` — thiếu thì nút không có tên trợ năng và gói cảnh báo ở console lúc phát triển.\n */\n onRemove?: () => void;\n /**\n * Tên trợ năng của nút ✕ — gói KHÔNG có nhãn mặc định, cố ý: nút chỉ có icon thì tên là thứ DUY NHẤT nói\n * nó gỡ CÁI GÌ (\"Gỡ Lan khỏi người theo dõi\"), mà câu đó là chữ nghiệp vụ của app. Một mặc định chung\n * kiểu \"Remove\" lặp N lần trong một danh sách là N nút trùng tên. (Và nó giữ `badge.tsx` là file tĩnh —\n * đọc nhãn từ `ComitorLocaleProvider` sẽ buộc nó thành client.)\n *\n * Vì sao không ép bằng kiểu (union \"có `onRemove` thì bắt buộc `removeLabel`\"): `BadgeProps` là interface\n * đã publish, đổi sang union là gãy mọi `interface X extends BadgeProps` của app.\n */\n removeLabel?: string;\n /** Khoá riêng nút ✕ (vd đang lưu) mà badge vẫn hiện bình thường. */\n removeDisabled?: boolean;\n}\n\nfunction Badge({\n className,\n variant,\n size,\n asChild = false,\n onRemove,\n removeLabel,\n removeDisabled,\n children,\n ...props\n}: BadgeProps) {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/badge.tsx"],"sourcesContent":["import { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * Style port từ comitor-ds (biến thể tonal + 2 cỡ sm/md).\n * API giữ tương thích shadcn (cva + asChild + data-slot).\n *\n * ─ BIẾN THỂ TONAL: NỀN LÀ `--x`, CHỮ LÀ `--x-ink` ─\n *\n * Badge in chữ 10–12px nên phải đạt AA 4,5:1 trên chính cái tint mà nó đứng lên. Hợp đồng\n * ba vai trò (docs/hop-dong-mau.md §1) đã có sẵn bậc chữ cho việc này: nền là `--success` /\n * `--warning` / `--destructive`, chữ là bậc `-ink` tương ứng. Đo trên tint hợp lên `--card`:\n *\n * success-ink #216A48 trên bg-success/15 #E0F2E9 = 5,60:1 ✓ (light)\n * #4ADE80 trên #1E362C = 7,44:1 ✓ (dark)\n * warning-ink #7A5800 trên bg-warning/20 #FCF4DB = 5,93:1 ✓ (light)\n * #FACC15 trên #443C1B = 7,19:1 ✓ (dark)\n * destructive-ink #9A3537 trên bg-destructive/15 #F7E2E2 = 5,76:1 ✓ (light)\n * #F87171 trên #311D22 = 5,71:1 ✓ (dark)\n *\n * Bản trước dẫn xuất bậc tối tại chỗ bằng `color-mix(--x, --ink N%)` cho light rồi lật về\n * `dark:text-success` / `dark:text-warning` / `dark:text-red` cho dark. Số thì đạt, nhưng\n * NHÁNH DARK vi phạm hợp đồng: `--success`/`--warning` là token TÔ NỀN, không phải màu chữ —\n * app nào đổi `--success` cho hợp brand mình là badge hỏng chữ mà không có gì báo. Bậc `-ink`\n * mới là biến mang ràng buộc tương phản, và nó tự lật theo theme (styles.css khai `-ink` riêng\n * cho `.dark`) nên không cần biến thể `dark:` nào ở đây nữa.\n *\n * ⚠ Trần tint: `/20` là mức đậm nhất an toàn cho MỌI tone ở cả hai theme. `warning` chịu được\n * `/20` thoải mái (trần của vàng là `/60` light · `/36` dark) nhưng đỏ ở dark chỉ tới `/22` —\n * muốn đậm hơn `/15` phải tra bảng trần tint ở docs/hop-dong-mau.md §2.\n *\n * ─ VIỀN CỦA BIẾN THỂ `outline` ─\n * `border-control-edge` chứ không `border-border`: nền badge trong suốt nên VIỀN là thứ duy\n * nhất khoanh vùng nó → chịu ngưỡng 3:1 (WCAG 1.4.11). `--border` là bậc TRANG TRÍ, chỉ\n * 1,16:1 (light) / 1,17:1 (dark) — badge gần như tàng hình. `--control-edge`: 3,83:1 / 4,94:1 ✓ ở bảng\n * TƯƠNG PHẢN CAO; ở bảng MẶC ĐỊNH nó cũng chỉ 1,16:1 (light) / 2,00:1 (dark) trên `--card` (đo trên 3.3.4) —\n * khoản dưới ngưỡng bảng mặc định đã chấp nhận cho viền control, người cần tương phản bật bảng tương phản cao.\n *\n * ─ BIẾN THỂ `default`/`primary` ─\n * Vẫn là thang gold thô (`gold-100`/`gold-600` ở light, `gold-500/20`+`gold-200` ở dark) vì\n * cặp này đã đo và dư biên: 7,77:1 (light) · 10,23:1 (dark).\n */\nconst badgeVariants = cva(\n \"inline-flex items-center font-medium rounded-md whitespace-nowrap gap-1 [&>svg]:size-3 [&>svg]:pointer-events-none\",\n {\n variants: {\n variant: {\n default: \"bg-gold-100 text-gold-600 dark:bg-gold-500/20 dark:text-gold-200\",\n primary: \"bg-gold-100 text-gold-600 dark:bg-gold-500/20 dark:text-gold-200\",\n secondary: \"bg-secondary text-secondary-foreground\",\n outline: \"bg-transparent border border-control-edge text-foreground\",\n success: \"bg-green/10 dark:bg-green/20 text-green-ink\",\n warning: \"bg-yellow/10 dark:bg-yellow/20 text-yellow-ink\",\n destructive: \"bg-red/10 dark:bg-red/15 text-destructive-ink\"\n },\n size: {\n sm: \"px-1.5 py-0.5 text-[10px]\",\n md: \"px-2 py-0.5 text-xs\"\n }\n },\n defaultVariants: {\n variant: \"default\",\n size: \"md\"\n }\n }\n);\n\nexport interface BadgeProps extends React.ComponentProps<\"span\">, VariantProps<typeof badgeVariants> {\n /** Render ra chính phần tử con (Radix Slot) thay vì thẻ <span>. Bị bỏ qua khi có `onRemove`. */\n asChild?: boolean;\n /**\n * Chip GỠ ĐƯỢC (từ 3.7.0): vẽ một nút ✕ ở cuối badge (người theo dõi, nhãn đã gắn…). Đi cùng\n * `removeLabel` — thiếu thì nút không có tên trợ năng và gói cảnh báo ở console lúc phát triển.\n */\n onRemove?: () => void;\n /**\n * Tên trợ năng của nút ✕ — gói KHÔNG có nhãn mặc định, cố ý: nút chỉ có icon thì tên là thứ DUY NHẤT nói\n * nó gỡ CÁI GÌ (\"Gỡ Lan khỏi người theo dõi\"), mà câu đó là chữ nghiệp vụ của app. Một mặc định chung\n * kiểu \"Remove\" lặp N lần trong một danh sách là N nút trùng tên. (Và nó giữ `badge.tsx` là file tĩnh —\n * đọc nhãn từ `ComitorLocaleProvider` sẽ buộc nó thành client.)\n *\n * Vì sao không ép bằng kiểu (union \"có `onRemove` thì bắt buộc `removeLabel`\"): `BadgeProps` là interface\n * đã publish, đổi sang union là gãy mọi `interface X extends BadgeProps` của app.\n */\n removeLabel?: string;\n /** Khoá riêng nút ✕ (vd đang lưu) mà badge vẫn hiện bình thường. */\n removeDisabled?: boolean;\n}\n\nfunction Badge({\n className,\n variant,\n size,\n asChild = false,\n onRemove,\n removeLabel,\n removeDisabled,\n children,\n ...props\n}: BadgeProps) {\n // `typeof process` — cùng lý do với `circular-progress.tsx`: ESM thuần trên trình duyệt không có `process`.\n if (onRemove && !removeLabel && typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[Badge] `onRemove` cần `removeLabel` — nút ✕ chỉ có icon thì tên trợ năng là thứ duy nhất nói nó gỡ gì.\"\n );\n }\n\n /*\n * ⚠ `asChild` đi RIÊNG một nhánh, chỉ trao `children`: `Slot` của Radix đòi ĐÚNG MỘT con. Viết chung một JSX với\n * nhánh `onRemove` thì con của nó là mảng `[children, null]` và `React.Children.only` ném lỗi — mọi\n * `<Badge asChild><a/></Badge>` đổ trang (3.7.0, sửa ở 3.7.1; `smoke:entries` nay render ca này).\n */\n if (asChild && !onRemove) {\n return (\n <Slot data-slot=\"badge\" className={cn(badgeVariants({ variant, size }), className)} {...props}>\n {children}\n </Slot>\n );\n }\n\n return (\n <span data-slot=\"badge\" className={cn(badgeVariants({ variant, size }), onRemove && \"pr-1\", className)} {...props}>\n {children}\n {onRemove ? (\n /*\n * `opacity-70`, KHÔNG `text-x/70`: icon lucide là nhiều nét cắt nhau, alpha trong `color` tô chỗ\n * giao hai lần (docs/hop-dong-mau.md §7b). Vùng bấm `size-4` nằm gọn trong chiều cao badge `md`.\n */\n <button\n type=\"button\"\n data-slot=\"badge-remove\"\n aria-label={removeLabel}\n disabled={removeDisabled}\n onClick={onRemove}\n className={cn(\n \"inline-flex size-4 shrink-0 items-center justify-center rounded-sm opacity-70 transition-opacity\",\n \"hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50\",\n \"disabled:pointer-events-none disabled:opacity-40\"\n )}\n >\n <X aria-hidden=\"true\" className=\"size-3\" />\n </button>\n ) : null}\n </span>\n );\n}\n\nexport { Badge, badgeVariants };\n"],"mappings":"AAqHM,cAOF,YAPE;AArHN,SAAS,YAAY;AACrB,SAAS,WAA8B;AACvC,SAAS,SAAS;AAElB,SAAS,UAAU;AAyCnB,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,WAAW;AAAA,QACX,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAwBA,SAAS,MAAM;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAe;AAEb,MAAI,YAAY,CAAC,eAAe,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa,cAAc;AACvG,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAOA,MAAI,WAAW,CAAC,UAAU;AACxB,WACE,oBAAC,QAAK,aAAU,SAAQ,WAAW,GAAG,cAAc,EAAE,SAAS,KAAK,CAAC,GAAG,SAAS,GAAI,GAAG,OACrF,UACH;AAAA,EAEJ;AAEA,SACE,qBAAC,UAAK,aAAU,SAAQ,WAAW,GAAG,cAAc,EAAE,SAAS,KAAK,CAAC,GAAG,YAAY,QAAQ,SAAS,GAAI,GAAG,OACzG;AAAA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,aAAU;AAAA,UACV,cAAY;AAAA,UACZ,UAAU;AAAA,UACV,SAAS;AAAA,UACT,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UAEA,8BAAC,KAAE,eAAY,QAAO,WAAU,UAAS;AAAA;AAAA,MAC3C;AAAA,QACE;AAAA,KACN;AAEJ;","names":[]}
|
package/package.json
CHANGED