@qijenchen/design-system 0.1.0-beta.87 → 0.1.0-beta.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  2. package/dist/components/Breadcrumb/breadcrumb.js +13 -51
  3. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  4. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  5. package/dist/components/DataTable/data-table.js +3 -40
  6. package/dist/components/DataTable/data-table.js.map +1 -1
  7. package/dist/components/Tag/tag.d.ts.map +1 -1
  8. package/dist/components/Tag/tag.js +11 -15
  9. package/dist/components/Tag/tag.js.map +1 -1
  10. package/dist/hooks/use-truncated.d.ts +27 -0
  11. package/dist/hooks/use-truncated.d.ts.map +1 -0
  12. package/dist/hooks/use-truncated.js +57 -0
  13. package/dist/hooks/use-truncated.js.map +1 -0
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +3 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/patterns/element-anatomy/index.d.ts +1 -0
  19. package/dist/patterns/element-anatomy/index.d.ts.map +1 -1
  20. package/dist/patterns/element-anatomy/index.js +2 -0
  21. package/dist/patterns/element-anatomy/index.js.map +1 -1
  22. package/dist/patterns/element-anatomy/item-anatomy.js +1 -1
  23. package/dist/patterns/element-anatomy/truncated-text.d.ts +27 -0
  24. package/dist/patterns/element-anatomy/truncated-text.d.ts.map +1 -0
  25. package/dist/patterns/element-anatomy/truncated-text.js +16 -0
  26. package/dist/patterns/element-anatomy/truncated-text.js.map +1 -0
  27. package/ds-story-manifest.json +4 -3
  28. package/llms-full.txt +1 -1
  29. package/llms.txt +1 -1
  30. package/package.json +1 -1
  31. package/src/components/Breadcrumb/breadcrumb.stories.tsx +66 -0
  32. package/src/components/Breadcrumb/breadcrumb.tsx +27 -77
  33. package/src/components/Calendar/calendar.stories.tsx +1 -1
  34. package/src/components/DataTable/data-table.tsx +9 -47
  35. package/src/components/Tag/tag.tsx +16 -16
  36. package/src/hooks/use-truncated.ts +110 -0
  37. package/src/index.ts +3 -0
  38. package/src/patterns/element-anatomy/index.ts +1 -0
  39. package/src/patterns/element-anatomy/truncated-text.spec.md +57 -0
  40. package/src/patterns/element-anatomy/truncated-text.tsx +61 -0
@@ -1 +1 @@
1
- {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/components/Tag/tag.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAwB9C,QAAA,MAAM,WAAW;;;8EAuBhB,CAAA;AAWD,MAAM,WAAW,QACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,EACpE,YAAY,CAAC,OAAO,WAAW,CAAC;IAClC,qDAAqD;IACrD,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,sCAAsC;IACtC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,iLAAiL;IACjL,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,0IAA0I;IAC1I,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2GAA2G;IAC3G,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAwID,QAAA,MAAM,GAAG,iFAAuD,CAAA;AAKhE,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CV,CAAA;AAEV,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"tag.d.ts","sourceRoot":"","sources":["../../../src/components/Tag/tag.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAyB9C,QAAA,MAAM,WAAW;;;8EAuBhB,CAAA;AAWD,MAAM,WAAW,QACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,EACpE,YAAY,CAAC,OAAO,WAAW,CAAC;IAClC,qDAAqD;IACrD,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,sCAAsC;IACtC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,iLAAiL;IACjL,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,0IAA0I;IAC1I,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2GAA2G;IAC3G,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAuID,QAAA,MAAM,GAAG,iFAAuD,CAAA;AAKhE,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CV,CAAA;AAEV,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAA"}
@@ -4,6 +4,7 @@ import { cva } from "class-variance-authority";
4
4
  import { X } from "lucide-react";
5
5
  import { cn } from "../../lib/utils.js";
6
6
  import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/tooltip.js";
7
+ import { useTruncated } from "../../hooks/use-truncated.js";
7
8
  import { ItemInlineActionButton } from "../../patterns/element-anatomy/item-anatomy.js";
8
9
  import { CAT_SUBTLE, CAT_SOLID, CAT_INTERACT } from "../../tokens/categorical-color.js";
9
10
  let _measureCtx = null;
@@ -63,15 +64,11 @@ function TagDismiss({ onRemove, label, solid, color }) {
63
64
  }
64
65
  function TagInner({ className, color, size, icon: Icon, avatar, onRemove, removeAriaLabel, dismissLabel, solid, unbounded = false, children, style, ...props }, forwardedRef) {
65
66
  const solidClass = solid ? SOLID_CLASSES[color ?? "neutral"] : void 0;
66
- const ownRef = React.useRef(null);
67
- const [isTruncated, setIsTruncated] = React.useState(false);
68
- React.useLayoutEffect(() => {
69
- const el = ownRef.current;
70
- if (!el) return;
71
- const ctx = getMeasureCtx();
72
- const check = () => {
67
+ const { ref: ownRef, isTruncated } = useTruncated({
68
+ measure: (el) => {
73
69
  const textSpan = el.querySelector("[data-tag-text]");
74
- if (!textSpan || !ctx) return;
70
+ const ctx = getMeasureCtx();
71
+ if (!textSpan || !ctx) return void 0;
75
72
  const text = textSpan.textContent || "";
76
73
  const cs = getComputedStyle(textSpan);
77
74
  ctx.font = `${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`;
@@ -79,13 +76,12 @@ function TagInner({ className, color, size, icon: Icon, avatar, onRemove, remove
79
76
  const padL = parseFloat(cs.paddingLeft) || 0;
80
77
  const padR = parseFloat(cs.paddingRight) || 0;
81
78
  const needed = textWidth + padL + padR;
82
- setIsTruncated(needed > textSpan.clientWidth + 1);
83
- };
84
- check();
85
- const obs = new ResizeObserver(check);
86
- obs.observe(el);
87
- return () => obs.disconnect();
88
- }, [children]);
79
+ return needed > textSpan.clientWidth + 1;
80
+ },
81
+ deps: [children],
82
+ timing: "layoutEffect",
83
+ recheckAfterPaint: false
84
+ });
89
85
  const label = typeof children === "string" ? children : "";
90
86
  if (process.env.NODE_ENV !== "production" && Icon && avatar) {
91
87
  console.warn(
@@ -1 +1 @@
1
- {"version":3,"file":"tag.js","sources":["../../../src/components/Tag/tag.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\nimport type { LucideIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Tooltip, TooltipTrigger, TooltipContent } from \"@/design-system/components/Tooltip/tooltip\"\nimport { ItemInlineActionButton } from \"@/design-system/patterns/element-anatomy/item-anatomy\"\nimport { CAT_SUBTLE, CAT_SOLID, CAT_INTERACT } from \"@/design-system/tokens/categorical-color\"\n\n// ── Tag(inline label)─────────────────────────────────────────────────────\n// 用於分類標籤、狀態標記、多選已選值。\n//\n// 三種尺寸(子元件補齊原則——消費端直接透傳 size,不做 mapping):\n// sm — 20px 高, 12px 字, 4px tag-px, font-medium(配 field sm)\n// md — 24px 高, 14px 字, 4px tag-px, font-normal(配 field md)— 預設\n// lg — 24px = md alias(配 field lg,子元件補齊原則)\n//\n// 截斷:max-w-40(160px),超出時文字 truncate + 自動 tooltip。\n// 用 Canvas measureText 偵測截斷(scrollWidth 在 flex 內不可靠)。\n\nlet _measureCtx: CanvasRenderingContext2D | null = null\nfunction getMeasureCtx() {\n if (!_measureCtx) _measureCtx = document.createElement('canvas').getContext('2d')\n return _measureCtx\n}\n\nconst tagVariants = cva(\n \"inline-flex items-center rounded-md border border-transparent transition-colors cursor-text\",\n {\n variants: {\n // color:categorical 色相(裝飾性分類,非語意狀態)。**消費 categorical-color SSOT**——\n // key X 一律對 `--color-X-*`(1:1,零 offset)。neutral 非色相(無 hue),用 secondary 底自處理。\n // 2026-06-04 修:原 `red` 誤接 `--color-deep-orange-*`(red=品牌紅 hue-25 ≠ deep-orange hue-38\n // ≠ 語意 --error〔= deep-orange〕);改消費 SSOT 後 red→`--color-red-*`,並補齊全 12 色相。\n color: {\n neutral: \"bg-secondary text-foreground\",\n ...CAT_SUBTLE,\n },\n size: {\n sm: \"h-5 px-1 text-caption font-medium\",\n md: \"h-6 px-1 text-body font-normal\",\n lg: \"h-6 px-1 text-body font-normal\",\n },\n },\n defaultVariants: {\n color: \"neutral\",\n size: \"md\",\n },\n }\n)\n\n// ── Solid variant 色彩(step-6 底 + on-emphasis 配對文字,消費 categorical-color SSOT)──\n// 白字 --on-emphasis(夠深的 hue)/ 深字 --on-emphasis-dark(亮 hue:yellow/amber/orange/lime);green 白字例外。\n// **消費 categorical-color SSOT**(CAT_SOLID,1:1 色相)。neutral 非色相,用 neutral-9\n// + --inverse-fg(light=白字, dark=深字,自動反轉)自處理。\nconst SOLID_CLASSES: Record<string, string> = {\n neutral: 'bg-[var(--color-neutral-9)] text-inverse-fg',\n ...CAT_SOLID,\n}\n\nexport interface TagProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'prefix' | 'color'>,\n VariantProps<typeof tagVariants> {\n /** 左側 icon(LucideIcon),由 Tag 統一 16px。與 avatar 互斥。 */\n icon?: LucideIcon\n /** 左側 avatar(ReactNode),與 icon 互斥。 */\n avatar?: React.ReactNode\n /** 可移除——Tag 自動渲染 remove 按鈕並控制尺寸與互動樣式(從集合移除 item) */\n onRemove?: () => void\n /** remove 按鈕的 aria-label 目標名(a11y)。children 為非字串 ReactNode 時建議傳,否則 SR 讀不出移除哪個 tag。預設取 string children。對齊 FileUpload `removeAriaLabel`(同「移除集合項目」場景 + DS `<動作>AriaLabel` 命名慣例)。 */\n removeAriaLabel?: string\n /** @deprecated 2026-07-17 改名為 `removeAriaLabel`(對齊 FileUpload + onRemove callback 語意;dismiss 保留給 Alert/Notice/Toast「通知被忽略」)。舊名過渡一版後移除。 */\n dismissLabel?: string\n /** 深底模式(step-6 背景 + on-emphasis 配對前景;亮色 hue yellow/amber/orange/lime 用深字 --on-emphasis-dark,green 白字例外) */\n solid?: boolean\n /**\n * 2026-05-15 Q3 真 SSOT fix(per user verbatim「同空間兩判斷點」+「不要冰山一角」):\n * Tag 寬度由 parent constrain,不套預設 max-w-40(160px)。用於 cell-as-input narrow cell\n * (< 160px)時 Tag fit cell 寬度 + truncate ellipsis,而非 160px 後被 cell `overflow-hidden`\n * 硬切。對齊「同 cell width → 同 overflow 判斷」SSOT。Default false 保 backward compat\n * (wrap layout / pill rail 等仍受 160px 保護)。\n */\n unbounded?: boolean\n}\n\n// ── Solid dismiss hover/active bg ──\n// **消費 categorical-color SSOT**(CAT_INTERACT,semantic --{hue}-hover/active token),\n// 跟 --primary-hover/active 同模式:solid 色彩 shade change(hover 較亮 step、active 較暗 step),\n// 在 semantic 層做 dark mode swap 確保方向跨 mode 一致。\n// neutral 特例:bg 是 neutral-9 隨 mode 反轉,用 --inverse-neutral-* 鏡射,自處理。\nconst SOLID_DISMISS_HOVER: Record<string, { hover: string; active: string }> = {\n neutral: { hover: 'var(--inverse-neutral-hover)', active: 'var(--inverse-neutral-active)' },\n ...CAT_INTERACT,\n}\n\n// ── Dismiss(internal)────────────────────────────────────────────────────\n// 走 `ItemInlineActionButton`(item-anatomy SSOT)+ `hoverBgClassName` override prop\n// (2026-05-01 整合,消除原 Tag 自刻 `<button>` 繞 DS infra 的 tech debt)。\n//\n// 視覺對齊:`size=\"md\"` → icon 16 / hover-bg 18,跟 Tag 既有手刻幾何完全相等。\n// Solid variant(blue/green/red 等)透過 `hoverBgClassName` 套色相 override token;\n// Subtle variant 落用 ItemInlineActionButton 預設 neutral-hover。\n// 圖標色繼承 Tag 文字色 → `text-current` 三態覆寫。\n\nfunction TagDismiss({ onRemove, label, solid, color }: { onRemove: () => void; label: string; solid?: boolean; color?: string }) {\n const solidColors = solid && color ? SOLID_DISMISS_HOVER[color] : undefined\n\n return (\n <ItemInlineActionButton\n icon={X}\n size=\"md\"\n onClick={(e) => { e.stopPropagation(); onRemove() }}\n aria-label={label ? `移除 ${label}` : '移除'}\n style={solidColors ? ({ '--dismiss-hover': solidColors.hover, '--dismiss-active': solidColors.active } as React.CSSProperties) : undefined}\n hoverBgClassName={\n solidColors\n ? 'group-hover/action:bg-[var(--dismiss-hover)] group-active/action:bg-[var(--dismiss-active)]'\n : undefined\n }\n // Override default fg-muted → 繼承 Tag 文字色(label 同色)\n className=\"text-current hover:text-current active:text-current\"\n />\n )\n}\n\nfunction TagInner(\n { className, color, size, icon: Icon, avatar, onRemove, removeAriaLabel, dismissLabel, solid, unbounded = false, children, style, ...props }: TagProps,\n forwardedRef: React.ForwardedRef<HTMLDivElement>,\n) {\n const solidClass = solid ? SOLID_CLASSES[color ?? 'neutral'] : undefined\n const ownRef = React.useRef<HTMLDivElement | null>(null)\n const [isTruncated, setIsTruncated] = React.useState(false)\n\n React.useLayoutEffect(() => {\n const el = ownRef.current\n if (!el) return\n const ctx = getMeasureCtx()\n const check = () => {\n const textSpan = el.querySelector('[data-tag-text]')\n if (!textSpan || !ctx) return\n const text = textSpan.textContent || ''\n const cs = getComputedStyle(textSpan)\n ctx.font = `${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`\n const textWidth = ctx.measureText(text).width\n const padL = parseFloat(cs.paddingLeft) || 0\n const padR = parseFloat(cs.paddingRight) || 0\n const needed = textWidth + padL + padR\n setIsTruncated(needed > (textSpan as HTMLElement).clientWidth + 1)\n }\n check()\n const obs = new ResizeObserver(check)\n obs.observe(el)\n return () => obs.disconnect()\n }, [children])\n\n const label = typeof children === 'string' ? children : ''\n\n // ── Dev-mode warning:icon 與 avatar 互斥 ──\n // 兩者同為 prefix slot(tag.spec.md Props 表「與 avatar 互斥」),同時傳會並列渲染破壞 Tag\n // 內部結構;原僅 jsdoc/spec 約束會靜默通過 → 2026-07-05 對齊 Button overlayBadge dev-warn\n // 先例(button.tsx「Dev-mode warning」段)補齊家族一致 runtime 防線。\n if (process.env.NODE_ENV !== 'production' && Icon && avatar) {\n // eslint-disable-next-line no-console\n console.warn(\n '[DS Tag] `icon` 與 `avatar` 互斥(同為 prefix slot),同時傳會並列渲染破壞 Tag 內部結構,請只擇一。SSOT:tag.spec.md Props 表 + tag.principles.stories.tsx IconRule。'\n )\n }\n\n const tag = (\n <div\n ref={(el) => {\n ownRef.current = el\n if (typeof forwardedRef === 'function') forwardedRef(el)\n else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = el\n }}\n data-tag-root=\"\"\n // 截斷時條件性可 focus(2026-07-06 user 拍板「確認世界級有這樣做就做」——APG Tooltip\n // pattern 原文:tooltip 在元素「收到鍵盤焦點」或 hover 時顯示,trigger 可 focus 是\n // W3C 官方 pattern 前提;Radix Tooltip focus-open 自動生效 → 鍵盤 Tab 到即見全文。\n // 未截斷不入 tab 序(維持 tag.spec「靜態 Tag 不取得 focus」預設,不汙染 tab order)。\n tabIndex={isTruncated ? 0 : undefined}\n className={cn(\n tagVariants({ color, size }),\n solidClass,\n 'w-fit min-w-0 overflow-hidden',\n isTruncated && 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n className,\n )}\n // 2026-05-18 Round 5 fix(per Codex M31 Round 5 verdict + user 拍板「那就開始做」):\n // 用 CSS var `--combobox-tag-area-inline-size`(由 Combobox useOverflowCount JS-injected)取代\n // `min(100%, 10rem)` cyclic percentage。CSS Sizing 3 §5.2.1:percentage 在 indefinite containing\n // block 退化為 initial value → Round 4 的 100% 沒 enforce。改 explicit px 值(JS measured)避此 trap。\n // unbounded=true:cap = inject 寬(回 cell-as-input narrow cell 原 behavior)\n // default:cap = min(inject 寬, 160px)— 兩 cap 取小。fallback(無 var,Form context 等)= 100% / 10rem。\n style={{\n maxWidth: unbounded\n ? 'var(--combobox-tag-area-inline-size, 100%)'\n : 'min(var(--combobox-tag-area-inline-size, 10rem), 10rem)',\n ...style,\n }}\n {...props}\n >\n {Icon && <Icon size={16} aria-hidden />}\n {avatar && <span className=\"shrink-0 w-4 h-4 rounded-full overflow-hidden inline-grid place-content-center [&>*]:w-full [&>*]:h-full\">{avatar}</span>}\n <span data-tag-text=\"\" className=\"px-1 truncate min-w-0\">{children}</span>\n {onRemove && <TagDismiss onRemove={onRemove} label={removeAriaLabel ?? dismissLabel ?? label} solid={solid} color={color ?? 'neutral'} />}\n </div>\n )\n\n if (!isTruncated) return tag\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{tag}</TooltipTrigger>\n <TooltipContent>{children}</TooltipContent>\n </Tooltip>\n )\n}\n\nconst Tag = React.forwardRef<HTMLDivElement, TagProps>(TagInner)\nTag.displayName = 'Tag'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const tagMeta = {\n component: 'Tag',\n family: 3,\n // categorical 色相(裝飾性分類,非語意狀態)。**1:1 對 `--color-{hue}-*` primitive,零 offset**。\n // 不對應語意 token——語意狀態(error/info/success/warning)走 Notice / Alert 等狀態元件,\n // 不是 Tag 色相。2026-06-04 修:移除原「red 對應 --error / blue 對應 --info ...」誤導框架\n // (red = 品牌紅 hue-25,跟語意 --error〔= deep-orange〕無關)。\n variants: {\n neutral: { purpose: '通用分類、草稿、無特定語義(secondary 底)' },\n blue: { purpose: 'categorical 色相(--color-blue-*)' },\n green: { purpose: 'categorical 色相(--color-green-*)' },\n 'deep-orange': { purpose: 'categorical 色相(--color-deep-orange-*,hue 38)' },\n yellow: { purpose: 'categorical 色相(--color-yellow-*,淺底深字)' },\n red: { purpose: 'categorical 色相(--color-red-*,品牌紅家族 hue 25;≠ 語意 --error)' },\n orange: { purpose: 'categorical 色相(--color-orange-*)' },\n amber: { purpose: 'categorical 色相(--color-amber-*,淺底深字)' },\n lime: { purpose: 'categorical 色相(--color-lime-*)' },\n turquoise: { purpose: 'categorical 色相(--color-turquoise-*)' },\n indigo: { purpose: 'categorical 色相(--color-indigo-*)' },\n purple: { purpose: 'categorical 色相(--color-purple-*)' },\n magenta: { purpose: 'categorical 色相(--color-magenta-*)' },\n },\n sizes: {\n // Tag 尺寸不引用 field-height token(spec「尺寸」段——Tag 與 Field 尺寸獨立;段名指法免行號漂移)。\n // height = Tag 自身高度(cva h-5/h-6/h-6 = 20/24/24,lg = md alias)。\n // iconSize 全尺寸統一 16(本檔 CAT icon render 硬寫 size={16})。\n sm: { height: 20, iconSize: 16, typography: 'caption' },\n md: { height: 24, iconSize: 16, typography: 'body' },\n lg: { height: 24, iconSize: 16, typography: 'body' },\n },\n // Tag 為純展示 indicator,無互動 state(spec「為何無 StateBehavior」段;段名指法免行號漂移)。\n // 唯一行為 dismiss 屬 Inline Action pattern,非 Tag 自有 state。\n states: ['default'],\n tokens: {\n // 2026-07-04 audit 註記:另有動態色相家族不逐一列 — CAT_SUBTLE 12 色相\n // bg-[var(--color-{hue}-1)] + step-7 深字、CAT_SOLID step-6 底 + --on-emphasis(-dark) 配字、\n // neutral solid bg-[var(--color-neutral-9)] — 全由 categorical-color.ts SSOT 衍生\n //(anatomy TOKEN_MAP 以 stripVar 機械衍生,零漂移)。\n bg: ['bg-neutral-active', 'bg-neutral-hover', 'bg-secondary', 'bg-transparent'],\n fg: ['text-foreground', 'text-inverse-fg'],\n ring: [],\n },\n defaultVariant: 'neutral',\n defaultSize: 'md',\n} as const\n\nexport { Tag, tagVariants }\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tag.js","sources":["../../../src/components/Tag/tag.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X } from \"lucide-react\"\nimport type { LucideIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Tooltip, TooltipTrigger, TooltipContent } from \"@/design-system/components/Tooltip/tooltip\"\nimport { useTruncated } from \"@/design-system/hooks/use-truncated\"\nimport { ItemInlineActionButton } from \"@/design-system/patterns/element-anatomy/item-anatomy\"\nimport { CAT_SUBTLE, CAT_SOLID, CAT_INTERACT } from \"@/design-system/tokens/categorical-color\"\n\n// ── Tag(inline label)─────────────────────────────────────────────────────\n// 用於分類標籤、狀態標記、多選已選值。\n//\n// 三種尺寸(子元件補齊原則——消費端直接透傳 size,不做 mapping):\n// sm — 20px 高, 12px 字, 4px tag-px, font-medium(配 field sm)\n// md — 24px 高, 14px 字, 4px tag-px, font-normal(配 field md)— 預設\n// lg — 24px = md alias(配 field lg,子元件補齊原則)\n//\n// 截斷:max-w-40(160px),超出時文字 truncate + 自動 tooltip。\n// 用 Canvas measureText 偵測截斷(scrollWidth 在 flex 內不可靠)。\n\nlet _measureCtx: CanvasRenderingContext2D | null = null\nfunction getMeasureCtx() {\n if (!_measureCtx) _measureCtx = document.createElement('canvas').getContext('2d')\n return _measureCtx\n}\n\nconst tagVariants = cva(\n \"inline-flex items-center rounded-md border border-transparent transition-colors cursor-text\",\n {\n variants: {\n // color:categorical 色相(裝飾性分類,非語意狀態)。**消費 categorical-color SSOT**——\n // key X 一律對 `--color-X-*`(1:1,零 offset)。neutral 非色相(無 hue),用 secondary 底自處理。\n // 2026-06-04 修:原 `red` 誤接 `--color-deep-orange-*`(red=品牌紅 hue-25 ≠ deep-orange hue-38\n // ≠ 語意 --error〔= deep-orange〕);改消費 SSOT 後 red→`--color-red-*`,並補齊全 12 色相。\n color: {\n neutral: \"bg-secondary text-foreground\",\n ...CAT_SUBTLE,\n },\n size: {\n sm: \"h-5 px-1 text-caption font-medium\",\n md: \"h-6 px-1 text-body font-normal\",\n lg: \"h-6 px-1 text-body font-normal\",\n },\n },\n defaultVariants: {\n color: \"neutral\",\n size: \"md\",\n },\n }\n)\n\n// ── Solid variant 色彩(step-6 底 + on-emphasis 配對文字,消費 categorical-color SSOT)──\n// 白字 --on-emphasis(夠深的 hue)/ 深字 --on-emphasis-dark(亮 hue:yellow/amber/orange/lime);green 白字例外。\n// **消費 categorical-color SSOT**(CAT_SOLID,1:1 色相)。neutral 非色相,用 neutral-9\n// + --inverse-fg(light=白字, dark=深字,自動反轉)自處理。\nconst SOLID_CLASSES: Record<string, string> = {\n neutral: 'bg-[var(--color-neutral-9)] text-inverse-fg',\n ...CAT_SOLID,\n}\n\nexport interface TagProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'prefix' | 'color'>,\n VariantProps<typeof tagVariants> {\n /** 左側 icon(LucideIcon),由 Tag 統一 16px。與 avatar 互斥。 */\n icon?: LucideIcon\n /** 左側 avatar(ReactNode),與 icon 互斥。 */\n avatar?: React.ReactNode\n /** 可移除——Tag 自動渲染 remove 按鈕並控制尺寸與互動樣式(從集合移除 item) */\n onRemove?: () => void\n /** remove 按鈕的 aria-label 目標名(a11y)。children 為非字串 ReactNode 時建議傳,否則 SR 讀不出移除哪個 tag。預設取 string children。對齊 FileUpload `removeAriaLabel`(同「移除集合項目」場景 + DS `<動作>AriaLabel` 命名慣例)。 */\n removeAriaLabel?: string\n /** @deprecated 2026-07-17 改名為 `removeAriaLabel`(對齊 FileUpload + onRemove callback 語意;dismiss 保留給 Alert/Notice/Toast「通知被忽略」)。舊名過渡一版後移除。 */\n dismissLabel?: string\n /** 深底模式(step-6 背景 + on-emphasis 配對前景;亮色 hue yellow/amber/orange/lime 用深字 --on-emphasis-dark,green 白字例外) */\n solid?: boolean\n /**\n * 2026-05-15 Q3 真 SSOT fix(per user verbatim「同空間兩判斷點」+「不要冰山一角」):\n * Tag 寬度由 parent constrain,不套預設 max-w-40(160px)。用於 cell-as-input narrow cell\n * (< 160px)時 Tag fit cell 寬度 + truncate ellipsis,而非 160px 後被 cell `overflow-hidden`\n * 硬切。對齊「同 cell width → 同 overflow 判斷」SSOT。Default false 保 backward compat\n * (wrap layout / pill rail 等仍受 160px 保護)。\n */\n unbounded?: boolean\n}\n\n// ── Solid dismiss hover/active bg ──\n// **消費 categorical-color SSOT**(CAT_INTERACT,semantic --{hue}-hover/active token),\n// 跟 --primary-hover/active 同模式:solid 色彩 shade change(hover 較亮 step、active 較暗 step),\n// 在 semantic 層做 dark mode swap 確保方向跨 mode 一致。\n// neutral 特例:bg 是 neutral-9 隨 mode 反轉,用 --inverse-neutral-* 鏡射,自處理。\nconst SOLID_DISMISS_HOVER: Record<string, { hover: string; active: string }> = {\n neutral: { hover: 'var(--inverse-neutral-hover)', active: 'var(--inverse-neutral-active)' },\n ...CAT_INTERACT,\n}\n\n// ── Dismiss(internal)────────────────────────────────────────────────────\n// 走 `ItemInlineActionButton`(item-anatomy SSOT)+ `hoverBgClassName` override prop\n// (2026-05-01 整合,消除原 Tag 自刻 `<button>` 繞 DS infra 的 tech debt)。\n//\n// 視覺對齊:`size=\"md\"` → icon 16 / hover-bg 18,跟 Tag 既有手刻幾何完全相等。\n// Solid variant(blue/green/red 等)透過 `hoverBgClassName` 套色相 override token;\n// Subtle variant 落用 ItemInlineActionButton 預設 neutral-hover。\n// 圖標色繼承 Tag 文字色 → `text-current` 三態覆寫。\n\nfunction TagDismiss({ onRemove, label, solid, color }: { onRemove: () => void; label: string; solid?: boolean; color?: string }) {\n const solidColors = solid && color ? SOLID_DISMISS_HOVER[color] : undefined\n\n return (\n <ItemInlineActionButton\n icon={X}\n size=\"md\"\n onClick={(e) => { e.stopPropagation(); onRemove() }}\n aria-label={label ? `移除 ${label}` : '移除'}\n style={solidColors ? ({ '--dismiss-hover': solidColors.hover, '--dismiss-active': solidColors.active } as React.CSSProperties) : undefined}\n hoverBgClassName={\n solidColors\n ? 'group-hover/action:bg-[var(--dismiss-hover)] group-active/action:bg-[var(--dismiss-active)]'\n : undefined\n }\n // Override default fg-muted → 繼承 Tag 文字色(label 同色)\n className=\"text-current hover:text-current active:text-current\"\n />\n )\n}\n\nfunction TagInner(\n { className, color, size, icon: Icon, avatar, onRemove, removeAriaLabel, dismissLabel, solid, unbounded = false, children, style, ...props }: TagProps,\n forwardedRef: React.ForwardedRef<HTMLDivElement>,\n) {\n const solidClass = solid ? SOLID_CLASSES[color ?? 'neutral'] : undefined\n // 截斷偵測用 SSOT 引擎 `useTruncated`(patterns/element-anatomy 的 hook)。Tag 走**變異型**:\n // - measure = Canvas measureText(flex 內 scrollWidth 不可靠,line 20):observe root、量測內層\n // `[data-tag-text]` span(trigger≠量測元素);回 undefined = 無法量測時保留前一 state。\n // - timing='layoutEffect'(避免 paint flash)/ deps=[children](children 變重量)/ recheckAfterPaint=false\n // (Tag 原本即無 rAF/timeout 二次量)—— 三處變異全走 options,行為零漂移。\n const { ref: ownRef, isTruncated } = useTruncated<HTMLDivElement>({\n measure: (el) => {\n const textSpan = el.querySelector('[data-tag-text]')\n const ctx = getMeasureCtx()\n if (!textSpan || !ctx) return undefined\n const text = textSpan.textContent || ''\n const cs = getComputedStyle(textSpan)\n ctx.font = `${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`\n const textWidth = ctx.measureText(text).width\n const padL = parseFloat(cs.paddingLeft) || 0\n const padR = parseFloat(cs.paddingRight) || 0\n const needed = textWidth + padL + padR\n return needed > (textSpan as HTMLElement).clientWidth + 1\n },\n deps: [children],\n timing: 'layoutEffect',\n recheckAfterPaint: false,\n })\n\n const label = typeof children === 'string' ? children : ''\n\n // ── Dev-mode warning:icon 與 avatar 互斥 ──\n // 兩者同為 prefix slot(tag.spec.md Props 表「與 avatar 互斥」),同時傳會並列渲染破壞 Tag\n // 內部結構;原僅 jsdoc/spec 約束會靜默通過 → 2026-07-05 對齊 Button overlayBadge dev-warn\n // 先例(button.tsx「Dev-mode warning」段)補齊家族一致 runtime 防線。\n if (process.env.NODE_ENV !== 'production' && Icon && avatar) {\n // eslint-disable-next-line no-console\n console.warn(\n '[DS Tag] `icon` 與 `avatar` 互斥(同為 prefix slot),同時傳會並列渲染破壞 Tag 內部結構,請只擇一。SSOT:tag.spec.md Props 表 + tag.principles.stories.tsx IconRule。'\n )\n }\n\n const tag = (\n <div\n ref={(el) => {\n ownRef.current = el\n if (typeof forwardedRef === 'function') forwardedRef(el)\n else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = el\n }}\n data-tag-root=\"\"\n // 截斷時條件性可 focus(2026-07-06 user 拍板「確認世界級有這樣做就做」——APG Tooltip\n // pattern 原文:tooltip 在元素「收到鍵盤焦點」或 hover 時顯示,trigger 可 focus 是\n // W3C 官方 pattern 前提;Radix Tooltip focus-open 自動生效 → 鍵盤 Tab 到即見全文。\n // 未截斷不入 tab 序(維持 tag.spec「靜態 Tag 不取得 focus」預設,不汙染 tab order)。\n tabIndex={isTruncated ? 0 : undefined}\n className={cn(\n tagVariants({ color, size }),\n solidClass,\n 'w-fit min-w-0 overflow-hidden',\n isTruncated && 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n className,\n )}\n // 2026-05-18 Round 5 fix(per Codex M31 Round 5 verdict + user 拍板「那就開始做」):\n // 用 CSS var `--combobox-tag-area-inline-size`(由 Combobox useOverflowCount JS-injected)取代\n // `min(100%, 10rem)` cyclic percentage。CSS Sizing 3 §5.2.1:percentage 在 indefinite containing\n // block 退化為 initial value → Round 4 的 100% 沒 enforce。改 explicit px 值(JS measured)避此 trap。\n // unbounded=true:cap = inject 寬(回 cell-as-input narrow cell 原 behavior)\n // default:cap = min(inject 寬, 160px)— 兩 cap 取小。fallback(無 var,Form context 等)= 100% / 10rem。\n style={{\n maxWidth: unbounded\n ? 'var(--combobox-tag-area-inline-size, 100%)'\n : 'min(var(--combobox-tag-area-inline-size, 10rem), 10rem)',\n ...style,\n }}\n {...props}\n >\n {Icon && <Icon size={16} aria-hidden />}\n {avatar && <span className=\"shrink-0 w-4 h-4 rounded-full overflow-hidden inline-grid place-content-center [&>*]:w-full [&>*]:h-full\">{avatar}</span>}\n <span data-tag-text=\"\" className=\"px-1 truncate min-w-0\">{children}</span>\n {onRemove && <TagDismiss onRemove={onRemove} label={removeAriaLabel ?? dismissLabel ?? label} solid={solid} color={color ?? 'neutral'} />}\n </div>\n )\n\n if (!isTruncated) return tag\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{tag}</TooltipTrigger>\n <TooltipContent>{children}</TooltipContent>\n </Tooltip>\n )\n}\n\nconst Tag = React.forwardRef<HTMLDivElement, TagProps>(TagInner)\nTag.displayName = 'Tag'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const tagMeta = {\n component: 'Tag',\n family: 3,\n // categorical 色相(裝飾性分類,非語意狀態)。**1:1 對 `--color-{hue}-*` primitive,零 offset**。\n // 不對應語意 token——語意狀態(error/info/success/warning)走 Notice / Alert 等狀態元件,\n // 不是 Tag 色相。2026-06-04 修:移除原「red 對應 --error / blue 對應 --info ...」誤導框架\n // (red = 品牌紅 hue-25,跟語意 --error〔= deep-orange〕無關)。\n variants: {\n neutral: { purpose: '通用分類、草稿、無特定語義(secondary 底)' },\n blue: { purpose: 'categorical 色相(--color-blue-*)' },\n green: { purpose: 'categorical 色相(--color-green-*)' },\n 'deep-orange': { purpose: 'categorical 色相(--color-deep-orange-*,hue 38)' },\n yellow: { purpose: 'categorical 色相(--color-yellow-*,淺底深字)' },\n red: { purpose: 'categorical 色相(--color-red-*,品牌紅家族 hue 25;≠ 語意 --error)' },\n orange: { purpose: 'categorical 色相(--color-orange-*)' },\n amber: { purpose: 'categorical 色相(--color-amber-*,淺底深字)' },\n lime: { purpose: 'categorical 色相(--color-lime-*)' },\n turquoise: { purpose: 'categorical 色相(--color-turquoise-*)' },\n indigo: { purpose: 'categorical 色相(--color-indigo-*)' },\n purple: { purpose: 'categorical 色相(--color-purple-*)' },\n magenta: { purpose: 'categorical 色相(--color-magenta-*)' },\n },\n sizes: {\n // Tag 尺寸不引用 field-height token(spec「尺寸」段——Tag 與 Field 尺寸獨立;段名指法免行號漂移)。\n // height = Tag 自身高度(cva h-5/h-6/h-6 = 20/24/24,lg = md alias)。\n // iconSize 全尺寸統一 16(本檔 CAT icon render 硬寫 size={16})。\n sm: { height: 20, iconSize: 16, typography: 'caption' },\n md: { height: 24, iconSize: 16, typography: 'body' },\n lg: { height: 24, iconSize: 16, typography: 'body' },\n },\n // Tag 為純展示 indicator,無互動 state(spec「為何無 StateBehavior」段;段名指法免行號漂移)。\n // 唯一行為 dismiss 屬 Inline Action pattern,非 Tag 自有 state。\n states: ['default'],\n tokens: {\n // 2026-07-04 audit 註記:另有動態色相家族不逐一列 — CAT_SUBTLE 12 色相\n // bg-[var(--color-{hue}-1)] + step-7 深字、CAT_SOLID step-6 底 + --on-emphasis(-dark) 配字、\n // neutral solid bg-[var(--color-neutral-9)] — 全由 categorical-color.ts SSOT 衍生\n //(anatomy TOKEN_MAP 以 stripVar 機械衍生,零漂移)。\n bg: ['bg-neutral-active', 'bg-neutral-hover', 'bg-secondary', 'bg-transparent'],\n fg: ['text-foreground', 'text-inverse-fg'],\n ring: [],\n },\n defaultVariant: 'neutral',\n defaultSize: 'md',\n} as const\n\nexport { Tag, tagVariants }\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ export interface UseTruncatedOptions {
3
+ /**
4
+ * 偵測策略:回傳 `true` = 截斷、`false` = 未截斷、`undefined` = 本次無法量測(保留前一狀態)。
5
+ * 預設 = `el.scrollWidth > el.clientWidth`(觀察元素自身)。
6
+ * Tag 傳自訂 fn:observe root、內部 `querySelector('[data-tag-text]')` + Canvas measureText。
7
+ */
8
+ measure?: (observedEl: HTMLElement) => boolean | undefined;
9
+ /** deps 變更時 cleanup + 重新量測 / 重新訂閱(Tag 傳 `[children]`)。預設 `[]`(mount-once)。 */
10
+ deps?: React.DependencyList;
11
+ /**
12
+ * 是否在首次 paint 後以 `requestAnimationFrame` + `setTimeout(100)` 再量一次,捕獲首幀 layout
13
+ * 未完成 / 字型 async load 的假陰性(Breadcrumb 已採此 robust 版)。預設 `true`。
14
+ */
15
+ recheckAfterPaint?: boolean;
16
+ /** `'layoutEffect'` = `useLayoutEffect`(量測在 paint 前,避免 flash;Tag 用)/ `'effect'` = `useEffect`。預設 `'effect'`。 */
17
+ timing?: 'effect' | 'layoutEffect';
18
+ }
19
+ /**
20
+ * 偵測 ref 所指元素的文字是否被截斷,回傳 `{ ref, isTruncated }`。
21
+ * `isTruncated` 用來驅動 Tooltip「僅截斷時顯示」(對齊 `tooltip.principles.stories.tsx`「沒被截斷就不該顯示 tooltip」)。
22
+ */
23
+ export declare function useTruncated<E extends HTMLElement = HTMLElement>(options?: UseTruncatedOptions): {
24
+ ref: React.MutableRefObject<E | null>;
25
+ isTruncated: boolean;
26
+ };
27
+ //# sourceMappingURL=use-truncated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-truncated.d.ts","sourceRoot":"","sources":["../../src/hooks/use-truncated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2C9B,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,WAAW,KAAK,OAAO,GAAG,SAAS,CAAA;IAC1D,8EAA8E;IAC9E,IAAI,CAAC,EAAE,KAAK,CAAC,cAAc,CAAA;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,gHAAgH;IAChH,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,CAAA;CACnC;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAC9D,OAAO,GAAE,mBAAwB,GAChC;IAAE,GAAG,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAwCjE"}
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ let sharedRO = null;
3
+ const sharedROCallbacks = /* @__PURE__ */ new WeakMap();
4
+ function getSharedRO() {
5
+ if (sharedRO) return sharedRO;
6
+ sharedRO = new ResizeObserver((entries) => {
7
+ entries.forEach((e) => {
8
+ const cb = sharedROCallbacks.get(e.target);
9
+ if (cb) cb(e);
10
+ });
11
+ });
12
+ return sharedRO;
13
+ }
14
+ function observeShared(el, cb) {
15
+ const obs = getSharedRO();
16
+ sharedROCallbacks.set(el, cb);
17
+ obs.observe(el);
18
+ return () => {
19
+ sharedROCallbacks.delete(el);
20
+ obs.unobserve(el);
21
+ };
22
+ }
23
+ const defaultMeasure = (el) => el.scrollWidth > el.clientWidth;
24
+ function useTruncated(options = {}) {
25
+ const { measure = defaultMeasure, deps = [], recheckAfterPaint = true, timing = "effect" } = options;
26
+ const ref = React.useRef(null);
27
+ const [isTruncated, setIsTruncated] = React.useState(false);
28
+ const measureRef = React.useRef(measure);
29
+ measureRef.current = measure;
30
+ const useIsoEffect = timing === "layoutEffect" ? React.useLayoutEffect : React.useEffect;
31
+ useIsoEffect(() => {
32
+ const el = ref.current;
33
+ if (!el) return;
34
+ const check = () => {
35
+ const r = measureRef.current(el);
36
+ if (r !== void 0) setIsTruncated(r);
37
+ };
38
+ check();
39
+ let raf = 0;
40
+ let t;
41
+ if (recheckAfterPaint) {
42
+ raf = requestAnimationFrame(check);
43
+ t = setTimeout(check, 100);
44
+ }
45
+ const cleanup = observeShared(el, check);
46
+ return () => {
47
+ if (raf) cancelAnimationFrame(raf);
48
+ if (t) clearTimeout(t);
49
+ cleanup();
50
+ };
51
+ }, deps);
52
+ return { ref, isTruncated };
53
+ }
54
+ export {
55
+ useTruncated
56
+ };
57
+ //# sourceMappingURL=use-truncated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-truncated.js","sources":["../../src/hooks/use-truncated.ts"],"sourcesContent":["import * as React from 'react'\n\n// ── useTruncated — 單行文字截斷偵測引擎(SSOT)─────────────────────────────────\n// 收斂 Breadcrumb TruncatedLabel / DataTable TruncateCell / Tag 三處**逐字重複**的\n// module-level shared ResizeObserver 引擎 + isTruncated 狀態機為唯一 owner(M17 SSOT)。\n//\n// 世界級對照(M8):MUI/MUI-X 官方無第一方 primitive,社群共識 = 「hook 量測 scrollWidth>clientWidth\n// + ref + 以 Tooltip `open` prop 條件控制」(issue #37211);此 hook 即該低階量測層,\n// 上層 `<TruncatedText>`(patterns/element-anatomy)對應 Ant `Typography.Text ellipsis` 高階元件。\n//\n// 三處變異點皆以 options 注入,**行為零漂移**:\n// - Breadcrumb / DataTable:預設 scrollWidth>clientWidth。\n// - Tag:trigger≠量測元素(observe root、量測內層 span)+ Canvas measureText(flex 內 scrollWidth\n// 不可靠)+ useLayoutEffect(避免 paint flash)+ deps=[children] → 全走 options。\n\ntype RoCallback = (entry: ResizeObserverEntry) => void\n\nlet sharedRO: ResizeObserver | null = null\nconst sharedROCallbacks = new WeakMap<Element, RoCallback>()\n\n// 全 DS 共用單一 ResizeObserver,dispatch 到 per-element callback(2026-04-22 DataTable D3 perf audit\n// 成果:10 col × 100 row = 1 RO 而非 1000)。element 卸載時 cleanup,singleton RO 本身不 disconnect。\nfunction getSharedRO(): ResizeObserver {\n if (sharedRO) return sharedRO\n sharedRO = new ResizeObserver((entries) => {\n entries.forEach((e) => {\n const cb = sharedROCallbacks.get(e.target)\n if (cb) cb(e)\n })\n })\n return sharedRO\n}\n\nfunction observeShared(el: Element, cb: RoCallback): () => void {\n const obs = getSharedRO()\n sharedROCallbacks.set(el, cb)\n obs.observe(el)\n return () => {\n sharedROCallbacks.delete(el)\n obs.unobserve(el)\n }\n}\n\nexport interface UseTruncatedOptions {\n /**\n * 偵測策略:回傳 `true` = 截斷、`false` = 未截斷、`undefined` = 本次無法量測(保留前一狀態)。\n * 預設 = `el.scrollWidth > el.clientWidth`(觀察元素自身)。\n * Tag 傳自訂 fn:observe root、內部 `querySelector('[data-tag-text]')` + Canvas measureText。\n */\n measure?: (observedEl: HTMLElement) => boolean | undefined\n /** deps 變更時 cleanup + 重新量測 / 重新訂閱(Tag 傳 `[children]`)。預設 `[]`(mount-once)。 */\n deps?: React.DependencyList\n /**\n * 是否在首次 paint 後以 `requestAnimationFrame` + `setTimeout(100)` 再量一次,捕獲首幀 layout\n * 未完成 / 字型 async load 的假陰性(Breadcrumb 已採此 robust 版)。預設 `true`。\n */\n recheckAfterPaint?: boolean\n /** `'layoutEffect'` = `useLayoutEffect`(量測在 paint 前,避免 flash;Tag 用)/ `'effect'` = `useEffect`。預設 `'effect'`。 */\n timing?: 'effect' | 'layoutEffect'\n}\n\nconst defaultMeasure = (el: HTMLElement): boolean => el.scrollWidth > el.clientWidth\n\n/**\n * 偵測 ref 所指元素的文字是否被截斷,回傳 `{ ref, isTruncated }`。\n * `isTruncated` 用來驅動 Tooltip「僅截斷時顯示」(對齊 `tooltip.principles.stories.tsx`「沒被截斷就不該顯示 tooltip」)。\n */\nexport function useTruncated<E extends HTMLElement = HTMLElement>(\n options: UseTruncatedOptions = {},\n): { ref: React.MutableRefObject<E | null>; isTruncated: boolean } {\n const { measure = defaultMeasure, deps = [], recheckAfterPaint = true, timing = 'effect' } = options\n // MutableRefObject(consumer 可經 callback ref 寫入 .current 以與 forwardedRef 合併,如 Tag)。\n const ref = React.useRef<E | null>(null)\n const [isTruncated, setIsTruncated] = React.useState(false)\n\n // measure 每 render 可能是新 closure(consumer inline);收進 ref 讓 effect 不因它重訂閱,\n // 但 RO 回呼仍讀到最新版。\n const measureRef = React.useRef(measure)\n measureRef.current = measure\n\n // timing 為 call-site 固定字面值(某 consumer 永遠同一值)→ 每個 component instance 每 render\n // 呼叫同一個 effect hook,符合 rules-of-hooks。\n const useIsoEffect = timing === 'layoutEffect' ? React.useLayoutEffect : React.useEffect\n\n useIsoEffect(() => {\n const el = ref.current\n if (!el) return\n const check = () => {\n const r = measureRef.current(el)\n if (r !== undefined) setIsTruncated(r)\n }\n check()\n let raf = 0\n let t: ReturnType<typeof setTimeout> | undefined\n if (recheckAfterPaint) {\n raf = requestAnimationFrame(check)\n t = setTimeout(check, 100)\n }\n const cleanup = observeShared(el, check)\n return () => {\n if (raf) cancelAnimationFrame(raf)\n if (t) clearTimeout(t)\n cleanup()\n }\n // deps 由 consumer 決定(Tag: [children];其餘: []);measure 走 ref 不需進 deps。\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n\n return { ref, isTruncated }\n}\n"],"names":[],"mappings":";AAiBA,IAAI,WAAkC;AACtC,MAAM,wCAAwB,QAAA;AAI9B,SAAS,cAA8B;AACrC,MAAI,SAAU,QAAO;AACrB,aAAW,IAAI,eAAe,CAAC,YAAY;AACzC,YAAQ,QAAQ,CAAC,MAAM;AACrB,YAAM,KAAK,kBAAkB,IAAI,EAAE,MAAM;AACzC,UAAI,OAAO,CAAC;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AACT;AAEA,SAAS,cAAc,IAAa,IAA4B;AAC9D,QAAM,MAAM,YAAA;AACZ,oBAAkB,IAAI,IAAI,EAAE;AAC5B,MAAI,QAAQ,EAAE;AACd,SAAO,MAAM;AACX,sBAAkB,OAAO,EAAE;AAC3B,QAAI,UAAU,EAAE;AAAA,EAClB;AACF;AAoBA,MAAM,iBAAiB,CAAC,OAA6B,GAAG,cAAc,GAAG;AAMlE,SAAS,aACd,UAA+B,IACkC;AACjE,QAAM,EAAE,UAAU,gBAAgB,OAAO,CAAA,GAAI,oBAAoB,MAAM,SAAS,SAAA,IAAa;AAE7F,QAAM,MAAM,MAAM,OAAiB,IAAI;AACvC,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,KAAK;AAI1D,QAAM,aAAa,MAAM,OAAO,OAAO;AACvC,aAAW,UAAU;AAIrB,QAAM,eAAe,WAAW,iBAAiB,MAAM,kBAAkB,MAAM;AAE/E,eAAa,MAAM;AACjB,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,QAAQ,MAAM;AAClB,YAAM,IAAI,WAAW,QAAQ,EAAE;AAC/B,UAAI,MAAM,OAAW,gBAAe,CAAC;AAAA,IACvC;AACA,UAAA;AACA,QAAI,MAAM;AACV,QAAI;AACJ,QAAI,mBAAmB;AACrB,YAAM,sBAAsB,KAAK;AACjC,UAAI,WAAW,OAAO,GAAG;AAAA,IAC3B;AACA,UAAM,UAAU,cAAc,IAAI,KAAK;AACvC,WAAO,MAAM;AACX,UAAI,0BAA0B,GAAG;AACjC,UAAI,gBAAgB,CAAC;AACrB,cAAA;AAAA,IACF;AAAA,EAGF,GAAG,IAAI;AAEP,SAAO,EAAE,KAAK,YAAA;AAChB;"}
package/dist/index.d.ts CHANGED
@@ -117,6 +117,7 @@ export * from './hooks/use-controllable';
117
117
  export * from './hooks/use-is-narrow-viewport';
118
118
  export * from './hooks/use-is-touch-device';
119
119
  export * from './hooks/use-overflow-items';
120
+ export * from './hooks/use-truncated';
120
121
  export * from './lib/drag-visual';
121
122
  export * from './lib/multi-select-ordering';
122
123
  export * from './lib/utils';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -70,6 +70,7 @@ import { useControllable } from "./hooks/use-controllable.js";
70
70
  import { useIsNarrowViewport } from "./hooks/use-is-narrow-viewport.js";
71
71
  import { useIsTouchDevice } from "./hooks/use-is-touch-device.js";
72
72
  import { useOverflowIndices, useScrollEdges } from "./hooks/use-overflow-items.js";
73
+ import { useTruncated } from "./hooks/use-truncated.js";
73
74
  import { dragActiveCursor, dragHandleCursorClass, dragSourceClass, dragSourceStyle, dropIndicatorColumn, dropIndicatorInside, dropIndicatorRow, isReorderNoop, reconstructFullRowGhost, snapToCursorModifier } from "./lib/drag-visual.js";
74
75
  import { applySelectAll, clearSelection } from "./lib/multi-select-ordering.js";
75
76
  import { cn, lineClampClass } from "./lib/utils.js";
@@ -330,6 +331,7 @@ export {
330
331
  useOverflowIndices,
331
332
  useRowSize,
332
333
  useScrollEdges,
333
- useSidebar
334
+ useSidebar,
335
+ useTruncated
334
336
  };
335
337
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,2 +1,3 @@
1
1
  export * from './item-anatomy';
2
+ export * from './truncated-text';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/patterns/element-anatomy/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/patterns/element-anatomy/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { AVATAR_SIZE, ICON_SIZE, INLINE_ACTION_HOVER_BG_SIZE, ItemAvatar, ItemContent, ItemIcon, ItemInlineAction, ItemInlineActionButton, ItemLabel, ItemPrefix, ItemSuffix, ROW_PADDING_BY_SIZE, RowSizeProvider, getUniformPrefixSlotStyle, itemPrefixAlignVariants, useRowSize } from "./item-anatomy.js";
2
+ import { TruncatedText } from "./truncated-text.js";
2
3
  export {
3
4
  AVATAR_SIZE,
4
5
  ICON_SIZE,
@@ -13,6 +14,7 @@ export {
13
14
  ItemSuffix,
14
15
  ROW_PADDING_BY_SIZE,
15
16
  RowSizeProvider,
17
+ TruncatedText,
16
18
  getUniformPrefixSlotStyle,
17
19
  itemPrefixAlignVariants,
18
20
  useRowSize
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cva } from "class-variance-authority";
4
- import { cn, lineClampClass } from "../../lib/utils.js";
4
+ import { lineClampClass, cn } from "../../lib/utils.js";
5
5
  import { Avatar } from "../../components/Avatar/avatar.js";
6
6
  import { Tooltip, TooltipTrigger, TooltipContent } from "../../components/Tooltip/tooltip.js";
7
7
  const ICON_SIZE = {
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ /** @internal 隨 `TruncatedText`(DS-internal 組合 primitive)一併不進 root barrel front-door;subpath 仍可取。 */
3
+ export interface TruncatedTextProps {
4
+ children: React.ReactNode;
5
+ /** 附加到截斷 span 的 className(如 DataTable 的 text-align)。 */
6
+ className?: string;
7
+ /**
8
+ * Tooltip 顯示內容,預設 = `children`。當 `children` 為非字串 ReactNode(icon+text 等)、
9
+ * 但已知完整文字時傳入(Breadcrumb 的 `fullText`)。
10
+ */
11
+ tooltip?: React.ReactNode;
12
+ /**
13
+ * span 的 display:`'inline'`(預設,保 baseline/垂直對齊,DataTable cell 用)/ `'block'`(Breadcrumb 用)。
14
+ * ⚠️ DataTable 刻意用 inline —— `block` 會改 baseline 使 Field/inline-flex cell 垂直對齊 collapse。
15
+ */
16
+ display?: 'block' | 'inline';
17
+ }
18
+ /**
19
+ * @internal DS-internal 組合 primitive:由 Breadcrumb / DataTable 內部消費以統一 truncate-tooltip 呈現。
20
+ * end-user app 若需「單行截斷 + tooltip」直接消費 public `useTruncated` hook 自組(trigger/a11y 自控),
21
+ * 或經 per-component subpath 取用本 component。不進 root barrel front-door。
22
+ */
23
+ export declare function TruncatedText({ children, className, tooltip, display }: TruncatedTextProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare namespace TruncatedText {
25
+ var displayName: string;
26
+ }
27
+ //# sourceMappingURL=truncated-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncated-text.d.ts","sourceRoot":"","sources":["../../../src/patterns/element-anatomy/truncated-text.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,oGAAoG;AACpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,OAAkB,EAAE,EAAE,kBAAkB,2CAYrG;yBAZe,aAAa"}
@@ -0,0 +1,16 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/utils.js";
3
+ import { Tooltip, TooltipTrigger, TooltipContent } from "../../components/Tooltip/tooltip.js";
4
+ import { useTruncated } from "../../hooks/use-truncated.js";
5
+ function TruncatedText({ children, className, tooltip, display = "inline" }) {
6
+ const { ref, isTruncated } = useTruncated();
7
+ return /* @__PURE__ */ jsxs(Tooltip, { open: isTruncated ? void 0 : false, children: [
8
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { ref, className: cn("truncate min-w-0", display === "block" && "block", className), children }) }),
9
+ /* @__PURE__ */ jsx(TooltipContent, { children: tooltip ?? children })
10
+ ] });
11
+ }
12
+ TruncatedText.displayName = "TruncatedText";
13
+ export {
14
+ TruncatedText
15
+ };
16
+ //# sourceMappingURL=truncated-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"truncated-text.js","sources":["../../../src/patterns/element-anatomy/truncated-text.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport { cn } from '@/lib/utils'\nimport { Tooltip, TooltipTrigger, TooltipContent } from '@/design-system/components/Tooltip/tooltip'\nimport { useTruncated } from '@/design-system/hooks/use-truncated'\n\n// ── 消費的 SSOT ──\n// - hooks/use-truncated(useTruncated):截斷偵測引擎 + isTruncated 狀態(本 primitive 的量測核心)\n// - components/Tooltip(Tooltip/TooltipTrigger/TooltipContent):截斷才顯示完整文字的補救 overlay\n// - lib/utils(cn):className 合併\n// - spec: patterns/element-anatomy/truncated-text.spec.md(本 primitive「何時用/為什麼」)\n// - governing: components/Tooltip/tooltip.principles.stories.tsx「沒被截斷就不該顯示 tooltip」→ 由 open 控制機械保證\n\n// ── TruncatedText — 單行截斷 + 截斷才顯示 tooltip 的組合式 primitive ──────────────\n// 消費 `useTruncated`(量測引擎 SSOT)+ 封裝「永遠 wrap Tooltip、以 `open` 控制可見」的正解結構。\n// 收斂 Breadcrumb `TruncatedLabel` 與 DataTable `TruncateCell` 兩處同構的 truncate-span+tooltip presentation。\n// Tag 因 trigger≠量測元素(整個 tag root vs 內層 text span)+ Canvas measure + 自訂 a11y,正當地\n// 只消費 `useTruncated` hook、不走本 component(見 tag.tsx)。\n//\n// 「永遠 wrap + `open={isTruncated?undefined:false}`」而非「未截斷回裸 span、截斷才 wrap」:\n// 後者切換 JSX 樹會讓 span unmount/remount → ref 換到新 DOM、useEffect[] 不重跑 → 觀察的 DOM 與\n// 實際 DOM 對不上(Breadcrumb 2026-05-11 抓的 bug)。always-wrap 保 DOM 節點生命週期穩定;\n// `open={false}` 時 TooltipTrigger asChild 只把 handler/data-state 合併到同一 span(無多餘 DOM、\n// tooltip 靜默),與裸 span 視覺行為等價。\n\n/** @internal 隨 `TruncatedText`(DS-internal 組合 primitive)一併不進 root barrel front-door;subpath 仍可取。 */\nexport interface TruncatedTextProps {\n children: React.ReactNode\n /** 附加到截斷 span 的 className(如 DataTable 的 text-align)。 */\n className?: string\n /**\n * Tooltip 顯示內容,預設 = `children`。當 `children` 為非字串 ReactNode(icon+text 等)、\n * 但已知完整文字時傳入(Breadcrumb 的 `fullText`)。\n */\n tooltip?: React.ReactNode\n /**\n * span 的 display:`'inline'`(預設,保 baseline/垂直對齊,DataTable cell 用)/ `'block'`(Breadcrumb 用)。\n * ⚠️ DataTable 刻意用 inline —— `block` 會改 baseline 使 Field/inline-flex cell 垂直對齊 collapse。\n */\n display?: 'block' | 'inline'\n}\n\n/**\n * @internal DS-internal 組合 primitive:由 Breadcrumb / DataTable 內部消費以統一 truncate-tooltip 呈現。\n * end-user app 若需「單行截斷 + tooltip」直接消費 public `useTruncated` hook 自組(trigger/a11y 自控),\n * 或經 per-component subpath 取用本 component。不進 root barrel front-door。\n */\nexport function TruncatedText({ children, className, tooltip, display = 'inline' }: TruncatedTextProps) {\n const { ref, isTruncated } = useTruncated<HTMLSpanElement>()\n return (\n <Tooltip open={isTruncated ? undefined : false}>\n <TooltipTrigger asChild>\n <span ref={ref} className={cn('truncate min-w-0', display === 'block' && 'block', className)}>\n {children}\n </span>\n </TooltipTrigger>\n <TooltipContent>{tooltip ?? children}</TooltipContent>\n </Tooltip>\n )\n}\nTruncatedText.displayName = 'TruncatedText'\n"],"names":[],"mappings":";;;;AA+CO,SAAS,cAAc,EAAE,UAAU,WAAW,SAAS,UAAU,YAAgC;AACtG,QAAM,EAAE,KAAK,YAAA,IAAgB,aAAA;AAC7B,SACE,qBAAC,SAAA,EAAQ,MAAM,cAAc,SAAY,OACvC,UAAA;AAAA,IAAA,oBAAC,gBAAA,EAAe,SAAO,MACrB,UAAA,oBAAC,UAAK,KAAU,WAAW,GAAG,oBAAoB,YAAY,WAAW,SAAS,SAAS,GACxF,UACH,GACF;AAAA,IACA,oBAAC,gBAAA,EAAgB,UAAA,WAAW,SAAA,CAAS;AAAA,EAAA,GACvC;AAEJ;AACA,cAAc,cAAc;"}
@@ -7,7 +7,7 @@
7
7
  "scripts/composition-fidelity-visual-diff.mjs",
8
8
  "product-workspace apps/template/src/AllDsComponents.stories.tsx (ImportSmoke portal → DS Storybook link)"
9
9
  ],
10
- "generatedAt": "2026-07-18T09:25:13.585Z"
10
+ "generatedAt": "2026-07-18T17:15:24.667Z"
11
11
  },
12
12
  "components": {
13
13
  "accordion": {
@@ -164,7 +164,8 @@
164
164
  "design-system-components-breadcrumb-展示--interactive-ellipsis",
165
165
  "design-system-components-breadcrumb-展示--declarative-auto-collapse",
166
166
  "design-system-components-breadcrumb-展示--paired-with-page-title",
167
- "design-system-components-breadcrumb-展示--integrate-router-link"
167
+ "design-system-components-breadcrumb-展示--integrate-router-link",
168
+ "design-system-components-breadcrumb-展示--integrate-router-link-auto-collapse"
168
169
  ],
169
170
  "設計規格": [
170
171
  "design-system-components-breadcrumb-設計規格--overview",
@@ -1712,5 +1713,5 @@
1712
1713
  }
1713
1714
  },
1714
1715
  "totalComponents": 64,
1715
- "totalStories": 917
1716
+ "totalStories": 918
1716
1717
  }
package/llms-full.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @qijenchen/design-system — 完整設計參考(llms-full)
2
2
 
3
- > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.87
3
+ > 全 component / pattern 的 variants / sizes / 禁止事項。build-time 從 spec.md frontmatter 生成,禁手改。v0.1.0-beta.89
4
4
 
5
5
  # Components
6
6
 
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @qijenchen/design-system
2
2
 
3
3
  > World-class React design system(Radix/shadcn + Tailwind v4 + 自訂 design token)。
4
- > 57 components + 4 public patterns + design tokens。v0.1.0-beta.87
4
+ > 57 components + 4 public patterns + design tokens。v0.1.0-beta.89
5
5
 
6
6
  本檔由 source(spec.md frontmatter + Storybook index)build-time 自動生成,**禁手改**(CI --check drift gate 守)。
7
7
  每元件 / pattern 的完整 variants / sizes / 禁止事項 全文見 [llms-full.txt](./llms-full.txt)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qijenchen/design-system",
3
- "version": "0.1.0-beta.87",
3
+ "version": "0.1.0-beta.89",
4
4
  "private": false,
5
5
  "description": "World-class design system — components, patterns, tokens, hooks (single source of truth for team distribution).",
6
6
  "type": "module",
@@ -306,3 +306,69 @@ export const IntegrateRouterLink: Story = {
306
306
  </div>
307
307
  ),
308
308
  }
309
+
310
+ // ── Router Link + 自動摺疊(2026-07-19 回歸防線)────────────────────────────
311
+ //
312
+ // @story-trait-rationale: 專驗 auto-collapse 抽取器把 `<BreadcrumbLink asChild><router Link>` 中段項目
313
+ // 摺疊進 ⋯ dropdown 時,link 元素(含 to/href/onClick)整個保留、非退化成純文字 —— DA3 對抗稽核抓到的
314
+ // 邏輯 gap 回歸鎖。play() 開摺疊選單並斷言摺疊項目為可點 <a href>(role=menuitem 落在 anchor 上,
315
+ // 非外層 div = 無 nested-interactive)。此處以 `<a onClick prevent>` 模擬 React Router / Next Link。
316
+ export const IntegrateRouterLinkAutoCollapse: Story = {
317
+ name: '整合 Router Link — 自動摺疊保留導航',
318
+ parameters: {
319
+ docs: {
320
+ description: {
321
+ story: '6 層路徑超過 maxItems(預設 4)自動摺疊中段;中段用 asChild 套 router Link。摺疊進 ⋯ 下拉後 link 必完整保留(SPA 導航不退化成整頁重載)。點 ⋯ 可見每個摺疊項目仍是可點連結。',
322
+ },
323
+ },
324
+ },
325
+ render: () => (
326
+ <Breadcrumb>
327
+ <BreadcrumbList>
328
+ <BreadcrumbItem>
329
+ <BreadcrumbLink asChild>
330
+ <a href="/" onClick={(e) => e.preventDefault()}>首頁</a>
331
+ </BreadcrumbLink>
332
+ </BreadcrumbItem>
333
+ <BreadcrumbItem>
334
+ <BreadcrumbLink asChild>
335
+ <a href="/org" onClick={(e) => e.preventDefault()}>組織</a>
336
+ </BreadcrumbLink>
337
+ </BreadcrumbItem>
338
+ <BreadcrumbItem>
339
+ <BreadcrumbLink asChild>
340
+ <a href="/org/team" onClick={(e) => e.preventDefault()}>產品團隊</a>
341
+ </BreadcrumbLink>
342
+ </BreadcrumbItem>
343
+ <BreadcrumbItem>
344
+ <BreadcrumbLink asChild>
345
+ <a href="/org/team/members" onClick={(e) => e.preventDefault()}>成員管理</a>
346
+ </BreadcrumbLink>
347
+ </BreadcrumbItem>
348
+ <BreadcrumbItem>
349
+ <BreadcrumbLink asChild>
350
+ <a href="/org/team/members/alice" onClick={(e) => e.preventDefault()}>Alice</a>
351
+ </BreadcrumbLink>
352
+ </BreadcrumbItem>
353
+ <BreadcrumbItem>
354
+ <BreadcrumbPage>權限設定</BreadcrumbPage>
355
+ </BreadcrumbItem>
356
+ </BreadcrumbList>
357
+ </Breadcrumb>
358
+ ),
359
+ play: async ({ canvasElement }) => {
360
+ const { userEvent, within, expect, waitFor } = await import('@storybook/test')
361
+ const canvas = within(canvasElement)
362
+ // 開摺疊選單(BreadcrumbEllipsis 預設 aria-label = 顯示折疊路徑)
363
+ const ellipsis = await canvas.findByLabelText('顯示折疊路徑')
364
+ await userEvent.click(ellipsis)
365
+ // 核心回歸斷言:摺疊進 dropdown 的 router-link 必保留為可點 <a href="/org/team">(修前會被丟掉 → anchor 不存在)。
366
+ // 註:DS DropdownMenuItem 內部把 children 包進 role="presentation" 的 MenuItem(role=menuitem 在 Radix Item 層),
367
+ // 故不斷言 anchor 自身 role;直接驗「導航目標的 anchor 存在且文字對」= 連結保留的真義。dropdown portal 到 body。
368
+ await waitFor(async () => {
369
+ const link = document.querySelector('a[href="/org/team"]')
370
+ await expect(link, '摺疊的 router-link「產品團隊」應保留為可點 <a href="/org/team">(修前退化成純文字)').not.toBeNull()
371
+ await expect(link?.textContent).toContain('產品團隊')
372
+ })
373
+ },
374
+ }