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

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 (477) 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 +11 -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 +7 -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 +9 -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/Field/field-context.ts +17 -11
  323. package/src/components/Field/field-controls.spec.md +44 -37
  324. package/src/components/Field/field-types.ts +14 -12
  325. package/src/components/Field/field-wrapper.tsx +59 -43
  326. package/src/components/Field/field.anatomy.stories.tsx +6 -6
  327. package/src/components/Field/field.principles.stories.tsx +5 -28
  328. package/src/components/Field/field.spec.md +4 -4
  329. package/src/components/Field/field.stories.tsx +103 -72
  330. package/src/components/Field/field.tsx +36 -4
  331. package/src/components/Field/form-validation.spec.md +2 -0
  332. package/src/components/Field/use-form-validation.ts +1 -1
  333. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  334. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  335. package/src/components/FileItem/file-item.tsx +10 -8
  336. package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
  337. package/src/components/FileUpload/file-upload.spec.md +3 -2
  338. package/src/components/FileUpload/file-upload.tsx +5 -1
  339. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  340. package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
  341. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  342. package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
  343. package/src/components/FileViewer/file-viewer.tsx +3 -2
  344. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
  345. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
  346. package/src/components/HoverCard/hover-card.spec.md +2 -2
  347. package/src/components/HoverCard/hover-card.tsx +4 -2
  348. package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
  349. package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
  350. package/src/components/InlineEdit/inline-edit.spec.md +71 -46
  351. package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
  352. package/src/components/InlineEdit/inline-edit.tsx +211 -125
  353. package/src/components/Input/input.anatomy.stories.tsx +15 -15
  354. package/src/components/Input/input.principles.stories.tsx +3 -3
  355. package/src/components/Input/input.spec.md +4 -4
  356. package/src/components/Input/input.stories.tsx +3 -3
  357. package/src/components/Input/input.tsx +13 -13
  358. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
  359. package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
  360. package/src/components/LinkInput/link-input.spec.md +6 -2
  361. package/src/components/LinkInput/link-input.stories.tsx +8 -8
  362. package/src/components/LinkInput/link-input.tsx +21 -19
  363. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  364. package/src/components/Notice/notice.tsx +8 -2
  365. package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
  366. package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
  367. package/src/components/NumberInput/number-input.spec.md +6 -6
  368. package/src/components/NumberInput/number-input.stories.tsx +4 -4
  369. package/src/components/NumberInput/number-input.tsx +17 -5
  370. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
  371. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
  372. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  373. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
  374. package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
  375. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
  376. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
  377. package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
  378. package/src/components/PeoplePicker/people-picker.tsx +39 -20
  379. package/src/components/PeoplePicker/person-display.tsx +3 -3
  380. package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
  381. package/src/components/Popover/popover.principles.stories.tsx +49 -28
  382. package/src/components/Popover/popover.spec.md +4 -2
  383. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
  384. package/src/components/ProfileCard/profile-card.tsx +1 -1
  385. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
  386. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
  387. package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
  388. package/src/components/README.md +1 -0
  389. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
  390. package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
  391. package/src/components/RadioGroup/radio-group.tsx +19 -14
  392. package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
  393. package/src/components/Rating/rating.principles.stories.tsx +3 -3
  394. package/src/components/Rating/rating.spec.md +2 -2
  395. package/src/components/Rating/rating.stories.tsx +4 -18
  396. package/src/components/Rating/rating.tsx +5 -1
  397. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  398. package/src/components/ScrollArea/scroll-area.spec.md +1 -1
  399. package/src/components/ScrollArea/scroll-area.tsx +5 -2
  400. package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
  401. package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
  402. package/src/components/SegmentedControl/segmented-control.tsx +6 -3
  403. package/src/components/Select/select.anatomy.stories.tsx +13 -7
  404. package/src/components/Select/select.principles.stories.tsx +3 -3
  405. package/src/components/Select/select.spec.md +9 -1
  406. package/src/components/Select/select.stories.tsx +41 -27
  407. package/src/components/Select/select.tsx +44 -26
  408. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
  409. package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
  410. package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
  411. package/src/components/SelectMenu/select-menu.tsx +38 -5
  412. package/src/components/SelectionControl/boolean-value.tsx +2 -2
  413. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  414. package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
  415. package/src/components/Separator/separator.principles.stories.tsx +1 -1
  416. package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
  417. package/src/components/Sheet/sheet.tsx +4 -1
  418. package/src/components/Sidebar/sidebar.tsx +1 -1
  419. package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
  420. package/src/components/Slider/slider.principles.stories.tsx +6 -6
  421. package/src/components/Slider/slider.spec.md +13 -1
  422. package/src/components/Slider/slider.stories.tsx +44 -32
  423. package/src/components/Slider/slider.tsx +13 -4
  424. package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
  425. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  426. package/src/components/Steps/steps.spec.md +4 -2
  427. package/src/components/Steps/steps.stories.tsx +4 -56
  428. package/src/components/Steps/steps.tsx +31 -7
  429. package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
  430. package/src/components/Switch/switch.principles.stories.tsx +1 -1
  431. package/src/components/Switch/switch.spec.md +4 -4
  432. package/src/components/Switch/switch.stories.tsx +3 -3
  433. package/src/components/Switch/switch.tsx +16 -10
  434. package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
  435. package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
  436. package/src/components/Tabs/tabs.spec.md +5 -2
  437. package/src/components/Tabs/tabs.stories.tsx +36 -44
  438. package/src/components/Tabs/tabs.tsx +101 -36
  439. package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
  440. package/src/components/Tag/tag.principles.stories.tsx +4 -4
  441. package/src/components/Tag/tag.spec.md +3 -1
  442. package/src/components/Tag/tag.tsx +5 -3
  443. package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
  444. package/src/components/Textarea/textarea.spec.md +5 -5
  445. package/src/components/Textarea/textarea.tsx +24 -24
  446. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
  447. package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
  448. package/src/components/TimePicker/time-picker.spec.md +6 -5
  449. package/src/components/TimePicker/time-picker.stories.tsx +3 -3
  450. package/src/components/TimePicker/time-picker.tsx +24 -14
  451. package/src/components/Toast/toast.principles.stories.tsx +1 -1
  452. package/src/components/Toast/toast.stories.tsx +1 -1
  453. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
  454. package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
  455. package/src/components/Tooltip/tooltip.spec.md +9 -3
  456. package/src/components/Tooltip/tooltip.stories.tsx +1 -1
  457. package/src/components/Tooltip/tooltip.tsx +12 -2
  458. package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
  459. package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
  460. package/src/components/TreeView/tree-view.spec.md +8 -6
  461. package/src/components/TreeView/tree-view.stories.tsx +9 -43
  462. package/src/components/TreeView/tree-view.tsx +36 -0
  463. package/src/index.ts +13 -10
  464. package/src/lib/drag-visual.ts +2 -2
  465. package/src/lib/utils.ts +19 -0
  466. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  467. package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
  468. package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
  469. package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
  470. package/src/tokens/categorical-color.ts +5 -5
  471. package/src/tokens/color/color.spec.md +4 -4
  472. package/src/tokens/color/primitives.css +9 -2
  473. package/src/tokens/opacity/opacity.spec.md +1 -1
  474. package/src/tokens/orphan-tokens.spec.md +1 -1
  475. package/src/tokens/typography/typography.spec.md +2 -0
  476. package/src/tokens/uiSize/uiSize.css +5 -0
  477. package/src/tokens/uiSize/uiSize.spec.md +1 -1
@@ -68,7 +68,7 @@ export const Overview: Story = {
68
68
  <table className="text-caption border-collapse">
69
69
  <thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
70
70
  <tbody>
71
- <tr><Td mono>children</Td><Td mono>ReactNode</Td><Td mono>—</Td><Td>anchor trigger element(Popover asChild)</Td></tr>
71
+ <tr><Td mono>children</Td><Td mono>ReactElement</Td><Td mono>—</Td><Td>anchor trigger element(Popover asChild Slot 單 element 契約,2026-07-05 型別收緊)</Td></tr>
72
72
  <tr><Td mono>open / onOpenChange</Td><Td mono>boolean / (o) =&gt; void</Td><Td mono>—</Td><Td>controlled 控制(多步 tour 必用)</Td></tr>
73
73
  <tr><Td mono>defaultOpen</Td><Td mono>boolean</Td><Td mono>—</Td><Td>uncontrolled 初始開啟狀態</Td></tr>
74
74
  <tr><Td mono>kind</Td><Td mono>'tips' | 'new-features' | ReactNode</Td><Td mono>—</Td><Td>有值則渲染 Header(tour-level title);'tips'→「使用技巧」、'new-features'→「新功能介紹」、ReactNode→自訂;multi-step tour 建議傳</Td></tr>
@@ -126,9 +126,9 @@ export const Inspector: Story = {
126
126
  </div>
127
127
  <Coachmark
128
128
  open
129
- image={hasImage ? <MediaGradient from="var(--color-indigo-6)" to="var(--color-purple-6)" icon={Sparkles} label="功能介紹" /> : undefined}
130
- title="試試新功能"
131
- description="這是 Coachmark 的說明文字;多行內容會自動換行並保持 leading 一致。"
129
+ image={hasImage ? <MediaGradient from="var(--color-indigo-6)" to="var(--color-purple-6)" icon={Sparkles} label="AI 續寫" /> : undefined}
130
+ title="讓 AI 幫你續寫草稿"
131
+ description="選取一段文字,AI 會依前後文續寫並保留你的語氣;不滿意可以一鍵重新產生。"
132
132
  step={hasStep ? { current: 2, total: 3 } : undefined}
133
133
  onPrev={hasPrev ? () => {} : undefined}
134
134
  onSkip={hasSkip ? () => {} : undefined}
@@ -137,7 +137,7 @@ export const Inspector: Story = {
137
137
  side="right"
138
138
  align="start"
139
139
  >
140
- <Button variant="primary" size="sm" startIcon={Sparkles}>Anchor</Button>
140
+ <Button variant="primary" size="sm" startIcon={Sparkles}>AI 續寫</Button>
141
141
  </Coachmark>
142
142
  </div>
143
143
  </div>
@@ -158,7 +158,7 @@ export const UsageGuidance: Story = {
158
158
  image={<MediaGradient from="var(--color-indigo-6)" to="var(--color-purple-6)" icon={Bot} label="AI 助理" />}
159
159
  title="試試新的 AI 助理"
160
160
  description="在任何文件中按下 AI 按鈕,讓 Claude 幫你摘要、翻譯或改寫內容。"
161
- onSkip={() => {}}
161
+ isLastStep
162
162
  onNext={() => {}}
163
163
  side="bottom"
164
164
  align="start"
@@ -306,7 +306,7 @@ export const Overview = {
306
306
  <thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
307
307
  <tbody>
308
308
  {[
309
- ['mode', "'edit'|'display'|'readonly'|'disabled'", "'edit'", 'FieldMode 四模式;顯式傳入時永遠最優先,不被 disabled 覆蓋'],
309
+ ['mode', "'edit'|'view'|'readonly'|'disabled'", "'edit'", 'FieldMode 四模式;顯式傳入時永遠最優先,不被 disabled 覆蓋'],
310
310
  ['size', "'sm'|'md'|'lg'", "'md'", '尺寸,與 Button 共用 field-height token'],
311
311
  ['options', 'ComboboxOption[]', '—', '選項列表(extends SelectMenuOption:value / label / icon / avatar / description / disabled / group)'],
312
312
  ['value', 'string[]', '[]', '已選中的值陣列'],
@@ -410,6 +410,7 @@ const InspectorInner = () => {
410
410
  value={value}
411
411
  onChange={setValue}
412
412
  placeholder="選擇分類"
413
+ aria-label="產品分類"
413
414
  className="w-72"
414
415
  />
415
416
  </div>
@@ -551,6 +552,7 @@ export const ColorMatrix = {
551
552
  size="sm"
552
553
  disabled={st === 'disabled'}
553
554
  onChange={() => {}}
555
+ aria-label={`產品分類(${st})`}
554
556
  />
555
557
  <TokenAnnotation colors={st === 'disabled' ? TOKEN_MAP.disabled.default : TOKEN_MAP.edit[st]} />
556
558
  </td>
@@ -567,6 +569,7 @@ export const ColorMatrix = {
567
569
  error={st !== 'disabled'}
568
570
  disabled={st === 'disabled'}
569
571
  onChange={() => {}}
572
+ aria-label={`產品分類(錯誤態、${st})`}
570
573
  />
571
574
  <TokenAnnotation colors={st === 'disabled' ? TOKEN_MAP.disabled.default : ERROR_COLORS[st]} />
572
575
  </td>
@@ -588,10 +591,10 @@ export const ColorMatrix = {
588
591
  <tr key={m}>
589
592
  <Td mono>{m}</Td>
590
593
  <td className="p-3 border-b border-divider align-top min-w-[180px]">
591
- <Combobox mode={m} options={categoryOptions} value={['electronics', 'food']} size="sm" />
594
+ <Combobox mode={m} options={categoryOptions} value={['electronics', 'food']} size="sm" aria-label={`產品分類(${m})`} />
592
595
  </td>
593
596
  <td className="p-3 border-b border-divider align-top min-w-[160px]">
594
- <Combobox mode={m} options={categoryOptions} value={[]} size="sm" />
597
+ <Combobox mode={m} options={categoryOptions} value={[]} size="sm" aria-label={`產品分類(${m}、空值)`} />
595
598
  </td>
596
599
  <td className="p-3 border-b border-divider align-top min-w-[160px]">
597
600
  <TokenAnnotation colors={TOKEN_MAP[m].default} />
@@ -726,7 +729,7 @@ export const SizeMatrix = {
726
729
  <div className="flex flex-col gap-3">
727
730
  {SIZES.map((sz) => (
728
731
  <div key={sz} className="flex items-center gap-3">
729
- <Combobox size={sz} options={categoryOptions} value={['electronics', 'food']} onChange={() => {}} className="w-64" />
732
+ <Combobox size={sz} options={categoryOptions} value={['electronics', 'food']} onChange={() => {}} className="w-64" aria-label={`產品分類(size=${sz})`} />
730
733
  <span className="text-caption text-fg-secondary font-mono">size=&quot;{sz}&quot;</span>
731
734
  </div>
732
735
  ))}
@@ -778,6 +781,7 @@ export const StateBehavior = {
778
781
  options={categoryOptions}
779
782
  value={overflowV}
780
783
  onChange={setOverflowV}
784
+ aria-label="產品分類(單行溢出 +N 範例)"
781
785
  />
782
786
  </div>
783
787
  <div className="flex flex-col gap-1 text-[11px] text-fg-secondary">
@@ -814,6 +818,7 @@ export const StateBehavior = {
814
818
  value={wrapV}
815
819
  onChange={setWrapV}
816
820
  wrap
821
+ aria-label="產品分類(換行模式範例)"
817
822
  />
818
823
  </div>
819
824
  </div>
@@ -846,6 +851,7 @@ export const StateBehavior = {
846
851
  onChange={setClearV}
847
852
  clearable
848
853
  className="w-64"
854
+ aria-label="產品分類(清除全部範例)"
849
855
  />
850
856
  <button
851
857
  type="button"
@@ -872,6 +878,7 @@ export const StateBehavior = {
872
878
  value={dismissV}
873
879
  onChange={setDismissV}
874
880
  className="w-72"
881
+ aria-label="產品分類(個別移除 Tag 範例)"
875
882
  />
876
883
  <button
877
884
  type="button"
@@ -60,10 +60,7 @@ export const UsageGuidance: Story = {
60
60
  <LinkTo kind="Design System/Components/Combobox/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">四模式</span></LinkTo>
61
61
  </li>
62
62
  <li>
63
- <LinkTo kind="Design System/Components/Combobox/展示" name="尺寸與 Button 對齊"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">尺寸與 Button 對齊</span></LinkTo>
64
- </li>
65
- <li>
66
- <LinkTo kind="Design System/Components/Combobox/展示" name="單行 vs 換行"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">單行 vs 換行</span></LinkTo>
63
+ <LinkTo kind="Design System/Components/Combobox/展示" name="三種尺寸"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">尺寸與 Button 對齊</span></LinkTo>
67
64
  </li>
68
65
  <li>
69
66
  <LinkTo kind="Design System/Components/Combobox/展示" name="搜尋"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">搜尋</span></LinkTo>
@@ -81,14 +78,14 @@ export const UsageGuidance: Story = {
81
78
  title="Combobox 的 sweet spot — 多選 + 空間受限 + 選項數 6+"
82
79
  note="Tag / 分類 / 協作成員 / 通知訂閱。使用者快速加選、移除,label 自帶語意不需描述"
83
80
  >
84
- <Combobox options={categoryOptions} value={tags} onChange={setTags} />
81
+ <Combobox options={categoryOptions} value={tags} onChange={setTags} aria-label="商品分類(多選 Tag 範例)" />
85
82
  </Rule>
86
83
 
87
84
  <Rule
88
85
  title="❌ 單選:用 Select"
89
86
  note="Combobox 永遠多選——單選塞進來使用者每次要手動先移除舊 Tag 再選新的,多餘的互動"
90
87
  >
91
- <Combobox options={categoryOptions} value={['electronics']} onChange={() => {}} />
88
+ <Combobox options={categoryOptions} value={['electronics']} onChange={() => {}} aria-label="商品分類(單選反例——應改用 Select)" />
92
89
  <Label warn>↑ 只選一個 electronics → 應該用 Select,一次點擊完成切換</Label>
93
90
  </Rule>
94
91
 
@@ -104,6 +101,7 @@ export const UsageGuidance: Story = {
104
101
  ]}
105
102
  value={['terms']}
106
103
  onChange={() => {}}
104
+ aria-label="同意項目"
107
105
  />
108
106
  <Label warn>↑ 條款勾選是「完整閱讀後同意」→ 用 Checkbox stack 全露內容</Label>
109
107
  </Rule>
@@ -121,6 +119,7 @@ export const UsageGuidance: Story = {
121
119
  ]}
122
120
  value={['eng-fe']}
123
121
  onChange={() => {}}
122
+ aria-label="部門"
124
123
  />
125
124
  <Label warn>↑ 用縮排偽造階層 → 沒有展開收合、不能按父節點全選、破壞資料結構</Label>
126
125
  </Rule>
@@ -141,7 +140,7 @@ export const WrapRule: Story = {
141
140
  title="單行(預設)— Table cell、空間受限的 Form"
142
141
  note="固定高度,與 Input / Select 等欄位並排對齊。多於可見寬度的 Tag 以 +N 指示器代替,hover 可看完整清單"
143
142
  >
144
- <Combobox options={categoryOptions} value={single} onChange={setSingle} />
143
+ <Combobox options={categoryOptions} value={single} onChange={setSingle} aria-label="商品分類(單行溢出範例)" />
145
144
  <Label>↑ 單行:高度固定,超出的 Tag → +N</Label>
146
145
  </Rule>
147
146
 
@@ -149,7 +148,7 @@ export const WrapRule: Story = {
149
148
  title="多行 wrap — 空間充裕的 Form"
150
149
  note="高度隨選中數量展開,每個 Tag 都完整可見。適合填答型表單——使用者一眼看到所有選項,不需 hover 溢出指示器"
151
150
  >
152
- <Combobox wrap options={categoryOptions} value={wrapped} onChange={setWrapped} />
151
+ <Combobox wrap options={categoryOptions} value={wrapped} onChange={setWrapped} aria-label="商品分類(多行 wrap 範例)" />
153
152
  <Label>↑ 多行:完整展開,無溢出</Label>
154
153
  </Rule>
155
154
 
@@ -189,7 +188,7 @@ export const TagOperationRule: Story = {
189
188
  title="Tag 只能被「移除」,不能被「編輯」或「重排」"
190
189
  note="每個 Tag 右側的 X 是唯一被允許的互動——保持 Tag 的心智模型單純:它代表一個已選中的選項,要換就刪了重選"
191
190
  >
192
- <Combobox options={categoryOptions} value={value} onChange={setValue} />
191
+ <Combobox options={categoryOptions} value={value} onChange={setValue} aria-label="商品分類(Tag 移除範例)" />
193
192
  <Label>↑ 點 Tag 的 X 移除;右側 clear all 一次清除;新增從 dropdown 選擇</Label>
194
193
  </Rule>
195
194
 
@@ -205,7 +204,7 @@ export const TagOperationRule: Story = {
205
204
  title="已選中的選項在下拉裡以打勾標示,再點一次即取消"
206
205
  note="桌機(預設)走自建浮層選單:已選項保留在清單中並以打勾呈現,再點一次即移除——避免使用者跨越下拉與 field 才能對照已選狀態。手機 / 觸控裝置改走原生 select,已選項則不重複出現在原生下拉"
207
206
  >
208
- <Combobox options={categoryOptions} value={['electronics']} onChange={() => {}} />
207
+ <Combobox options={categoryOptions} value={['electronics']} onChange={() => {}} aria-label="商品分類(已選項打勾範例)" />
209
208
  <Label>↑ 打開 dropdown,Electronics 仍在清單裡並打勾,可再點一次取消</Label>
210
209
  </Rule>
211
210
  </div>
@@ -223,7 +222,7 @@ export const OverflowRule: Story = {
223
222
  title="+N 指示器是單行模式的必要元件"
224
223
  note="使用者必須知道「還有多少沒看到」——沒有 +N 的單行會誤導使用者以為選項已完整顯示。Hover 可看完整清單"
225
224
  >
226
- <Combobox options={categoryOptions} value={value} onChange={setValue} />
225
+ <Combobox options={categoryOptions} value={value} onChange={setValue} aria-label="商品分類(+N 溢出指示範例)" />
227
226
  <Label>↑ 窄容器內多個 Tag,自動出現 +N</Label>
228
227
  </Rule>
229
228
 
@@ -124,7 +124,7 @@ value 軸 controlled-only;open 軸方向相反 — **uncontrolled-only**:`defaul
124
124
 
125
125
  ## Loading
126
126
 
127
- `loading?: boolean`(forward 給 SelectMenu SSOT,2026-05-15 audit B ):dropdown body 內取代 options 顯 `<Empty icon={<CircularProgress size={48}/>}/>`(消費 既有 empty.spec.md「現有消費者」SelectMenu loading row SSOT)。Trigger 不變,user 隨時可開。對齊 Ant AutoComplete loading idiom。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
127
+ `loading?: boolean`(forward 給 SelectMenu SSOT,2026-05-15 audit B 補;措辭對齊 `select.spec.md`「Loading」2026-07-04 Q3 拍板 — 不清空 stale options):spinner 只在**無可顯示選項時**佔 empty slot 顯 `<Empty icon={<CircularProgress size={48}/>}/>`(cmdk `CommandEmpty` 機制;已有 options 時保留顯示,不取代)。Trigger 不變,user 隨時可開。對齊 MUI Autocomplete「loading 只在無 suggestions 時顯 loadingText」共識(cite 詳 select.spec.md 同段)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
128
128
 
129
129
  ---
130
130
 
@@ -132,7 +132,7 @@ value 軸 controlled-only;open 軸方向相反 — **uncontrolled-only**:`defaul
132
132
 
133
133
  - **Disabled**:Field SSOT own(`Field/field-controls.spec.md`)。trigger / tag dismiss / 搜尋 input 全部 disabled,token 走 M24 state precedence(`text-fg-disabled`);已選 Tag 的 dismiss X 自動隱藏(見「readonly / disabled 的 Tag」段)。
134
134
  - **Loading**:已 codify(見「Loading」段)。
135
- - **Empty(no search results)**:dropdown body 內渲 SelectMenu 預設 `emptyText`「沒有符合的選項」(Combobox 不傳 `emptyText`,走 SelectMenu primitive default)。Combobox 不暴露 `creatable` / `onCreate` prop( SelectMenu primitive 本身支援),故 Combobox consumer 無「建立 "xxx"」row
135
+ - **Empty(no search results)**:dropdown body 內渲 `emptyText`(Combobox 暴露 `emptyText` prop 並 forward 給 SelectMenu;未傳時走 SelectMenu 預設「沒有符合的選項」)。Combobox **暴露 `creatable` / `onCreate` / `createLabel` prop 並 forward 給 SelectMenu**(2026-07-18 user 拍板;搜尋非空且無完全同名既有選項時,dropdown 顯 create row `Plus + createLabel`)——邏輯/顯示/互動 SSOT 住在 SelectMenu(`select-menu.tsx` :271-275 顯隱 / render)。僅 searchable 桌機路徑生效(native mobile 不支援)。對齊 Ant tags / react-select Creatable
136
136
  - **Empty(no value selected)**:multi mode `value=[]` 時 trigger 顯 placeholder(如「請選擇」);empty state 不渲 tag 區。
137
137
  - **Dark mode / density**:走 Field + SelectMenu SSOT 自動 adapt。
138
138
 
@@ -145,6 +145,10 @@ value 軸 controlled-only;open 軸方向相反 — **uncontrolled-only**:`defaul
145
145
  - multi mode 可附 `min` / `max` selected count 限制(consumer 自驗,Combobox 不獨立 own validation rules)
146
146
  - Validation timing:預設 onBlur + onSubmit,onChange 不立即 validate(避免邊選邊紅)
147
147
 
148
+ ## Ref 契約(cross-mode 例外,2026-07-17 user 拍板)
149
+
150
+ Combobox 是 **4-mode field**(edit / view / readonly / disabled),各 mode 渲染**本質不同的 subtree**(edit = trigger + hidden select;view/readonly = `ReadonlyMultiSelect`)。**`ref` 只保證在 edit mode 指向 trigger root(`__triggerRef`)**;view / readonly / disabled mode **不保證穩定 root**(對齊 React「avoid over-exposing DOM nodes in higher-level components」)。需在任意 mode 取穩定 DOM root 的 consumer 應在外層自包一層容器,不依賴 Combobox `ref` 跨 mode 一致。與 T1 RadioGroup / Switch 的跨 mode 缺口採同一哲學(記錄例外,不硬造統一 host)。
151
+
148
152
  ---
149
153
 
150
154
  ## 禁止事項
@@ -26,7 +26,7 @@ const meta: Meta<typeof Combobox> = {
26
26
  export default meta
27
27
  type Story = StoryObj<typeof Combobox>
28
28
 
29
- // @story-trait-rationale: pre-existing trait gaps (Default/WithError) tracked separately; this PR scope = add display mode card to Modes story only.
29
+ // @story-trait-rationale: pre-existing trait gaps (Default/WithError) tracked separately; this PR scope = add view mode card to Modes story only.
30
30
  /* ── 四模式 ── */
31
31
  export const Modes: Story = {
32
32
  name: '四模式',
@@ -39,8 +39,8 @@ export const Modes: Story = {
39
39
  <Combobox options={categoryOptions} value={value} onChange={setValue} aria-label="類別(edit mode demo)" />
40
40
  </div>
41
41
  <div>
42
- <h3 className="text-body font-bold text-foreground mb-2">display</h3>
43
- <Combobox mode="display" options={categoryOptions} value={value} aria-label="類別(display mode demo)" />
42
+ <h3 className="text-body font-bold text-foreground mb-2">view</h3>
43
+ <Combobox mode="view" options={categoryOptions} value={value} aria-label="類別(view 模式示範)" />
44
44
  </div>
45
45
  <div>
46
46
  <h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
@@ -61,7 +61,7 @@ export const Modes: Story = {
61
61
 
62
62
  /* ── 尺寸與 Button 對齊 ── */
63
63
  export const SizeAlignment: Story = {
64
- name: '尺寸與 Button 對齊',
64
+ name: '三種尺寸',
65
65
  render: () => {
66
66
  const [sm, setSm] = React.useState(['electronics', 'food', 'lifestyle'])
67
67
  const [md, setMd] = React.useState(['electronics', 'food', 'lifestyle'])
@@ -71,7 +71,7 @@ export const SizeAlignment: Story = {
71
71
  <div className="flex flex-col gap-4">
72
72
  {(['sm', 'md', 'lg'] as const).map(size => (
73
73
  <div key={size} className="flex items-center gap-3">
74
- <Combobox size={size} options={categoryOptions} value={states[size][0]} onChange={states[size][1]} className="max-w-xs" />
74
+ <Combobox size={size} options={categoryOptions} value={states[size][0]} onChange={states[size][1]} className="max-w-xs" aria-label={`商品分類(size=${size})`} />
75
75
  <Button variant="primary" size={size}>送出</Button>
76
76
  <span className="text-caption text-fg-muted">size="{size}"</span>
77
77
  </div>
@@ -81,37 +81,10 @@ export const SizeAlignment: Story = {
81
81
  },
82
82
  }
83
83
 
84
- /* ── 單行 vs 換行 ── */
85
- export const WrapModes: Story = {
86
- name: '單行 vs 換行',
87
- render: () => {
88
- const init = ['electronics', 'food', 'lifestyle', 'clothing', 'furniture']
89
- const [v1, setV1] = React.useState(init)
90
- const [v2, setV2] = React.useState(init)
91
- return (
92
- <div className="flex flex-col gap-6 w-72">
93
- <div>
94
- <h3 className="text-body font-bold text-foreground mb-2">單行(預設)</h3>
95
- <p className="text-caption text-fg-muted mb-3">塞不下的自動收進 +N,hover 顯示隱藏項</p>
96
- <Combobox options={categoryOptions} value={v1} onChange={setV1} />
97
- </div>
98
- <div>
99
- <h3 className="text-body font-bold text-foreground mb-2">單行 readonly</h3>
100
- <Combobox mode="readonly" options={categoryOptions} value={init} />
101
- </div>
102
- <div>
103
- <h3 className="text-body font-bold text-foreground mb-2">換行(wrap)</h3>
104
- <p className="text-caption text-fg-muted mb-3">高度隨內容長,badges 自動換行</p>
105
- <Combobox options={categoryOptions} value={v2} onChange={setV2} wrap />
106
- </div>
107
- <div>
108
- <h3 className="text-body font-bold text-foreground mb-2">換行 readonly</h3>
109
- <Combobox mode="readonly" options={categoryOptions} value={init} wrap />
110
- </div>
111
- </div>
112
- )
113
- },
114
- }
84
+ // @story-trait-rationale: 原 WrapModes(單行 vs 換行)retired 2026-07-17(Dim 24 earn-existence)—
85
+ // 單行/wrap × edit/readonly 矩陣已由 anatomy「狀態行為」(單行溢出 + readonly 溢出 + edit/readonly wrap)
86
+ // 完整覆蓋機制,wrap 選擇原則由 principles「Wrap 模式選擇」(WrapRule)owns。展示層不重演矩陣(三層定位);
87
+ // principles 使用指引頁對應 LinkTo(單行 vs 換行)同步移除。
115
88
 
116
89
  /* ── 搜尋 ── */
117
90
  export const Searchable: Story = {
@@ -1,5 +1,5 @@
1
1
  // @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
2
- // @renderer-symmetry-allow: ComboboxTagStack(display path)接 consumer tagRenderer 是 Stream C 下 cycle 工作 — 2026-05-12 先 ship Issues 2/3/4 surgical fixes(placeholder vocabulary + cell surface metrics + placeholder truncate),tagRenderer display-path unify deferred per field-controls.spec.md 共享 contract a。當前 multi=1 顯示已透過 PeoplePicker tagRenderer(people-picker.tsx:426-444)PersonDisplay SSOT 對齊;其他 Combobox consumer 走 default `<Tag>` 純文字 backward-compat。
2
+ // @renderer-symmetry-allow: ComboboxTagStack(view path)接 consumer tagRenderer 是 Stream C 下 cycle 工作 — 2026-05-12 先 ship Issues 2/3/4 surgical fixes(placeholder vocabulary + cell surface metrics + placeholder truncate),tagRenderer view-path unify deferred per field-controls.spec.md 共享 contract a。當前 multi=1 顯示已透過 PeoplePicker tagRenderer(people-picker.tsx:426-444)PersonDisplay SSOT 對齊;其他 Combobox consumer 走 default `<Tag>` 純文字 backward-compat。
3
3
  // code-quality-allow: file-size — Combobox 含 NativeCombobox/CustomCombobox/useOverflowCount/OverflowTagList/ComboboxTagStack 5 子元件 + 共用 helpers,split-into-files 會破壞 measurement closures + 重複 type definitions。
4
4
  import * as React from 'react'
5
5
  import { X, ChevronDown } from 'lucide-react'
@@ -237,7 +237,7 @@ interface OverflowTagListProps {
237
237
  * 2026-05-14 I4 fix(per codex M31 verdict + user 抓「display overflow 有 avatar / edit 無」):
238
238
  * Optional renderer for hidden items in `+N` overflow popover。Default fallback = `<Tag>{label}</Tag>`
239
239
  * (純文字 chip,backward-compat)。Consumer pass 此 prop 讓 hidden items 顯示同 avatar 視覺
240
- * (對齊 display MultiPersonDisplay overflow popover Tag avatar SSOT)。
240
+ * (對齊 view path MultiPersonDisplay overflow popover Tag avatar SSOT)。
241
241
  */
242
242
  renderHiddenTag?: (item: { value: string; label: string }) => React.ReactNode
243
243
  onRemove?: (value: string) => void
@@ -297,7 +297,7 @@ function OverflowTagList({ containerRef, items, size, wrap, renderTag, renderHid
297
297
  {items.map((item, i) => (
298
298
  // 2026-05-14 I5 fix(per codex M31 verdict + user 抓「avatar stack 堆疊方向不一致」):
299
299
  // 加 z-index per-index — 前 item z 高(對齊 MultiPersonDisplay zIndex: visible.length - i
300
- // canonical + MUI AvatarGroup surplus pattern)。display + edit stack 堆疊方向統一。
300
+ // canonical + MUI AvatarGroup surplus pattern)。view + edit stack 堆疊方向統一。
301
301
  <div key={item.value} ref={el => { tagEls.current[i] = el }} className={cn('shrink-0 max-w-full', tagWrapperClassName)} style={{ zIndex: items.length - i }}>{renderTag(item, i)}</div>
302
302
  ))}
303
303
  <div ref={overflowEl} className={cn('shrink-0', overflowWrapperClassName)}>
@@ -316,9 +316,9 @@ function OverflowTagList({ containerRef, items, size, wrap, renderTag, renderHid
316
316
  )
317
317
  }
318
318
 
319
- // ── Internal tag-stack renderer (consumed by ReadonlyMultiSelect / mode='display') ───
319
+ // ── Internal tag-stack renderer (consumed by ReadonlyMultiSelect / mode='view') ───
320
320
  //
321
- // Phase B2(2026-05-05):原 ComboboxDisplay sub-component 已 retire,改 inline `<Combobox mode="display">`。
321
+ // Phase B2(2026-05-05):原 ComboboxDisplay sub-component 已 retire,改 inline `<Combobox mode="view">`。
322
322
  // 本 helper 只負責 tag-stack 內容渲染(OverflowTagList 消費),不包 Field wrapper。
323
323
  function ComboboxTagStack({
324
324
  value, options, tagSize = 'md', wrap = false, containerRef: externalRef, disabled = false,
@@ -328,7 +328,7 @@ function ComboboxTagStack({
328
328
  }) {
329
329
  const emptyDisplay = useFieldEmptyDisplay()
330
330
  const ownRef = React.useRef<HTMLDivElement>(null)
331
- if (!value || value.length === 0) return <span className={fieldEmptyColorClass(disabled ? 'disabled' : 'display')}>{emptyDisplay}</span>
331
+ if (!value || value.length === 0) return <span className={fieldEmptyColorClass(disabled ? 'disabled' : 'view')}>{emptyDisplay}</span>
332
332
  const items = value.map(v => ({ value: v, label: options?.find(o => o.value === v)?.label ?? v }))
333
333
  const disabledClass = disabled ? 'bg-disabled text-fg-disabled' : undefined
334
334
 
@@ -345,10 +345,10 @@ function ComboboxTagStack({
345
345
  )
346
346
 
347
347
  if (externalRef) return content
348
- // 2026-05-05 v9 fix(Bug 4):display path 內 wrapper 必須 `flex-1 min-w-0`,否則在 cell flex
348
+ // 2026-05-05 v9 fix(Bug 4):view path 內 wrapper 必須 `flex-1 min-w-0`,否則在 cell flex
349
349
  // parent 下不認領完整可用寬度 → OverflowTagList 量得寬度小於 edit path → 顯 `+N` 多於 edit。
350
350
  // edit path tagAreaRef wrapper 已是 `flex-1 min-w-0`(NativeCombobox/CustomCombobox line 258 / 354),
351
- // display 必對稱才 SSOT。
351
+ // view 必對稱才 SSOT。
352
352
  // 2026-05-15 F1 Q3 fix(per user round 3 verbatim「單人選取時 Tag 越界蓋 indicator」):
353
353
  // `overflow-visible` → `overflow-hidden` 讓 narrow cell width 強制 clip(Tag 內建 truncate
354
354
  // 處理 text ellipsis,stack `-ml-0.5` 負 margin 在 wrapper 內不受影響)。對齊
@@ -384,8 +384,9 @@ export interface ComboboxProps {
384
384
  /** 啟用搜尋 */
385
385
  searchable?: boolean
386
386
  /** Loading state(2026-05-15 audit B fix per user verbatim「dropdown 隨時可開,讀取在 panel 中間 CircularProgress」)。
387
- * Forward 給 SelectMenu primitive SSOT;dropdown 開啟時取代 options 顯 CircularProgress + loadingText。
388
- * Trigger 不變(user 隨時可開)。對齊 MUI Autocomplete `loadingText` + Field SSOT + Empty 元件 compose。*/
387
+ * Forward 給 SelectMenu primitive SSOT;spinner 只在無可顯示選項時佔 CommandEmpty slot 顯 CircularProgress
388
+ * (已有 options 保留顯示,不取代)。Trigger 不變(user 隨時可開)
389
+ * 對齊 MUI Autocomplete「loading 只在無 suggestions 時顯 loadingText」+ Field SSOT + Empty 元件 compose。*/
389
390
  loading?: boolean
390
391
  /** 搜尋框位置:menu(浮層內,預設)或 trigger(inline input) */
391
392
  searchIn?: 'menu' | 'trigger'
@@ -397,6 +398,14 @@ export interface ComboboxProps {
397
398
  emptyPlaceholder?: string
398
399
  /** 搜尋無結果提示(2026-07-04 Q4 拍板接線)— forward SelectMenu primitive SSOT(default「沒有符合的選項」) */
399
400
  emptyText?: string
401
+ /** 可建立新選項(creatable tag,2026-07-18 user 拍板 forward)—— 搜尋非空且無完全同名既有選項時,
402
+ * dropdown 顯 create row(Plus + `createLabel`);forward 給底層 SelectMenu(邏輯/顯示/互動 SSOT 住在 SelectMenu)。
403
+ * 僅 searchable 桌機路徑生效(需打字);native mobile 路徑不支援。對齊 Ant tags / react-select Creatable。 */
404
+ creatable?: boolean
405
+ /** 建立新選項 callback(forward SelectMenu `onCreate`)。 */
406
+ onCreate?: (value: string) => void
407
+ /** create row 文字格式(forward SelectMenu `createLabel`,預設「直接使用「{query}」」)。 */
408
+ createLabel?: (query: string) => string
400
409
  /** a11y:無 Field wrapper 時提供 role='combobox' 的 accessible name(axe aria-input-field-name) */
401
410
  'aria-label'?: string
402
411
  /** Initial open state(uncontrolled)— 對齊 Select.defaultOpen / Radix Popover canonical。
@@ -416,8 +425,8 @@ export interface ComboboxProps {
416
425
  tagRenderer?: (item: { value: string; label: string }, onRemove: () => void) => React.ReactNode
417
426
  /**
418
427
  * 2026-05-14 I4 fix:Optional renderer for hidden items in `+N` overflow popover
419
- * (對齊 display MultiPersonDisplay overflow popover 含 avatar SSOT)。PeoplePicker stack
420
- * pass 此 prop 讓 hidden items 顯 avatar + name(同 display path)。Default fallback
428
+ * (對齊 view path MultiPersonDisplay overflow popover 含 avatar SSOT)。PeoplePicker stack
429
+ * pass 此 prop 讓 hidden items 顯 avatar + name(同 view path)。Default fallback
421
430
  * `<Tag>{label}</Tag>` 純文字 backward-compat。
422
431
  */
423
432
  renderHiddenTag?: (item: { value: string; label: string }) => React.ReactNode
@@ -477,8 +486,8 @@ export interface ComboboxProps {
477
486
  visibleCountOverride?: number
478
487
  /**
479
488
  * Display 是否渲 ChevronDown + Field naked wrapper(D-path opt-in,2026-05-08)
480
- * — DataTable cell display↔edit 像素級對齊用。預設 false(裸 tag stack,backward compat)。
481
- * 設 true 時 display 走 fieldWrapperStyles(naked variant)+ ItemSuffix ChevronDown,
489
+ * — DataTable cell view↔edit 像素級對齊用。預設 false(裸 tag stack,backward compat)。
490
+ * 設 true 時 view 走 fieldWrapperStyles(naked variant)+ ItemSuffix ChevronDown,
482
491
  * 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。
483
492
  */
484
493
  showDisplayEndIcon?: boolean
@@ -487,7 +496,7 @@ export interface ComboboxProps {
487
496
  // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反)
488
497
  const getIconSize = (size: string) => ICON_SIZE[size as 'sm' | 'md' | 'lg']
489
498
 
490
- // ── Shared readonly/disabled/display render ─────────────────────────────────
499
+ // ── Shared readonly/disabled/view render ─────────────────────────────────
491
500
 
492
501
  function ReadonlyMultiSelect({
493
502
  mode, variant: variantProp, width, size, options, value, wrap, className, showDisplayEndIcon = false,
@@ -504,13 +513,13 @@ function ReadonlyMultiSelect({
504
513
  const containerRef = React.useRef<HTMLDivElement>(null)
505
514
  const hasTags = (value?.length ?? 0) > 0
506
515
 
507
- // mode='display'(Phase B2 2026-05-05):純內容輸出 — tag stack 不包 Field wrapper / 不 reserve 高度。
516
+ // mode='view'(Phase B2 2026-05-05):純內容輸出 — tag stack 不包 Field wrapper / 不 reserve 高度。
508
517
  // 對齊原 ComboboxDisplay sub-component(retired)。
509
518
  // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix ChevronDown,
510
- // 與 edit 同結構消除 cell display↔edit 像素偏移(Layer-B padding mismatch)。
511
- if (resolvedMode === 'display') {
519
+ // 與 edit 同結構消除 cell view↔edit 像素偏移(Layer-B padding mismatch)。
520
+ if (resolvedMode === 'view') {
512
521
  if (!showDisplayEndIcon) {
513
- // 2026-05-14 I2 fix(spec contract (e) display typography canonical):empty bare span 套
522
+ // 2026-05-14 I2 fix(spec contract (e) view typography canonical):empty bare span 套
514
523
  // `fieldDisplayTextClass(sz)`(sm/md→text-body,lg→text-body-lg)— 對齊跨 Field family 統一。
515
524
  if (!hasTags) return <span className={cn(fieldDisplayTextClass(sz), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>
516
525
  return (
@@ -519,8 +528,8 @@ function ReadonlyMultiSelect({
519
528
  }
520
529
  return (
521
530
  <div
522
- className={cn(fieldWrapperStyles({ mode: 'display', variant, width, size: sz }), hasTags && tagPadding[sz], className)}
523
- data-field-mode="display"
531
+ className={cn(fieldWrapperStyles({ mode: 'view', variant, width, size: sz }), hasTags && tagPadding[sz], className)}
532
+ data-field-mode="view"
524
533
  >
525
534
  {hasTags ? (
526
535
  <ComboboxTagStack value={value} options={options} tagSize={sz} wrap={wrap} />
@@ -538,8 +547,8 @@ function ReadonlyMultiSelect({
538
547
  <div ref={containerRef}
539
548
  className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), hasTags && tagPadding[sz],
540
549
  // 2026-05-18 #6A Round 1 Step 1/4(per user 拍板「決策6選a」+ codex M31 Step 5 verdict cite combobox.tsx:451):
541
- // readonly/disabled path 對齊 L293 display wrapper 已 ship 的 overflow-hidden fix。
542
- // M10 propagation:原 overflow-visible 讓 readonly tag 越界蓋 indicator,跟 display 不對稱。
550
+ // readonly/disabled path 對齊 L293 view wrapper 已 ship 的 overflow-hidden fix。
551
+ // M10 propagation:原 overflow-visible 讓 readonly tag 越界蓋 indicator,跟 view 不對稱。
543
552
  // 2026-06-27 對齊 edit path(L598-617):wrap 時 items-start + chevron self-start/tagHeight 鎖第一行;
544
553
  // paddingRight: var(--field-px) re-assert 右緣 12px(tagPadding 對稱 calc 會吃掉右緣,跟 edit 一致)。
545
554
  wrap ? 'flex-wrap items-start py-1' : 'overflow-hidden', className)}
@@ -586,7 +595,7 @@ function NativeCombobox({
586
595
  const fieldCtx = useFieldContext()
587
596
  const disabled = useResolvedFieldDisabled(disabledProp)
588
597
  const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
589
- // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="display"> 漏 cascade
598
+ // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="view"> 漏 cascade
590
599
  const resolvedMode = useResolvedFieldMode({ mode, disabled })
591
600
  const iconSize = getIconSize(size)
592
601
  const showClear = clearable && value.length > 0 && resolvedMode === 'edit'
@@ -633,7 +642,7 @@ function NativeCombobox({
633
642
  onClick={(e) => { if (e.target === e.currentTarget) { selectRef.current?.showPicker?.(); selectRef.current?.focus() } }}>
634
643
  {/* 2026-05-18 F2 sync(per user verbatim「modifying 修好 PeoplePicker stack 後改壞 Combobox tag display」
635
644
  + 「tag 應該要判斷所在空間最多可以呈現幾個tag(包括+n)去自動判斷何時要變成+n」):
636
- edit path tagArea 對齊 display path L293 已 ship 的 `overflow-hidden` fix。原 `overflow-visible`
645
+ edit path tagArea 對齊 view path L293 已 ship 的 `overflow-hidden` fix。原 `overflow-visible`
637
646
  讓 tag 視覺越界蓋 chevron / +N indicator(useOverflowCount measurement 對但 CSS overflow 仍露)。
638
647
  M10 violation root cause:2026-05-15 F1 Q3 只 fix display path,edit + Native(L518)沒同步。 */}
639
648
  <div ref={tagAreaRef} className={cn('flex-1 min-w-0 flex items-center relative', nakedCellRowModeAlign, wrap ? 'flex-wrap' : 'overflow-hidden')} style={{ gap: GAP }}
@@ -670,6 +679,9 @@ function CustomCombobox({
670
679
  searchAriaLabel = '搜尋選項', // i18n-allow: DS default
671
680
  emptyPlaceholder = '選擇…', // i18n-allow: DS default
672
681
  emptyText,
682
+ creatable = false,
683
+ onCreate,
684
+ createLabel,
673
685
  defaultOpen = false,
674
686
  onOpenChange,
675
687
  __triggerRef,
@@ -688,7 +700,7 @@ function CustomCombobox({
688
700
  const fieldCtx = useFieldContext()
689
701
  const error = useResolvedFieldInvalid(errorProp)
690
702
  const disabled = useResolvedFieldDisabled(disabledProp)
691
- // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="display"> 漏 cascade
703
+ // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="view"> 漏 cascade
692
704
  const resolvedMode = useResolvedFieldMode({ mode, disabled })
693
705
  const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
694
706
  const iconSize = getIconSize(size)
@@ -797,7 +809,7 @@ function CustomCombobox({
797
809
  if (e.key === 'Escape') setOpen(false)
798
810
  }}>
799
811
  {/* 2026-05-18 #6A Round 1 Step 2/4(per user 拍板「決策6選a」+ codex M31 Step 5 verdict cite combobox.tsx:648):
800
- CustomCombobox edit non-wrap tagArea 對齊 L293 display + L451 readonly + L518 native edit 已 ship 的 overflow-hidden fix。
812
+ CustomCombobox edit non-wrap tagArea 對齊 L293 view + L451 readonly + L518 native edit 已 ship 的 overflow-hidden fix。
801
813
  原 overflow-visible 讓 tag 越界蓋 chevron / +N indicator(user 圖三)。M10 propagation 完整 4-path align。 */}
802
814
  <div ref={tagAreaRef} className={cn('flex-1 min-w-0 flex items-center relative', nakedCellRowModeAlign, wrap ? 'flex-wrap' : 'overflow-hidden')} style={{ gap: tagAreaGap, paddingLeft: tagAreaPaddingLeftPx }}>
803
815
  {value.length > 0 ? (
@@ -863,6 +875,9 @@ function CustomCombobox({
863
875
  value={value}
864
876
  onValueChange={onChange as (value: string | string[]) => void}
865
877
  multiple
878
+ creatable={creatable}
879
+ onCreate={onCreate}
880
+ createLabel={createLabel}
866
881
  searchable={searchable && searchIn === 'menu'}
867
882
  searchPlaceholder={searchPlaceholder}
868
883
  size={size}
@@ -20,7 +20,7 @@ Command 是**搜尋 + 鍵盤導覽的指令清單**——提供搜尋框、分
20
20
 
21
21
  **實作基礎**:shadcn passthrough——基於 cmdk + Radix Dialog(Command Palette 模式)。本 DS 保留 shadcn 原結構,視覺樣式以 inline Tailwind class 直接掛在 cmdk primitive 上(`command.tsx`),token 值對齊(非結構繼承)MenuItem / Input / Empty 的 canonical token——改 MenuItem 不會自動連動 Command。
22
22
 
23
- **分類**:Internal primitive——由 SelectMenu 消費(Select / Combobox / PeoplePicker 透過 SelectMenu 使用)。App 不直接使用 Command;若要 Command Palette 類 UX,用本檔 export `CommandDialog`(cmdk + Radix Dialog 包裝,showcase「CommandPalette」story 消費)。
23
+ **分類**:Internal primitive——由 SelectMenu 消費(Select / Combobox / PeoplePicker 透過 SelectMenu 使用)。App **不從 root barrel front-door 直接 import** Command 家族;若要 Command Palette 類 UX,經 per-component subpath `@qijenchen/design-system/components/Command` 取用 `CommandDialog`(自行包裝確認後使用,對齊 `.claude/rules/ui-development.md`「Root barrel front-door 排除」— internal ≠ 禁用;cmdk + Radix Dialog 包裝,showcase「CommandPalette」story 消費)。
24
24
 
25
25
  **Layout Family**:非上述 family — composite / multi-section(多區塊組合,自 own layout)。
26
26
 
@@ -66,7 +66,7 @@ Command 通常由 `SelectMenu` 或自訂 Command Palette 元件消費——直
66
66
  - **SelectMenu**:Select / Combobox / PeoplePicker 的 searchable 浮層(**由 Field 觸發**,帶有 input field + 選中值回填)
67
67
  - **獨立 Command Palette**(Cmd+K):也用 Command 但**不在 Field 觸發**——透過全域快捷鍵開啟,用於跨頁面搜尋 / 快速動作入口
68
68
 
69
- 兩者底層都消費 Command。Field 場景用 SelectMenu(已處理 input binding);非 Field 場景(Cmd+K)用 `CommandDialog`(cmdk + Radix Dialog 包裝)。
69
+ 兩者底層都消費 Command。Field 場景用 SelectMenu(已處理 input binding);非 Field 場景(Cmd+K)用 `CommandDialog`(經 subpath `@qijenchen/design-system/components/Command` 取用,cmdk + Radix Dialog 包裝)。
70
70
 
71
71
  ---
72
72
 
@@ -97,7 +97,7 @@ Consumer 無需額外處理 a11y,保留 cmdk 原結構 + 使用 `<CommandInput>`
97
97
  - **Disabled item**:`<CommandItem disabled>` 透過 cmdk 內建支援,視覺繼承 MenuItem SSOT(`text-fg-disabled` + `aria-disabled=true` + 鍵盤導覽自動 skip + Enter 不觸發 onSelect)。skip 是「不進導覽清單」(cmdk `:not([aria-disabled="true"])` selector)— ↑/↓ 直接跳到下一個可選項,無中途 highlight。
98
98
  - **Group heading 不可選中**:heading 是 `aria-hidden` 裝飾 div(group container `role="presentation"`),不參與導覽與選取;SR 經 items wrapper `role="group"` + `aria-labelledby` 取得群組語意。
99
99
  - **Empty 後持續打字**:cmdk 每個 keystroke 重新過濾(`filtered.count` 驅動),`<CommandEmpty>` 持續顯示直到有 match;input 不鎖、不清空。
100
- - **Loading**:Command 本身非 async surface。async option fetch 應由 consumer(SelectMenu / Cmd+K palette)在外層處理 — `<CommandList>` body 切換為 `<Empty icon={<CircularProgress size={48}/>}/>`(對齊 `empty.spec.md`「現有消費者」SelectMenu loading row SSOT,禁止事項見其「禁止事項」loading 條)。Command primitive 不獨立 own loading prop。
100
+ - **Loading**:Command 本身非 async surface。async option fetch 應由 consumer(SelectMenu / Cmd+K palette)在外層處理 — `<CommandEmpty>` slot 內渲 `<Empty icon={<CircularProgress size={48}/>}/>`(spinner 只在無可顯示選項時出現;已有 options 保留顯示,不取代 — 對齊 `select.spec.md`「Loading」2026-07-04 拍板措辭 + `empty.spec.md`「現有消費者」SelectMenu loading SSOT,禁止事項見其「禁止事項」loading 條)。Command primitive 不獨立 own loading prop。
101
101
  - **Empty(no results)**:`<CommandEmpty>` 自動偵測 search filter result = 0 時渲;consumer 必傳 fallback 文案(預設 cmdk 不渲)。
102
102
  - **Dark mode / density**:Command 的 inline Tailwind class 直接消費 semantic token(`bg-neutral-hover` / `text-fg-muted` / `text-fg-disabled` 等),token 在 dark mode 自動切值——非透過 MenuItem / Input / Empty primitive 連動。density 不獨立 own(由 consumer SelectMenu 控)。
103
103
 
@@ -48,7 +48,7 @@ const CommandInput = React.forwardRef<
48
48
  React.ElementRef<typeof CommandPrimitive.Input>,
49
49
  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
50
50
  >(({ className, ...props }, ref) => (
51
- <div className="flex items-center border-b border-divider px-3" cmdk-input-wrapper="">
51
+ <div className="flex shrink-0 items-center border-b border-divider px-3" cmdk-input-wrapper="">
52
52
  <Search className="mr-2 h-4 w-4 shrink-0 text-fg-muted" />
53
53
  <CommandPrimitive.Input
54
54
  ref={ref}
@@ -80,7 +80,14 @@ const CommandList = React.forwardRef<
80
80
  React.ElementRef<typeof CommandPrimitive.List>,
81
81
  React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
82
82
  >(({ className, ...props }, ref) => (
83
- <ScrollArea className="max-h-[var(--menu-max-height,300px)]">
83
+ /* @story-baseline: overlay-surface.spec.md#Viewport-aware-scroll-chain-invariant(M25 SSOT owner)
84
+ owner spec: overlay-surface.spec.md:34/53/347-362 —— 「浮層 body 永遠 flex-1 min-h-0 overflow-y-auto;
85
+ 中間 wrapper 都必 flex flex-col h-full min-h-0;viewport 太小 body 內壓縮捲動」。
86
+ conflicting code(修前): 本 ScrollArea 固定 max-h-300 無 flex-1 → 在夾住的 SelectMenu PopoverContent
87
+ (max-h=available-height)內撐破外殼、底部選項被裁(320px viewport 實測 bottom 425 > 320 溢出)。
88
+ 修: 加 flex-1 min-h-0(對齊 M25 canonical + HoverCard/Popover),max-h-300 降為上限。Command root 已
89
+ `flex h-full flex-col`(command.tsx:23)= chain 完整;非 flex 容器內 flex-1 為 no-op(spec:34 backward compat)。 */
90
+ <ScrollArea className="flex-1 min-h-0 max-h-[var(--menu-max-height,300px)]">
84
91
  <CommandPrimitive.List ref={ref} className={cn("overflow-x-hidden", className)} {...props} />
85
92
  </ScrollArea>
86
93
  ))