@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
@@ -1,5 +1,6 @@
1
1
  // code-quality-allow: file-size — foundational composite(Field + FieldLabel + FieldDescription + FieldError + context + 8 layout variants),拆檔會讓 Field 家族互相 import 循環
2
2
  import * as React from 'react'
3
+ import { cva } from 'class-variance-authority'
3
4
  import { Info as InfoIcon } from 'lucide-react'
4
5
  import { cn } from '@/lib/utils'
5
6
  import { Tooltip, TooltipTrigger, TooltipContent } from '@/design-system/components/Tooltip/tooltip'
@@ -102,6 +103,20 @@ function detectControlLayout(controlNodes: React.ReactNode[]): FieldControlLayou
102
103
  return 'inline'
103
104
  }
104
105
 
106
+ /**
107
+ * 偵測 control child 的偏好尺寸——control primitive 在自己的檔案掛 static `fieldPreferredSize`
108
+ * (如 InlineEdit = 'sm',view 態無邊框,md 過大會讓版面鬆散),Field 未顯式指定 size 時收此偏好。
109
+ * 對齊 detectControlLayout 讀 `type.fieldLayout` 的機制(first-declared-wins);全部缺宣告 → null。
110
+ */
111
+ function detectPreferredSize(controlNodes: React.ReactNode[]): FieldSize | null {
112
+ for (const node of controlNodes) {
113
+ if (!React.isValidElement(node)) continue
114
+ const pref = (node.type as { fieldPreferredSize?: FieldSize } | null | undefined)?.fieldPreferredSize
115
+ if (pref) return pref
116
+ }
117
+ return null
118
+ }
119
+
105
120
  // ── Field ───────────────────────────────────────────────────────────────────
106
121
 
107
122
  export interface FieldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'id'> {
@@ -111,7 +126,7 @@ export interface FieldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
111
126
  * 視覺外殼(2026-05-05)。
112
127
  * - `default`(預設)— 含 border + bg(一般 form input)
113
128
  * - `naked` — cell-as-input(@internal;不在公開 FieldVariant 型別 — 2026-07-14 收窄至 FieldVariantInternal;2026-07-09 `bare` 退役)。edit×naked 自畫 border-based state
114
- * machine(rest/hover/focus/error);display/readonly/disabled×naked 用 transparent border,host cell 提供視覺邊框
129
+ * machine(rest/hover/focus/error);view×naked 用 transparent border,host cell 提供視覺邊框
115
130
  *
116
131
  * 透傳機制:Field 一次宣告,所有 child Field control 自動繼承(per-control prop override 可覆寫)。
117
132
  */
@@ -155,7 +170,7 @@ const Field = React.forwardRef<HTMLDivElement, FieldProps>(
155
170
  mode = 'edit',
156
171
  variant = 'default',
157
172
  orientation = 'vertical',
158
- size = 'md',
173
+ size: sizeProp,
159
174
  required = false,
160
175
  disabled: disabledProp = false,
161
176
  invalid = false,
@@ -199,6 +214,10 @@ const Field = React.forwardRef<HTMLDivElement, FieldProps>(
199
214
  const controlLayout: FieldControlLayout =
200
215
  controlLayoutProp ?? detectControlLayout(controlNodes)
201
216
 
217
+ // 解析 size:consumer 顯式 > control primitive 偏好(如 InlineEdit='sm')> 預設 md
218
+ // (對齊 controlLayout 三層優先序;InlineEdit 進 Field 不指定 size 時自動收 sm)
219
+ const size: FieldSize = sizeProp ?? detectPreferredSize(controlNodes) ?? 'md'
220
+
202
221
  const contextValue = React.useMemo<FieldContextValue>(
203
222
  () => ({
204
223
  id,
@@ -496,9 +515,22 @@ export interface FieldGroupProps extends React.HTMLAttributes<HTMLDivElement> {
496
515
  horizontalLabelWidth?: string
497
516
  }
498
517
 
518
+ // gap variant 收進 cva(className-only 差異 → cva,per ui-development.md「cva 適用」);值仍走 Tailwind gap utility。
519
+ const fieldGroupVariants = cva('flex flex-col min-w-0', {
520
+ variants: {
521
+ gap: {
522
+ compact: 'gap-3',
523
+ normal: 'gap-4',
524
+ loose: 'gap-6',
525
+ },
526
+ },
527
+ defaultVariants: {
528
+ gap: 'normal',
529
+ },
530
+ })
531
+
499
532
  const FieldGroup = React.forwardRef<HTMLDivElement, FieldGroupProps>(
500
533
  ({ className, gap = 'normal', horizontalLabelWidth, ...props }, ref) => {
501
- const gapClass = gap === 'compact' ? 'gap-3' : gap === 'loose' ? 'gap-6' : 'gap-4'
502
534
  const groupCtxValue = React.useMemo(
503
535
  () => ({ horizontalLabelWidth }),
504
536
  [horizontalLabelWidth],
@@ -507,7 +539,7 @@ const FieldGroup = React.forwardRef<HTMLDivElement, FieldGroupProps>(
507
539
  <FieldGroupContext.Provider value={groupCtxValue}>
508
540
  <div
509
541
  ref={ref}
510
- className={cn('flex flex-col min-w-0', gapClass, className)}
542
+ className={cn(fieldGroupVariants({ gap }), className)}
511
543
  data-field-group=""
512
544
  {...props}
513
545
  />
@@ -156,6 +156,8 @@ Form validation 的 ARIA / 鍵盤行為(對齊 WCAG 3.3.1 Error Identification +
156
156
  - **Required indicator**:label 的 `*` 為純視覺、對讀屏隱藏(`aria-hidden="true"`,field.tsx:395);required 語意由內部輸入控件的 `aria-required`(input.tsx:188)承擔,避免讀屏讀出「asterisk」語義不清
157
157
  - **Color-only error 警告**:error border 紅色之外必有文字訊息(WCAG 1.4.1 不僅靠顏色)— 由 `<FieldError>` 文字承擔;DS **不**在 input 內放 error 狀態 icon(見 `field-controls.spec.md`「禁止事項」)
158
158
 
159
+ **空值 / Loading(明文 N/A)**:本 spec 是跨 Field 控件的驗證時機原則(非 UI 元件)——空值渲染與 loading 狀態分屬各控件 spec(SSOT → `field-controls.spec.md`「null / undefined 值」+「Loading state」段)。
160
+
159
161
  ## 相關
160
162
 
161
163
  - `field.spec.md` — Field wrapper 的 error 視覺 chrome(紅框 + error message slot)
@@ -166,10 +168,12 @@ Form validation 的 ARIA / 鍵盤行為(對齊 WCAG 3.3.1 Error Identification +
166
168
 
167
169
  > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
168
170
 
171
+ - `checkbox.spec.md`
169
172
  - `combobox.spec.md`
170
173
  - `date-picker.spec.md`
171
174
  - `field-controls.spec.md`
172
175
  - `field.spec.md`
173
176
  - `link-input.spec.md`
177
+ - `select.spec.md`
174
178
  - `textarea.spec.md`
175
179
  - `time-picker.spec.md`
@@ -12,7 +12,7 @@
12
12
  * errors store / resetField;驗證「時機」由本 hook own(RHF 的 mode/reValidateMode 不外露)。
13
13
  *
14
14
  * ── 與 Field 家族的關係(engine-agnostic 分層,field.spec.md「定位」段)──
15
- * Field 保持純 layout + context(MUI FormControl 派,可用於 cell / display / 無引擎場景);
15
+ * Field 保持純 layout + context(MUI FormControl 派,可用於 cell / view / 無引擎場景);
16
16
  * 本 hook 住 form 層,錯誤經 consumer 一行 `<Field invalid={!!form.errors.x}>` 接入——
17
17
  * Field 層零耦合。這是「A 派的自由 + B 派的 DX」混合位置。
18
18
  *
@@ -91,13 +91,13 @@ export const UsageGuidance: Story = {
91
91
  <ul className="text-body text-fg-secondary list-disc pl-5 space-y-1">
92
92
  <li>Children 同 size — 不混用 sm/md/lg(視覺高度需一致)</li>
93
93
  <li>Children 自管 width — fixed selects + flex input 是典型 pattern</li>
94
- <li>Mode A(包進 Field):size 從 Field context 自動繼承,children 不傳 size</li>
94
+ <li>繼承 Field 尺寸(包進 Field):size 從 Field context 自動繼承,children 不傳 size</li>
95
95
  <li>Children scope:Input / NumberInput / Select / Combobox / DatePicker / Button(對齊 Ant 限定 list)</li>
96
96
  </ul>
97
97
  </section>
98
98
 
99
99
  <section>
100
- <h2 className="text-body-lg font-bold mb-3">Mode A — 包進 Field 當 control slot</h2>
100
+ <h2 className="text-body-lg font-bold mb-3">繼承 Field 尺寸 — 包進 Field 當 control slot</h2>
101
101
  <Field>
102
102
  <FieldLabel>聯絡電話</FieldLabel>
103
103
  <FieldControlGroup block>
@@ -109,7 +109,7 @@ export const UsageGuidance: Story = {
109
109
  </section>
110
110
 
111
111
  <section>
112
- <h2 className="text-body-lg font-bold mb-3">Mode B — Filter row 場景</h2>
112
+ <h2 className="text-body-lg font-bold mb-3">Standalone 逐一設尺寸 — Filter row 場景</h2>
113
113
  <FieldControlGroup block>
114
114
  <Select className="!w-[140px]" options={FIELDS} value="name" onChange={() => {}} aria-label="篩選欄位" />
115
115
  <Select className="!w-[120px]" options={OPS} value="contains" onChange={() => {}} aria-label="篩選運算子" />
@@ -89,7 +89,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
89
89
 
90
90
  **FCG `size` prop 目前為 no-op**:本元件只負責 border-collapse 接合(border / radius / z-index),不傳遞 size 給 children(無 Context Provider / 無 cloneElement)。Children 的尺寸完全由 child 自己決定 — 以下三種來源:
91
91
 
92
- **Mode A**(包進 Field 當 control slot):children 各自透過 `useFieldContext().size` 讀外層 `<Field>` 的 size(是 Field 的 context,不是 FCG 的)。
92
+ **繼承 Field 尺寸**(包進 Field 當 control slot):children 各自透過 `useFieldContext().size` 讀外層 `<Field>` 的 size(是 Field 的 context,不是 FCG 的)。
93
93
  ```tsx
94
94
  <Field size="lg">
95
95
  <FieldLabel>電話</FieldLabel>
@@ -100,7 +100,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
100
100
  </Field>
101
101
  ```
102
102
 
103
- **Mode B**(standalone):**逐一給每個 child 設 `size`**(設 `<FieldControlGroup size>` 無效)。
103
+ **Standalone 逐一設尺寸**:**逐一給每個 child 設 `size`**(設 `<FieldControlGroup size>` 無效)。
104
104
  ```tsx
105
105
  <FieldControlGroup>
106
106
  <Select size="md" className="!w-[120px]" options={fields} />
@@ -108,7 +108,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
108
108
  </FieldControlGroup>
109
109
  ```
110
110
 
111
- **Mode C**(進 Popover,density 鎖 md):children 自然吃 md(Popover density canonical)。
111
+ **Popover 鎖 md**(進 Popover,density 鎖 md):children 自然吃 md(Popover density canonical)。
112
112
 
113
113
  ## A11y 預設
114
114
 
@@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
5
5
  import { Avatar } from '@/design-system/components/Avatar/avatar'
6
6
  import { Button } from '@/design-system/components/Button/button'
7
7
  import { ProgressBar } from '@/design-system/components/ProgressBar/progress-bar'
8
- import { ItemContent, ItemPrefix } from '@/design-system/patterns/element-anatomy/item-anatomy'
8
+ import { ItemContent, ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'
9
9
 
10
10
  /**
11
11
  * FileItem — 檔案顯示 / 上傳進度
@@ -134,9 +134,8 @@ const FileItem = React.forwardRef<HTMLDivElement, FileItemProps>(
134
134
  ) : null
135
135
 
136
136
  // suffix 對齊 label 第一行(item-anatomy「24px 閾值對齊規則」小 suffix canonical):
137
- // icons 16 ≤ 24 屬小 suffix,統一 `h-[1lh]` inline,不因 desc wrap 改公式。
138
- // mode 同公式,跟 item-anatomy 一致。
139
- const suffixAlign = 'h-[1lh]'
137
+ // icons 16 ≤ 24 屬小 suffix,統一 h-[1lh] inline(由 ItemSuffix primitive 提供),
138
+ // 不因 desc wrap 改公式。兩 mode 同公式,跟 item-anatomy 一致。
140
139
 
141
140
  // Status slot 幾何(2026-04-23 user 統一):rich + compact 都用 `var(--field-height-xs)`(24)
142
141
  // 容器,裡面 Button xs iconOnly variant="text"(auto data-unbounded)。
@@ -181,11 +180,14 @@ const FileItem = React.forwardRef<HTMLDivElement, FileItemProps>(
181
180
  </span>
182
181
  ) : null
183
182
 
183
+ // 2026-07-16 dim 39/DA3:消費 ItemSuffix primitive(照 Notice 2026-06-15 已遷模式)。
184
+ // ItemSuffix base geometry(h-[1lh] shrink-0 ml-auto flex items-center gap-2)= 原手刻
185
+ // div 的 flex items-center gap-2 shrink-0 h-[1lh];多出的 ml-auto 為 no-op(左鄰
186
+ // ItemContent 是 flex-1,已把 suffix 推到右緣)→ 像素相等。hoverReveal 預設 false,
187
+ // 不干擾本檔自有的 statusSlot hover-swap 機制(group-hover/row 系)。
184
188
  const suffix = (
185
- <div
189
+ <ItemSuffix
186
190
  className={cn(
187
- 'flex items-center gap-2 shrink-0',
188
- suffixAlign,
189
191
  // data-unbounded chrome-canonical trick:let Button xs (24) live inside h-[1lh]
190
192
  // wrapper(compact ~18.2 / rich ~18.2 scanning)without pushing row height。
191
193
  // 視覺/hit area 仍 24,layout footprint 收斂到 1lh。同 overlay-surface
@@ -201,7 +203,7 @@ const FileItem = React.forwardRef<HTMLDivElement, FileItemProps>(
201
203
  )}
202
204
  {statusSlot}
203
205
  {actions}
204
- </div>
206
+ </ItemSuffix>
205
207
  )
206
208
 
207
209
  // content row — 消費 ItemContent primitive(封裝 label + desc + mt-gap token SSOT)。
@@ -166,10 +166,10 @@ export const MultipleRule: Story = {
166
166
  note="每個欄位只收一份檔案時用預設 multiple=false。世界級對照:LinkedIn 履歷上傳、Instagram 大頭貼、Notion page cover——使用者心智模型是「這個位置放一個檔案」。"
167
167
  >
168
168
  <FileUpload
169
- accept=".pdf,.doc,.docx"
170
- maxSize={5_000_000}
171
- title="上傳履歷"
172
- description="PDF / Word,最大 5 MB"
169
+ accept="image/png,image/jpeg"
170
+ maxSize={2_000_000}
171
+ title="上傳大頭貼"
172
+ description="PNG / JPG,最大 2 MB"
173
173
  onUpload={noop}
174
174
  />
175
175
  <Label>↑ 多拖幾個只取第一個,符合 Ant Upload.Dragger / Polaris DropZone 慣例</Label>
@@ -212,7 +212,7 @@ export const DivisionOfLaborRule: Story = {
212
212
  <div>
213
213
  <Rule
214
214
  title="✅ FileUpload 觸發 + FileItem 顯示"
215
- note="FileUpload 只負責「拖放偵測 + 觸發選檔」,已上傳檔案清單由 consumer 用 FileItem map 渲染——兩個元件各司其職,職責清楚。這正是 Notion / Slack / Figma 的附件 flow 做法。"
215
+ note="本範例走 consumer 自組清單:FileUpload 負責拖放偵測 + 觸發選檔,已上傳清單由 consumer 用 FileItem map 渲染。DS 同時支援第二條路——把清單交給 FileUpload 的 files prop 內建渲染(見下一則規則);兩條路都合法,差別只在觸發與清單狀態由誰持有,重點是都不重畫 FileItem 內部視覺。這正是 Notion / Slack / Figma 的附件 flow 做法。"
216
216
  >
217
217
  <FileUpload
218
218
  multiple
@@ -253,9 +253,9 @@ export const DivisionOfLaborRule: Story = {
253
253
 
254
254
  <Rule
255
255
  title="❌ 不在 FileUpload 裡自己重刻 FileItem primitives"
256
- note="2026-04-24 設計準則更新:DS 提供 dual path — FileUpload own list via `files` prop(內部 compose `<FileItem>`);OR consumer 自組 `{files.map(f => <FileItem ... />)}`。禁止的是「自畫 status icon / thumbnail / progress bar / row layout 等 FileItem primitives 視覺規格」。世界級(Material / Ant / Carbon) own list composing 自家 FileItem primitive"
256
+ note="DS 提供兩條清單管理路徑:一是把清單交給 FileUpload files prop,由它內部組合 FileItem 渲染;二是 consumer 自己用 files.map 逐項渲染 FileItem。兩條路都合法,真正禁止的是「自己重畫 FileItem 的內部視覺規格」——狀態圖示、縮圖比例、進度條、列排版都屬 FileItem SSOT。世界級(Material / Ant / Carbon)也都是自己擁有清單、組合自家的 FileItem primitive,不另刻一套。"
257
257
  >
258
- <Label warn>⚠️ 要顯示清單 → 傳 `files` prop(DS built-in)OR 自組 FileItem map;但不要自己重畫 FileItem 內部結構</Label>
258
+ <Label warn>⚠️ 要顯示清單 → 傳 `files` prop(內建)或自組 FileItem map;但不要自己重畫 FileItem 內部結構</Label>
259
259
  </Rule>
260
260
  </div>
261
261
  )
@@ -22,7 +22,7 @@ benchmark:
22
22
 
23
23
  ## 定位
24
24
 
25
- FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點擊觸發檔案選取浮窗。負責「上傳觸發 + 拖放偵測」,不負責「已上傳檔案清單顯示」(那是 `FileItem` 的職責)
25
+ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點擊觸發檔案選取浮窗。核心職責是「上傳觸發 + 拖放偵測」;它**不 own 單筆檔案列的視覺規格**(狀態圖示 / 縮圖 / 進度條 / 列排版屬 `FileItem` 的 SSOT),但**可 orchestrate 內建清單**——透過 `files` prop 內部組合 `FileItem` 渲染已上傳清單(dual-path,見「Props 行為」與「禁止事項」);consumer 自組 `FileItem` 清單亦合法。
26
26
 
27
27
  **實作基礎**:自建 — native HTML5 `<input type="file">` + drag-and-drop event(dragenter/dragover/dragleave/drop)+ DOM-level state 切換。無 external primitive,因為 Radix 沒 File Upload 對應 primitive,其他 UI kit 的 file upload 多數也是自建這層。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
28
28
 
@@ -146,7 +146,8 @@ FileUpload 是**拖放 / 點擊上傳區塊**——可拖曳檔案進入或點
146
146
  - `role="button"` + `tabIndex=0`(disabled 或 loading 時 `-1`)
147
147
  - Enter / Space 鍵觸發檔案選取浮窗(模擬 click)
148
148
  - `aria-disabled={true}` 當 disabled
149
- - `<input type="file">` 以 `className="hidden"`(`display:none`)隱藏 — 移出無障礙樹、不可聚焦;互動由外層 `role="button"` wrapper 承載。Accessible name 來自 wrapper 內 Empty 的 title + description 文字(name-from-content),非 input 本身
149
+ - `<input type="file">` 以 `className="hidden"`(`display:none`)隱藏 — 移出無障礙樹、不可聚焦;互動由外層 `role="button"` wrapper 承載
150
+ - **Accessible name(name-from-content + custom children 例外)**:預設(未傳 children)時 name 來自 wrapper 內 `<Empty>` 的 title + description 文字,非 input 本身。**但 `children` 會整個覆寫預設結構**——若 consumer 傳入**只有 icon / branding 圖像、無可見文字**的 children,`role="button"` wrapper 就失去 accessible name(SR 讀不出用途)。此情況 consumer **必須**在 `<FileUpload>` root 傳 `aria-label`(經 `{...props}` spread 到 wrapper div),或確保 children 內含可見文字。dropzone 與 button 兩 variant 同理
150
151
 
151
152
  ---
152
153
 
@@ -222,7 +222,11 @@ const FileUpload = React.forwardRef<HTMLDivElement, FileUploadProps>(
222
222
  <div ref={ref} className={cn('w-full', hasFiles && 'flex flex-col')}>
223
223
  {variant === 'button' ? (
224
224
  // ── button variant:緊湊觸發(form-friendly,省空間),click-only(無拖放區)──
225
- <div className={className} {...props}>
225
+ // consumer onClick 在此 wrapper div 承載(type = MouseEvent<HTMLDivElement>,對齊 FileUploadProps
226
+ // extends HTMLAttributes<HTMLDivElement>)。Button 觸發開檔會冒泡至此 → 同時觸發 consumer handler。
227
+ // 2026-07-17 修:onClick 於 L128 destructure 後未再 compose,button variant 靜默丟失公開 DOM prop
228
+ // (dropzone variant 經 handleClick 的 onClick?.(e) 正確 compose,button variant 漏接)。
229
+ <div className={className} onClick={onClick} {...props}>
226
230
  <input
227
231
  ref={inputRef}
228
232
  type="file"
@@ -710,7 +710,7 @@ export const SizeMatrix: Story = {
710
710
  <tr>
711
711
  <Td>Body 垂直 padding</Td>
712
712
  <Td mono>pt-[var(--layout-space-tight)] pb-[var(--layout-space-loose)]</Td>
713
- <Td>非對稱:top tight / bottom loose(對齊 layoutSpace v6 規則 4「bounded region 容器底 = loose」)</Td>
713
+ <Td>非對稱:top tight / bottom loose(bounded region 容器底部留白較寬鬆,內容不貼底、捲動時收尾更透氣)</Td>
714
714
  </tr>
715
715
  <tr>
716
716
  <Td>Section 間 gap</Td>
@@ -423,7 +423,7 @@ export const ReadOnlyRule: Story = {
423
423
 
424
424
  <Rule
425
425
  title="✅ readOnly — 其他人上傳的 / 公開公告 / 只讀 link 分享"
426
- note="readOnly=true 時說明區依 DS Textarea readonly canonical:有值 → 可聚焦選取但不可改的 native readonly textarea;空值 → display div 顯 emptyDisplay(`-`,非 placeholder);onDescriptionChange 不觸發。故 readonly 無 description 時顯示 `-`,不會出現「為這個檔案加上說明...」的編輯誘導 placeholder"
426
+ note="readOnly=true 時說明區依 DS Textarea readonly canonical:有值 → 可聚焦選取但不可改的 native readonly textarea;空值 → view div 顯 emptyDisplay(`-`,非 placeholder);onDescriptionChange 不觸發。故 readonly 無 description 時顯示 `-`,不會出現「為這個檔案加上說明...」的編輯誘導 placeholder"
427
427
  >
428
428
  <Button variant="tertiary" startIcon={FolderOpen} onClick={() => setRoOpen(true)}>
429
429
  開啟公司公告(readOnly)
@@ -140,7 +140,7 @@ FileViewer Toolbar 消費 `patterns/action-bar/action-bar.spec.md` 的**左中
140
140
  - **zoom** — 同一張檔案的微調(影響範圍:當前 renderer 的 transform state),影響最小
141
141
  - **info** — 切換附加資訊面板(影響範圍:layout,不破壞現有內容)
142
142
  - **download** — 把內容搬出 viewer(影響範圍:外部檔案系統,不可逆副作用)
143
- - **close** — 結束整個 viewing session(影響範圍:最大)。註:close = `setOpen(false)`,**僅卸載 `DialogContent` 子樹**;外層 FileViewer 元件恆掛載,shell state(zoom / infoOpen / index 等 `useState`)**保留** —— 重開同一檔沿用上次 zoom / infoOpen,非 fresh state
143
+ - **close** — 結束整個 viewing session(影響範圍:最大)。註:close = `setOpen(false)`,**僅卸載 `DialogContent` 子樹**;外層 FileViewer 元件恆掛載,shell state(zoom / infoOpen 等 `useState`)**保留** —— 重開同一檔沿用上次 zoom / infoOpen,非 fresh state(**index 例外**:uncontrolled 時開啟即重置為 `initialIndex`,`file-viewer.tsx` 開啟 effect)
144
144
 
145
145
  ### Dismiss canonical(Close X)
146
146
 
@@ -203,7 +203,7 @@ const figmaDesignFile: FileInfo[] = [
203
203
  mimeType: 'image/png',
204
204
  size: 2_840_000,
205
205
  description:
206
- 'Dashboard v2 最終設計稿 — 對齊 spec 2.3 節「Frozen columns + row actions」,可 zoom 看細節對齊。',
206
+ '營運儀表板 v2 最終設計稿 — 凍結欄與列操作已定案,可 zoom 檢視 spacing 與色彩細節。',
207
207
  metadata: {
208
208
  '設計師': 'Claire Wu',
209
209
  'Figma link': 'figma.com/file/xyz/Dashboard',
@@ -530,13 +530,13 @@ const InfoPanel = React.memo(function InfoPanel({
530
530
  {/* 檔案資訊 — 用 DS DescriptionList horizontal + divided(Google Drive /
531
531
  Notion file info panel 模式):
532
532
  - section header 用 FieldLabel 同款 typography 保視覺一致
533
- - DescriptionList direction="horizontal" divided 提供 row 下底線
533
+ - DescriptionList orientation="horizontal" divided 提供 row 下底線
534
534
  對齊格線,key 長度不一也易讀
535
535
  - 不再手刻 dl/dt/dd — canonical 由 DS primitive own */}
536
536
  {/* heading → first-item gap = item → item gap(Gestalt proximity,見 description-list.spec.md) */}
537
537
  <div className="flex flex-col gap-[var(--layout-space-tight)]">
538
538
  <span className="text-body font-normal text-foreground">{labels.fileInfoHeading}</span>
539
- <DescriptionList direction="horizontal" divided>
539
+ <DescriptionList orientation="horizontal" divided>
540
540
  <DescriptionItem label="檔名">{file.name}</DescriptionItem>
541
541
  <DescriptionItem label="類型">{file.mimeType || '-'}</DescriptionItem>
542
542
  {sizeText && (
@@ -775,6 +775,7 @@ const FileViewer = React.forwardRef<HTMLDivElement, FileViewerProps>(function Fi
775
775
  allowDownload = true,
776
776
  onDownload,
777
777
  labels: labelsOverride,
778
+ className,
778
779
  ...props
779
780
  }, ref) {
780
781
  const labels = React.useMemo(
@@ -89,7 +89,7 @@ export const VisualVariants: Story = {
89
89
  <div className="flex items-center gap-6">
90
90
  <HoverCard>
91
91
  <HoverCardTrigger asChild>
92
- <Button variant="tertiary" size="sm">hover 看 Card</Button>
92
+ <Button variant="tertiary" size="sm">Ada Chen</Button>
93
93
  </HoverCardTrigger>
94
94
  <HoverCardContent className="bg-surface-raised border border-border rounded-lg p-4 shadow-[var(--elevation-200)]">
95
95
  <div className="w-56">
@@ -108,11 +108,13 @@ export const VisualVariants: Story = {
108
108
  <div className="flex items-center gap-6">
109
109
  <HoverCard>
110
110
  <HoverCardTrigger asChild>
111
- <Button variant="tertiary" size="sm">hover 看 Tooltip 樣式</Button>
111
+ <Button variant="tertiary" size="sm">+3 位協作者</Button>
112
112
  </HoverCardTrigger>
113
113
  <HoverCardContent className="bg-tooltip rounded-md px-3 py-2" data-theme="dark">
114
- <div className="text-caption w-56">
115
- 深色樣式——常見於 OverflowIndicator 顯示溢出清單(+3 more 的 hover 展開)
114
+ <div className="text-caption w-56 flex flex-col gap-1">
115
+ <span>林思妤</span>
116
+ <span>陳柏宇</span>
117
+ <span>Aisha Khan</span>
116
118
  </div>
117
119
  </HoverCardContent>
118
120
  </HoverCard>
@@ -97,7 +97,12 @@ export const UsageGuidance: Story = {
97
97
  >
98
98
  <HoverCard>
99
99
  <HoverCardTrigger asChild>
100
- <a className="text-primary underline cursor-pointer inline-flex items-center gap-1">
100
+ <a
101
+ href="https://help.acme.com/user-manual"
102
+ target="_blank"
103
+ rel="noreferrer"
104
+ className="text-primary underline cursor-pointer inline-flex items-center gap-1"
105
+ >
101
106
  使用者手冊 <ExternalLink size={12} />
102
107
  </a>
103
108
  </HoverCardTrigger>
@@ -22,7 +22,7 @@ Hover 觸發的**可互動浮層**,基於 Radix HoverCard。
22
22
  **分類**:Internal primitive——不進 root barrel front-door、不裸用;DS 內由 Avatar(`hoverCard`)/ OverflowIndicator / ProfileCard 場景組合消費;consumer 需要時可經 per-component subpath 包裝並自行確認後使用(`.claude/rules/ui-development.md`「Root barrel front-door 排除」SSOT,internal ≠ 禁用)(frontmatter `isInternal`:root barrel 排除,storybook `Design System/Internal/HoverCard`)。
23
23
 
24
24
  - **是**:hover 顯示可互動內容(按鈕、連結、可選取文字——**滑鼠互動**;鍵盤進不了卡片,見「A11y 預設」)的浮層容器,定位為滑鼠 / focus 使用者的補充閱讀 surface
25
- - **不是**:Tooltip(純文字提示、不可互動、hover 離開即消失);也不是鍵盤可操作的互動浮層(那是 Popover)
25
+ - **不是**:Tooltip(純文字提示、**內容不可互動**;指標可短暫移到浮層屬 WCAG 1.4.13 hoverable grace,非「立即消失」);也不是鍵盤可操作的互動浮層(那是 Popover)
26
26
 
27
27
  **Layout Family**:非上述 family — composite / multi-section(多區塊組合,自 own layout)。
28
28
 
@@ -54,7 +54,7 @@ Hover 觸發的**可互動浮層**,基於 Radix HoverCard。
54
54
  |---|---|---|
55
55
  | 觸發 | hover | hover |
56
56
  | **內容可互動** | 是——限滑鼠(Radix 設 content 內 tabbable node 為 `tabindex="-1"`,鍵盤不可達,見「A11y 預設」) | 否(純文字) |
57
- | 停留行為 | 滑鼠移到浮層上不消失 | 滑鼠離開 trigger 即消失 |
57
+ | 停留行為 | 滑鼠可移到浮層互動不消失 | 滑鼠可短暫移到浮層(WCAG 1.4.13 grace,`tooltip.tsx` 未設 `disableHoverableContent`),但內容不可互動 |
58
58
  | 視覺樣式 | 由 consumer 決定(亮色 card 或深色 tooltip 風格皆可) | 統一深色背景 |
59
59
  | 典型用例 | ProfileCard、內容預覽、溢出清單 | icon 用途、shortcut、截斷文字補全 |
60
60
 
@@ -85,8 +85,10 @@ export const hoverCardMeta = {
85
85
  },
86
86
  states: ['default'],
87
87
  tokens: {
88
- bg: ['bg-surface-raised'],
89
- fg: ['--foreground'],
88
+ // 2026-07-16 deep-audit A.1b:清 Phase-1 佔位 — HoverCard 為純行為 primitive,
89
+ // Content 不套任何 bg/fg(spec「純行為 primitive」段);bg-surface-raised 屬 consumer(ProfileCard)。
90
+ bg: [],
91
+ fg: [],
90
92
  ring: [],
91
93
  },
92
94
  } as const
@@ -0,0 +1,184 @@
1
+ import * as React from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react'
3
+ import { InlineEdit } from './inline-edit'
4
+ import { Select } from '@/design-system/components/Select/select'
5
+ import { Field, FieldLabel } from '@/design-system/components/Field/field'
6
+
7
+ // 就地編輯無 disabled 態的世界級對照(cite,per M22):
8
+ // MUI X isCellEditable/cellMode https://mui.com/x/react-data-grid/editing/
9
+ // AG Grid editable https://www.ag-grid.com/react-data-grid/cell-editing/
10
+ // Atlassian inline-edit readView/editView https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx
11
+ //
12
+ // InlineEdit 設計規格(anatomy)—— view ↔ edit 二態就地編輯 primitive。
13
+ // Model A(2026-07-16):view 不自帶幾何 cva,委派控件 view mode / fieldViewGeometry helper;read↔edit 零跳。
14
+ const meta: Meta<typeof InlineEdit> = {
15
+ title: 'Design System/Components/InlineEdit/設計規格',
16
+ component: InlineEdit,
17
+ parameters: { layout: 'padded' },
18
+ }
19
+ export default meta
20
+ type Story = StoryObj<typeof InlineEdit>
21
+
22
+ // ── shared UI ────────────────────────────────────────────────────────────────
23
+ function Section({ title, children }: { title: string; children: React.ReactNode }) {
24
+ return (
25
+ <section className="mb-8">
26
+ <h3 className="mb-3 border-b border-divider pb-2 text-body-lg font-semibold text-foreground">{title}</h3>
27
+ {children}
28
+ </section>
29
+ )
30
+ }
31
+ function PropRow({ name, type, def, desc }: { name: string; type: string; def: string; desc: string }) {
32
+ return (
33
+ <tr className="border-b border-divider align-top">
34
+ <td className="py-2 pr-4 font-mono text-caption text-foreground">{name}</td>
35
+ <td className="py-2 pr-4 font-mono text-caption text-fg-secondary">{type}</td>
36
+ <td className="py-2 pr-4 font-mono text-caption text-fg-muted">{def}</td>
37
+ <td className="py-2 text-caption text-fg-secondary">{desc}</td>
38
+ </tr>
39
+ )
40
+ }
41
+ function Stateful({ value: initial, ...props }: Omit<React.ComponentProps<typeof InlineEdit>, 'onCommit'>) {
42
+ const [value, setValue] = React.useState(initial)
43
+ return <InlineEdit {...props} value={value} onCommit={setValue} />
44
+ }
45
+
46
+ export const Overview: Story = {
47
+ name: '元件總覽',
48
+ render: () => (
49
+ <div className="max-w-[720px]">
50
+ <Section title="定位">
51
+ <p className="text-body text-fg-secondary">
52
+ 就地編輯 primitive:值平常以 <b>view 態</b>(純內容)呈現,滑鼠移入顯示灰底提示、點擊(或鍵盤 Enter)切成
53
+ <b> edit 態</b>(真正的 Field 控件)。與 DataTable cell 同一份就地編輯語義 —— 只有 <code>view ↔ edit</code> 兩態
54
+ + <code>editable</code> 判準閘,<b>無 disabled 態</b>(世界級就地編輯共識,cite 見檔頭)。
55
+ </p>
56
+ </Section>
57
+ <Section title="Model A —— view 幾何來源">
58
+ <p className="mb-2 text-body text-fg-secondary">
59
+ InlineEdit <b>不自帶</b> geometry cva(消 M17 重複)。view 幾何來源分兩路,read↔edit 零跳因兩態同一顆控件、只差 chrome:
60
+ </p>
61
+ <ul className="ml-5 list-disc text-body text-fg-secondary">
62
+ <li><b>值-格式化</b>(Select→Tag / 日期 / avatar):<code>renderRead</code> 委派 <code>{'<Control mode="view">'}</code>,
63
+ 幾何 + 內部間距(多 tag gap / avatar↔label)皆由控件 view×default 提供。</li>
64
+ <li><b>純值 / 標題</b>(<code>as="h1"</code>):套 <code>fieldViewGeometry(size, multiline)</code> helper
65
+ (= view×default 幾何 SSOT:<code>px-field-px</code> + <code>min-h-field</code> + 多行 <code>py-2</code>)。</li>
66
+ </ul>
67
+ </Section>
68
+ </div>
69
+ ),
70
+ }
71
+
72
+ export const Inspector: Story = {
73
+ name: '屬性一覽',
74
+ render: () => (
75
+ <div className="max-w-[860px]">
76
+ <table className="w-full">
77
+ <thead>
78
+ <tr className="border-b-2 border-border text-left">
79
+ <th className="py-2 pr-4 text-caption font-semibold text-fg-muted">Prop</th>
80
+ <th className="py-2 pr-4 text-caption font-semibold text-fg-muted">型別</th>
81
+ <th className="py-2 pr-4 text-caption font-semibold text-fg-muted">預設</th>
82
+ <th className="py-2 text-caption font-semibold text-fg-muted">說明</th>
83
+ </tr>
84
+ </thead>
85
+ <tbody>
86
+ <PropRow name="value" type="T" def="—" desc="已提交的欄位值(view 顯示 / edit 初值)。泛型:string / number / string[] / PersonValue" />
87
+ <PropRow name="onCommit" type="(next: T) => void" def="—" desc="blur / Enter 且草稿有變更時提交" />
88
+ <PropRow name="editable" type="boolean" def="true" desc="false → view 無 hover 入口、無藍框、不可進 edit、不灰化(鎖定 = 純 view)" />
89
+ <PropRow name="renderRead" type="(v: T) => ReactNode" def="String(value)" desc="格式化 view;鐵律:渲染對應控件 mode='view',不可自刻" />
90
+ <PropRow name="renderEdit" type="(p) => ReactNode" def="Input/Textarea" desc="edit 控件(必 mode='edit');非 string 值必傳" />
91
+ <PropRow name="multiline" type="boolean" def="false" desc="Jira description 類:view 換行 + edit 用 Textarea(py-2 = Textarea)" />
92
+ <PropRow name="as" type="'span'|'h1'..'h6'|'p'|'div'" def="'span'" desc="view 語意標籤;標題保留文件大綱 a11y" />
93
+ <PropRow name="size" type="'sm'|'md'|'lg'" def="sm(fieldCtx cascade)" desc="接 fieldCtx.size;standalone fallback sm。靜態 fieldPreferredSize='sm' 讓外層 Field 自動收 sm" />
94
+ <PropRow name="label" type="string" def="—" desc="view 按鈕 aria-label(「編輯 {label}」)+ 控件 aria-label" />
95
+ </tbody>
96
+ </table>
97
+ </div>
98
+ ),
99
+ }
100
+
101
+ export const StateBehavior: Story = {
102
+ name: '狀態行為',
103
+ render: () => (
104
+ <div className="grid max-w-[720px] grid-cols-[140px_1fr] gap-x-6 gap-y-5">
105
+ <span className="text-body font-medium text-foreground">view(可編)</span>
106
+ <div><Stateful value="Fix flaky e2e test" label="標題" /></div>
107
+ <span className="text-body font-medium text-foreground">view(鎖定)</span>
108
+ <div><Stateful value="PROJ-482(系統產生)" label="編號" editable={false} /></div>
109
+ <span className="text-body font-medium text-foreground">多行</span>
110
+ <div><Stateful value={'第一行說明\n第二行說明(換行保留上下 padding)'} label="說明" multiline /></div>
111
+ <span className="text-body font-medium text-foreground">格式化(Tag)</span>
112
+ <div>
113
+ <Stateful
114
+ value="in-progress"
115
+ label="狀態"
116
+ renderRead={(v) => (
117
+ <Select mode="view" display="tag" size="sm" value={v as string} options={[
118
+ { value: 'in-progress', label: '進行中', tagVariant: 'blue' },
119
+ { value: 'done', label: '完成', tagVariant: 'green' },
120
+ ]} />
121
+ )}
122
+ renderEdit={(p) => (
123
+ <Select
124
+ autoFocus
125
+ display="tag"
126
+ size="sm"
127
+ defaultOpen
128
+ value={p.value as string}
129
+ aria-label={p['aria-label']}
130
+ options={[
131
+ { value: 'in-progress', label: '進行中', tagVariant: 'blue' },
132
+ { value: 'done', label: '完成', tagVariant: 'green' },
133
+ ]}
134
+ onChange={(next) => { p.onChange(next as string); p.commit() }}
135
+ />
136
+ )}
137
+ />
138
+ </div>
139
+ <span className="text-body font-medium text-foreground">空值</span>
140
+ <div><Stateful value="" label="負責人" placeholder="指派負責人…" /></div>
141
+ </div>
142
+ ),
143
+ }
144
+
145
+ // auto-sm 靠 Field 偵測 child 靜態 `fieldPreferredSize`(對齊 detectControlLayout 讀 fieldLayout)——
146
+ // **必 InlineEdit 為 Field 直接子**(包一層 wrapper 元件會遮蔽靜態,Field 偵測不到 → 退回 md)。
147
+ function CascadeDemo() {
148
+ const [title, setTitle] = React.useState('Fix flaky checkout e2e test')
149
+ const [owner, setOwner] = React.useState('')
150
+ return (
151
+ <div className="w-[420px] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]">
152
+ {/* Field 偵測 InlineEdit 靜態 fieldPreferredSize='sm' → 未指定 size 自動收 sm;
153
+ vertical orientation → InlineEdit -mx 讓 view 值落 FieldLabel 同左緣 */}
154
+ <Field className="mb-4">
155
+ <FieldLabel>任務標題</FieldLabel>
156
+ <InlineEdit value={title} onCommit={setTitle} label="任務標題" />
157
+ </Field>
158
+ <Field>
159
+ <FieldLabel>負責人</FieldLabel>
160
+ <InlineEdit value={owner} onCommit={setOwner} label="負責人" placeholder="指派…" />
161
+ </Field>
162
+ </div>
163
+ )
164
+ }
165
+ export const InFieldCascade: Story = {
166
+ name: '包進表單欄位:尺寸自動收斂與左緣對齊',
167
+ render: () => <CascadeDemo />,
168
+ }
169
+
170
+ export const Accessibility: Story = {
171
+ name: '無障礙',
172
+ render: () => (
173
+ <div className="max-w-[640px] text-body text-fg-secondary">
174
+ <ul className="ml-5 list-disc space-y-1">
175
+ <li>view 疊隱形 <code>{'<button aria-label="編輯 {label}">'}</code> —— Tab 可聚焦、Enter/Space 進 edit;
176
+ 與 <code>as="h1"</code> 分離(HTML 不允許 heading 巢狀於 button),標題大綱不破壞。</li>
177
+ <li>鍵盤 focus 顯 <code>border-primary</code> 藍框(<code>focus-visible</code>,僅鍵盤);滑鼠點擊不顯藍框。</li>
178
+ <li>結算焦點路徑分流:鍵盤 Enter/Esc → 焦點回 view 按鈕;滑鼠 blur → 純 view 不搶焦。</li>
179
+ <li><code>editable=false</code>:不渲染 Pressable,鍵盤跳過,語意 = 唯讀值(非 disabled)。</li>
180
+ <li>edit 態 = 真正的 Field 控件,繼承其 a11y(Input/Textarea/Select 各自 ARIA)。</li>
181
+ </ul>
182
+ </div>
183
+ ),
184
+ }