@qijenchen/design-system 0.1.0-beta.83 → 0.1.0-beta.84

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 (604) hide show
  1. package/CLAUDE.md +8 -8
  2. package/README.md +3 -2
  3. package/dist/components/Alert/alert.js.map +1 -1
  4. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  5. package/dist/components/AppShell/app-shell.d.ts +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  7. package/dist/components/AppShell/app-shell.js +22 -2
  8. package/dist/components/AppShell/app-shell.js.map +1 -1
  9. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  10. package/dist/components/Avatar/avatar.js +4 -4
  11. package/dist/components/Avatar/avatar.js.map +1 -1
  12. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  13. package/dist/components/Breadcrumb/breadcrumb.js +1 -1
  14. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  15. package/dist/components/Button/button.d.ts +2 -2
  16. package/dist/components/Button/button.d.ts.map +1 -1
  17. package/dist/components/Button/button.js +12 -3
  18. package/dist/components/Button/button.js.map +1 -1
  19. package/dist/components/Calendar/calendar.d.ts +13 -5
  20. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  21. package/dist/components/Calendar/calendar.js +7 -1
  22. package/dist/components/Calendar/calendar.js.map +1 -1
  23. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  24. package/dist/components/Carousel/carousel.js +8 -3
  25. package/dist/components/Carousel/carousel.js.map +1 -1
  26. package/dist/components/Chart/chart.d.ts.map +1 -1
  27. package/dist/components/Chart/chart.js +1 -0
  28. package/dist/components/Chart/chart.js.map +1 -1
  29. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  30. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  31. package/dist/components/Checkbox/checkbox.js +8 -4
  32. package/dist/components/Checkbox/checkbox.js.map +1 -1
  33. package/dist/components/Chip/chip.d.ts +1 -1
  34. package/dist/components/Chip/chip.d.ts.map +1 -1
  35. package/dist/components/Chip/chip.js.map +1 -1
  36. package/dist/components/Coachmark/coachmark.d.ts +10 -3
  37. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  38. package/dist/components/Coachmark/coachmark.js.map +1 -1
  39. package/dist/components/Combobox/combobox.d.ts +5 -1
  40. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  41. package/dist/components/Combobox/combobox.js +30 -13
  42. package/dist/components/Combobox/combobox.js.map +1 -1
  43. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  44. package/dist/components/DataTable/cell-registry.js +53 -62
  45. package/dist/components/DataTable/cell-registry.js.map +1 -1
  46. package/dist/components/DataTable/column-types.d.ts +10 -4
  47. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  48. package/dist/components/DataTable/column-types.js.map +1 -1
  49. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +1 -1
  50. package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
  51. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
  52. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  53. package/dist/components/DataTable/data-table-filter-panel.js +607 -0
  54. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  55. package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
  56. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  57. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  58. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  59. package/dist/components/DataTable/data-table-sort-manager.js +140 -0
  60. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  61. package/dist/components/DataTable/data-table.d.ts +5 -6
  62. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  63. package/dist/components/DataTable/data-table.js +36 -15
  64. package/dist/components/DataTable/data-table.js.map +1 -1
  65. package/dist/components/DataTable/filter-operators.js +140 -0
  66. package/dist/components/DataTable/filter-operators.js.map +1 -0
  67. package/dist/components/DataTable/filter-tree.js +312 -0
  68. package/dist/components/DataTable/filter-tree.js.map +1 -0
  69. package/dist/components/DataTable/index.d.ts +3 -0
  70. package/dist/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/components/DataTable/index.js +12 -1
  72. package/dist/components/DataTable/index.js.map +1 -1
  73. package/dist/components/DataTable/lib/column-meta.js +25 -0
  74. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  75. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  76. package/dist/components/DateGrid/date-grid.js +3 -1
  77. package/dist/components/DateGrid/date-grid.js.map +1 -1
  78. package/dist/components/DatePicker/date-picker.d.ts +7 -1
  79. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  80. package/dist/components/DatePicker/date-picker.js +105 -74
  81. package/dist/components/DatePicker/date-picker.js.map +1 -1
  82. package/dist/components/Dialog/dialog.d.ts +20 -3
  83. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  84. package/dist/components/Dialog/dialog.js +38 -20
  85. package/dist/components/Dialog/dialog.js.map +1 -1
  86. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  87. package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
  88. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  89. package/dist/components/Field/field-context.d.ts +45 -5
  90. package/dist/components/Field/field-context.d.ts.map +1 -1
  91. package/dist/components/Field/field-context.js +19 -0
  92. package/dist/components/Field/field-context.js.map +1 -1
  93. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  94. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  95. package/dist/components/Field/field-edit-keys.js +19 -0
  96. package/dist/components/Field/field-edit-keys.js.map +1 -0
  97. package/dist/components/Field/field-types.d.ts +20 -1
  98. package/dist/components/Field/field-types.d.ts.map +1 -1
  99. package/dist/components/Field/field-types.js.map +1 -1
  100. package/dist/components/Field/field-wrapper.d.ts +3 -2
  101. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  102. package/dist/components/Field/field-wrapper.js +38 -39
  103. package/dist/components/Field/field-wrapper.js.map +1 -1
  104. package/dist/components/Field/field.d.ts +2 -1
  105. package/dist/components/Field/field.d.ts.map +1 -1
  106. package/dist/components/Field/field.js.map +1 -1
  107. package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
  108. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  109. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  110. package/dist/components/FileItem/file-item.js +1 -1
  111. package/dist/components/FileItem/file-item.js.map +1 -1
  112. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  113. package/dist/components/FileUpload/file-upload.js.map +1 -1
  114. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  115. package/dist/components/FileViewer/file-viewer.js +7 -2
  116. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  117. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  118. package/dist/components/FileViewer/image-renderer.js +22 -1
  119. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  120. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  121. package/dist/components/HoverCard/hover-card.js +5 -3
  122. package/dist/components/HoverCard/hover-card.js.map +1 -1
  123. package/dist/components/InlineEdit/index.d.ts +2 -0
  124. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  125. package/dist/components/InlineEdit/index.js +5 -0
  126. package/dist/components/InlineEdit/index.js.map +1 -0
  127. package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
  128. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  129. package/dist/components/InlineEdit/inline-edit.js +164 -0
  130. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  131. package/dist/components/Input/input.d.ts +6 -11
  132. package/dist/components/Input/input.d.ts.map +1 -1
  133. package/dist/components/Input/input.js +43 -13
  134. package/dist/components/Input/input.js.map +1 -1
  135. package/dist/components/LinkInput/link-input.d.ts +2 -2
  136. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  137. package/dist/components/LinkInput/link-input.js +6 -5
  138. package/dist/components/LinkInput/link-input.js.map +1 -1
  139. package/dist/components/Menu/menu-item.d.ts +14 -2
  140. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  141. package/dist/components/Menu/menu-item.js +25 -3
  142. package/dist/components/Menu/menu-item.js.map +1 -1
  143. package/dist/components/NumberInput/number-input.d.ts +3 -3
  144. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  145. package/dist/components/NumberInput/number-input.js +6 -5
  146. package/dist/components/NumberInput/number-input.js.map +1 -1
  147. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  148. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
  149. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  150. package/dist/components/Pagination/pagination.js +1 -1
  151. package/dist/components/Pagination/pagination.js.map +1 -1
  152. package/dist/components/PeoplePicker/index.js +7 -1
  153. package/dist/components/PeoplePicker/index.js.map +1 -1
  154. package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
  155. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  156. package/dist/components/PeoplePicker/people-picker.js +23 -10
  157. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  158. package/dist/components/PeoplePicker/person-display.d.ts +6 -3
  159. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  160. package/dist/components/PeoplePicker/person-display.js +19 -14
  161. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  162. package/dist/components/Popover/popover.d.ts.map +1 -1
  163. package/dist/components/Popover/popover.js +3 -1
  164. package/dist/components/Popover/popover.js.map +1 -1
  165. package/dist/components/ProfileCard/profile-card.d.ts +2 -2
  166. package/dist/components/ProfileCard/profile-card.js +3 -3
  167. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  168. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  169. package/dist/components/RadioGroup/radio-group.js +15 -9
  170. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  171. package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
  172. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  173. package/dist/components/ScrollArea/scroll-area.js +6 -3
  174. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  175. package/dist/components/Select/select.d.ts +46 -5
  176. package/dist/components/Select/select.d.ts.map +1 -1
  177. package/dist/components/Select/select.js +109 -28
  178. package/dist/components/Select/select.js.map +1 -1
  179. package/dist/components/SelectMenu/select-menu.d.ts +3 -1
  180. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  181. package/dist/components/SelectMenu/select-menu.js +8 -1
  182. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  183. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  184. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  185. package/dist/components/SelectionControl/boolean-value.js +23 -0
  186. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  187. package/dist/components/SelectionControl/index.d.ts +1 -0
  188. package/dist/components/SelectionControl/index.d.ts.map +1 -1
  189. package/dist/components/SelectionControl/index.js +2 -0
  190. package/dist/components/SelectionControl/index.js.map +1 -1
  191. package/dist/components/Sheet/index.js +2 -1
  192. package/dist/components/Sheet/sheet.d.ts +7 -1
  193. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  194. package/dist/components/Sheet/sheet.js +5 -2
  195. package/dist/components/Sheet/sheet.js.map +1 -1
  196. package/dist/components/Sidebar/sidebar.d.ts +9 -4
  197. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  198. package/dist/components/Sidebar/sidebar.js +8 -10
  199. package/dist/components/Sidebar/sidebar.js.map +1 -1
  200. package/dist/components/Slider/slider.d.ts.map +1 -1
  201. package/dist/components/Slider/slider.js.map +1 -1
  202. package/dist/components/Switch/switch.d.ts +6 -6
  203. package/dist/components/Switch/switch.d.ts.map +1 -1
  204. package/dist/components/Switch/switch.js +3 -2
  205. package/dist/components/Switch/switch.js.map +1 -1
  206. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  207. package/dist/components/Tabs/tabs.js +15 -1
  208. package/dist/components/Tabs/tabs.js.map +1 -1
  209. package/dist/components/Textarea/textarea.d.ts +2 -2
  210. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  211. package/dist/components/Textarea/textarea.js +50 -42
  212. package/dist/components/Textarea/textarea.js.map +1 -1
  213. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  214. package/dist/components/TimePicker/time-columns.js +42 -38
  215. package/dist/components/TimePicker/time-columns.js.map +1 -1
  216. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  217. package/dist/components/TimePicker/time-picker.js +8 -6
  218. package/dist/components/TimePicker/time-picker.js.map +1 -1
  219. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  220. package/dist/components/Tooltip/tooltip.js +7 -5
  221. package/dist/components/Tooltip/tooltip.js.map +1 -1
  222. package/dist/components/TreeView/tree-view.d.ts +56 -1
  223. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  224. package/dist/components/TreeView/tree-view.js +211 -12
  225. package/dist/components/TreeView/tree-view.js.map +1 -1
  226. package/dist/index.d.ts +115 -58
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +93 -108
  229. package/dist/index.js.map +1 -1
  230. package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
  231. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  232. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  233. package/dist/tokens/categorical-color.js +15 -0
  234. package/dist/tokens/categorical-color.js.map +1 -1
  235. package/dist/tokens/motion/motion.d.ts +7 -6
  236. package/dist/tokens/motion/motion.d.ts.map +1 -1
  237. package/dist/tokens/motion/motion.js +6 -6
  238. package/dist/tokens/motion/motion.js.map +1 -1
  239. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  240. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  241. package/dist/tokens/motion/overlay-motion.js +9 -0
  242. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  243. package/dist/tokens/uiSize/icon-size.js +5 -0
  244. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  245. package/ds-canonical/fork/governance.lock +31 -19
  246. package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
  247. package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
  248. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
  249. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +2 -1
  250. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
  251. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
  252. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
  253. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
  254. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +28 -0
  255. package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -0
  256. package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
  257. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  258. package/ds-canonical/fork/manifest.json +15 -0
  259. package/ds-canonical/fork/preamble.md +20 -18
  260. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
  261. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
  262. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
  263. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
  264. package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
  265. package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
  266. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
  267. package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -2
  268. package/ds-canonical/hooks/README.md +1 -1
  269. package/ds-canonical/hooks/block_prototype_imports.py +15 -4
  270. package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
  271. package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
  272. package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
  273. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
  274. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
  275. package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
  276. package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
  277. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
  278. package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
  279. package/ds-canonical/hooks/check_opacity_token_usage.sh +28 -0
  280. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  281. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
  282. package/ds-canonical/hooks/check_ssot_header_declaration.sh +3 -1
  283. package/ds-canonical/hooks/check_story_determinism.sh +2 -0
  284. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
  285. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  286. package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
  287. package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
  288. package/ds-canonical/hooks/stop_self_audit.sh +31 -0
  289. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
  290. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
  291. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
  292. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
  293. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
  294. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  295. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
  296. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
  297. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +39 -0
  298. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  299. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  300. package/ds-canonical/references/build-ui-canonicals.md +2 -1
  301. package/ds-canonical/references/failure-class-registry.json +145 -0
  302. package/ds-canonical/references/governance-audit-coverage.md +33 -0
  303. package/ds-canonical/references/item-anatomy-recipe.md +2 -2
  304. package/ds-canonical/references/scenario-definition.md +6 -9
  305. package/ds-canonical/references/ssot-consultation.md +2 -2
  306. package/ds-canonical/references/ssot-index.md +5 -4
  307. package/ds-canonical/references/story-baseline-registry.json +8 -2
  308. package/ds-canonical/references/structural-token-retention.md +6 -9
  309. package/ds-canonical/rules/meta-patterns.md +8 -8
  310. package/ds-canonical/rules/self-verify.md +4 -3
  311. package/ds-canonical/rules/story-rules.md +2 -2
  312. package/ds-canonical/rules/ui-development.md +2 -2
  313. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -3
  314. package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
  315. package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
  316. package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
  317. package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
  318. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  319. package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
  320. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +51 -58
  321. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
  322. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
  323. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
  324. package/ds-canonical/skills/delivery-handoff/SKILL.md +1 -1
  325. package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
  326. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
  327. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
  328. package/ds-canonical/skills/governance-health/SKILL.md +1 -1
  329. package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
  330. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
  331. package/ds-canonical/skills/new-component/SKILL.md +5 -5
  332. package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
  333. package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
  334. package/ds-canonical/skills/propose-options/SKILL.md +3 -3
  335. package/ds-canonical/skills/prototype/SKILL.md +1 -1
  336. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
  337. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
  338. package/ds-canonical/skills/story-writing/SKILL.md +3 -3
  339. package/ds-canonical/skills/visual-audit/SKILL.md +2 -2
  340. package/ds-story-manifest.json +29 -20
  341. package/llms-full.txt +22 -1
  342. package/llms.txt +5 -4
  343. package/package.json +7 -3
  344. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  345. package/src/components/Accordion/accordion.stories.tsx +16 -1
  346. package/src/components/Alert/alert.principles.stories.tsx +1 -1
  347. package/src/components/Alert/alert.spec.md +8 -10
  348. package/src/components/Alert/alert.stories.tsx +28 -28
  349. package/src/components/Alert/alert.tsx +1 -1
  350. package/src/components/AppShell/_demo-helpers.tsx +6 -3
  351. package/src/components/AppShell/app-shell.spec.md +4 -4
  352. package/src/components/AppShell/app-shell.tsx +28 -2
  353. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
  354. package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
  355. package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
  356. package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
  357. package/src/components/Avatar/avatar.spec.md +2 -2
  358. package/src/components/Avatar/avatar.stories.tsx +8 -6
  359. package/src/components/Avatar/avatar.tsx +13 -5
  360. package/src/components/Badge/badge.principles.stories.tsx +1 -1
  361. package/src/components/Badge/badge.spec.md +2 -0
  362. package/src/components/Badge/badge.stories.tsx +4 -2
  363. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
  364. package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
  365. package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
  366. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  367. package/src/components/Button/button.principles.stories.tsx +1 -1
  368. package/src/components/Button/button.spec.md +2 -2
  369. package/src/components/Button/button.tsx +18 -3
  370. package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
  371. package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
  372. package/src/components/Calendar/calendar.spec.md +5 -4
  373. package/src/components/Calendar/calendar.stories.tsx +7 -4
  374. package/src/components/Calendar/calendar.tsx +26 -7
  375. package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
  376. package/src/components/Carousel/carousel.spec.md +2 -2
  377. package/src/components/Carousel/carousel.tsx +11 -3
  378. package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
  379. package/src/components/Chart/chart.principles.stories.tsx +1 -1
  380. package/src/components/Chart/chart.stories.tsx +2 -8
  381. package/src/components/Chart/chart.tsx +3 -0
  382. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
  383. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  384. package/src/components/Checkbox/checkbox.spec.md +11 -26
  385. package/src/components/Checkbox/checkbox.stories.tsx +11 -8
  386. package/src/components/Checkbox/checkbox.tsx +18 -13
  387. package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
  388. package/src/components/Chip/chip.principles.stories.tsx +1 -1
  389. package/src/components/Chip/chip.spec.md +5 -4
  390. package/src/components/Chip/chip.tsx +5 -1
  391. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
  392. package/src/components/CircularProgress/circular-progress.spec.md +3 -3
  393. package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
  394. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  395. package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
  396. package/src/components/Coachmark/coachmark.spec.md +5 -5
  397. package/src/components/Coachmark/coachmark.stories.tsx +3 -1
  398. package/src/components/Coachmark/coachmark.tsx +10 -3
  399. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  400. package/src/components/Combobox/combobox.tsx +53 -19
  401. package/src/components/Command/command.anatomy.stories.tsx +2 -2
  402. package/src/components/Command/command.principles.stories.tsx +1 -1
  403. package/src/components/Command/command.stories.tsx +42 -10
  404. package/src/components/DataTable/cell-registry.tsx +86 -63
  405. package/src/components/DataTable/column-types.ts +10 -1
  406. package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
  407. package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
  408. package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
  409. package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
  410. package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
  411. package/src/components/DataTable/data-table.spec.md +32 -12
  412. package/src/components/DataTable/data-table.stories.tsx +16 -17
  413. package/src/components/DataTable/data-table.tsx +103 -60
  414. package/src/components/DataTable/index.ts +3 -0
  415. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
  416. package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
  417. package/src/components/DateGrid/date-grid.spec.md +1 -1
  418. package/src/components/DateGrid/date-grid.tsx +3 -1
  419. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
  420. package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
  421. package/src/components/DatePicker/date-picker.spec.md +3 -3
  422. package/src/components/DatePicker/date-picker.stories.tsx +4 -5
  423. package/src/components/DatePicker/date-picker.tsx +99 -45
  424. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
  425. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  426. package/src/components/DescriptionList/description-list.spec.md +3 -3
  427. package/src/components/DescriptionList/description-list.stories.tsx +7 -6
  428. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
  429. package/src/components/Dialog/dialog.spec.md +9 -0
  430. package/src/components/Dialog/dialog.stories.tsx +66 -5
  431. package/src/components/Dialog/dialog.tsx +60 -25
  432. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  433. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
  434. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
  435. package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
  436. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  437. package/src/components/Empty/empty.spec.md +2 -2
  438. package/src/components/Field/field-context.ts +69 -5
  439. package/src/components/Field/field-controls.spec.md +62 -8
  440. package/src/components/Field/field-edit-keys.ts +66 -0
  441. package/src/components/Field/field-types.ts +58 -9
  442. package/src/components/Field/field-wrapper.tsx +50 -42
  443. package/src/components/Field/field.anatomy.stories.tsx +9 -6
  444. package/src/components/Field/field.spec.md +1 -1
  445. package/src/components/Field/field.stories.tsx +1 -1
  446. package/src/components/Field/field.tsx +2 -1
  447. package/src/components/Field/form-validation.spec.md +3 -3
  448. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
  449. package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
  450. package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
  451. package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
  452. package/src/components/FileItem/file-item.spec.md +5 -8
  453. package/src/components/FileItem/file-item.stories.tsx +36 -25
  454. package/src/components/FileItem/file-item.tsx +8 -6
  455. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
  456. package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
  457. package/src/components/FileUpload/file-upload.spec.md +1 -1
  458. package/src/components/FileUpload/file-upload.tsx +1 -1
  459. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
  460. package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
  461. package/src/components/FileViewer/file-viewer.spec.md +30 -12
  462. package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
  463. package/src/components/FileViewer/file-viewer.tsx +12 -4
  464. package/src/components/FileViewer/image-renderer.tsx +35 -7
  465. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
  466. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
  467. package/src/components/HoverCard/hover-card.spec.md +5 -5
  468. package/src/components/HoverCard/hover-card.stories.tsx +16 -16
  469. package/src/components/HoverCard/hover-card.tsx +5 -3
  470. package/src/components/InlineEdit/index.ts +5 -0
  471. package/src/components/InlineEdit/inline-edit.spec.md +149 -0
  472. package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
  473. package/src/components/InlineEdit/inline-edit.tsx +287 -0
  474. package/src/components/Input/input.anatomy.stories.tsx +8 -7
  475. package/src/components/Input/input.principles.stories.tsx +2 -12
  476. package/src/components/Input/input.spec.md +13 -18
  477. package/src/components/Input/input.tsx +69 -22
  478. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  479. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  480. package/src/components/LinkInput/link-input.tsx +11 -10
  481. package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
  482. package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
  483. package/src/components/Menu/menu-item.spec.md +22 -15
  484. package/src/components/Menu/menu-item.stories.tsx +39 -11
  485. package/src/components/Menu/menu-item.tsx +38 -4
  486. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  487. package/src/components/Notice/notice.spec.md +16 -3
  488. package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
  489. package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
  490. package/src/components/NumberInput/number-input.spec.md +4 -4
  491. package/src/components/NumberInput/number-input.tsx +19 -12
  492. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
  493. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
  494. package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
  495. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
  496. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
  497. package/src/components/Pagination/pagination.spec.md +1 -1
  498. package/src/components/Pagination/pagination.stories.tsx +2 -6
  499. package/src/components/Pagination/pagination.tsx +1 -1
  500. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
  501. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
  502. package/src/components/PeoplePicker/people-picker.spec.md +6 -6
  503. package/src/components/PeoplePicker/people-picker.tsx +56 -16
  504. package/src/components/PeoplePicker/person-display.tsx +32 -16
  505. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  506. package/src/components/Popover/popover.tsx +3 -1
  507. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
  508. package/src/components/ProfileCard/profile-card.spec.md +3 -3
  509. package/src/components/ProfileCard/profile-card.tsx +6 -6
  510. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
  511. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
  512. package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
  513. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
  514. package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
  515. package/src/components/RadioGroup/radio-group.tsx +41 -15
  516. package/src/components/Rating/rating.spec.md +11 -3
  517. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  518. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  519. package/src/components/ScrollArea/scroll-area.spec.md +7 -3
  520. package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
  521. package/src/components/ScrollArea/scroll-area.tsx +17 -4
  522. package/src/components/Select/select.anatomy.stories.tsx +3 -3
  523. package/src/components/Select/select.spec.md +27 -17
  524. package/src/components/Select/select.stories.tsx +33 -0
  525. package/src/components/Select/select.tsx +190 -36
  526. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
  527. package/src/components/SelectMenu/select-menu.spec.md +15 -7
  528. package/src/components/SelectMenu/select-menu.tsx +16 -3
  529. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  530. package/src/components/SelectionControl/index.ts +1 -0
  531. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
  532. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  533. package/src/components/SelectionControl/selection-item.spec.md +3 -3
  534. package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
  535. package/src/components/Separator/separator.spec.md +2 -2
  536. package/src/components/Separator/separator.stories.tsx +6 -27
  537. package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
  538. package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
  539. package/src/components/Sheet/sheet.spec.md +2 -2
  540. package/src/components/Sheet/sheet.tsx +9 -5
  541. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
  542. package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
  543. package/src/components/Sidebar/sidebar.spec.md +13 -15
  544. package/src/components/Sidebar/sidebar.stories.tsx +8 -7
  545. package/src/components/Sidebar/sidebar.tsx +37 -25
  546. package/src/components/Skeleton/skeleton.stories.tsx +4 -11
  547. package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
  548. package/src/components/Slider/slider.principles.stories.tsx +2 -2
  549. package/src/components/Slider/slider.spec.md +5 -5
  550. package/src/components/Slider/slider.tsx +3 -1
  551. package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
  552. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  553. package/src/components/Steps/steps.spec.md +1 -1
  554. package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
  555. package/src/components/Switch/switch.principles.stories.tsx +4 -4
  556. package/src/components/Switch/switch.spec.md +8 -8
  557. package/src/components/Switch/switch.stories.tsx +9 -24
  558. package/src/components/Switch/switch.tsx +17 -13
  559. package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
  560. package/src/components/Tabs/tabs.spec.md +1 -1
  561. package/src/components/Tabs/tabs.tsx +21 -1
  562. package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
  563. package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
  564. package/src/components/Textarea/textarea.spec.md +4 -4
  565. package/src/components/Textarea/textarea.tsx +72 -46
  566. package/src/components/TimePicker/time-columns.tsx +6 -2
  567. package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
  568. package/src/components/TimePicker/time-picker.spec.md +3 -3
  569. package/src/components/TimePicker/time-picker.tsx +16 -10
  570. package/src/components/Toast/toast.stories.tsx +7 -4
  571. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
  572. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  573. package/src/components/Tooltip/tooltip.spec.md +1 -1
  574. package/src/components/Tooltip/tooltip.stories.tsx +4 -39
  575. package/src/components/Tooltip/tooltip.tsx +8 -6
  576. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
  577. package/src/components/TreeView/tree-view.spec.md +42 -11
  578. package/src/components/TreeView/tree-view.stories.tsx +1 -0
  579. package/src/components/TreeView/tree-view.tsx +322 -17
  580. package/src/index.ts +584 -60
  581. package/src/patterns/action-bar/action-bar.spec.md +9 -0
  582. package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
  583. package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
  584. package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
  585. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
  586. package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
  587. package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
  588. package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
  589. package/src/styles/tokens.css +3 -0
  590. package/src/tokens/README.md +1 -1
  591. package/src/tokens/color/color.spec.md +3 -3
  592. package/src/tokens/color/color.stories.tsx +3 -3
  593. package/src/tokens/density/density.stories.tsx +16 -3
  594. package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
  595. package/src/tokens/motion/motion.css +33 -21
  596. package/src/tokens/motion/motion.spec.md +32 -15
  597. package/src/tokens/motion/motion.stories.tsx +58 -5
  598. package/src/tokens/motion/motion.ts +7 -6
  599. package/src/tokens/motion/overlay-motion.ts +26 -0
  600. package/src/tokens/orphan-tokens.spec.md +4 -4
  601. package/src/tokens/token-system.spec.md +1 -0
  602. package/src/tokens/uiSize/uiSize.spec.md +1 -1
  603. package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
  604. package/src/tokens/utility-registry.json +160 -23
@@ -0,0 +1,246 @@
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
+ const meta: Meta<typeof InlineEdit> = {
8
+ title: 'Design System/Components/InlineEdit/展示',
9
+ component: InlineEdit,
10
+ tags: ['autodocs'],
11
+ }
12
+ export default meta
13
+ type Story = StoryObj<typeof InlineEdit>
14
+
15
+ // 受控 helper —— InlineEdit 的 value 由 consumer 持有,onCommit 回寫
16
+ type StatefulProps = Omit<React.ComponentProps<typeof InlineEdit>, 'onCommit'>
17
+ function StatefulInlineEdit({ value: initial, ...props }: StatefulProps) {
18
+ const [value, setValue] = React.useState(initial)
19
+ return <InlineEdit {...props} value={value} onCommit={setValue} />
20
+ }
21
+
22
+ /* ── 真實業務:工作項目詳情面板標題就地編輯 ── */
23
+ export const TaskTitle: Story = {
24
+ name: '任務標題就地編輯',
25
+ render: () => {
26
+ const [title, setTitle] = React.useState('Fix flaky checkout e2e test on Safari')
27
+ return (
28
+ <div className="w-[560px] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]">
29
+ <p className="mb-1 text-caption text-fg-secondary">PROJ-482</p>
30
+ <InlineEdit
31
+ as="h1"
32
+ value={title}
33
+ onCommit={setTitle}
34
+ label="task title"
35
+ readClassName="text-h4 font-bold text-foreground"
36
+ placeholder="Add a title…"
37
+ />
38
+ <p className="mt-4 text-body text-fg-muted">
39
+ 標題平常是純文字,滑鼠移入顯示灰底提示可編輯;點擊(或鍵盤 Tab + Enter)切成輸入框,Enter 儲存、Esc 取消。
40
+ </p>
41
+ </div>
42
+ )
43
+ },
44
+ }
45
+
46
+ /* ── 真實業務:詳情面板多個 metadata 欄位逐欄就地編輯 ── */
47
+ function MetaRow({ label, value, placeholder }: { label: string; value: string; placeholder?: string }) {
48
+ return (
49
+ <div className="grid grid-cols-[128px_1fr] items-center gap-[var(--layout-space-tight)]">
50
+ <span className="text-body text-fg-secondary">{label}</span>
51
+ <StatefulInlineEdit value={value} label={label} placeholder={placeholder} size="sm" />
52
+ </div>
53
+ )
54
+ }
55
+
56
+ export const MetadataFields: Story = {
57
+ name: '詳情欄位就地編輯',
58
+ render: () => (
59
+ <div className="w-[440px] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]">
60
+ {/* heading → fields = tight(functional labeling);fields 彼此 = loose(parallel 獨立欄位,layoutSpace.spec.md 親疏 3 級) */}
61
+ <h2 className="mb-[var(--layout-space-tight)] text-body font-bold text-foreground">Details</h2>
62
+ <div className="flex flex-col gap-[var(--layout-space-loose)]">
63
+ <MetaRow label="Summary" value="Q3 launch checklist owner handoff" />
64
+ <MetaRow label="Story points" value="5" />
65
+ <MetaRow label="External link" value="figma.com/file/checkout-flow" placeholder="Add a link…" />
66
+ <MetaRow label="Reviewer note" value="" placeholder="Add a note…" />
67
+ </div>
68
+ </div>
69
+ ),
70
+ }
71
+
72
+ /* ── 三態 matrix:read / hover / edit(play 驅動 hover + edit 態顯示)── */
73
+ function StateRow({
74
+ caption,
75
+ children,
76
+ ...rest
77
+ }: { caption: string; children: React.ReactNode } & React.HTMLAttributes<HTMLDivElement>) {
78
+ return (
79
+ <div {...rest}>
80
+ <p className="mb-2 text-caption text-fg-muted">{caption}</p>
81
+ {/* px-field-px 抵銷 InlineEdit 的 -mx-field-px,讓文字左緣與說明對齊 */}
82
+ <div className="px-[var(--field-px)]">{children}</div>
83
+ </div>
84
+ )
85
+ }
86
+
87
+ export const States: Story = {
88
+ name: '三態:閱讀/懸停/編輯',
89
+ render: () => (
90
+ <div className="flex w-[440px] flex-col gap-6">
91
+ <StateRow caption="read(靜態)—— 純文字,透明邊框(預留、不可見)、無底色">
92
+ <StatefulInlineEdit value="Design review notes" label="標題" />
93
+ </StateRow>
94
+ <StateRow
95
+ caption="read + hover —— 灰底 bg-neutral-hover + rounded-md(非邊框)"
96
+ data-testid="inline-edit-hover-row"
97
+ >
98
+ <StatefulInlineEdit value="Design review notes" label="標題" />
99
+ </StateRow>
100
+ <StateRow
101
+ caption="edit —— 真正的 Field 控件(border + focus 藍框 + input)"
102
+ data-testid="inline-edit-edit-row"
103
+ >
104
+ <StatefulInlineEdit value="Design review notes" label="標題" />
105
+ </StateRow>
106
+ </div>
107
+ ),
108
+ play: async ({ canvasElement }) => {
109
+ const { userEvent } = await import('@storybook/test')
110
+ // hover 態:滑鼠移入第二列 → 顯示 bg-neutral-hover 灰底
111
+ const hoverBtn = canvasElement
112
+ .querySelector('[data-testid="inline-edit-hover-row"]')
113
+ ?.querySelector('button')
114
+ if (hoverBtn) await userEvent.hover(hoverBtn)
115
+ // edit 態:點擊第三列 → 切成真正的 Input 控件
116
+ const editBtn = canvasElement
117
+ .querySelector('[data-testid="inline-edit-edit-row"]')
118
+ ?.querySelector('button')
119
+ if (editBtn) await userEvent.click(editBtn)
120
+ },
121
+ }
122
+
123
+ /* ── 真實業務:工作項目 Status 就地編輯(型別化 read = Tag)──
124
+ SSOT 示範:read 態的 Tag **不是** InlineEdit 自刻,而是渲染 `<Select mode="display" display="tag">`
125
+ —— 「select 值 → Tag」的格式化 SSOT 住在 Select 的 display mode(= DataTable 格子消費的同一份)。
126
+ read 與 edit 用同一個 Select、只切 mode,格式零分歧。 */
127
+ const STATUS_OPTIONS = [
128
+ { value: 'todo', label: 'To Do', tagVariant: 'neutral' },
129
+ { value: 'in-progress', label: 'In Progress', tagVariant: 'blue' },
130
+ { value: 'in-review', label: 'In Review', tagVariant: 'yellow' },
131
+ { value: 'done', label: 'Done', tagVariant: 'green' },
132
+ ]
133
+
134
+ // 架構理由(移出 story name,per story-rules.md name 必人話):read 態顯示為 Tag,SSOT = Select
135
+ // 的 display mode —— InlineEdit 委派 Select 控件,read view 消費 Select 的 display 呈現(標籤/色點)。
136
+ export const SelectTagField: Story = {
137
+ name: '狀態就地編輯(讀取時顯示為標籤)',
138
+ render: () => {
139
+ const [status, setStatus] = React.useState('in-progress')
140
+ return (
141
+ <div className="grid w-[440px] grid-cols-[128px_1fr] items-center gap-[var(--layout-space-tight)] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]">
142
+ <span className="text-body text-fg-secondary">Status</span>
143
+ <InlineEdit<string>
144
+ value={status}
145
+ onCommit={setStatus}
146
+ label="status"
147
+ size="sm"
148
+ // read:Select 的 display mode 輸出 Tag(格式化 SSOT 住在 Select,InlineEdit 不自刻)。
149
+ // Tag **盒子左緣**對齊 label / 其他純文字值,tag 文字被 pill 內距自然縮排 —— 對齊 Meegle 實測
150
+ // (PIL 量測 2026-07-10:Meegle pill box=label 左緣、text 縮排 ~8px@1x)。**不做 text outdent**。
151
+ renderRead={(v) => (
152
+ <Select mode="display" display="tag" size="sm" value={v} options={STATUS_OPTIONS} />
153
+ )}
154
+ // edit:同一個 Select 切 edit mode + 立即開選單;選完即 commit 回 read
155
+ renderEdit={(p) => (
156
+ <Select
157
+ autoFocus
158
+ display="tag"
159
+ size="sm"
160
+ value={p.value}
161
+ options={STATUS_OPTIONS}
162
+ defaultOpen
163
+ onChange={(next) => {
164
+ p.onChange(next as string)
165
+ p.commit()
166
+ }}
167
+ />
168
+ )}
169
+ />
170
+ </div>
171
+ )
172
+ },
173
+ }
174
+
175
+ /* ── 真實業務:工作項目 Description 多行就地編輯(Jira description 類)──
176
+ multiline:read 態換行顯示、edit 態預設 Textarea(Enter=換行、Cmd/Ctrl+Enter 或 blur 提交)。 */
177
+ export const MultilineDescription: Story = {
178
+ name: '多行描述就地編輯(Jira 描述欄類)',
179
+ render: () => {
180
+ const [desc, setDesc] = React.useState(
181
+ 'Safari 上點擊結帳時,e2e 測試會間歇性失敗。\n\n重現:連跑測試 5 次,約 2 次失敗;疑似 checkout webhook race。',
182
+ )
183
+ return (
184
+ <div className="w-[560px] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]">
185
+ <p className="mb-1 text-caption text-fg-secondary">DESCRIPTION</p>
186
+ <InlineEdit
187
+ multiline
188
+ value={desc}
189
+ onCommit={setDesc}
190
+ label="description"
191
+ placeholder="Add a description…"
192
+ />
193
+ </div>
194
+ )
195
+ },
196
+ }
197
+
198
+ /* ── 真實業務:工作項目詳情面板 —— vertical Field(FieldLabel + control)+ InlineEdit ──
199
+ 混合 plain / Tag / 多行欄位,驗證 Tag 值與 FieldLabel + 其他 plain 值的左緣視覺對齊。 */
200
+ export const VerticalFieldForm: Story = {
201
+ name: '詳情面板:垂直排列 Field + InlineEdit(Tag 對齊)',
202
+ render: () => {
203
+ const [status, setStatus] = React.useState('in-progress')
204
+ const [desc, setDesc] = React.useState('Safari 上點擊結帳時,e2e 測試會間歇性失敗。')
205
+ return (
206
+ <div
207
+ data-testid="vfield-form"
208
+ className="flex w-[420px] flex-col gap-[var(--layout-space-loose)] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]"
209
+ >
210
+ <Field orientation="vertical">
211
+ <FieldLabel>Assignee</FieldLabel>
212
+ <StatefulInlineEdit value="Alex Rivera" label="assignee" />
213
+ </Field>
214
+ <Field orientation="vertical">
215
+ <FieldLabel>Status</FieldLabel>
216
+ <InlineEdit<string>
217
+ value={status}
218
+ onCommit={setStatus}
219
+ label="status"
220
+ renderRead={(v) => (
221
+ <Select mode="display" display="tag" size="sm" value={v} options={STATUS_OPTIONS} />
222
+ )}
223
+ renderEdit={(p) => (
224
+ <Select
225
+ autoFocus
226
+ display="tag"
227
+ size="sm"
228
+ value={p.value}
229
+ options={STATUS_OPTIONS}
230
+ defaultOpen
231
+ onChange={(next) => {
232
+ p.onChange(next as string)
233
+ p.commit()
234
+ }}
235
+ />
236
+ )}
237
+ />
238
+ </Field>
239
+ <Field orientation="vertical">
240
+ <FieldLabel>Description</FieldLabel>
241
+ <InlineEdit multiline value={desc} onCommit={setDesc} label="description" />
242
+ </Field>
243
+ </div>
244
+ )
245
+ },
246
+ }
@@ -0,0 +1,287 @@
1
+ // ── 消費的 SSOT ──
2
+ // - components/Field/field-controls.spec.md(「Field 框架地圖」段:InlineEdit = read↔edit 疊在 Field 之上;edit-in-place)
3
+ // - components/Field/field-edit-keys.ts(makeEditSettleKeyHandler = Enter/Esc + 中文 IME guard 結算 SSOT,與 DataTable cell 同源)
4
+ // - components/Input/input.tsx / Textarea/textarea.tsx(預設 renderEdit;不重刻 input chrome)
5
+ // - components/Field/field-context.ts(FieldSize 型別)
6
+ // - tokens/uiSize/uiSize.css(--field-px 水平內距 SSOT / --field-height-{sm,md,lg} read 態高度)
7
+ // - tokens/color/color.spec.md(bg-neutral-hover = hover 回饋)
8
+ // - components/Field/field-wrapper.tsx(read focus = border-primary,消費 Field edit focus 藍框語言;非 Button ring)
9
+ // - 世界級對照(泛型 readView/editView 就地編輯 + read 態底色 tint + 隱形 Pressable):
10
+ // Atlassian inline-edit(readView/editView 兩 render prop + 泛型 <FieldValue>)
11
+ // https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/types.tsx
12
+ // read-view hover = 背景 tint 非邊框 + 隱形 Pressable 供鍵盤 focus+Enter:
13
+ // https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx
14
+ //
15
+ // ── 型別化 read / 多行(2026-07-09 L2,edit-in-place SSOT 研究落地)──
16
+ // InlineEdit 現為泛型 <T>(非只 string)。**格式化 read view 的 SSOT 鐵律**:renderRead 要顯示
17
+ // Tag / 日期 / label 等格式化值時,**渲染對應 Field 控件的 `mode="display"`**(如 `<Select mode="display"
18
+ // display="tag">`),**不可自刻 Tag / 格式化**——「select 值 → Tag」的 SSOT 住在 Select 的 display mode
19
+ // (= DataTable cell 消費的同一份)。這保證 read 與 edit 用同一個控件、格式零分歧。多行(multiline)
20
+ // 供 Jira description 類欄位。
21
+ import * as React from 'react'
22
+ import { cn } from '@/lib/utils'
23
+ import { Input } from '@/design-system/components/Input/input'
24
+ import { Textarea } from '@/design-system/components/Textarea/textarea'
25
+ import type { FieldSize } from '@/design-system/components/Field/field-context'
26
+ import { cva } from 'class-variance-authority'
27
+ import { fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
28
+ import { makeEditSettleKeyHandler } from '@/design-system/components/Field/field-edit-keys'
29
+
30
+ // ── Types ───────────────────────────────────────────────────────────────────
31
+
32
+ /** renderEdit 回呼收到的受控介面 —— consumer 換任意 Field 控件(Textarea / Select / DatePicker …)接線用 */
33
+ export interface InlineEditRenderProps<T = string> {
34
+ /** 目前編輯中的草稿值(內部管理,非已 commit 的 value) */
35
+ value: T
36
+ /** 草稿變更 */
37
+ onChange: (next: T) => void
38
+ /** 提交草稿 → onCommit(next) + 回 read 態 */
39
+ commit: () => void
40
+ /** 放棄草稿 → 回 read 態不 commit */
41
+ cancel: () => void
42
+ /** 進 edit 態時自動 focus 控件 */
43
+ autoFocus: boolean
44
+ /** 尺寸(cascade 給控件,與 read 態高度對齊) */
45
+ size: FieldSize
46
+ /** 控件無障礙標籤(= label prop) */
47
+ 'aria-label'?: string
48
+ }
49
+
50
+ type ReadTag = 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
51
+
52
+ export interface InlineEditProps<T = string> {
53
+ /** 已提交的欄位值(read 態顯示;edit 進入時作草稿初值)。泛型:string / number / string[] / PersonValue 等 */
54
+ value: T
55
+ /** 提交回呼 —— blur / Enter 且草稿有變更時觸發 */
56
+ onCommit: (next: T) => void
57
+ /**
58
+ * 自訂 edit 態控件(換 Textarea / Select / DatePicker 等 Field 控件)。
59
+ * 不傳則預設:multiline → `<Textarea>`,否則 `<Input>`(僅 T=string 時預設有效;非 string 值必傳此 prop)。
60
+ */
61
+ renderEdit?: (props: InlineEditRenderProps<T>) => React.ReactNode
62
+ /**
63
+ * 自訂 read 態格式化顯示。不傳則顯示 `String(value)`(空值顯 placeholder)。
64
+ * **SSOT 鐵律**:要顯示 Tag / 日期格式 / option label 等,**渲染對應控件的 `mode="display"`**
65
+ * (如 `renderRead={(v) => <Select mode="display" display="tag" value={v} options={opts} />}`),
66
+ * 不可自刻 —— 格式化 SSOT 住在該控件的 display mode(見檔頭)。
67
+ */
68
+ renderRead?: (value: T) => React.ReactNode
69
+ /** 多行欄位(Jira description 類):read 態換行 + 預設 edit 態用 `<Textarea>`,Enter=換行、Cmd/Ctrl+Enter 或 blur 提交 */
70
+ multiline?: boolean
71
+ /** 欄位人類可讀名稱 —— 組 read 按鈕 aria-label(「編輯 {label}」)+ 控件 aria-label */
72
+ label?: string
73
+ /** 空值時 read 態顯示的提示文字(fg-muted) */
74
+ placeholder?: string
75
+ /** 尺寸 —— read 態最小高度 + 預設 Input 尺寸(與 Field 家族一致) */
76
+ size?: FieldSize
77
+ /** read 態語意標籤(標題場景傳 'h1'/'h2' 維持文件大綱;預設 'span') */
78
+ as?: ReadTag
79
+ /** read 態文字樣式(如標題 `text-h4 font-bold`) */
80
+ readClassName?: string
81
+ /** 外層容器 className */
82
+ className?: string
83
+ }
84
+
85
+ // ── Read-view geometry cva ────────────────────────────────────────────────────
86
+ // base = 排版結構;size variant = min-height(與 edit 態 Field 高度一致,降低態切換位移)。
87
+ // multiline variant:單行 → `items-center`(文字垂直置中於 field-height);多行 → `items-start py-1.5`
88
+ // (頂對齊 + 上下內距,對齊 Textarea `py-2` 多行閱讀語意,內容換行往下長)。
89
+ // read 態幾何典範對照(M22 cite,2026-07-09 逐行核對原始碼,見檔頭 read-view URL):
90
+ // hover = 底色 tint 非邊框;focus = 實線 solid border;wrapper 恆帶透明 border 預留邊框盒。
91
+ const inlineEditReadStyles = cva(
92
+ // relative(疊隱形 Pressable)/ border-transparent(預留 1px 邊框盒 → 態切換零位移;focus 才染色)/
93
+ // px-[--field-px](文字↔底色水平內距 = Field 內距 SSOT,read=edit x 位置零跳)/ hover 底色 tint /
94
+ // [&:has(button:focus-visible)]:border-primary(鍵盤 focus = Field edit 藍框語言,非 Button ring)。
95
+ 'relative flex rounded-md border border-transparent px-[var(--field-px)] transition-colors duration-150 hover:bg-neutral-hover [&:has(button:focus-visible)]:border-primary',
96
+ {
97
+ variants: {
98
+ size: {
99
+ sm: 'min-h-[var(--field-height-sm)]',
100
+ md: 'min-h-[var(--field-height-md)]',
101
+ lg: 'min-h-[var(--field-height-lg)]',
102
+ },
103
+ multiline: {
104
+ false: 'items-center',
105
+ true: 'items-start py-1.5',
106
+ },
107
+ },
108
+ // 預設 sm + 單行(2026-07-09 user 拍板):read 態無邊框、視覺 = 純文字,尺寸過大會讓版面鬆散。
109
+ defaultVariants: { size: 'sm', multiline: false },
110
+ },
111
+ )
112
+
113
+ /**
114
+ * InlineEdit — 就地編輯 primitive(read ↔ edit 二態,泛型 <T>)
115
+ *
116
+ * 互動模型 world-class 對照見檔頭「消費的 SSOT」+ inline-edit.spec.md benchmark 表:
117
+ * - **read 態** = 純文字 / 格式化 renderRead、寬度 fill 容器、透明邊框(預留)→ 視覺 = 純內容
118
+ * - **hover(read)** = 灰色底色 `bg-neutral-hover` + `rounded-md`(**非邊框**)+ cursor
119
+ * - **鍵盤 focus(read)** = 外框染 `border-primary` 藍框(Field focus 語言,非 Button ring)
120
+ * - **click / Enter/Space** = 切 edit 態 = 真正的 Field 控件(border + focus + input);多行走 Textarea
121
+ * - **退出**:blur / Enter → commit;Esc → cancel 還原不 commit;focus 返回 read 按鈕
122
+ * - **鍵盤結算**:消費 `makeEditSettleKeyHandler`(含中文 IME guard,與 DataTable cell 同源)
123
+ *
124
+ * read 態用隱形 Pressable(絕對定位 `<button>` 疊於內容上)—— 讓 `as="h1"` 等標題標籤保留文件大綱
125
+ * a11y,同時提供鍵盤 focus + Enter 進 edit(HTML 內容模型不允許 heading 巢狀於 button,故分離)。
126
+ */
127
+ function InlineEditImpl<T = string>(
128
+ {
129
+ value,
130
+ onCommit,
131
+ renderEdit,
132
+ renderRead,
133
+ multiline = false,
134
+ label,
135
+ placeholder,
136
+ size = 'sm',
137
+ as = 'span',
138
+ readClassName,
139
+ className,
140
+ }: InlineEditProps<T>,
141
+ ref: React.Ref<HTMLDivElement>,
142
+ ) {
143
+ const [editing, setEditing] = React.useState(false)
144
+ const [draft, setDraft] = React.useState<T>(value)
145
+ // draft 最新值 ref —— commit 從事件 handler 呼叫時避免 stale closure
146
+ const draftRef = React.useRef(draft)
147
+ // 單次結算 guard —— Enter/Esc 已結算後,unmount 觸發的 onBlur 不再重複 commit
148
+ const finalizedRef = React.useRef(false)
149
+ // 鍵盤結算後把 focus 送回 read 按鈕(a11y:焦點不遺失)
150
+ const returnFocusRef = React.useRef(false)
151
+ const readButtonRef = React.useRef<HTMLButtonElement>(null)
152
+
153
+ const setDraftValue = React.useCallback((next: T) => {
154
+ draftRef.current = next
155
+ setDraft(next)
156
+ }, [])
157
+
158
+ const enterEdit = React.useCallback(() => {
159
+ finalizedRef.current = false
160
+ setDraftValue(value)
161
+ setEditing(true)
162
+ }, [value, setDraftValue])
163
+
164
+ const commit = React.useCallback(() => {
165
+ if (finalizedRef.current) return
166
+ finalizedRef.current = true
167
+ returnFocusRef.current = true
168
+ setEditing(false)
169
+ if (draftRef.current !== value) onCommit(draftRef.current)
170
+ }, [value, onCommit])
171
+
172
+ const cancel = React.useCallback(() => {
173
+ if (finalizedRef.current) return
174
+ finalizedRef.current = true
175
+ returnFocusRef.current = true
176
+ setEditing(false)
177
+ }, [])
178
+
179
+ // 結算後把 focus 送回 read 按鈕(所有結算路徑含 blur / Enter / Esc 皆 return —— commit/cancel
180
+ // 一律 set returnFocusRef=true;對齊 spec「任一路徑結算後 focus 送回 read 按鈕」L39/L141「焦點不遺失」)
181
+ React.useEffect(() => {
182
+ if (!editing && returnFocusRef.current) {
183
+ returnFocusRef.current = false
184
+ readButtonRef.current?.focus()
185
+ }
186
+ }, [editing])
187
+
188
+ const isEmpty =
189
+ value == null || (value as unknown) === '' || (Array.isArray(value) && value.length === 0)
190
+ const Tag = as as React.ElementType
191
+ // 外框(read/edit 兩態共用同一幾何 → 保證「底色範圍 = 輸入框範圍」逐 pixel 一致):
192
+ // -mx-[--field-px] 左右各外擴 field-px;w-[calc(100% + 2*--field-px)] 對稱補回 → 兩側等寬填滿。
193
+ const rootClass = cn('-mx-[var(--field-px)] w-[calc(100%_+_2_*_var(--field-px))]', className)
194
+
195
+ if (editing) {
196
+ const editProps: InlineEditRenderProps<T> = {
197
+ value: draft,
198
+ onChange: setDraftValue,
199
+ commit,
200
+ cancel,
201
+ autoFocus: true,
202
+ size,
203
+ 'aria-label': label,
204
+ }
205
+ // 預設 edit 控件:multiline → Textarea(Enter=換行、Cmd/Ctrl+Enter/blur 提交);否則 Input。
206
+ // 兩者 Enter/Esc 結算都走 makeEditSettleKeyHandler(含 IME guard)。string-only 預設路徑;
207
+ // 非 string 值必傳 renderEdit。
208
+ const defaultEditNode = multiline ? (
209
+ <Textarea
210
+ autoFocus
211
+ value={draft as unknown as string}
212
+ size={size}
213
+ aria-label={label}
214
+ onChange={(e) => setDraftValue(e.target.value as unknown as T)}
215
+ onBlur={commit}
216
+ // 多行:plain Enter=換行、Esc=cancel、Cmd/Ctrl+Enter=commit —— 全走
217
+ // makeEditSettleKeyHandler SSOT(含 IME guard + Cmd/Ctrl+Enter),不再手刻 capture。
218
+ onKeyDown={makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel, commitOnEnter: false })}
219
+ />
220
+ ) : (
221
+ <Input
222
+ autoFocus
223
+ value={draft as unknown as string}
224
+ size={size}
225
+ aria-label={label}
226
+ onChange={(e) => setDraftValue(e.target.value as unknown as T)}
227
+ onBlur={commit}
228
+ onKeyDown={makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel })}
229
+ />
230
+ )
231
+ const editNode = renderEdit ? renderEdit(editProps) : defaultEditNode
232
+ return (
233
+ <div ref={ref} data-editing={editing} className={rootClass}>
234
+ {editNode}
235
+ </div>
236
+ )
237
+ }
238
+
239
+ return (
240
+ <div
241
+ ref={ref}
242
+ data-editing={editing}
243
+ // 外框(rootClass 幾何)+ read chrome(inlineEditReadStyles:border 預留 / px / min-h / hover 底色 / focus 藍框)
244
+ className={cn(rootClass, inlineEditReadStyles({ size, multiline }))}
245
+ >
246
+ {renderRead ? (
247
+ // 格式化 read view(consumer 傳 <Select mode="display"> 等)。invisible Pressable 疊其上供點擊進 edit。
248
+ <div className="w-full min-w-0">{renderRead(value)}</div>
249
+ ) : (
250
+ <Tag
251
+ data-empty={isEmpty}
252
+ className={cn(
253
+ 'w-full min-w-0',
254
+ // read 態 typography 預設消費 Field display SSOT(sm/md→text-body,lg→text-body-lg)——
255
+ // plain 用法 read 字級 = edit <Input>(text-body)= <Input mode="display"> 字級,三者一致零跳字。
256
+ fieldDisplayTextClass(size),
257
+ // 多行:保留換行(pre-wrap)+ 長字斷行 —— read 忠實呈現 Textarea 內容(Jira description 類),
258
+ // 對齊 Textarea mode="display" 的 `whitespace-pre-wrap break-words`。
259
+ multiline && 'whitespace-pre-wrap break-words',
260
+ readClassName,
261
+ isEmpty && 'text-fg-muted',
262
+ )}
263
+ >
264
+ {isEmpty ? placeholder : String(value)}
265
+ </Tag>
266
+ )}
267
+ {/* 隱形 Pressable:提供 click + 鍵盤 Tab focus + Enter/Space 進 edit;透明疊於內容上。
268
+ hover 底色 + focus 藍框(border-primary)由外層 div 承載(Field focus 語言,非 ring),
269
+ 故本 button 只需 outline-none 消除瀏覽器預設外框。 */}
270
+ <button
271
+ ref={readButtonRef}
272
+ type="button"
273
+ aria-label={label ? `編輯 ${label}` : '編輯'}
274
+ onClick={enterEdit}
275
+ className="absolute inset-0 cursor-text rounded-md focus-visible:outline-none"
276
+ />
277
+ </div>
278
+ )
279
+ }
280
+
281
+ // 泛型 forwardRef:forwardRef 不原生支援泛型,以 cast 還原公開型別(標準 workaround)。
282
+ const InlineEdit = React.forwardRef(InlineEditImpl) as (<T = string>(
283
+ props: InlineEditProps<T> & { ref?: React.Ref<HTMLDivElement> },
284
+ ) => React.ReactElement) & { displayName?: string }
285
+ InlineEdit.displayName = 'InlineEdit'
286
+
287
+ export { InlineEdit }
@@ -58,7 +58,7 @@ const SIZE_SPECS: Record<SizeKey, SizeSpec> = {
58
58
 
59
59
  const MODE_DESC: Record<ModeKey, string> = {
60
60
  edit: '表單可編輯欄位 — bg-surface + border + hover/focus 回饋',
61
- display: '純展示資料 — 無 chrome(transparent + !px-0 !py-0 零內距,field-wrapper.tsx Path Ⅰ)+ 文字正常色,空值顯示 —(em dash)',
61
+ display: '純展示資料 — 無 chrome(transparent + !px-0 !py-0 零內距,field-wrapper.tsx Path Ⅰ)+ 文字正常色,空值顯示半形 -(hyphen,同 value 前景色)',
62
62
  readonly: '不可編輯但可見 — bg-readonly(neutral-2) + 無邊框 + 文字正常色',
63
63
  disabled: '被停用的欄位 — bg-disabled(neutral-2) + 無邊框 + 文字灰化',
64
64
  }
@@ -211,8 +211,9 @@ export const Overview = {
211
211
  {MODES.map((m) => (
212
212
  <div key={m} className="flex items-center gap-4">
213
213
  <div className="w-64 shrink-0">
214
- {/* display mode 讀 value(非 defaultValue)渲染 <span>;其餘 mode 用 defaultValue 保持 uncontrolled */}
215
- {m === 'display' ? (
214
+ {/* display + readonly controlled value:兩者「空值」皆走 <span> '-'(input.tsx:121),
215
+ demo 要展示有值狀態故傳 value;edit / disabled 用 defaultValue 保持 uncontrolled */}
216
+ {(m === 'display' || m === 'readonly') ? (
216
217
  <Input mode={m} value="Wireless Bluetooth Headphones" size="md" />
217
218
  ) : (
218
219
  <Input mode={m} defaultValue="Wireless Bluetooth Headphones" size="md" />
@@ -242,7 +243,7 @@ export const Overview = {
242
243
  <tbody>
243
244
  {[
244
245
  ['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'mode 未顯式指定時依序推導:有效 disabled(prop 或 <Field disabled>)→ fieldCtx.mode → native readOnly → edit;顯式 mode prop 永遠最優先(field-context.ts useResolvedFieldMode)'],
245
- ['variant', "'default' | 'bare'", "'default'", '視覺 chrome(正交於 mode);bare = 透明、hover/focus 才出現 border,用於 toolbar inline editing'],
246
+ ['variant', "'default'", "'default'", '視覺 chrome(正交於 mode;公開型別僅 default,2026-07-14 收窄);naked = chrome,host cell 提供 border/focus(@internal cell-as-input);bare 2026-07-09 退役'],
246
247
  ['error', 'boolean', 'false', '紅色邊框僅 edit 模式生效;aria-invalid 不分 mode(readonly / disabled 渲染的 input 同樣帶)'],
247
248
  ['size', "'sm' | 'md' | 'lg'", "'md'", '高度與字體,與 Button 共用 field-height token'],
248
249
  ['startIcon', 'LucideIcon', '—', '左側靜態 icon,fg-muted,pointer-events-none'],
@@ -698,11 +699,11 @@ export const StateBehavior = {
698
699
  </div>
699
700
  </div>
700
701
 
701
- {/* Click-through behavior */}
702
+ {/* startIcon pointer-events (non-intercepting) */}
702
703
  <div className="flex flex-col gap-4">
703
- <span className="text-caption font-medium text-fg-secondary">點擊穿透startIcon pointer-events-none</span>
704
+ <span className="text-caption font-medium text-fg-secondary">startIcon 不攔截點擊 — pointer-events-none</span>
704
705
  <div className="flex flex-col gap-2 max-w-sm">
705
- <span className="text-[11px] text-fg-muted">點擊 icon 區域,focus 穿透到 inputendAction 有自己的點擊行為,不穿透。</span>
706
+ <span className="text-[11px] text-fg-muted">startIcon(SVG)帶 pointer-events-none,不攔截點擊、不擋文字選取(不成為死區)。注意:點 icon 區域**不會** focus input(icon input 為 sibling,無 click-to-focus)。endAction 是互動元素,有自己的點擊行為。</span>
706
707
  <Input
707
708
  startIcon={Search}
708
709
  value={query}
@@ -60,16 +60,6 @@ export const UsageGuidance: Story = {
60
60
  </Section>
61
61
 
62
62
  <Section title="何時不用 + 替代方案">
63
- <Rule
64
- title="❌ 不用 Input variant='bare' 在表單主區域"
65
- note="bare 是 toolbar inline edit 專用(適合外層已有 affordance)。表單需要明確 field chrome → 用 variant='default'。Notion 的 property inline edit 用 bare"
66
- >
67
- <div>
68
- <Input variant="bare" placeholder="搜尋..." />
69
- <Label warn>❌ 表單用 default,bare 只用 toolbar</Label>
70
- </div>
71
- </Rule>
72
-
73
63
  <Rule
74
64
  title="❌ 不用 Input startIcon 表達狀態(有效 / 無效)"
75
65
  note="startIcon 是「欄位用途」識別符(郵件、搜尋、網址)。狀態用邊框 + help text。Stripe 信卡欄的有效狀態由邊框顏色傳達"
@@ -119,7 +109,7 @@ export const ModeRule: Story = {
119
109
  title="readonly — 表單中顯示但當下不可編輯"
120
110
  note="同 edit 高度(neutral-2 底色、無邊框),確保與其他欄位並排對齊。常見場景:viewing mode、送出後鎖定但仍在表單脈絡"
121
111
  >
122
- <Input mode="readonly" defaultValue="alice@example.com" />
112
+ <Input mode="readonly" value="alice@example.com" />
123
113
  </Rule>
124
114
 
125
115
  <Rule
@@ -140,7 +130,7 @@ export const ModeRule: Story = {
140
130
  title="❌ 不用 readonly 作為 DataTable cell"
141
131
  note="readonly 仍然有 padding + 視覺 chrome,密集列表會顯得鬆散。Table cell 用 `<Input mode='display'>`(零視覺 chrome、無左右 padding;wrapper 高度同 readonly)"
142
132
  >
143
- <Input mode="readonly" defaultValue="alice@example.com" />
133
+ <Input mode="readonly" value="alice@example.com" />
144
134
  <Label warn>↑ 在 table cell 中會過高且有多餘 padding → 用 &lt;Input mode=&quot;display&quot; /&gt;</Label>
145
135
  </Rule>
146
136
  </div>