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

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 (481) hide show
  1. package/AGENTS.md +210 -0
  2. package/CLAUDE.md +17 -191
  3. package/cli-init.mjs +30 -2
  4. package/dist/components/Accordion/accordion.d.ts +2 -2
  5. package/dist/components/Accordion/accordion.d.ts.map +1 -1
  6. package/dist/components/Accordion/accordion.js.map +1 -1
  7. package/dist/components/Avatar/avatar.js +1 -1
  8. package/dist/components/Avatar/avatar.js.map +1 -1
  9. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
  10. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  11. package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
  12. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  13. package/dist/components/Button/button-group.d.ts +2 -2
  14. package/dist/components/Button/button-group.d.ts.map +1 -1
  15. package/dist/components/Button/button-group.js +2 -2
  16. package/dist/components/Button/button-group.js.map +1 -1
  17. package/dist/components/Button/button.d.ts +2 -1
  18. package/dist/components/Button/button.d.ts.map +1 -1
  19. package/dist/components/Button/button.js +1 -1
  20. package/dist/components/Button/button.js.map +1 -1
  21. package/dist/components/Carousel/carousel.d.ts +14 -8
  22. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  23. package/dist/components/Carousel/carousel.js +64 -37
  24. package/dist/components/Carousel/carousel.js.map +1 -1
  25. package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
  26. package/dist/components/Checkbox/checkbox-group.js +21 -14
  27. package/dist/components/Checkbox/checkbox-group.js.map +1 -1
  28. package/dist/components/Checkbox/checkbox.d.ts +2 -2
  29. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  30. package/dist/components/Checkbox/checkbox.js +49 -10
  31. package/dist/components/Checkbox/checkbox.js.map +1 -1
  32. package/dist/components/Combobox/combobox.d.ts +15 -6
  33. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  34. package/dist/components/Combobox/combobox.js +13 -7
  35. package/dist/components/Combobox/combobox.js.map +1 -1
  36. package/dist/components/Command/command.d.ts.map +1 -1
  37. package/dist/components/Command/command.js +11 -2
  38. package/dist/components/Command/command.js.map +1 -1
  39. package/dist/components/DataTable/cell-registry.d.ts +5 -5
  40. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  41. package/dist/components/DataTable/cell-registry.js +41 -38
  42. package/dist/components/DataTable/cell-registry.js.map +1 -1
  43. package/dist/components/DataTable/column-types.d.ts +1 -17
  44. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  45. package/dist/components/DataTable/column-types.js.map +1 -1
  46. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
  47. package/dist/components/DataTable/data-table-column-visibility-panel.js +71 -64
  48. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
  49. package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
  50. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  51. package/dist/components/DataTable/data-table-filter-panel.js +3 -4
  52. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
  53. package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
  54. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  55. package/dist/components/DataTable/data-table-sort-manager.js +2 -2
  56. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
  57. package/dist/components/DataTable/data-table.d.ts +1 -1
  58. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  59. package/dist/components/DataTable/data-table.js +44 -32
  60. package/dist/components/DataTable/data-table.js.map +1 -1
  61. package/dist/components/DatePicker/date-picker.d.ts +3 -3
  62. package/dist/components/DatePicker/date-picker.js +4 -4
  63. package/dist/components/DatePicker/date-picker.js.map +1 -1
  64. package/dist/components/DescriptionList/description-list.d.ts +3 -3
  65. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  66. package/dist/components/DescriptionList/description-list.js +6 -6
  67. package/dist/components/DescriptionList/description-list.js.map +1 -1
  68. package/dist/components/Dialog/dialog.d.ts +2 -2
  69. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  70. package/dist/components/Dialog/dialog.js.map +1 -1
  71. package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
  72. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  73. package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
  74. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  75. package/dist/components/Field/field-context.d.ts +16 -10
  76. package/dist/components/Field/field-context.d.ts.map +1 -1
  77. package/dist/components/Field/field-context.js.map +1 -1
  78. package/dist/components/Field/field-types.d.ts +2 -2
  79. package/dist/components/Field/field-types.d.ts.map +1 -1
  80. package/dist/components/Field/field-types.js.map +1 -1
  81. package/dist/components/Field/field-wrapper.d.ts +20 -4
  82. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  83. package/dist/components/Field/field-wrapper.js +30 -31
  84. package/dist/components/Field/field-wrapper.js.map +1 -1
  85. package/dist/components/Field/field.d.ts +1 -1
  86. package/dist/components/Field/field.d.ts.map +1 -1
  87. package/dist/components/Field/field.js +25 -3
  88. package/dist/components/Field/field.js.map +1 -1
  89. package/dist/components/Field/use-form-validation.d.ts +1 -1
  90. package/dist/components/Field/use-form-validation.js.map +1 -1
  91. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  92. package/dist/components/FileItem/file-item.js +2 -5
  93. package/dist/components/FileItem/file-item.js.map +1 -1
  94. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  95. package/dist/components/FileUpload/file-upload.js +5 -1
  96. package/dist/components/FileUpload/file-upload.js.map +1 -1
  97. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  98. package/dist/components/FileViewer/file-viewer.js +2 -1
  99. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  100. package/dist/components/HoverCard/hover-card.d.ts +2 -2
  101. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  102. package/dist/components/HoverCard/hover-card.js +4 -2
  103. package/dist/components/HoverCard/hover-card.js.map +1 -1
  104. package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
  105. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
  106. package/dist/components/InlineEdit/inline-edit.js +104 -62
  107. package/dist/components/InlineEdit/inline-edit.js.map +1 -1
  108. package/dist/components/Input/input.d.ts +1 -1
  109. package/dist/components/Input/input.d.ts.map +1 -1
  110. package/dist/components/Input/input.js +6 -6
  111. package/dist/components/Input/input.js.map +1 -1
  112. package/dist/components/LinkInput/link-input.d.ts +5 -5
  113. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  114. package/dist/components/LinkInput/link-input.js +3 -3
  115. package/dist/components/LinkInput/link-input.js.map +1 -1
  116. package/dist/components/Notice/notice.d.ts +17 -1
  117. package/dist/components/Notice/notice.d.ts.map +1 -1
  118. package/dist/components/Notice/notice.js +7 -1
  119. package/dist/components/Notice/notice.js.map +1 -1
  120. package/dist/components/NumberInput/number-input.d.ts +1 -1
  121. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  122. package/dist/components/NumberInput/number-input.js +12 -1
  123. package/dist/components/NumberInput/number-input.js.map +1 -1
  124. package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
  125. package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
  126. package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
  127. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  128. package/dist/components/PeoplePicker/people-picker.js +7 -5
  129. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  130. package/dist/components/PeoplePicker/person-display.d.ts +1 -1
  131. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  132. package/dist/components/ProfileCard/profile-card.d.ts +1 -1
  133. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  134. package/dist/components/RadioGroup/radio-group.d.ts +3 -3
  135. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  136. package/dist/components/RadioGroup/radio-group.js +1 -1
  137. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  138. package/dist/components/Rating/rating.d.ts.map +1 -1
  139. package/dist/components/Rating/rating.js +4 -0
  140. package/dist/components/Rating/rating.js.map +1 -1
  141. package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
  142. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  143. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  144. package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
  145. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  146. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  147. package/dist/components/Select/select.d.ts +16 -6
  148. package/dist/components/Select/select.d.ts.map +1 -1
  149. package/dist/components/Select/select.js +17 -5
  150. package/dist/components/Select/select.js.map +1 -1
  151. package/dist/components/SelectMenu/select-menu.d.ts +17 -1
  152. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  153. package/dist/components/SelectMenu/select-menu.js +17 -3
  154. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  155. package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
  156. package/dist/components/SelectionControl/boolean-value.js.map +1 -1
  157. package/dist/components/Sheet/sheet.d.ts +2 -2
  158. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  159. package/dist/components/Sheet/sheet.js.map +1 -1
  160. package/dist/components/Sidebar/sidebar.d.ts +1 -1
  161. package/dist/components/Sidebar/sidebar.js +2 -2
  162. package/dist/components/Sidebar/sidebar.js.map +1 -1
  163. package/dist/components/Slider/slider.d.ts.map +1 -1
  164. package/dist/components/Slider/slider.js +6 -3
  165. package/dist/components/Slider/slider.js.map +1 -1
  166. package/dist/components/Steps/steps.d.ts +6 -4
  167. package/dist/components/Steps/steps.d.ts.map +1 -1
  168. package/dist/components/Steps/steps.js +19 -5
  169. package/dist/components/Steps/steps.js.map +1 -1
  170. package/dist/components/Switch/switch.d.ts +4 -4
  171. package/dist/components/Switch/switch.d.ts.map +1 -1
  172. package/dist/components/Switch/switch.js +3 -1
  173. package/dist/components/Switch/switch.js.map +1 -1
  174. package/dist/components/Tabs/tabs.d.ts +1 -1
  175. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  176. package/dist/components/Tabs/tabs.js +73 -23
  177. package/dist/components/Tabs/tabs.js.map +1 -1
  178. package/dist/components/Tag/tag.d.ts +3 -1
  179. package/dist/components/Tag/tag.d.ts.map +1 -1
  180. package/dist/components/Tag/tag.js +2 -2
  181. package/dist/components/Tag/tag.js.map +1 -1
  182. package/dist/components/Textarea/textarea.d.ts +4 -4
  183. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  184. package/dist/components/Textarea/textarea.js +15 -15
  185. package/dist/components/Textarea/textarea.js.map +1 -1
  186. package/dist/components/TimePicker/time-picker.d.ts +4 -4
  187. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  188. package/dist/components/TimePicker/time-picker.js +8 -5
  189. package/dist/components/TimePicker/time-picker.js.map +1 -1
  190. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  191. package/dist/components/Tooltip/tooltip.js +27 -23
  192. package/dist/components/Tooltip/tooltip.js.map +1 -1
  193. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  194. package/dist/components/TreeView/tree-view.js +16 -0
  195. package/dist/components/TreeView/tree-view.js.map +1 -1
  196. package/dist/index.d.ts +7 -7
  197. package/dist/index.d.ts.map +1 -1
  198. package/dist/index.js +6 -13
  199. package/dist/index.js.map +1 -1
  200. package/dist/lib/drag-visual.d.ts +2 -2
  201. package/dist/lib/drag-visual.js.map +1 -1
  202. package/dist/lib/utils.d.ts +1 -0
  203. package/dist/lib/utils.d.ts.map +1 -1
  204. package/dist/lib/utils.js +21 -1
  205. package/dist/lib/utils.js.map +1 -1
  206. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
  207. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  208. package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
  209. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  210. package/dist/tokens/categorical-color.d.ts +3 -3
  211. package/dist/tokens/categorical-color.d.ts.map +1 -1
  212. package/dist/tokens/categorical-color.js +2 -2
  213. package/dist/tokens/categorical-color.js.map +1 -1
  214. package/ds-canonical/fork/AGENTS.md +213 -0
  215. package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
  216. package/ds-canonical/fork/codex/hooks.json +16 -0
  217. package/ds-canonical/fork/governance.lock +22 -7
  218. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
  219. package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
  220. package/ds-canonical/fork/manifest.json +10 -1
  221. package/ds-canonical/fork/preamble.md +3 -2
  222. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
  223. package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
  224. package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
  225. package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
  226. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
  227. package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +19 -2
  228. package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
  229. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
  230. package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
  231. package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
  232. package/ds-canonical/references/certified-surfaces.md +20 -0
  233. package/ds-canonical/references/preflight-gate-baseline.json +5 -0
  234. package/ds-canonical/references/principle-dim-map.json +20 -2
  235. package/ds-canonical/references/props-naming.md +2 -1
  236. package/ds-canonical/references/ssot-index.md +2 -2
  237. package/ds-canonical/references/story-baseline-registry.json +2 -2
  238. package/ds-canonical/rules/story-rules.md +1 -1
  239. package/ds-canonical/rules/ui-development.md +2 -1
  240. package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
  241. package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
  242. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  243. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
  244. package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
  245. package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
  246. package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
  247. package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
  248. package/ds-story-manifest.json +21 -21
  249. package/llms-full.txt +3 -3
  250. package/llms.txt +1 -1
  251. package/package.json +2 -1
  252. package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
  253. package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
  254. package/src/components/Accordion/accordion.spec.md +10 -1
  255. package/src/components/Accordion/accordion.stories.tsx +10 -5
  256. package/src/components/Accordion/accordion.tsx +7 -2
  257. package/src/components/Alert/alert.principles.stories.tsx +2 -8
  258. package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
  259. package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
  260. package/src/components/AppShell/app-shell.spec.md +4 -0
  261. package/src/components/AppShell/app-shell.stories.tsx +4 -5
  262. package/src/components/Avatar/avatar.tsx +1 -1
  263. package/src/components/Badge/badge.spec.md +2 -2
  264. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
  265. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
  266. package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
  267. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
  268. package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
  269. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
  270. package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
  271. package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
  272. package/src/components/Button/button-group.tsx +4 -4
  273. package/src/components/Button/button.anatomy.stories.tsx +26 -7
  274. package/src/components/Button/button.principles.stories.tsx +1 -1
  275. package/src/components/Button/button.spec.md +2 -2
  276. package/src/components/Button/button.stories.tsx +22 -28
  277. package/src/components/Button/button.tsx +2 -1
  278. package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
  279. package/src/components/Calendar/calendar.spec.md +1 -1
  280. package/src/components/Carousel/carousel.spec.md +17 -1
  281. package/src/components/Carousel/carousel.tsx +93 -42
  282. package/src/components/Checkbox/checkbox-group.tsx +26 -15
  283. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
  284. package/src/components/Checkbox/checkbox.spec.md +12 -3
  285. package/src/components/Checkbox/checkbox.stories.tsx +4 -4
  286. package/src/components/Checkbox/checkbox.tsx +79 -19
  287. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
  288. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  289. package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
  290. package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
  291. package/src/components/Combobox/combobox.spec.md +6 -2
  292. package/src/components/Combobox/combobox.stories.tsx +9 -36
  293. package/src/components/Combobox/combobox.tsx +42 -27
  294. package/src/components/Command/command.spec.md +3 -3
  295. package/src/components/Command/command.tsx +9 -2
  296. package/src/components/DataTable/cell-registry.tsx +75 -76
  297. package/src/components/DataTable/column-types.ts +4 -17
  298. package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
  299. package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
  300. package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
  301. package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
  302. package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
  303. package/src/components/DataTable/data-table.css +2 -3
  304. package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
  305. package/src/components/DataTable/data-table.spec.md +8 -7
  306. package/src/components/DataTable/data-table.stories.tsx +39 -29
  307. package/src/components/DataTable/data-table.tsx +90 -67
  308. package/src/components/DataTable/filter-operators.spec.md +2 -0
  309. package/src/components/DateGrid/date-grid.spec.md +1 -1
  310. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
  311. package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
  312. package/src/components/DatePicker/date-picker.spec.md +10 -9
  313. package/src/components/DatePicker/date-picker.stories.tsx +9 -9
  314. package/src/components/DatePicker/date-picker.tsx +13 -13
  315. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
  316. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  317. package/src/components/DescriptionList/description-list.spec.md +12 -12
  318. package/src/components/DescriptionList/description-list.stories.tsx +3 -3
  319. package/src/components/DescriptionList/description-list.tsx +10 -10
  320. package/src/components/Dialog/dialog.tsx +4 -1
  321. package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
  322. package/src/components/Empty/empty.spec.md +0 -1
  323. package/src/components/Field/field-context.ts +17 -11
  324. package/src/components/Field/field-controls.spec.md +46 -37
  325. package/src/components/Field/field-types.ts +14 -12
  326. package/src/components/Field/field-wrapper.tsx +59 -43
  327. package/src/components/Field/field.anatomy.stories.tsx +6 -6
  328. package/src/components/Field/field.principles.stories.tsx +5 -28
  329. package/src/components/Field/field.spec.md +5 -4
  330. package/src/components/Field/field.stories.tsx +103 -72
  331. package/src/components/Field/field.tsx +36 -4
  332. package/src/components/Field/form-validation.spec.md +4 -0
  333. package/src/components/Field/use-form-validation.ts +1 -1
  334. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  335. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  336. package/src/components/FileItem/file-item.tsx +10 -8
  337. package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
  338. package/src/components/FileUpload/file-upload.spec.md +3 -2
  339. package/src/components/FileUpload/file-upload.tsx +5 -1
  340. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  341. package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
  342. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  343. package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
  344. package/src/components/FileViewer/file-viewer.tsx +3 -2
  345. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
  346. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
  347. package/src/components/HoverCard/hover-card.spec.md +2 -2
  348. package/src/components/HoverCard/hover-card.tsx +4 -2
  349. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
  350. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  351. package/src/components/InlineEdit/inline-edit.spec.md +71 -46
  352. package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
  353. package/src/components/InlineEdit/inline-edit.tsx +211 -125
  354. package/src/components/Input/input.anatomy.stories.tsx +15 -15
  355. package/src/components/Input/input.principles.stories.tsx +3 -3
  356. package/src/components/Input/input.spec.md +4 -4
  357. package/src/components/Input/input.stories.tsx +3 -3
  358. package/src/components/Input/input.tsx +13 -13
  359. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
  360. package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
  361. package/src/components/LinkInput/link-input.spec.md +6 -2
  362. package/src/components/LinkInput/link-input.stories.tsx +8 -8
  363. package/src/components/LinkInput/link-input.tsx +21 -19
  364. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  365. package/src/components/Notice/notice.tsx +8 -2
  366. package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
  367. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  368. package/src/components/NumberInput/number-input.spec.md +6 -6
  369. package/src/components/NumberInput/number-input.stories.tsx +4 -4
  370. package/src/components/NumberInput/number-input.tsx +17 -5
  371. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
  372. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
  373. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  374. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
  375. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  376. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
  377. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
  378. package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
  379. package/src/components/PeoplePicker/people-picker.tsx +39 -20
  380. package/src/components/PeoplePicker/person-display.tsx +3 -3
  381. package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
  382. package/src/components/Popover/popover.principles.stories.tsx +49 -28
  383. package/src/components/Popover/popover.spec.md +4 -2
  384. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  385. package/src/components/ProfileCard/profile-card.tsx +1 -1
  386. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
  387. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
  388. package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
  389. package/src/components/README.md +1 -0
  390. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
  391. package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
  392. package/src/components/RadioGroup/radio-group.tsx +19 -14
  393. package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
  394. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  395. package/src/components/Rating/rating.spec.md +2 -2
  396. package/src/components/Rating/rating.stories.tsx +4 -18
  397. package/src/components/Rating/rating.tsx +5 -1
  398. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  399. package/src/components/ScrollArea/scroll-area.spec.md +1 -1
  400. package/src/components/ScrollArea/scroll-area.tsx +5 -2
  401. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
  402. package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
  403. package/src/components/SegmentedControl/segmented-control.tsx +6 -3
  404. package/src/components/Select/select.anatomy.stories.tsx +13 -7
  405. package/src/components/Select/select.principles.stories.tsx +3 -3
  406. package/src/components/Select/select.spec.md +10 -1
  407. package/src/components/Select/select.stories.tsx +41 -27
  408. package/src/components/Select/select.tsx +44 -26
  409. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
  410. package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
  411. package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
  412. package/src/components/SelectMenu/select-menu.tsx +38 -5
  413. package/src/components/SelectionControl/boolean-value.tsx +2 -2
  414. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  415. package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
  416. package/src/components/Separator/separator.principles.stories.tsx +1 -1
  417. package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
  418. package/src/components/Sheet/sheet.tsx +4 -1
  419. package/src/components/Sidebar/sidebar.tsx +1 -1
  420. package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
  421. package/src/components/Slider/slider.principles.stories.tsx +6 -6
  422. package/src/components/Slider/slider.spec.md +13 -1
  423. package/src/components/Slider/slider.stories.tsx +44 -32
  424. package/src/components/Slider/slider.tsx +13 -4
  425. package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
  426. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  427. package/src/components/Steps/steps.spec.md +4 -2
  428. package/src/components/Steps/steps.stories.tsx +4 -56
  429. package/src/components/Steps/steps.tsx +31 -7
  430. package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
  431. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  432. package/src/components/Switch/switch.spec.md +4 -4
  433. package/src/components/Switch/switch.stories.tsx +3 -3
  434. package/src/components/Switch/switch.tsx +16 -10
  435. package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
  436. package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
  437. package/src/components/Tabs/tabs.spec.md +5 -2
  438. package/src/components/Tabs/tabs.stories.tsx +36 -44
  439. package/src/components/Tabs/tabs.tsx +101 -36
  440. package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
  441. package/src/components/Tag/tag.principles.stories.tsx +4 -4
  442. package/src/components/Tag/tag.spec.md +4 -1
  443. package/src/components/Tag/tag.tsx +5 -3
  444. package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
  445. package/src/components/Textarea/textarea.spec.md +5 -5
  446. package/src/components/Textarea/textarea.tsx +24 -24
  447. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
  448. package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
  449. package/src/components/TimePicker/time-picker.spec.md +6 -5
  450. package/src/components/TimePicker/time-picker.stories.tsx +3 -3
  451. package/src/components/TimePicker/time-picker.tsx +24 -14
  452. package/src/components/Toast/toast.principles.stories.tsx +1 -1
  453. package/src/components/Toast/toast.stories.tsx +1 -1
  454. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
  455. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
  456. package/src/components/Tooltip/tooltip.spec.md +9 -3
  457. package/src/components/Tooltip/tooltip.stories.tsx +1 -1
  458. package/src/components/Tooltip/tooltip.tsx +12 -2
  459. package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
  460. package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
  461. package/src/components/TreeView/tree-view.spec.md +8 -6
  462. package/src/components/TreeView/tree-view.stories.tsx +9 -43
  463. package/src/components/TreeView/tree-view.tsx +36 -0
  464. package/src/index.ts +13 -10
  465. package/src/lib/drag-visual.ts +2 -2
  466. package/src/lib/utils.ts +19 -0
  467. package/src/patterns/action-bar/action-bar.spec.md +1 -0
  468. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  469. package/src/patterns/element-anatomy/item-anatomy.spec.md +4 -0
  470. package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
  471. package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
  472. package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
  473. package/src/tokens/categorical-color.ts +5 -5
  474. package/src/tokens/color/color.spec.md +4 -4
  475. package/src/tokens/color/primitives.css +9 -2
  476. package/src/tokens/motion/motion.spec.md +6 -7
  477. package/src/tokens/opacity/opacity.spec.md +1 -1
  478. package/src/tokens/orphan-tokens.spec.md +1 -1
  479. package/src/tokens/typography/typography.spec.md +2 -0
  480. package/src/tokens/uiSize/uiSize.css +5 -0
  481. package/src/tokens/uiSize/uiSize.spec.md +1 -1
@@ -63,18 +63,33 @@ const dataWithNotes: Product[] = sampleData.map((p, i) => ({
63
63
  : 'Standard delivery.',
64
64
  }))
65
65
 
66
+ // 真實商品名池(品名 × 規格版本組合,避免 "Product item N" 佔位符)
67
+ const PRODUCT_NAMES: Record<string, string[]> = {
68
+ Electronics: ['Wireless Bluetooth Headphones', 'USB-C 7-in-1 Hub', 'Mechanical Keyboard', '4K Webcam', 'Portable SSD 1TB', '65W GaN Charger', 'Noise-Cancelling Earbuds', 'Ergonomic Wireless Mouse'],
69
+ Furniture: ['Ergonomic Office Chair', 'Standing Desk Converter', '5-Tier Bookshelf', 'Storage Cabinet', '3-Seat Fabric Sofa', 'Bedside Table', 'Coat Rack Stand', 'Adjustable Monitor Arm'],
70
+ Food: ['Organic Green Tea 100 Bags', 'Single-Origin Coffee Beans', 'Mixed Nuts Gift Box', 'Dark Chocolate Bar', 'Rolled Oats 1kg', 'Manuka Honey Jar', 'Herbal Tea Sampler', 'Cold-Pressed Olive Oil'],
71
+ Lifestyle: ['Stainless Steel Water Bottle', 'Scented Candle Set', 'Cordless Vacuum Cleaner', 'HEPA Air Purifier', 'Leather Notebook A5', 'Non-Slip Yoga Mat', 'Aroma Diffuser', 'Weighted Blanket'],
72
+ }
73
+ const PRODUCT_EDITIONS = ['Standard', 'Pro', 'Lite', 'Max', 'Mini', '2-Pack', 'Bundle', 'Refill']
74
+
66
75
  function generateLargeData(count: number): Product[] {
67
76
  const categories = ['Electronics', 'Furniture', 'Food', 'Lifestyle']
68
77
  const stocks = ['In stock', 'Low stock', 'Out of stock', 'Pre-order']
69
- return Array.from({ length: count }, (_, i) => ({
70
- sku: `PRD-${String(i + 1).padStart(4, '0')}`,
71
- name: `Product item ${i + 1} — ${categories[i % 4]}`,
72
- category: categories[i % 4],
73
- stock: stocks[i % 4],
74
- seller: SELLERS[i % SELLERS.length],
75
- updatedAt: `2025/03/${String(1 + (i % 28)).padStart(2, '0')}`,
76
- price: Math.round(100 + Math.random() * 9900),
77
- }))
78
+ return Array.from({ length: count }, (_, i) => {
79
+ const category = categories[i % 4]
80
+ const names = PRODUCT_NAMES[category]
81
+ const baseName = names[i % names.length]
82
+ const edition = PRODUCT_EDITIONS[Math.floor(i / names.length) % PRODUCT_EDITIONS.length]
83
+ return {
84
+ sku: `PRD-${String(i + 1).padStart(4, '0')}`,
85
+ name: `${baseName} (${edition})`,
86
+ category,
87
+ stock: stocks[i % 4],
88
+ seller: SELLERS[i % SELLERS.length],
89
+ updatedAt: `2025/03/${String(1 + (i % 28)).padStart(2, '0')}`,
90
+ price: Math.round(100 + Math.random() * 9900),
91
+ }
92
+ })
78
93
  }
79
94
 
80
95
  // ── Column Definitions ───────────────────────────────────────────────────────
@@ -230,7 +245,7 @@ export const ColumnReorder: Story = {
230
245
 
231
246
  /* ── 行高模式 — autoRowHeight prop(每 row 內容驅動高度) ── */
232
247
  export const RowAutoHeightInlineEdit: Story = {
233
- name: '自動行高 × 就地編輯(顯示與編輯位置驗證)',
248
+ name: '自動行高 × 就地編輯',
234
249
  render: () => {
235
250
  // 2026-05-14 I7 fix(per codex verdict):note 初始化進 state 一次,commit 寫同一 state。
236
251
  // 原 `dataWithNotes = list.map(...)` 每 render derive 覆蓋 commit value → user 看似不能編。
@@ -258,7 +273,7 @@ export const RowAutoHeightInlineEdit: Story = {
258
273
  <div className="max-w-5xl">
259
274
  <p className="text-caption text-fg-muted mb-2">
260
275
  autoRowHeight=true。Note 欄位 wrap text 撐高 row。其他單行 cell 在高 row 中應**頂對齊**。
261
- Click 任一 cell 進 edit:文字位置 display↔edit 不偏移(仍頂對齊),
276
+ Click 任一 cell 進 edit:文字位置 view↔edit 不偏移(仍頂對齊),
262
277
  frame 填 cell,Field 自帶 state ring(focus-within → primary)。
263
278
  </p>
264
279
  <DataTable
@@ -594,7 +609,7 @@ export const InlineEdit: Story = {
594
609
  * 3. `spreadsheetMode` — Excel-like cell selection: click 1=select / click 2=edit /
595
610
  * Shift+click=range / ArrowKeys=nav
596
611
  * 4. `experimentalActiveEditorController` — portal Field active editor(Slice D Step 5 D.3,
597
- * cell 永遠 mode="display" SSOT preserved + portal host renders mode="edit")
612
+ * cell 永遠 mode="view" SSOT preserved + portal host renders mode="edit")
598
613
  *
599
614
  * 驗證點:
600
615
  * - hover editable cell → 1px overlay 邊框 var(--border-hover)
@@ -651,7 +666,7 @@ export const InlineEditWithSpreadsheetOverlay: Story = {
651
666
 
652
667
  /**
653
668
  * Issue 9 cell error system(2026-05-10):consumer-supplied `cellErrors` map shows error message
654
- * 14px text-error 在 display content 下方,gap-1 spacing。Edit cell 自動 clear visual error。
669
+ * 14px text-error 在 view content 下方,gap-1 spacing。Edit cell 自動 clear visual error。
655
670
  * Multi-error 用 array → ul li 分行。aria-describedby + aria-invalid for AT。
656
671
  *
657
672
  * 對齊 AG Grid `cellClassRules='ag-cell-error'` + Material X-DataGrid `errorMessage` cell prop +
@@ -1002,7 +1017,6 @@ export const WithBulkActions: Story = {
1002
1017
  </PopoverTrigger>
1003
1018
  <PopoverContent align="end" className="w-auto p-0">
1004
1019
  <DataTableFilterPanel
1005
- mode="flat"
1006
1020
  columns={baseColumns}
1007
1021
  value={filterTree}
1008
1022
  onChange={setFilterTree}
@@ -1132,7 +1146,7 @@ export const WithBulkActions: Story = {
1132
1146
  <BulkActionBar
1133
1147
  selection={visibleSelectedIds}
1134
1148
  totalSelected={selectedCount}
1135
- onClear={() => setSelection({ mode: 'include', ids: [] })}
1149
+ onClearSelection={() => setSelection({ mode: 'include', ids: [] })}
1136
1150
  actions={
1137
1151
  <>
1138
1152
  <Button variant="tertiary" size="md" startIcon={Download}>下載</Button>
@@ -1266,7 +1280,6 @@ export const FilterPanelEmpty: Story = {
1266
1280
  return (
1267
1281
  <div className="w-full max-w-[680px]">
1268
1282
  <DataTableFilterPanel
1269
- mode="flat"
1270
1283
  columns={[...FILTER_COLUMNS]}
1271
1284
  value={value}
1272
1285
  onChange={setValue}
@@ -1292,7 +1305,6 @@ export const FilterPanelWithConditions: Story = {
1292
1305
  return (
1293
1306
  <div className="w-full max-w-[680px]">
1294
1307
  <DataTableFilterPanel
1295
- mode="flat"
1296
1308
  columns={[...FILTER_COLUMNS]}
1297
1309
  value={value}
1298
1310
  onChange={setValue}
@@ -1329,7 +1341,6 @@ export const FilterPanelNested: Story = {
1329
1341
  return (
1330
1342
  <div className="w-full max-w-[680px]">
1331
1343
  <DataTableFilterPanel
1332
- mode="nested"
1333
1344
  columns={[...FILTER_COLUMNS]}
1334
1345
  value={value}
1335
1346
  onChange={setValue}
@@ -1354,7 +1365,6 @@ export const FilterPanelRelativeDate: Story = {
1354
1365
  <div className="w-full max-w-[680px]">
1355
1366
  <p className="text-caption text-fg-muted mb-3">時間下拉分 過去 / 目前 / 未來 三組(Linear/Notion 共識)。</p>
1356
1367
  <DataTableFilterPanel
1357
- mode="flat"
1358
1368
  columns={[...FILTER_COLUMNS]}
1359
1369
  value={value}
1360
1370
  onChange={setValue}
@@ -1382,7 +1392,6 @@ export const FilterPanelModified: Story = {
1382
1392
  <div className="w-full max-w-[680px]">
1383
1393
  <p className="text-caption text-fg-muted mb-3">值偏離 default 時 header 出現 ↻ — 點擊 reset 回 default。</p>
1384
1394
  <DataTableFilterPanel
1385
- mode="flat"
1386
1395
  columns={[...FILTER_COLUMNS]}
1387
1396
  value={value}
1388
1397
  defaultValue={initial}
@@ -1424,7 +1433,6 @@ export const FilterPanelLongTagOverflow: Story = {
1424
1433
  <div className="w-full max-w-[680px]">
1425
1434
  <p className="text-caption text-fg-muted mb-3">產品分類名稱很長(製造業 ERP 常見)時,已選的多個標籤超出單行寬度,Combobox 自動收斂為 +N 摘要。</p>
1426
1435
  <DataTableFilterPanel
1427
- mode="flat"
1428
1436
  columns={[...longLabelColumns]}
1429
1437
  value={value}
1430
1438
  onChange={setValue}
@@ -1603,6 +1611,9 @@ const ROADMAP_DATA: RoadmapItem[] = [
1603
1611
  { id: 'RDM-008', title: 'Dark mode 全站支援', status: 'in-progress', priority: 'P2', owner: SELLERS[1], reviewers: [SELLERS[0], SELLERS[3]], tags: ['frontend', 'design'], startDate: '2026-04-20', dueDate: '2026-06-10', progress: 75, spec: 'https://notion.so/dark-mode', hours: 90, standup: '10:00' },
1604
1612
  ]
1605
1613
 
1614
+ // 大量 roadmap row 衍生用的真實工作範疇後綴(避免 "(#N)" 佔位符計數)
1615
+ const ROADMAP_SCOPES = ['行動版', '桌面版', 'API 層', '後台管理', '資料遷移', '效能優化', '無障礙改善', '國際化', '監控告警', '權限控管']
1616
+
1606
1617
  // Roadmap shared columns helper(主 demo + 5 stress stories 共用)
1607
1618
  function useRoadmapColumns() {
1608
1619
  const col = createColumnHelper<RoadmapItem>()
@@ -1666,13 +1677,13 @@ export const RoadmapAllInOne: Story = {
1666
1677
  const item: RoadmapItem = {
1667
1678
  ...base,
1668
1679
  id,
1669
- title: `${base.title} (#${i + 1})`,
1680
+ title: `${base.title} — ${ROADMAP_SCOPES[i % ROADMAP_SCOPES.length]}`,
1670
1681
  }
1671
1682
  // 前 8 個 top row 補 sub-tasks(展示 nested row + drag canonical:top-level only)
1672
1683
  if (i < 8) {
1673
1684
  item.children = [
1674
- { ...base, id: `${id}-1`, title: `${base.title} 子任務 A`, progress: Math.min(100, base.progress + 10), hours: Math.max(0, Math.floor(base.hours / 3)) },
1675
- { ...base, id: `${id}-2`, title: `${base.title} 子任務 B`, progress: Math.max(0, base.progress - 10), hours: Math.max(0, Math.floor(base.hours / 4)) },
1685
+ { ...base, id: `${id}-1`, title: `${base.title}:介面設計與前端實作`, progress: Math.min(100, base.progress + 10), hours: Math.max(0, Math.floor(base.hours / 3)) },
1686
+ { ...base, id: `${id}-2`, title: `${base.title}:API 整合與 QA 驗收`, progress: Math.max(0, base.progress - 10), hours: Math.max(0, Math.floor(base.hours / 4)) },
1676
1687
  ]
1677
1688
  }
1678
1689
  arr.push(item)
@@ -1762,7 +1773,6 @@ export const RoadmapAllInOne: Story = {
1762
1773
  </PopoverTrigger>
1763
1774
  <PopoverContent align="end" className="w-auto p-0">
1764
1775
  <DataTableFilterPanel
1765
- mode="flat"
1766
1776
  columns={columns}
1767
1777
  value={filterTree}
1768
1778
  onChange={setFilterTree}
@@ -1867,7 +1877,7 @@ export const RoadmapAllInOne: Story = {
1867
1877
  {selection.length > 0 && (
1868
1878
  <BulkActionBar
1869
1879
  selection={selection}
1870
- onClear={() => setSelection([])}
1880
+ onClearSelection={() => setSelection([])}
1871
1881
  actions={
1872
1882
  <>
1873
1883
  <Button variant="tertiary" size="md" startIcon={Download}>匯出</Button>
@@ -1901,7 +1911,7 @@ export const RoadmapAllInOne: Story = {
1901
1911
  scripts/runtime-perf-datatable.mjs:20 仍指向本 story,gate 形同不可跑):──────────────────
1902
1912
  獨立 perf 量測 story — 同 Roadmap 13 cols rich-cell data(useRoadmapColumns SSOT),但**禁用**
1903
1913
  row drag / column reorder / column resize / selection / spreadsheet overlay,只保 inline edit
1904
- display。目的:隔離 feature-stack 疊加 cost(SortableRowProvider / filter / sort / columnOrder
1914
+ view。目的:隔離 feature-stack 疊加 cost(SortableRowProvider / filter / sort / columnOrder
1905
1915
  state),驗證 rich cell 本體 scroll budget(avg ≤ 50ms / p95 ≤ 80ms / long-task ≤ 1,CPU 4x
1906
1916
  throttle)。**不動** RoadmapAllInOne demo IA(user 2026-05-10 directive 全合一)。
1907
1917
  重建版去掉原手刻說明 banner(px-loose + border-b border-divider = R9 chrome-header drift
@@ -1916,7 +1926,7 @@ export const RoadmapPerfBudget: Story = {
1916
1926
  for (let i = 0; i < 500; i++) {
1917
1927
  const base = ROADMAP_DATA[i % ROADMAP_DATA.length]
1918
1928
  const id = `RDM-${String(i + 100).padStart(3, '0')}`
1919
- arr.push({ ...base, id, title: `${base.title} (#${i + 1})` })
1929
+ arr.push({ ...base, id, title: `${base.title} — ${ROADMAP_SCOPES[i % ROADMAP_SCOPES.length]}` })
1920
1930
  }
1921
1931
  return arr
1922
1932
  }, [])
@@ -1956,7 +1966,7 @@ const ORDER_ROWS: OrderRow[] = Array.from({ length: 128 }, (_, i) => ({
1956
1966
  const orderColumns: ColumnDef<OrderRow>[] = [
1957
1967
  { accessorKey: 'orderNo', header: '訂單編號' },
1958
1968
  { accessorKey: 'customer', header: '客戶' },
1959
- { accessorKey: 'amount', header: '金額', cell: ({ getValue }) => `$${(getValue() as number).toLocaleString()}` },
1969
+ { accessorKey: 'amount', header: '金額', meta: { type: 'currency', prefix: '$' } },
1960
1970
  { accessorKey: 'placedAt', header: '成立日期' },
1961
1971
  ]
1962
1972
 
@@ -7,7 +7,7 @@
7
7
  // - data-table-filter-panel.tsx + data-table-sort-manager.tsx + data-table-column-visibility-panel.tsx(panel state 隔離)
8
8
  // - data-table-filter-group.tsx + data-table-filter-value-picker.tsx(@internal;2026-07-14 filter panel
9
9
  // 過 800 hard cap 拆檔 — row/nested-group renderer + ValueShape picker,只被 panel 消費)
10
- // - cell-registry.tsx(column type → cell display / edit 解析 SSOT)
10
+ // - cell-registry.tsx(column type → cell view / edit 解析 SSOT)
11
11
  // - data-table-interaction-layer.tsx + active-editor-controller.ts(spreadsheet overlay + portal editor)
12
12
  // - column-types.ts + filter-operators.ts(✓ Rule-of-3 SSOT,3+ consumer)
13
13
  // - filter-tree.ts(pure data + eval,test isolation)
@@ -96,7 +96,7 @@ export interface DataTableProps<TData>
96
96
  /**
97
97
  * Issue 9 cell error system(2026-05-10)。
98
98
  *
99
- * Map of `${rowId}:${colId}` → error message(string | string[])。Cell display mode 在 content
99
+ * Map of `${rowId}:${colId}` → error message(string | string[])。Cell view mode 在 content
100
100
  * 下方渲 `text-body text-error` 訊息,gap-1 spacing。Multi-error 用 array → ul / li 分行渲。
101
101
  *
102
102
  * **使用建議**:
@@ -279,13 +279,13 @@ export interface DataTablePaginationOptions {
279
279
  //
280
280
  // 原 `renderTypedValue` switch + `EditableCellContent` switch 兩條平行 type-switch
281
281
  // 已 collapse 為單一 `cellRegistry`(./cell-registry.ts)— 每個 type → 一個 cell component,
282
- // 同時處理 display + edit mode(底層 Field control 的 mode prop)。對齊 M17 SSOT consolidation。
282
+ // 同時處理 view + edit mode(底層 Field control 的 mode prop)。對齊 M17 SSOT consolidation。
283
283
  //
284
284
  // 對齊 Notion / Airtable type-aware inline edit canonical(詳 spec §十二):
285
285
  // - string / number / currency:cell click → inline edit
286
286
  // - date / time / select / multiSelect / person / multiPerson:cell click → 進 edit mode
287
287
  // - boolean:不分 read/edit mode,直接 `<Checkbox>` 點即 toggle + commit
288
- // - url:read = LinkInput display + hover Pencil button,click Pencil 才進 edit mode
288
+ // - url:read = LinkInput view + hover Pencil button,click Pencil 才進 edit mode
289
289
  //
290
290
  // Cell id format: `${rowId}__${columnId}`(編輯狀態 keying)
291
291
  // Commit: blur OR Enter OR overlay close → 呼叫 onCellCommit
@@ -393,10 +393,10 @@ function columnSizeStyle(
393
393
  //
394
394
  // **flex-basis: baseSize(2026-05-06 v14.2)**:把 baseSize 當 explicit basis(不是 `0%`)。
395
395
  // 為什麼:flex item base = basis + padding(box-sizing: border-box content-box 行為)。前 `0%`
396
- // basis → cell padding 變 base 一部分。display(padding 12)vs edit(padding 0,Field 接管)
396
+ // basis → cell padding 變 base 一部分。view(padding 12)vs edit(padding 0,Field 接管)
397
397
  // 兩態 base 不同 → flex 重分配 → user 報「Price cell 進 edit 寬度縮 12px」
398
- // (量測:Price display 130.5 → edit 118.5 = -12px)。
399
- // explicit basis = baseSize 讓 padding 不參與 base 計算 → display↔edit 寬度穩定。
398
+ // (量測:Price view 130.5 → edit 118.5 = -12px)。
399
+ // explicit basis = baseSize 讓 padding 不參與 base 計算 → view↔edit 寬度穩定。
400
400
  return { flex: `1 1 ${baseSize}px`, minWidth: baseSize, maxWidth: maxSize }
401
401
  }
402
402
 
@@ -649,12 +649,33 @@ function DraggableHeaderCell({
649
649
  : dropIndicatorSide === 'after'
650
650
  ? dropIndicatorColumn.pseudoAfter
651
651
  : ''
652
+ // dnd-kit KeyboardSensor 的 activator = listeners.onKeyDown,掛在整個 header cell 上(column
653
+ // 無獨立 activator node,不像 row drag 的 button-only handle)。巢狀互動 descendant(more-menu
654
+ // 按鈕 / resize separator / 排序 chevron)的 Enter/Space 冒泡到此 → dnd-kit preventDefault 啟動
655
+ // 欄位拖曳,吃掉按鈕原生啟動。包一層 guard:事件來自互動 descendant(非 header cell 本身)時
656
+ // 交回原生,不啟動拖曳。對齊 dnd-kit「activator node 排除互動 descendant」idiom。
657
+ const rawListeners = draggable.listeners as Record<string, ((e: React.SyntheticEvent) => void) | undefined> | undefined
658
+ const guardedListeners: Record<string, unknown> | undefined = rawListeners
659
+ ? {
660
+ ...rawListeners,
661
+ onKeyDown: (e: React.KeyboardEvent) => {
662
+ const target = e.target as HTMLElement
663
+ if (
664
+ target !== e.currentTarget &&
665
+ target.closest('button, a, input, select, textarea, [role="menuitem"], [role="separator"]')
666
+ ) {
667
+ return
668
+ }
669
+ rawListeners.onKeyDown?.(e)
670
+ },
671
+ }
672
+ : rawListeners
652
673
  return React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
653
674
  ref: setRefs,
654
675
  style: { ...(childProps.style ?? {}), ...dragStyle },
655
676
  'data-column-id': id,
656
677
  'data-column-locked': isLocked || undefined,
657
- ...(disabled ? {} : { ...draggableAttrs, ...(draggable.listeners as unknown as Record<string, unknown>) }),
678
+ ...(disabled ? {} : { ...draggableAttrs, ...(guardedListeners ?? {}) }),
658
679
  // 2026-05-06 v14.9 cursor canonical(對齊 Notion / Jira):
659
680
  // **idle hover NOT 顯 cursor-grab** — header click 觸發 sort,grab cursor 會誤導 user 以為「點 = 拖」;
660
681
  // **drag activation 後**(isDragging=true,過 8px activationConstraint)才顯 cursor-grabbing。
@@ -869,7 +890,7 @@ function RowDragHandle({ disabled, anyDragActive }: { disabled: boolean; anyDrag
869
890
  // 但 renderCellContent 每次 render 傳 3 個全新 inline closure(onCommit / onCommitLive /
870
891
  // onRequestEdit)+ `meta ?? {}` 新 object → memo shallow-compare 100% fail,防線靜默失效
871
892
  // (DataTableInner 任何 re-render — scroll / resize / drag / spreadsheet 選取 / portal keystroke —
872
- // 都讓全部 visible cells 的 Field display subtree 整棵重渲)。
893
+ // 都讓全部 visible cells 的 Field view subtree 整棵重渲)。
873
894
  // Fix:中介 memo slot — 從 parent 接 stable callback(commitCell / cancelCellEdit / onCellCommit /
874
895
  // enterCellEdit)+ primitive rowId / colId,在 slot 內 useCallback 綁 per-cell closure;
875
896
  // props 全 primitive / stable reference → memo 真命中,unchanged cells render 成本歸零。
@@ -884,11 +905,10 @@ interface MemoCellSlotProps {
884
905
  value: any
885
906
  // any-allow: free-form consumer meta bag(同 CellComponentProps.meta)
886
907
  meta: Record<string, any>
887
- mode: 'display' | 'edit'
908
+ mode: 'view' | 'edit'
888
909
  size: 'sm' | 'md' | 'lg'
889
910
  autoRowHeight: boolean
890
911
  isEditable: boolean
891
- isDisabled: boolean
892
912
  onCommitCell: (rowId: string, colId: string, next: unknown) => void
893
913
  onCancelCell: (rowId: string, colId: string) => void
894
914
  onCellCommitLive?: (rowId: string, colId: string, next: unknown) => void
@@ -897,13 +917,12 @@ interface MemoCellSlotProps {
897
917
 
898
918
  const MemoCellSlot = React.memo(function MemoCellSlot({
899
919
  Cell, rowId, colId, value, meta, mode, size, autoRowHeight,
900
- isEditable, isDisabled, onCommitCell, onCancelCell, onCellCommitLive, onEnterEdit,
920
+ isEditable, onCommitCell, onCancelCell, onCellCommitLive, onEnterEdit,
901
921
  }: MemoCellSlotProps) {
902
922
  const onCommit = React.useCallback((next: unknown) => onCommitCell(rowId, colId, next), [onCommitCell, rowId, colId])
903
923
  const onCommitLive = React.useCallback((next: unknown) => onCellCommitLive?.(rowId, colId, next), [onCellCommitLive, rowId, colId])
904
924
  const onCancel = React.useCallback(() => onCancelCell(rowId, colId), [onCancelCell, rowId, colId])
905
- // disabled guard 保留原 renderCellContent inline closure 語意(`() => !disabled && setEditingCellId(...)`)
906
- const onRequestEdit = React.useCallback(() => { if (!isDisabled) onEnterEdit(rowId, colId) }, [onEnterEdit, isDisabled, rowId, colId])
925
+ const onRequestEdit = React.useCallback(() => onEnterEdit(rowId, colId), [onEnterEdit, rowId, colId])
907
926
  return (
908
927
  <Cell
909
928
  value={value}
@@ -912,7 +931,6 @@ const MemoCellSlot = React.memo(function MemoCellSlot({
912
931
  size={size}
913
932
  autoRowHeight={autoRowHeight}
914
933
  isEditable={isEditable}
915
- isDisabled={isDisabled}
916
934
  onCommit={onCommit}
917
935
  onCommitLive={onCommitLive}
918
936
  onCancel={onCancel}
@@ -1040,28 +1058,13 @@ function DataTableInner<TData>(
1040
1058
  },
1041
1059
  [],
1042
1060
  )
1043
- // 2026-05-13 Stream C Cluster B Q3 ship(per codex Q3 verdict + user 拍板「全部馬不停蹄做完」):
1044
- // Mirror isCellEditable pattern。`column.meta.disabled` 接 bool 或 (row) => boolean fn。
1045
- // cell disabled → (a) TD 加 `bg-disabled cursor-not-allowed` + 抑制 hover, (b) inner Field
1046
- // 透過 isDisabled prop 走 mode='disabled'(各 Field type 內部具體 disabled token,非 wrapper blanket opacity),
1047
- // (c) edit entry condition: `cellEditable && !cellDisabled`。
1048
- const isCellDisabled = React.useCallback(
1049
- // any-allow: free-form consumer meta — same rationale as L143 renderTypedValue
1050
- (meta: Record<string, any> | undefined, row: unknown): boolean => {
1051
- const d = meta?.disabled
1052
- if (typeof d === 'function') return d(row) === true
1053
- return d === true
1054
- },
1055
- [],
1056
- )
1057
- // 2026-05-13:canEditCell helper consolidation(per codex V4 follow-up + user「沒理由不做」拍板)
1058
- // 抽 4-site repeated `editable && !disabled` pattern。3 path sites(keyboard / Tab / InteractionLayer)
1059
- // call canEditCell;另 2 sites(renderCellContent + onEditableCellClick)用 already-computed `editable`/
1060
- // `disabled` 變數(因為 disabled 還要單獨給 isDisabled prop + bg-disabled class),不 collapse to helper。
1061
+ // 2026-07-16 round16:cell「disabled」態廢除(世界級 grid disabled cell;meta.disabled 全庫 0 消費)
1062
+ // 鎖定 cell = `editable:(row)=>false`(= isCellEditable 模型),非灰化 disabled 態。原 isCellDisabled
1063
+ // helper + bg-disabled TD 灰化 + isDisabled prop 全移除;canEditCell 收斂為 isCellEditable。
1061
1064
  const canEditCell = React.useCallback(
1062
1065
  (meta: Record<string, unknown> | undefined, row: unknown): boolean =>
1063
- isCellEditable(meta, row) && !isCellDisabled(meta, row),
1064
- [isCellEditable, isCellDisabled],
1066
+ isCellEditable(meta, row),
1067
+ [isCellEditable],
1065
1068
  )
1066
1069
  const [sorting, setSorting] = React.useState<SortingState>(tableOptions?.state?.sorting as SortingState ?? [])
1067
1070
 
@@ -1502,7 +1505,7 @@ function DataTableInner<TData>(
1502
1505
  const hoverProps = (_idx: number): Record<string, never> => ({})
1503
1506
 
1504
1507
  // ── Cell render(Phase C 2026-05-05 — type-keyed registry SSOT)──
1505
- // 命中 columnType → 走 cellRegistry(display / edit mode 同元件 with `mode` prop);
1508
+ // 命中 columnType → 走 cellRegistry(view / edit mode 同元件 with `mode` prop);
1506
1509
  // 無 columnType → consumer 自訂 cell.columnDef.cell。
1507
1510
  const renderCellContent = (cell: ReturnType<typeof rows[number]['getVisibleCells']>[number]) => {
1508
1511
  const meta = cell.column.columnDef.meta
@@ -1510,27 +1513,25 @@ function DataTableInner<TData>(
1510
1513
  const wrap = autoRowHeight && meta?.wrap === true
1511
1514
  // 已知 compound 欄位(Tag / PersonDisplay / LinkInput 等自帶 layout)直接 bypass TruncateCell,
1512
1515
  // 因為 `truncate` 的 inline baseline context 會破壞自訂 layout 的垂直對齊。
1513
- // 2026-05-09 D-path:date / time 加入(showDisplayEndIcon → Field naked-display 需 full width 才能
1516
+ // 2026-05-09 D-path:date / time 加入(showDisplayEndIcon → Field naked-view 需 full width 才能
1514
1517
  // 右對齊 ItemSuffix。TruncateCell 的 `<span truncate min-w-0>` block-display 會 collapse Field
1515
1518
  // to content size,讓 Calendar / Clock icon 緊貼 value text 而非右邊緣)。
1516
1519
  const isKnownCompound = colType === 'select' || colType === 'multiSelect' || colType === 'person' || colType === 'multiPerson' || colType === 'url' || colType === 'date' || colType === 'time'
1517
1520
  const rowId = cell.row.id
1518
1521
  const colId = cell.column.id
1519
1522
  const editable = isCellEditable(meta, cell.row.original)
1520
- const disabled = isCellDisabled(meta, cell.row.original)
1521
1523
  const isEditingThisCell = editingCellId === cellEditId(rowId, colId)
1522
1524
 
1523
1525
  let content: React.ReactNode
1524
1526
  if (colType) {
1525
1527
  const Cell = resolveCellComponent(colType)
1526
1528
  // 2026-05-10 Slice D Step 5(D.3 portal Field):當 portal flag 啟 + cell 編輯中 →
1527
- // cell 保持 display mode(SSOT preserved per codex Q6.2),portal layer 渲 edit Field 在上。
1528
- // 預設 inline-edit:isEditingThisCell ? edit : display
1529
- // 2026-05-13 Q3 cell-disabled:disabled cell 永遠 display lifecycle(state overlay,不進 edit)。
1530
- const cellMode: 'edit' | 'display' =
1529
+ // cell 保持 view mode(SSOT preserved per codex Q6.2),portal layer 渲 edit Field 在上。
1530
+ // 預設 inline-edit:isEditingThisCell ? edit : view
1531
+ const cellMode: 'edit' | 'view' =
1531
1532
  (experimentalActiveEditorController && isEditingThisCell)
1532
- ? 'display'
1533
- : (isEditingThisCell && !disabled) ? 'edit' : 'display'
1533
+ ? 'view'
1534
+ : isEditingThisCell ? 'edit' : 'view'
1534
1535
  // 2026-07-05 D3 perf fix:改經 MemoCellSlot — 原 inline closure props 讓 CellWithSurface
1535
1536
  // memo 100% miss(詳 MemoCellSlot 檔頭註解)。
1536
1537
  content = (
@@ -1544,7 +1545,6 @@ function DataTableInner<TData>(
1544
1545
  size={size}
1545
1546
  autoRowHeight={autoRowHeight}
1546
1547
  isEditable={editable}
1547
- isDisabled={disabled}
1548
1548
  onCommitCell={commitCell}
1549
1549
  onCancelCell={cancelCellEdit}
1550
1550
  onCellCommitLive={onCellCommit}
@@ -1572,8 +1572,8 @@ function DataTableInner<TData>(
1572
1572
  const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
1573
1573
 
1574
1574
  // 2026-05-09 D-path retired:`getEditIndicator(colType)` parallel system 移除。
1575
- // Indicator authority 從 DataTable cellEl 移交 **Field naked-display branch via `showDisplayEndIcon` opt-in**
1576
- // — Select / TimePicker / DatePicker / Combobox / PeoplePicker 5 picker 的 display mode 內建
1575
+ // Indicator authority 從 DataTable cellEl 移交 **Field naked-view branch via `showDisplayEndIcon` opt-in**
1576
+ // — Select / TimePicker / DatePicker / Combobox / PeoplePicker 5 picker 的 view mode 內建
1577
1577
  // `<ItemSuffix>` 渲對應 trigger icon(同 edit DOM 結構)。LinkInput URL anchor 例外(無 suffix)。
1578
1578
  // SSOT chain:cell-registry.tsx(opt-in props)→ Field component(intrinsic icon + ItemSuffix DOM)→
1579
1579
  // item-anatomy ItemPrefix/ItemSuffix layout SSOT。詳 `.claude/planning/cell-indicator-ssot-rfc.md`。
@@ -1681,17 +1681,15 @@ function DataTableInner<TData>(
1681
1681
  const cellRowId = cell.row.id
1682
1682
  const cellColId = cell.column.id
1683
1683
  const cellEditable = isCellEditable(meta, cell.row.original)
1684
- const cellDisabled = isCellDisabled(meta, cell.row.original)
1685
1684
  const isEditingThisCell = editingCellId === cellEditId(cellRowId, cellColId)
1686
- // Indicator canonical(2026-05-09 D-path retire):**Field naked-display branch own** via
1685
+ // Indicator canonical(2026-05-09 D-path retire):**Field naked-view branch own** via
1687
1686
  // `showDisplayEndIcon` opt-in(per-picker `<ItemSuffix>` 渲 ChevronDown/Calendar/Clock)。
1688
1687
  // DataTable cellEl 不再 render parallel indicator — SSOT 對齊 Field family。
1689
1688
  // 詳 `.claude/planning/cell-indicator-ssot-rfc.md` Step 9。
1690
1689
  // Cell click → 進 edit mode(boolean 不需 — 自己 toggle;url 不需 — 走內部 Pencil button,Phase C 由 UrlCell 內處理)
1691
1690
  const cellSpreadsheetId = `${cellRowId}:${cellColId}`
1692
1691
  const isSelectedCell = spreadsheetMode && selectedCellId === cellSpreadsheetId
1693
- // 2026-05-13 Q3:cellDisabled 抑制 editable click(對齊 `editable && !disabled` invariant)
1694
- const onEditableCellClick = cellEditable && !cellDisabled && colType !== 'boolean' && colType !== 'url' && !isEditingThisCell
1692
+ const onEditableCellClick = cellEditable && colType !== 'boolean' && colType !== 'url' && !isEditingThisCell
1695
1693
  ? (e: React.MouseEvent) => {
1696
1694
  if (spreadsheetMode) {
1697
1695
  // Slice D Step 4 spreadsheet semantics(2026-05-10 user 拍板,2026-05-12 v2 fix):
@@ -1767,7 +1765,7 @@ function DataTableInner<TData>(
1767
1765
  role="cell"
1768
1766
  // group/cell + data-row-mode:讓 Field naked 用 `group-data-[row-mode=...]/cell:items-X`
1769
1767
  // 從 cell 取 alignment(autoRowHeight=auto 頂對齊 / fixed=fixed 置中)。CSS propagation,
1770
- // Field API 不變;每個 mode 內 display↔edit 同 alignment(同 Field, 同 group → 同 items)。
1768
+ // Field API 不變;每個 mode 內 view↔edit 同 alignment(同 Field, 同 group → 同 items)。
1771
1769
  // H1(2026-05-10):per-row error → effectiveAutoRowForCell 同 row.tsx effectiveAutoRow
1772
1770
  data-row-mode={effectiveAutoRowForCell ? 'auto' : 'fixed'}
1773
1771
  data-column-id={cell.column.id}
@@ -1777,8 +1775,6 @@ function DataTableInner<TData>(
1777
1775
  // Phase 9 Issue 1 fix(2026-05-10):range cell bg fill via CSS [data-range-cell],
1778
1776
  // 不在 overlay layer(避免 layer fixed-position bg 蓋 cell content)。
1779
1777
  data-range-cell={spreadsheetMode && rangeCellIdSet.has(`${cell.row.id}:${cell.column.id}`) ? '' : undefined}
1780
- // 2026-05-13 V1.6:data-cell-disabled attribute 給 CSS `:not([data-cell-disabled])` 過濾,disabled cell 不被 range fill 蓋
1781
- data-cell-disabled={cellDisabled ? 'true' : undefined}
1782
1778
  // Issue 9 cell error(2026-05-10):aria-describedby 接 error message id 給 AT 讀
1783
1779
  aria-describedby={cellErrorId}
1784
1780
  aria-invalid={hasCellError || undefined}
@@ -1817,19 +1813,16 @@ function DataTableInner<TData>(
1817
1813
  // Field naked edit border 仍 own(per Field SSOT)— 編輯時 Field 自帶 border 1px,
1818
1814
  // 跟 cell 4 邊 inset divider 視覺相疊(同 pixel)= 1 line visual,不雙線。
1819
1815
  inlineEdit && 'dtCellGrid',
1820
- onEditableCellClick && ['cursor-pointer', nakedCellEditableDisplayHover], // editable cell display hover affordance(對齊 Notion / Airtable hover-cell-shows-border canonical)
1816
+ onEditableCellClick && ['cursor-pointer', nakedCellEditableDisplayHover], // editable cell view hover affordance(對齊 Notion / Airtable hover-cell-shows-border canonical)
1821
1817
  // a11y(2026-07-14 dim-10 修):非 spreadsheet inlineEdit cell 可 Tab 聚焦(見下方
1822
1818
  // tabIndex/onKeyDown)— focus-visible ring 對齊本檔 expand button / sortable header canonical。
1823
1819
  onEditableCellClick && !spreadsheetMode && 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
1824
- // 2026-05-13 Q3 cell disabled SSOT(per codex Q3 verdict + user 拍板「全部馬不停蹄做完」):
1825
- // bg `--bg-disabled` component-state token(color.spec.md:671 owner)+ cursor 抑制 click affordance。
1826
- cellDisabled && 'bg-disabled cursor-not-allowed',
1827
1820
  // z-10 raise inline-edit cell;portal mode 不需(layer z-3 already on top)。
1828
1821
  isEditingThisCell && !experimentalActiveEditorController && 'z-10',
1829
1822
  )}
1830
1823
  style={{
1831
1824
  ...columnSizeStyle(cell.column, { resize: enableColumnResize, isSystemCol: isSystemColumn(cell.column.id) }),
1832
- // Padding override 只在 inline-edit cell(naked Field 撐滿 cell);portal mode cell 走正常 display padding
1825
+ // Padding override 只在 inline-edit cell(naked Field 撐滿 cell);portal mode cell 走正常 view padding
1833
1826
  ...(isEditingThisCell && !experimentalActiveEditorController ? {} : cellPadding),
1834
1827
  // Slice D Step 2(2026-05-10):flag 開時 set CSS variable 抑制 Field naked hover outline,
1835
1828
  // 讓 overlay layer 接管 hover ring paint(per RFC Contract 8 「one geometry owner, two paint owners」)。
@@ -1839,7 +1832,7 @@ function DataTableInner<TData>(
1839
1832
  onClick={onEditableCellClick}
1840
1833
  // a11y(2026-07-14 dim-10 修):非 spreadsheet 的 inlineEdit cell 原本只有 onClick —
1841
1834
  // 鍵盤 user 無法聚焦 cell、無法進入編輯(WCAG 2.1.1)。對齊 Atlassian InlineEdit
1842
- // read-view-focusable idiom:editable display cell 可 Tab 聚焦,Enter / F2 進 edit。
1835
+ // read-view-focusable idiom:editable view cell 可 Tab 聚焦,Enter / F2 進 edit。
1843
1836
  // spreadsheetMode 不加(該模式走 table-level selectedCellId 鍵盤導覽 SSOT,per-cell
1844
1837
  // tab stop 會跟 roving 模型衝突);cell 內 embedded 控件事件不攔(target guard)。
1845
1838
  tabIndex={onEditableCellClick && !spreadsheetMode ? 0 : undefined}
@@ -1852,7 +1845,7 @@ function DataTableInner<TData>(
1852
1845
  } : undefined}
1853
1846
  >
1854
1847
  {/* Issue 9 cell error(2026-05-10):有 error → cell 內外結構切 flex-col,
1855
- 上 row 渲既有 nested + content,下 row 渲 error message 14px text-error
1848
+ 上 row 渲既有 nested + content,下 row 渲 error message `text-body text-error`。
1856
1849
  無 error 時走原 flex-row(backward-compat 0 layout shift)。 */}
1857
1850
  {hasCellError ? (
1858
1851
  <span className="flex flex-col self-stretch w-full min-w-0 gap-1">
@@ -2085,8 +2078,7 @@ function DataTableInner<TData>(
2085
2078
  else if (e.key === 'ArrowLeft' && curColIdx > 0) { nextColIdx = curColIdx - 1 }
2086
2079
  else if (e.key === 'ArrowRight' && curColIdx < allCols.length - 1) { nextColIdx = curColIdx + 1 }
2087
2080
  else if (e.key === 'Enter' || e.key === 'F2') {
2088
- // Enter / F2 → 進 edit(若 cell editable + 非 boolean / url + disabled)
2089
- // 2026-05-13 codex V1 fix:加 `!isCellDisabled(meta, row)` gate(對齊 mouse click invariant)
2081
+ // Enter / F2 → 進 edit(若 cell editable + 非 boolean / url)。edit 入口統一走 canEditCell(= isCellEditable)
2090
2082
  const row = table.getRowModel().rowsById[curRowId]
2091
2083
  const colDef = table.getAllLeafColumns().find((c) => c.id === curColId)
2092
2084
  // any-allow: column meta free-form
@@ -2142,7 +2134,7 @@ function DataTableInner<TData>(
2142
2134
  },
2143
2135
  // D3 dep-hygiene fix(2026-07-06):handler 內(portal edit Enter 分支)呼叫的是 canEditCell,
2144
2136
  // 非 isCellEditable — 原 deps 列 isCellEditable 是靠 canEditCell identity-stable 才無實害的
2145
- // stale-closure 地雷;改列真實依賴 canEditCell(其 useCallback deps 已含 isCellEditable + isCellDisabled)。
2137
+ // stale-closure 地雷;改列真實依賴 canEditCell(其 useCallback deps 已含 isCellEditable)。
2146
2138
  [enabled, mode, hasAnySelection, selectableVisibleIds, setSelection,
2147
2139
  spreadsheetMode, selectedCellId, editingCellId, table, canEditCell]
2148
2140
  )
@@ -2315,16 +2307,40 @@ function DataTableInner<TData>(
2315
2307
  const colOptIn = colMeta?.resizable !== false
2316
2308
  const isResizable = enableColumnResize && !isSystemColumn(colId) && colOptIn
2317
2309
  const isResizing = header.column.getIsResizing?.()
2310
+ // 鍵盤 resize(WAI-ARIA window-splitter):方向鍵調整欄寬,公開目前值 + 下限。
2311
+ // effectiveMinWidth 對齊 drag 路徑(resolveColumnSizing minSize),step 16px。
2312
+ const effectiveMinWidth = header.column.columnDef.minSize ?? MIN_COLUMN_WIDTH
2313
+ const currentWidth = Math.round(header.column.getSize())
2314
+ const RESIZE_KEY_STEP = 16
2318
2315
  // H2: 不論 showDivider,只要 isResizable 就 render hot zone(panel boundary col 仍可拖)
2319
2316
  if (!showDivider && !isResizable) return null
2320
2317
  return (
2321
2318
  <span
2322
2319
  role={isResizable ? 'separator' : undefined}
2323
2320
  aria-orientation={isResizable ? 'vertical' : undefined}
2324
- aria-label={isResizable ? '拖曳調整欄寬' : undefined}
2321
+ aria-label={isResizable ? '調整欄寬' : undefined}
2322
+ // 鍵盤可操作 separator(WAI-ARIA window-splitter):focusable + 方向鍵調整 +
2323
+ // 公開目前寬度 / 下限。aria-valuetext 給 SR px 值(避免無 valuemax 時被誤讀成百分比)。
2324
+ tabIndex={isResizable ? 0 : undefined}
2325
+ aria-valuenow={isResizable ? currentWidth : undefined}
2326
+ aria-valuemin={isResizable ? effectiveMinWidth : undefined}
2327
+ aria-valuetext={isResizable ? `${currentWidth}px` : undefined}
2328
+ onKeyDown={isResizable ? (e: React.KeyboardEvent<HTMLSpanElement>) => {
2329
+ let next: number | null = null
2330
+ if (e.key === 'ArrowLeft') next = Math.max(effectiveMinWidth, currentWidth - RESIZE_KEY_STEP)
2331
+ else if (e.key === 'ArrowRight') next = currentWidth + RESIZE_KEY_STEP
2332
+ else if (e.key === 'Home') next = effectiveMinWidth
2333
+ if (next == null) return
2334
+ // stopPropagation:不讓方向鍵冒泡到 header 排序 / 欄位拖曳 keyboard listener。
2335
+ e.preventDefault()
2336
+ e.stopPropagation()
2337
+ table.setColumnSizing((prev) => ({ ...prev, [colId]: next as number }))
2338
+ // onColumnResize 由 columnSizing useEffect 自動 fire(非 drag 路徑不經 isResizingColumn)。
2339
+ } : undefined}
2325
2340
  className={cn(
2326
2341
  'group/resize absolute top-0 bottom-0 right-0 -mr-[3px] w-[7px]',
2327
2342
  isResizable && 'cursor-col-resize select-none',
2343
+ isResizable && 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',
2328
2344
  )}
2329
2345
  // 2026-05-12 fix v2(user 抓 R3 stopPropagation 沒生效):dnd-kit PointerSensor
2330
2346
  // 監聽 `pointerdown`,我前一輪只 stop `onMouseDown` → pointerdown 仍冒泡 →
@@ -2721,12 +2737,19 @@ function DataTableInner<TData>(
2721
2737
  // Center body 同時擁有 H + V scroll;maxHeight 限制讓 H scrollbar 落在 visible 底部
2722
2738
  data-datatable-hscroll
2723
2739
  data-datatable-panel="center"
2740
+ // a11y(scrollable-region-focusable,對齊 DS ScrollArea Viewport canonical):唯讀表格
2741
+ // 的可捲動 body 若無任何 focusable descendant,鍵盤使用者無法捲動。read-only 模式
2742
+ // (非 enabled / spreadsheet)outer table 也不 focusable → 這裡補 tabIndex=0 + 具名 +
2743
+ // inset focus ring,讓鍵盤可聚焦後方向鍵捲動;互動模式已由 outer table keyboard handler
2744
+ // + focusable cells 覆蓋,不重複加 tab stop。
2745
+ tabIndex={enabled || spreadsheetMode ? undefined : 0}
2746
+ aria-label={enabled || spreadsheetMode ? undefined : '表格內容,可捲動'}
2724
2747
  // overflow-x/y: auto — 沒 overflow 就不顯 bar。wrapper minWidth 仍 trigger H 真 overflow。
2725
2748
  // **不**用 scrollbar-gutter: stable — 那會永遠保留 V 軸 15px 空間,
2726
2749
  // content fit 時看起來像「永遠有 V 捲軸」(Image #5 bug)。
2727
2750
  // trade-off:V scroll 出現時 body 內側少 15px,header 不縮 → 右端微 misalign,
2728
2751
  // 但 content fit 視覺乾淨優先(Mac 用戶 overlay scrollbar 不可見)。
2729
- className="flex-1 min-w-0 overflow-x-auto overflow-y-auto"
2752
+ className="flex-1 min-w-0 overflow-x-auto overflow-y-auto focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
2730
2753
  // isFillHeight:用 JS 算的 px(bodyMaxHeight),bypass CSS % 在 flex 場景的不可靠 shrink。
2731
2754
  // 固定 px(300px etc):直接套 height。
2732
2755
  style={
@@ -2779,7 +2802,7 @@ function DataTableInner<TData>(
2779
2802
  activeEditorEnabled={experimentalActiveEditorController}
2780
2803
  // Slice D Step 5(D.3 portal Field,2026-05-10):real portal Field render callback。
2781
2804
  // Layer 在 ActiveEditorHost(z 3 float rect)render `<Cell mode="edit" />` 同 registry。
2782
- // Cell wrapper 保持 mode="display"(SSOT preserved per codex Q6.2)。
2805
+ // Cell wrapper 保持 mode="view"(SSOT preserved per codex Q6.2)。
2783
2806
  activeEditorRender={experimentalActiveEditorController ? (cellId) => {
2784
2807
  const lastColon = cellId.lastIndexOf(':')
2785
2808
  if (lastColon < 0) return null
@@ -245,6 +245,8 @@
245
245
  - **巢狀深度**:1-level cap 由 TypeScript 型別鎖死(`FilterGroup.children: FilterCondition[]`),無 runtime guard;繞過型別塞入的非 condition 物件缺 `field`/`op` → incomplete → pass-through,不 crash
246
246
  - **relative 區間邊界**:全部 local time、雙端 inclusive(`startOfDay`–`endOfDay`);`past_7_days` = 7 天前 00:00 起**含今天整天**;week 以週一起算(`weekStartsOn: 1`)
247
247
 
248
+ **A11y / Loading(明文 N/A)**:本 spec 是 Operator × ColumnType × ValueShape 資料模型 SSOT(非 UI 元件)——filter panel 的互動 a11y / loading 狀態屬 UI 層 owner(`data-table.spec.md` + filter panel 家族元件)與各 Field 控件 spec。
249
+
248
250
  ## 9. Phase B 進度
249
251
 
250
252
  - [x] ValueShape × DS 元件對照 — 完成,coverage 8/12 直接消費 + 4 預留;datetime 走 `<DatePicker showTime>` / `<DatePickerRange showTime>` prop variant(M21,非新元件)
@@ -58,7 +58,7 @@ DateGrid 是 internal primitive(見「定位」),一般 consumer 經 `DatePicker
58
58
  | 場景 | 改用 | 原因 |
59
59
  |------|------|------|
60
60
  | 日期輸入欄位 | `DatePicker` | DateGrid 是 inline,欄位需要 trigger + popup 結構 |
61
- | 純顯示單日期 | `<DatePicker mode="display">` / `Intl.DateTimeFormat` | 不需 interactive 月曆 |
61
+ | 純顯示單日期 | `<DatePicker mode="view">` / `Intl.DateTimeFormat` | 不需 interactive 月曆 |
62
62
  | 時間選擇(時分) | `TimePicker`(見 `../TimePicker/time-picker.spec.md`)| DateGrid 只處理日期層級 |
63
63
  | 事件行事曆(日程本) | 專用行事曆元件 | DateGrid 是日期選擇;事件日誌需要 event overlay / drag / week/month view 切換 |
64
64