@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 +1 @@
1
- {"version":3,"file":"dialog.js","sources":["../../../src/components/Dialog/dialog.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X as XIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/design-system/components/Button/button\"\nimport { ButtonDivider } from \"@/design-system/components/Button/button-group\"\nimport { SurfaceHeader, SurfaceFooter, type SurfaceHeaderProps } from \"@/design-system/patterns/overlay-surface/overlay-surface\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { surfaceMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n/**\n * Dialog (Modal) — Radix Dialog + 設計系統 token\n *\n * ── Layout ──\n * px = layout-space-loose, header/footer py = layout-space-tight。\n * Body pt = layout-space-tight, pb = layout-space-bottom。\n * Density:繼承 page `data-density`(v5 校準,跟 Sheet 對齊;header 自動對齊\n * `--chrome-header-height` 48/56)。詳 dialog.spec.md「Density」節。\n *\n * ── Viewport Inset ──\n * Modal 與 viewport 四邊保持 layout-space-bottom (48px) 最小間距。\n *\n * ── 高度行為 ──\n * 預設:height 填滿 viewport(扣除 inset),body 捲動。防止動態內容跳動。\n * autoHeight(boolean):高度隨內容,超過 viewport 時 max-height 安全帽。\n */\n\nconst Dialog = DialogPrimitive.Root\nconst DialogTrigger = DialogPrimitive.Trigger\nconst DialogPortal = DialogPrimitive.Portal\nconst DialogClose = DialogPrimitive.Close\n\n// Modal 與 viewport 四邊的最小間距 = layout-space-bottom (48px)\nconst DIALOG_INSET_VAR = 'var(--layout-space-bottom)'\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-overlay\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\ninterface DialogContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /** 最大寬度。預設 512px。傳 number 視為 px。 */\n maxWidth?: string | number\n /**\n * 高度模式。\n * - 不傳(預設):填滿 viewport(height = 100vh - inset*2),body 捲動。防止內容跳動。\n * - true:高度隨內容,超過 viewport 時捲動(max-height 安全帽)。\n */\n autoHeight?: boolean\n}\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n DialogContentProps\n>(({ className, maxWidth = '512px', autoHeight, children, style, ...props }, ref) => {\n const insetCalc = `${DIALOG_INSET_VAR} * 2`\n const viewportH = `calc(100vh - ${insetCalc})`\n const maxWidthCss = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth\n\n const heightStyle: React.CSSProperties = autoHeight\n ? { maxHeight: viewportH }\n : { height: viewportH }\n\n // AutoFocus canonical(對齊 Material / Polaris / Atlassian)—\n // 開啟時 focus 落在 body 第一個有意義互動元素(input / button),不是 chrome close X。\n // 預設 Radix 會 focus first tabbable = close X → Button iconOnly 的 focus-triggered\n // tooltip 會立即顯示「關閉」,user-hostile。此 callback 攔截:先找 body 第一個\n // input/textarea/select/button(排除 data-dismiss)focus;找不到就 focus container(不 focus X)。\n const handleOpenAutoFocus = (e: Event) => {\n e.preventDefault()\n const content = e.currentTarget as HTMLElement\n const firstBodyTarget = content.querySelector<HTMLElement>(\n '[data-dialog-body] input:not([disabled]),[data-dialog-body] textarea:not([disabled]),[data-dialog-body] select:not([disabled]),[data-dialog-body] button:not([disabled]):not([data-dismiss])'\n )\n const firstFooterButton = content.querySelector<HTMLElement>(\n '[data-dialog-footer] button:not([disabled]):not([data-dismiss])'\n )\n ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })\n }\n\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n // Density:**全繼承 page**(layout-space + ui-size 都不自鎖)。2026-06-16 定論(撤回本 session 一度加的\n // data-layout-space=\"lg\"):density.spec 第 10 行親自定義 layout-space 管「dialog body padding」——\n // Dialog 鎖死它 = override 自家 dial 對它點名要管的對象失效 = 自相矛盾。有同類 padding-density dial 的\n // 世界級(SAP Fiori syncStyleClass / AWS Cloudscape「all view types」)都讓 modal 跟 page dial 走、不鎖固定 tier。\n // 效果:md page → body px-loose 16 / header py-tight 12(header 48);lg page → 24 / 16(header 56),隨 page。\n // 「modal 要寬鬆」需求在 lg 階自然滿足(Polaris modal 16 = 世界級下限,證明 md 16 合格);「button 不撐高\n // header」由 ui-size 繼承 page 解決(button=page sm),與 layout-space 鎖不鎖無關 → 故不需鎖。\n onOpenAutoFocus={handleOpenAutoFocus}\n className={cn(\n \"fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2\",\n \"flex flex-col bg-surface-raised rounded-lg border border-border\",\n surfaceMotion,\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]\",\n \"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]\",\n className,\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n maxWidth: `min(${maxWidthCss}, calc(100vw - ${insetCalc}))`,\n ...heightStyle,\n ...style,\n }}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n})\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\n// DialogHeader: SurfaceHeader + Close 按鈕(Dialog 特有)\n// Close 靠右由「第一 child flex-1 grow」達成(justify-between 已移除,見 return 註解);Close 用 Radix DialogPrimitive.Close 包裝。\n// 2026-05-18 audit gap fix:type 用 SurfaceHeaderProps 對齊 — DialogHeader 是 SurfaceHeader\n// 薄包裝,withTabs / tabsSlot props 透過 spread 已 forward,但 TS type 沒 expose\n// 導致 consumer 不能用 `<DialogHeader withTabs>` 而只能寫 `as any` 繞。Type lift 修\n// per header-canonical.spec.md W1 跨 6 consumer 同契約。\nexport interface DialogHeaderProps extends SurfaceHeaderProps {\n /**\n * Header 級操作 cluster(2026-07-08 WM 戰役 codify — 關閉鈕分隔線 SSOT 化)。\n *\n * 提供時 render 在 Close X 左側,DS 自動在 actions 與 Close 之間放 `<ButtonDivider>`\n * (action-bar.spec.md:281「最右側為關閉/解除按鈕 → 分隔線必須(誤觸保護)」;\n * 幾何 = ButtonDivider mx-1 於 gap-2 cluster → 12px 對稱,同 spec「分隔線幾何」段)。\n *\n * 用例:任務詳情 prev/next 導覽、header 級溢出選單(⋮)。\n * Consumer 傳 `<Button variant=\"text\" iconOnly …>`(text variant 自動 data-unbounded,\n * 不撐高 chrome header)。**禁**在 children 內自刻 cluster + divider(分隔線落點歸 DS)。\n */\n actions?: React.ReactNode\n}\n\nconst DialogHeader = React.forwardRef<\n HTMLDivElement,\n DialogHeaderProps\n>(({ className, children, actions, ...props }, ref) => {\n // Dismiss X(chrome-slot canonical,v5):Button 本身 native sm(28 md / 32 lg,touch target 亦同),\n // 但 `dismiss` prop 自動標 `data-unbounded`,SurfaceHeader CSS rule 對其套負 my 讓\n // layout 佔位 = 24(`data-dismiss` 僅作 openAutoFocus 排除 marker,與縮位無關),\n // header = 24 + 2×tight = 48 / 56 chrome-header-height ✓。\n // 詳 overlay-surface.spec.md「Chrome dismiss size canonical」\n const closeButton = (\n <DialogPrimitive.Close asChild>\n <Button data-dismiss iconOnly dismiss size=\"sm\" startIcon={XIcon} aria-label=\"關閉\" />\n </DialogPrimitive.Close>\n )\n return (\n // 2026-05-18:className 不再硬加 justify-between(冗餘:row 1 是 flex items-center gap-2,\n // 第一 child flex-1 grow 自然 push close X 靠右,跟 justify-between 同視覺)。\n // 並且 column mode(tabsSlot 提供)justify-between 會把 row 1 / row 2 上下推開 = 破裂。\n // tabsSlot via `...props` spread 自動 forward(type 來自 SurfaceHeaderProps)。\n <SurfaceHeader\n ref={ref}\n className={className}\n {...props}\n >\n <div className=\"flex-1 min-w-0\">{children}</div>\n {actions != null ? (\n // actions 提供 → 右側一體 cluster:{actions} + ButtonDivider + Close。\n // 分隔線 = 關閉保護 canonical(action-bar.spec.md:281),由 DS 放,consumer 零自刻。\n // gap-2 + ButtonDivider mx-1 = 兩側 12px 對稱(action-bar.spec.md「分隔線幾何」)。\n <div className=\"flex items-center gap-2 shrink-0\">\n {actions}\n <ButtonDivider />\n {closeButton}\n </div>\n ) : (\n // 無 actions → 原結構(Close 直接是 SurfaceHeader child),行為零回歸\n closeButton\n )}\n </SurfaceHeader>\n )\n})\nDialogHeader.displayName = \"DialogHeader\"\n\n// DialogBody: flex-1 ScrollArea + chrome padding(對齊 overlay-surface SSOT + ScrollArea canonical)\n// 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。\n// padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom(Dialog 「大容器」底部多一拍呼吸)。\n// data-dialog-body:讓 DialogContent onOpenAutoFocus 找得到 body 第一個有意義互動元素(避免 focus 到 close X)\n//\n// ── List-as-region 場景(menu group / Cmd+K)──\n// 不再提供 `flush` variant(2026-05-01 移除,先前曾叫 `variant=\"list\"`)。\n// **canonical pattern** = consumer 自管 list outer wrapper + 用 `className` override 撤掉 chrome padding:\n// ```tsx\n// <DialogBody className=\"!px-0 !pt-0 !pb-0\"> ← @tabs-content-gap-ok: JSDoc 文件範例(list-as-region canonical,非 tabs !pt-0 hack)\n// <div className=\"py-2\"> {/* list outer wrapper 自帶 py-2(menu group canonical)*/}\n// {items.map(item => <MenuItem className=\"px-[var(--layout-space-loose)] rounded-md\" />)}\n// </div>\n// </DialogBody>\n// ```\n// **rationale**:flush 只為 list-only body 省一行 className,但 (a) 多一個 row(search / banner)\n// 就破功 → 保留 chrome padding 反而更穩,(b) 加新 variant 不解決底層脆弱(consumer 仍要管 list py\n// 且 item px-loose),反而把 1 個 surface decision 拆兩 API。世界級主流(Material/Atlassian/Mantine/\n// shadcn)無 universal LayoutBody flush variant,Polaris flush API 只用於極窄 scope。\n// 詳 `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」節\n// `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——\n// consumer `<DialogBody className=\"flex flex-col gap-X\">` 期望作用於 children 排列;\n// 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 modal form field 完全貼邊。\nconst DialogBody = React.forwardRef<\n HTMLDivElement,\n React.ComponentPropsWithoutRef<typeof ScrollArea>\n>(({ className, children, ...props }, ref) => (\n <ScrollArea fillX ref={ref} data-dialog-body className=\"flex-1 min-h-0\" {...props}>\n <div\n className={cn(\n \"px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]\",\n className,\n )}\n >\n {children}\n </div>\n </ScrollArea>\n))\nDialogBody.displayName = \"DialogBody\"\n\n// DialogFooter: SurfaceFooter wrap 加 data-dialog-footer(autoFocus fallback target)\nconst DialogFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => <SurfaceFooter ref={ref} data-dialog-footer {...props} />)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"text-body-lg font-medium truncate\", className)}\n {...props}\n />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n // title → description 間距 canonical:DialogTitle 是 body-lg(16)+ desc body(14)→ reading-lg token\n // (label tier 決定 token 選擇;item-anatomy Family 2 reading-family token 對照表)\n className={cn(\"mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary\", className)}\n {...props}\n />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dialogMeta = {\n component: 'Dialog',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n // 2026-07-04 audit 對齊:容器無 hover/active/disabled 態(spec「狀態處理的職責邊界」明文;對齊 popover meta 已修 pattern)\n states: ['default'],\n tokens: {\n bg: ['bg-surface-raised'],\n fg: ['text-fg-secondary'],\n ring: [],\n },\n} as const\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogBody,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n}\n"],"names":["XIcon"],"mappings":"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"}
1
+ {"version":3,"file":"dialog.js","sources":["../../../src/components/Dialog/dialog.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X as XIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/design-system/components/Button/button\"\nimport { ButtonDivider } from \"@/design-system/components/Button/button-group\"\nimport { SurfaceHeader, SurfaceFooter, type SurfaceHeaderProps } from \"@/design-system/patterns/overlay-surface/overlay-surface\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { surfaceMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n/**\n * Dialog (Modal) — Radix Dialog + 設計系統 token\n *\n * ── Layout ──\n * px = layout-space-loose, header/footer py = layout-space-tight。\n * Body pt = layout-space-tight, pb = layout-space-bottom。\n * Density:繼承 page `data-density`(v5 校準,跟 Sheet 對齊;header 自動對齊\n * `--chrome-header-height` 48/56)。詳 dialog.spec.md「Density」節。\n *\n * ── Viewport Inset ──\n * Modal 與 viewport 四邊保持 layout-space-bottom (48px) 最小間距。\n *\n * ── 高度行為 ──\n * 預設:height 填滿 viewport(扣除 inset),body 捲動。防止動態內容跳動。\n * autoHeight(boolean):高度隨內容,超過 viewport 時 max-height 安全帽。\n */\n\nconst Dialog = DialogPrimitive.Root\nconst DialogTrigger = DialogPrimitive.Trigger\nconst DialogPortal = DialogPrimitive.Portal\nconst DialogClose = DialogPrimitive.Close\n\n// Modal 與 viewport 四邊的最小間距 = layout-space-bottom (48px)\nconst DIALOG_INSET_VAR = 'var(--layout-space-bottom)'\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-overlay\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\n// asChild Omit(2026-07-18 決策2):DialogContent 是固定 surface chrome(overlay + 定位 +\n// bg/rounded/shadow + onOpenAutoFocus),恆渲染 {children}(consumer body,通常 Header+Body+Footer\n// 多節點)→ <Content asChild> Radix Slot React.Children.only crash。children 保留(consumer body)。\ninterface DialogContentProps extends Omit<React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>, 'asChild'> {\n /** 最大寬度。預設 512px。傳 number 視為 px。 */\n maxWidth?: string | number\n /**\n * 高度模式。\n * - 不傳(預設):填滿 viewport(height = 100vh - inset*2),body 捲動。防止內容跳動。\n * - true:高度隨內容,超過 viewport 時捲動(max-height 安全帽)。\n */\n autoHeight?: boolean\n}\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n DialogContentProps\n>(({ className, maxWidth = '512px', autoHeight, children, style, ...props }, ref) => {\n const insetCalc = `${DIALOG_INSET_VAR} * 2`\n const viewportH = `calc(100vh - ${insetCalc})`\n const maxWidthCss = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth\n\n const heightStyle: React.CSSProperties = autoHeight\n ? { maxHeight: viewportH }\n : { height: viewportH }\n\n // AutoFocus canonical(對齊 Material / Polaris / Atlassian)—\n // 開啟時 focus 落在 body 第一個有意義互動元素(input / button),不是 chrome close X。\n // 預設 Radix 會 focus first tabbable = close X → Button iconOnly 的 focus-triggered\n // tooltip 會立即顯示「關閉」,user-hostile。此 callback 攔截:先找 body 第一個\n // input/textarea/select/button(排除 data-dismiss)focus;找不到就 focus container(不 focus X)。\n const handleOpenAutoFocus = (e: Event) => {\n e.preventDefault()\n const content = e.currentTarget as HTMLElement\n const firstBodyTarget = content.querySelector<HTMLElement>(\n '[data-dialog-body] input:not([disabled]),[data-dialog-body] textarea:not([disabled]),[data-dialog-body] select:not([disabled]),[data-dialog-body] button:not([disabled]):not([data-dismiss])'\n )\n const firstFooterButton = content.querySelector<HTMLElement>(\n '[data-dialog-footer] button:not([disabled]):not([data-dismiss])'\n )\n ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })\n }\n\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n // Density:**全繼承 page**(layout-space + ui-size 都不自鎖)。2026-06-16 定論(撤回本 session 一度加的\n // data-layout-space=\"lg\"):density.spec 第 10 行親自定義 layout-space 管「dialog body padding」——\n // Dialog 鎖死它 = override 自家 dial 對它點名要管的對象失效 = 自相矛盾。有同類 padding-density dial 的\n // 世界級(SAP Fiori syncStyleClass / AWS Cloudscape「all view types」)都讓 modal 跟 page dial 走、不鎖固定 tier。\n // 效果:md page → body px-loose 16 / header py-tight 12(header 48);lg page → 24 / 16(header 56),隨 page。\n // 「modal 要寬鬆」需求在 lg 階自然滿足(Polaris modal 16 = 世界級下限,證明 md 16 合格);「button 不撐高\n // header」由 ui-size 繼承 page 解決(button=page sm),與 layout-space 鎖不鎖無關 → 故不需鎖。\n onOpenAutoFocus={handleOpenAutoFocus}\n className={cn(\n \"fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2\",\n \"flex flex-col bg-surface-raised rounded-lg border border-border\",\n surfaceMotion,\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]\",\n \"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]\",\n className,\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n maxWidth: `min(${maxWidthCss}, calc(100vw - ${insetCalc}))`,\n ...heightStyle,\n ...style,\n }}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n})\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\n// DialogHeader: SurfaceHeader + Close 按鈕(Dialog 特有)\n// Close 靠右由「第一 child flex-1 grow」達成(justify-between 已移除,見 return 註解);Close 用 Radix DialogPrimitive.Close 包裝。\n// 2026-05-18 audit gap fix:type 用 SurfaceHeaderProps 對齊 — DialogHeader 是 SurfaceHeader\n// 薄包裝,withTabs / tabsSlot props 透過 spread 已 forward,但 TS type 沒 expose\n// 導致 consumer 不能用 `<DialogHeader withTabs>` 而只能寫 `as any` 繞。Type lift 修\n// per header-canonical.spec.md W1 跨 6 consumer 同契約。\nexport interface DialogHeaderProps extends SurfaceHeaderProps {\n /**\n * Header 級操作 cluster(2026-07-08 WM 戰役 codify — 關閉鈕分隔線 SSOT 化)。\n *\n * 提供時 render 在 Close X 左側,DS 自動在 actions 與 Close 之間放 `<ButtonDivider>`\n * (action-bar.spec.md:281「最右側為關閉/解除按鈕 → 分隔線必須(誤觸保護)」;\n * 幾何 = ButtonDivider mx-1 於 gap-2 cluster → 12px 對稱,同 spec「分隔線幾何」段)。\n *\n * 用例:任務詳情 prev/next 導覽、header 級溢出選單(⋮)。\n * Consumer 傳 `<Button variant=\"text\" iconOnly …>`(text variant 自動 data-unbounded,\n * 不撐高 chrome header)。**禁**在 children 內自刻 cluster + divider(分隔線落點歸 DS)。\n */\n actions?: React.ReactNode\n}\n\nconst DialogHeader = React.forwardRef<\n HTMLDivElement,\n DialogHeaderProps\n>(({ className, children, actions, ...props }, ref) => {\n // Dismiss X(chrome-slot canonical,v5):Button 本身 native sm(28 md / 32 lg,touch target 亦同),\n // 但 `dismiss` prop 自動標 `data-unbounded`,SurfaceHeader CSS rule 對其套負 my 讓\n // layout 佔位 = 24(`data-dismiss` 僅作 openAutoFocus 排除 marker,與縮位無關),\n // header = 24 + 2×tight = 48 / 56 chrome-header-height ✓。\n // 詳 overlay-surface.spec.md「Chrome dismiss size canonical」\n const closeButton = (\n <DialogPrimitive.Close asChild>\n <Button data-dismiss iconOnly dismiss size=\"sm\" startIcon={XIcon} aria-label=\"關閉\" />\n </DialogPrimitive.Close>\n )\n return (\n // 2026-05-18:className 不再硬加 justify-between(冗餘:row 1 是 flex items-center gap-2,\n // 第一 child flex-1 grow 自然 push close X 靠右,跟 justify-between 同視覺)。\n // 並且 column mode(tabsSlot 提供)justify-between 會把 row 1 / row 2 上下推開 = 破裂。\n // tabsSlot via `...props` spread 自動 forward(type 來自 SurfaceHeaderProps)。\n <SurfaceHeader\n ref={ref}\n className={className}\n {...props}\n >\n <div className=\"flex-1 min-w-0\">{children}</div>\n {actions != null ? (\n // actions 提供 → 右側一體 cluster:{actions} + ButtonDivider + Close。\n // 分隔線 = 關閉保護 canonical(action-bar.spec.md:281),由 DS 放,consumer 零自刻。\n // gap-2 + ButtonDivider mx-1 = 兩側 12px 對稱(action-bar.spec.md「分隔線幾何」)。\n <div className=\"flex items-center gap-2 shrink-0\">\n {actions}\n <ButtonDivider />\n {closeButton}\n </div>\n ) : (\n // 無 actions → 原結構(Close 直接是 SurfaceHeader child),行為零回歸\n closeButton\n )}\n </SurfaceHeader>\n )\n})\nDialogHeader.displayName = \"DialogHeader\"\n\n// DialogBody: flex-1 ScrollArea + chrome padding(對齊 overlay-surface SSOT + ScrollArea canonical)\n// 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。\n// padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom(Dialog 「大容器」底部多一拍呼吸)。\n// data-dialog-body:讓 DialogContent onOpenAutoFocus 找得到 body 第一個有意義互動元素(避免 focus 到 close X)\n//\n// ── List-as-region 場景(menu group / Cmd+K)──\n// 不再提供 `flush` variant(2026-05-01 移除,先前曾叫 `variant=\"list\"`)。\n// **canonical pattern** = consumer 自管 list outer wrapper + 用 `className` override 撤掉 chrome padding:\n// ```tsx\n// <DialogBody className=\"!px-0 !pt-0 !pb-0\"> ← @tabs-content-gap-ok: JSDoc 文件範例(list-as-region canonical,非 tabs !pt-0 hack)\n// <div className=\"py-2\"> {/* list outer wrapper 自帶 py-2(menu group canonical)*/}\n// {items.map(item => <MenuItem className=\"px-[var(--layout-space-loose)] rounded-md\" />)}\n// </div>\n// </DialogBody>\n// ```\n// **rationale**:flush 只為 list-only body 省一行 className,但 (a) 多一個 row(search / banner)\n// 就破功 → 保留 chrome padding 反而更穩,(b) 加新 variant 不解決底層脆弱(consumer 仍要管 list py\n// 且 item px-loose),反而把 1 個 surface decision 拆兩 API。世界級主流(Material/Atlassian/Mantine/\n// shadcn)無 universal LayoutBody flush variant,Polaris flush API 只用於極窄 scope。\n// 詳 `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」節\n// `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——\n// consumer `<DialogBody className=\"flex flex-col gap-X\">` 期望作用於 children 排列;\n// 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 modal form field 完全貼邊。\nconst DialogBody = React.forwardRef<\n HTMLDivElement,\n React.ComponentPropsWithoutRef<typeof ScrollArea>\n>(({ className, children, ...props }, ref) => (\n <ScrollArea fillX ref={ref} data-dialog-body className=\"flex-1 min-h-0\" {...props}>\n <div\n className={cn(\n \"px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]\",\n className,\n )}\n >\n {children}\n </div>\n </ScrollArea>\n))\nDialogBody.displayName = \"DialogBody\"\n\n// DialogFooter: SurfaceFooter wrap 加 data-dialog-footer(autoFocus fallback target)\nconst DialogFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => <SurfaceFooter ref={ref} data-dialog-footer {...props} />)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"text-body-lg font-medium truncate\", className)}\n {...props}\n />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n // title → description 間距 canonical:DialogTitle 是 body-lg(16)+ desc body(14)→ reading-lg token\n // (label tier 決定 token 選擇;item-anatomy Family 2 reading-family token 對照表)\n className={cn(\"mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary\", className)}\n {...props}\n />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dialogMeta = {\n component: 'Dialog',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n // 2026-07-04 audit 對齊:容器無 hover/active/disabled 態(spec「狀態處理的職責邊界」明文;對齊 popover meta 已修 pattern)\n states: ['default'],\n tokens: {\n bg: ['bg-surface-raised'],\n fg: ['text-fg-secondary'],\n ring: [],\n },\n} as const\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogBody,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n}\n"],"names":["XIcon"],"mappings":"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"}
@@ -26,6 +26,7 @@ import { type RowSize } from "../../patterns/element-anatomy/item-anatomy";
26
26
  * `:focus-visible` 行為不一致,導致 mouse hover 有時無 bg。改用 `data-[highlighted]:` 後行為
27
27
  * 一致 —— 世界級 canonical(shadcn / Radix docs / Ariakit 皆此)。
28
28
  */
29
+ /** @internal — menu 形浮層共用 class(DropdownMenu/SelectMenu/Command 內部);consumer 不 hand-craft menu surface,用元件本身。root barrel 排除(subpath 仍可 wrap 後用)。 */
29
30
  declare const floatingLayerClass: string;
30
31
  type SizeKey = RowSize;
31
32
  declare const DropdownMenu: React.FC<DropdownMenuPrimitive.DropdownMenuProps>;
@@ -34,11 +35,14 @@ declare const DropdownMenuGroup: React.ForwardRefExoticComponent<Omit<DropdownMe
34
35
  declare const DropdownMenuPortal: React.FC<DropdownMenuPrimitive.DropdownMenuPortalProps>;
35
36
  declare const DropdownMenuSub: React.FC<DropdownMenuPrimitive.DropdownMenuSubProps>;
36
37
  declare const DropdownMenuRadioGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuRadioGroupProps & React.RefAttributes<HTMLDivElement>>;
37
- interface DropdownMenuContentProps extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {
38
+ interface DropdownMenuContentProps extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>, 'asChild'> {
38
39
  size?: SizeKey;
39
40
  /** 最小寬度(px),預設 `max(180px, 觸發元件寬度)`——窄 trigger 時吃 180px 地板 */
40
41
  minWidth?: number;
41
- /** 最大高度(px),超過時捲動 */
42
+ /**
43
+ * 最大高度(px)——**可選更低上限**。預設已 viewport-adaptive(夾到 trigger→視窗碰撞邊界的剩餘高度、
44
+ * 離窗 ≥8px、超過即捲動);傳 `maxHeight` 只在「想比視窗剩餘更矮」時用(取 `min(視窗剩餘, maxHeight)`)。
45
+ */
42
46
  maxHeight?: number;
43
47
  }
44
48
  declare const DropdownMenuContent: React.ForwardRefExoticComponent<DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>>;
@@ -84,7 +88,8 @@ interface DropdownMenuCheckboxItemProps extends Omit<React.ComponentPropsWithout
84
88
  declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>>;
85
89
  declare const DropdownMenuLabel: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
86
90
  declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
87
- interface DropdownMenuRadioItemProps extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> {
91
+ interface DropdownMenuRadioItemProps extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>, 'children'> {
92
+ children: React.ReactNode;
88
93
  /** Prefix icon(LucideIcon) */
89
94
  startIcon?: LucideIcon;
90
95
  /** 次要說明文字 */
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"names":[],"mappings":"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"}
@@ -57,18 +57,18 @@ const DropdownMenuContent = React.forwardRef(({ className, size = "md", sideOffs
57
57
  sideOffset,
58
58
  collisionPadding,
59
59
  align,
60
- className: cn(floatingLayerClass, !maxHeight && "py-2", className),
60
+ className: cn(floatingLayerClass, "flex flex-col min-h-0", className),
61
61
  style: {
62
62
  boxShadow: "var(--elevation-200)",
63
63
  minWidth: minWidth ?? "max(180px, var(--radix-dropdown-menu-trigger-width))",
64
- maxHeight
64
+ // 2026-07-18 viewport-adaptive max-h(對齊 HoverCard/Popover 既有 canonical):夾到「trigger 到
65
+ // 碰撞邊界(已內縮 collisionPadding=8px)的剩餘高度」→ 高選單不溢出視窗、離視窗邊 ≥8px、
66
+ // 短選單仍貼內容;consumer `maxHeight` 為**可選更低上限**(min 取小者)。inline style 非 class,
67
+ // 故動態 maxHeight 不觸 Tailwind 掃描陷阱。
68
+ maxHeight: maxHeight ? `min(var(--radix-dropdown-menu-content-available-height, 100vh), ${maxHeight}px)` : "var(--radix-dropdown-menu-content-available-height, 100vh)"
65
69
  },
66
70
  ...props,
67
- children: /* @__PURE__ */ jsx(RowSizeProvider, { value: size, children: maxHeight ? (
68
- // 長選單用 ScrollArea 跨 OS 一致捲動(不吃寬度,macOS/Windows 視覺一致)
69
- // py-2 移到內層,ScrollArea Viewport 才能 scroll 整個 padded 區
70
- /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[inherit]", children: /* @__PURE__ */ jsx("div", { className: "py-2", children }) })
71
- ) : children })
71
+ children: /* @__PURE__ */ jsx(RowSizeProvider, { value: size, children: /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx("div", { className: "py-2", children }) }) })
72
72
  }
73
73
  ) }));
74
74
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-menu.js","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"sourcesContent":["import * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { AvatarData } from \"@/design-system/components/Avatar/avatar\"\nimport { MenuItem } from \"@/design-system/components/Menu/menu-item\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { OVERLAY_SIDE_OFFSET, OVERLAY_COLLISION_PADDING } from \"@/design-system/tokens/elevation/overlay-geometry\"\nimport {\n RowSizeProvider,\n useRowSize,\n ICON_SIZE as ROW_ICON_SIZE,\n type RowSize,\n} from \"@/design-system/patterns/element-anatomy/item-anatomy\"\nimport { overlayMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n/**\n * DropdownMenu — Radix DropdownMenu + MenuItem visual layer\n *\n * 架構分工:\n * - Radix primitives:behavior(keyboard nav, focus management, aria roles)\n * - MenuItem:visual(layout, padding, icon alignment, typography)\n *\n * Radix primitive 是外層容器,控制 `data-[highlighted]:bg-neutral-hover`。\n * MenuItem 內層只負責佈局,不加互動樣式。\n *\n * ── Hover / highlight canonical(2026-04-22 修正)──\n * 用 Radix 官方的 `data-[highlighted]` attribute,**不用 `:focus-visible` / `:hover` /\n * `:focus`**:\n * - Radix 在 **mouse hover、keyboard arrow nav、focus move in** 時自動 set `data-highlighted`\n * - mouse leave / focus move out / menu close 時自動清掉\n * - 不會在 click 後留殘影(Radix 內部已處理)\n * - 跨瀏覽器一致(不依賴 `:focus-visible` 的 heuristic)\n *\n * 曾經用過 `focus-visible:bg-neutral-hover` 的理由:避免 click 後殘影。但實測:mouse hover\n * 觸發 Radix 程式化 `.focus()`,Chromium / Safari / Firefox 對 programmatic focus 是否 fire\n * `:focus-visible` 行為不一致,導致 mouse hover 有時無 bg。改用 `data-[highlighted]:` 後行為\n * 一致 —— 世界級 canonical(shadcn / Radix docs / Ariakit 皆此)。\n */\n\n// ── Floating layer 共用樣式 ──\nconst floatingLayerClass = [\n 'z-50 overflow-hidden rounded-lg border border-border bg-surface-raised',\n overlayMotion,\n 'data-[state=open]:animate-in data-[state=closed]:animate-out',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n 'origin-[var(--radix-dropdown-menu-content-transform-origin)]',\n].join(' ')\n\n// ── Size:統一用 RowSizeContext(item-layout module),消除本地 SizeContext 漂移 ──\ntype SizeKey = RowSize\n// Re-export for backward compat(內部命名)\nconst ICON_SIZE = ROW_ICON_SIZE\n\n// ── Shared item classes on Radix primitive ──\n// Highlight(hover + keyboard nav): 用 Radix `data-[highlighted]` canonical(見 docblock)\nconst radixItemClass = [\n 'relative cursor-pointer select-none outline-none',\n 'transition-colors duration-150',\n 'data-[highlighted]:bg-neutral-hover',\n 'data-[disabled]:pointer-events-none data-[disabled]:text-fg-disabled data-[disabled]:cursor-default',\n].join(' ')\n\n// ── Root ──\nconst DropdownMenu = DropdownMenuPrimitive.Root\nconst DropdownMenuTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n // 2026-07-05:非 asChild 裸用需可見 focus 指示(WCAG 2.4.7)——原 `outline-none` 讓裸 Trigger\n // 完全無 focus ring。消費 Button focus canonical(button.tsx buttonVariants base)。\n // asChild+Button 場景同名 class 重複,無影響。\n className={cn(\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n className,\n )}\n {...props}\n />\n))\nDropdownMenuTrigger.displayName = DropdownMenuPrimitive.Trigger.displayName\n// DropdownMenuGroup — 對齊 MenuGroup 的 group separation 設計語言\n//\n// 設計語言(跨 Menu-like 元件統一,SSOT 見 item-anatomy.spec.md\n// 「Group auto-separation」):\n// 每個 group 上下各 8px padding,相鄰 group 之間用 border-divider 分隔\n// 兩個 group 之間視覺 gap = 8px(上一個 bottom)+ 8px(下一個 top)= 16px + border\n//\n// MenuGroup(menu-item.tsx)實作:`py-2 [&+&]:border-t [&+&]:border-divider`\n// (在 Command.List 下提供 Content 邊界 8px + group 間 16px gap)\n//\n// DropdownMenuGroup(本元件)實作:`[&+&]:mt-2 [&+&]:pt-2 [&+&]:border-t\n// [&+&]:border-divider`(因為 DropdownMenuContent 已有 py-2 提供 Content 邊界\n// 的 8px,只需在第二個起的 group 加 8+8 = 16px gap + border)\n//\n// **視覺結果等同**:兩種實作的 visual output 一致,只是「padding 住在哪層」\n// 不同。不強制統一 CSS 表達式——DropdownMenuContent 的 py-2 是純視覺 Content\n// 邊界 padding(Radix roving focus 依 collection refs + element.focus(),不讀\n// CSS padding;對齊 spec.md「為什麼不直接套 py-2」段)。Group 若再套 py-2 會\n// double padding,故 Group 用 mt-2/pt-2 只加在第二個起。\nconst DropdownMenuGroup = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Group\n ref={ref}\n className={cn('[&+&]:border-t [&+&]:border-divider [&+&]:mt-2 [&+&]:pt-2', className)}\n {...props}\n />\n))\nDropdownMenuGroup.displayName = 'DropdownMenuGroup'\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\n// ── Content ──\ninterface DropdownMenuContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {\n size?: SizeKey\n /** 最小寬度(px),預設 `max(180px, 觸發元件寬度)`——窄 trigger 時吃 180px 地板 */\n minWidth?: number\n /** 最大高度(px),超過時捲動 */\n maxHeight?: number\n}\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n DropdownMenuContentProps\n>(({ className, size = 'md', sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, align = 'start', minWidth, maxHeight, children, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n align={align}\n // Density:繼承 page density(2026-06-15 canonical)。menu item 高度 = field-height-{size},而\n // field-height 隨 density 變(md 28/32/36 → lg 32/36/40)→ 鎖 data-density=\"md\" 會把選單釘在 md-scale,\n // lg page 上對不上 lg 觸發點。原 data-density 是 409b91da a11y 批次順手加(對齊 Popover),非設計\n // 決策 → 移除,item 隨 page density 與觸發點一致(tier 仍由 size prop 決定)。\n // Focus return on close:不 override `onCloseAutoFocus` — 用 Radix 內建 default\n // (close 時 focus 還 trigger;outside-interaction 例外由 Radix `hasInteractedOutsideRef` 自管)。\n // W3C APG menubar「Escape: …return focus to the element…from which the menu was opened」\n // (w3.org/WAI/ARIA/apg/patterns/menubar/);shadcn dropdown-menu 同樣不 override。\n // 2026-06-11 移除 2026-04-08 的 `(e) => e.preventDefault()` hardcode:它跑在 Radix\n // composeEventHandlers 內建 handler 之前,defaultPrevented → trigger focus 被 skip →\n // Esc 關閉後 focus 掉到 body(APG violation)。原動機「mouse close 後 trigger 殘留\n // focus ring」不構成 override 理由:實測(2026-06-12 playwright)pointer item-click\n // close 後 programmatic refocus 在 Chromium 仍可 match `:focus-visible`(UA heuristic),\n // 但與 shadcn 官方 live demo 同流程 DOM 比對 IDENTICAL — Radix 生態一致接受此\n // tradeoff:APG keyboard focus-return 優先於 cosmetic ring。\n className={cn(floatingLayerClass, !maxHeight && 'py-2', className)}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(180px, var(--radix-dropdown-menu-trigger-width))',\n maxHeight,\n }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {maxHeight ? (\n // 長選單用 ScrollArea 跨 OS 一致捲動(不吃寬度,macOS/Windows 視覺一致)\n // py-2 移到內層,ScrollArea Viewport 才能 scroll 整個 padded 區\n <ScrollArea className=\"max-h-[inherit]\">\n <div className=\"py-2\">{children}</div>\n </ScrollArea>\n ) : (\n children\n )}\n </RowSizeProvider>\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\n// ── SubContent ──\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n sideOffset={OVERLAY_SIDE_OFFSET}\n className={cn(floatingLayerClass, 'py-2', className)}\n style={{ boxShadow: 'var(--elevation-200)', minWidth: 180 }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {children}\n </RowSizeProvider>\n </DropdownMenuPrimitive.SubContent>\n )\n})\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName\n\n// ── Helper: build endContent from badge + endIcon + shortcut ──\nfunction buildEndContent(\n size: SizeKey,\n badge?: React.ReactNode,\n endIcon?: LucideIcon,\n shortcut?: string,\n): React.ReactNode | undefined {\n const EndIcon = endIcon\n if (!badge && !EndIcon && !shortcut) return undefined\n const iconPx = ICON_SIZE[size]\n return (\n <>\n {badge}\n {EndIcon && <EndIcon size={iconPx} className=\"text-fg-muted\" aria-hidden />}\n {shortcut && <span className=\"text-caption text-fg-muted tracking-shortcut\">{shortcut}</span>}\n </>\n )\n}\n\n// ── Item ──\ninterface DropdownMenuItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 左側頭像資料(AvatarData),與 startIcon 互斥 */\n avatar?: AvatarData\n /** 次要說明文字 */\n description?: React.ReactNode\n /** 後綴 Tag(ReactNode) */\n tag?: React.ReactNode\n /** 後綴 Badge(ReactNode) */\n badge?: React.ReactNode\n /** 後綴指示型 icon(LucideIcon),fg-muted */\n endIcon?: LucideIcon\n /** 鍵盤快捷鍵提示(canonical)——渲染進 MenuItem `endContent` 正規後綴 slot。\n * 替代方案是 `<DropdownMenuShortcut>` child(composition escape-hatch);**同 item 勿混用**。 */\n shortcut?: string\n /** 單選選中(bg-neutral-selected,持續選中狀態)*/\n selected?: boolean\n}\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n DropdownMenuItemProps\n>(({ className, children, startIcon, avatar, description, tag, badge, endIcon, shortcut, selected, disabled, ...props }, ref) => {\n const size = useRowSize()\n const endContent = buildEndContent(size, badge, endIcon, shortcut)\n\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n disabled={disabled}\n // 2026-07-05:typeahead 只比對 label——children 為字串時 auto-forward textValue,避免 Radix\n // fallback 拿整段 textContent(label+description+badge+shortcut 串接)比對。consumer 顯式\n // textValue 在 {...props} 較後 spread,仍優先。\n textValue={typeof children === 'string' ? children : undefined}\n className={cn(\n radixItemClass,\n // 2026-07-04 Q2 拍板:selected bg 勝 hover/highlighted(bg 是唯一選中指示器,被 hover 洗掉\n // = 選中資訊消失)。對齊 MenuItem(menu-item.tsx selected 時關 hover bg)+ Ant Menu\n // `:not(-item-selected)` + VS Code list `:hover:not(.selected)`;Radix highlighted 同時是\n // keyboard cursor,selected 項停留時維持 selected bg(DS bg 通道單一職責)。\n // 2026-07-05 D4 補完 Q2 附帶條件:selected×highlighted(鍵盤 cursor 停留)用加深一階\n // bg-neutral-selected-active(neutral-3;M23 nearest canonical = Button toggle active 同\n // family)— 深化而非洗掉 selection,cursor 可感知(WCAG 2.4.7),對齊 MUI selectedOpacity+hover idiom。\n selected && 'bg-neutral-selected data-[highlighted]:bg-neutral-selected-active',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n avatar={avatar}\n description={description}\n tag={tag}\n endContent={endContent}\n disabled={disabled}\n // Pure visual — Radix parent handles role/aria/interaction\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Item>\n )\n})\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\n// ── SubTrigger(子選單觸發器,自動附加 ChevronRight)──\ninterface DropdownMenuSubTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 子選單目前狀態值文字(如 \"深色\") */\n value?: string\n /** 子選單狀態 badge */\n badge?: React.ReactNode\n}\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n DropdownMenuSubTriggerProps\n>(({ className, children, startIcon, value, badge, ...props }, ref) => {\n const size = useRowSize()\n const iconPx = ICON_SIZE[size]\n\n // SubTrigger suffix: [value?] [badge?] [ChevronRight] with gap-1\n const endContent = (\n <div className=\"flex items-center gap-1\">\n {value && <span className=\"text-fg-muted\">{value}</span>}\n {badge}\n <ChevronRight size={iconPx} className=\"text-fg-muted\" />\n </div>\n )\n\n return (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(endContent 的 value/badge 不進比對)。\n textValue={typeof children === 'string' ? children : undefined}\n className={cn(\n radixItemClass,\n 'data-[state=open]:bg-neutral-hover',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n endContent={endContent}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.SubTrigger>\n )\n})\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName\n\n// ── CheckboxItem ──\ninterface DropdownMenuCheckboxItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n DropdownMenuCheckboxItemProps\n>(({ className, children, startIcon, description, checked, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n checked={checked}\n disabled={disabled}\n // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(M10 同 pattern:帶 description)。\n textValue={typeof children === 'string' ? children : undefined}\n onSelect={(e) => e.preventDefault()}\n className={cn(radixItemClass, className)}\n {...props}\n >\n <MenuItem\n size={size}\n checkbox\n checked={!!checked}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.CheckboxItem>\n )\n})\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName\n\n// ── Label(群組標題)──\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn('outline-none', className)}\n {...props}\n >\n <MenuItem\n size={size}\n header\n role=\"presentation\"\n className=\"pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Label>\n )\n})\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\n// ── Separator ──\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"my-2 h-px bg-divider\", className)}\n {...props}\n />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\n// ── RadioItem(單選,排序方式等)──\n// Radix handles checked state;checked 底色套在外層 Radix RadioItem 本身(parent-bg\n// pattern,詳下方 2026-05-31 #10 註解),內層 MenuItem 恆 !bg-transparent 讓它透出。\ninterface DropdownMenuRadioItemProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> {\n /** Prefix icon(LucideIcon) */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n DropdownMenuRadioItemProps\n>(({ className, children, startIcon, description, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n disabled={disabled}\n // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(M10 同 pattern:帶 description)。\n textValue={typeof children === 'string' ? children : undefined}\n onSelect={(e) => e.preventDefault()}\n // 2026-05-31 #10:selected 底色套在外層 Radix RadioItem 本身(非 `[&>*]` 子 MenuItem),\n // 因內層 MenuItem 自帶 `!bg-transparent` 會蓋掉子層 bg → 選中底色從不顯示。\n // 改 parent-bg pattern(對齊 DropdownMenuItem selected):RadioItem 上底色,MenuItem 透明讓它透出。\n // 2026-07-04 Q2:checked 亦勝 highlighted(同 DropdownMenuItem selected 規則)\n className={cn(radixItemClass, 'data-[state=checked]:bg-neutral-selected data-[state=checked]:data-[highlighted]:bg-neutral-selected-active', className)}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.RadioItem>\n )\n})\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\n// ── Shortcut(鍵盤快捷鍵提示 child;escape-hatch,不靠右)──\n// **Canonical 是 `<DropdownMenuItem shortcut=\"⌘C\">`** prop —— 走 MenuItem `endContent` 正規後綴 slot\n// (跟 badge / endIcon 同槽、gap/對齊一致,真正靠右對齊列尾)。本 child 是 **composition\n// escape-hatch**(相容 shadcn DropdownMenuShortcut idiom = `ml-auto` span,source:\n// https://ui.shadcn.com/docs/components/dropdown-menu;npm published API 不砍)。⚠️ 在\n// DropdownMenuItem 內 children 會進 MenuItem ItemContent 的 label `<span>`(inline context,\n// 非 flex row),故 `ml-auto` **不生效、不會靠右**——只會 inline 附加在 label 文字尾;`ml-auto`\n// 僅在 consumer 自組 flex row 時生效。要靠右一律用 canonical `shortcut` prop。**同一 item\n// 只用一種,勿混用**(見 spec.md「快捷鍵提示 API」+ 禁止事項)。\n// 字體樣式統一:text-caption + tracking-shortcut + fg-muted(對齊 prop 後綴 + CommandShortcut);位置不等價。\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn('ml-auto text-caption text-fg-muted tracking-shortcut', className)}\n {...props}\n />\n)\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dropdownMenuMeta = {\n component: 'DropdownMenu',\n family: 1, // 對齊 dropdown-menu.spec.md frontmatter L3(Family 1 消費者;DS 慣例 = meta 對齊 spec frontmatter,原 null 是 Phase 1 boilerplate 殘留)\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = 單選/checked item 持續選中(bg-neutral-selected);'active' 保留 — highlight-on-selected\n // 走 bg-neutral-selected-active(active 階 token,M23 nearest canonical;2026-07-07 詞彙統一補修)。\n states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-neutral-selected-active', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: ['ring-ring'], // 2026-07-05:Trigger focus canonical ring(對齊 Checkbox/Tabs 等 meta 慣例)\n },\n} as const\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuShortcut,\n floatingLayerClass,\n}\nexport type { SizeKey, DropdownMenuItemProps }\n"],"names":["ROW_ICON_SIZE"],"mappings":";;;;;;;;;;AA0CA,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAKV,MAAM,YAAYA;AAIlB,MAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,GAAG;AAGV,MAAM,eAAe,sBAAsB;AAC3C,MAAM,sBAAsB,MAAM,WAGhC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,sBAAsB;AAAA,EAAtB;AAAA,IACC;AAAA,IAIA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,oBAAoB,cAAc,sBAAsB,QAAQ;AAoBhE,MAAM,oBAAoB,MAAM,WAG9B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,sBAAsB;AAAA,EAAtB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,6DAA6D,SAAS;AAAA,IACnF,GAAG;AAAA,EAAA;AACN,CACD;AACD,kBAAkB,cAAc;AAEhC,MAAM,qBAAqB,sBAAsB;AACjD,MAAM,kBAAkB,sBAAsB;AAC9C,MAAM,yBAAyB,sBAAsB;AAYrD,MAAM,sBAAsB,MAAM,WAGhC,CAAC,EAAE,WAAW,OAAO,MAAM,aAAa,qBAAqB,mBAAmB,2BAA2B,QAAQ,SAAS,UAAU,WAAW,UAAU,GAAG,MAAA,GAAS,QACvK,oBAAC,sBAAsB,QAAtB,EACC,UAAA;AAAA,EAAC,sBAAsB;AAAA,EAAtB;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IAgBA,WAAW,GAAG,oBAAoB,CAAC,aAAa,QAAQ,SAAS;AAAA,IACjE,OAAO;AAAA,MACL,WAAW;AAAA,MACX,UAAU,YAAY;AAAA,MACtB;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAA,oBAAC,iBAAA,EAAgB,OAAO,MACrB,UAAA;AAAA;AAAA;AAAA,MAGC,oBAAC,cAAW,WAAU,mBACpB,8BAAC,OAAA,EAAI,WAAU,QAAQ,SAAA,CAAS,EAAA,CAClC;AAAA,QAEA,SAAA,CAEJ;AAAA,EAAA;AACF,GACF,CACD;AACD,oBAAoB,cAAc,sBAAsB,QAAQ;AAGhE,MAAM,yBAAyB,MAAM,WAGnC,CAAC,EAAE,WAAW,UAAU,GAAG,MAAA,GAAS,QAAQ;AAC5C,QAAM,OAAO,WAAA;AACb,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC;AAAA,MACA,YAAY;AAAA,MACZ,WAAW,GAAG,oBAAoB,QAAQ,SAAS;AAAA,MACnD,OAAO,EAAE,WAAW,wBAAwB,UAAU,IAAA;AAAA,MACrD,GAAG;AAAA,MAEJ,UAAA,oBAAC,iBAAA,EAAgB,OAAO,MACrB,SAAA,CACH;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AACD,uBAAuB,cAAc,sBAAsB,WAAW;AAGtE,SAAS,gBACP,MACA,OACA,SACA,UAC6B;AAC7B,QAAM,UAAU;AAChB,MAAI,CAAC,SAAS,CAAC,WAAW,CAAC,SAAU,QAAO;AAC5C,QAAM,SAAS,UAAU,IAAI;AAC7B,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACA,+BAAY,SAAA,EAAQ,MAAM,QAAQ,WAAU,iBAAgB,eAAW,MAAC;AAAA,IACxE,YAAY,oBAAC,QAAA,EAAK,WAAU,gDAAgD,UAAA,SAAA,CAAS;AAAA,EAAA,GACxF;AAEJ;AAyBA,MAAM,mBAAmB,MAAM,WAG7B,CAAC,EAAE,WAAW,UAAU,WAAW,QAAQ,aAAa,KAAK,OAAO,SAAS,UAAU,UAAU,UAAU,GAAG,MAAA,GAAS,QAAQ;AAC/H,QAAM,OAAO,WAAA;AACb,QAAM,aAAa,gBAAgB,MAAM,OAAO,SAAS,QAAQ;AAEjE,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC;AAAA,MACA;AAAA,MAIA,WAAW,OAAO,aAAa,WAAW,WAAW;AAAA,MACrD,WAAW;AAAA,QACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQA,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UAEA,MAAK;AAAA,UACL,WAAU;AAAA,UAET;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,CAAC;AACD,iBAAiB,cAAc,sBAAsB,KAAK;AAc1D,MAAM,yBAAyB,MAAM,WAGnC,CAAC,EAAE,WAAW,UAAU,WAAW,OAAO,OAAO,GAAG,MAAA,GAAS,QAAQ;AACrE,QAAM,OAAO,WAAA;AACb,QAAM,SAAS,UAAU,IAAI;AAG7B,QAAM,aACJ,qBAAC,OAAA,EAAI,WAAU,2BACZ,UAAA;AAAA,IAAA,SAAS,oBAAC,QAAA,EAAK,WAAU,iBAAiB,UAAA,OAAM;AAAA,IAChD;AAAA,IACD,oBAAC,cAAA,EAAa,MAAM,QAAQ,WAAU,gBAAA,CAAgB;AAAA,EAAA,GACxD;AAGF,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC;AAAA,MAEA,WAAW,OAAO,aAAa,WAAW,WAAW;AAAA,MACrD,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,WAAU;AAAA,UAET;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,CAAC;AACD,uBAAuB,cAAc,sBAAsB,WAAW;AAYtE,MAAM,2BAA2B,MAAM,WAGrC,CAAC,EAAE,WAAW,UAAU,WAAW,aAAa,SAAS,UAAU,GAAG,MAAA,GAAS,QAAQ;AACvF,QAAM,OAAO,WAAA;AAEb,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MAEA,WAAW,OAAO,aAAa,WAAW,WAAW;AAAA,MACrD,UAAU,CAAC,MAAM,EAAE,eAAA;AAAA,MACnB,WAAW,GAAG,gBAAgB,SAAS;AAAA,MACtC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,UAAQ;AAAA,UACR,SAAS,CAAC,CAAC;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,WAAU;AAAA,UAET;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,CAAC;AACD,yBAAyB,cAAc,sBAAsB,aAAa;AAG1E,MAAM,oBAAoB,MAAM,WAG9B,CAAC,EAAE,WAAW,UAAU,GAAG,MAAA,GAAS,QAAQ;AAC5C,QAAM,OAAO,WAAA;AACb,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC;AAAA,MACA,WAAW,GAAG,gBAAgB,SAAS;AAAA,MACtC,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,QAAM;AAAA,UACN,MAAK;AAAA,UACL,WAAU;AAAA,UAET;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,CAAC;AACD,kBAAkB,cAAc,sBAAsB,MAAM;AAG5D,MAAM,wBAAwB,MAAM,WAGlC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,sBAAsB;AAAA,EAAtB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,wBAAwB,SAAS;AAAA,IAC9C,GAAG;AAAA,EAAA;AACN,CACD;AACD,sBAAsB,cAAc,sBAAsB,UAAU;AAapE,MAAM,wBAAwB,MAAM,WAGlC,CAAC,EAAE,WAAW,UAAU,WAAW,aAAa,UAAU,GAAG,MAAA,GAAS,QAAQ;AAC9E,QAAM,OAAO,WAAA;AAEb,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC;AAAA,MACA;AAAA,MAEA,WAAW,OAAO,aAAa,WAAW,WAAW;AAAA,MACrD,UAAU,CAAC,MAAM,EAAE,eAAA;AAAA,MAKnB,WAAW,GAAG,gBAAgB,+GAA+G,SAAS;AAAA,MACrJ,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAK;AAAA,UACL,WAAU;AAAA,UAET;AAAA,QAAA;AAAA,MAAA;AAAA,IACH;AAAA,EAAA;AAGN,CAAC;AACD,sBAAsB,cAAc,sBAAsB,UAAU;AAYpE,MAAM,uBAAuB,CAAC,EAAE,WAAW,GAAG,YAC5C;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAW,GAAG,wDAAwD,SAAS;AAAA,IAC9E,GAAG;AAAA,EAAA;AACN;AAEF,qBAAqB,cAAc;AAI5B,MAAM,mBAAmB;AAAA,EAC9B,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA;AAAA;AAAA,EAKP,QAAQ,CAAC,WAAW,SAAS,UAAU,YAAY,iBAAiB,UAAU;AAAA,EAC9E,QAAQ;AAAA,IACN,IAAI,CAAC,oBAAoB,uBAAuB,8BAA8B,qBAAqB,gBAAgB;AAAA,IACnH,IAAI,CAAC,oBAAoB,eAAe;AAAA,IACxC,MAAM,CAAC,WAAW;AAAA;AAAA,EAAA;AAEtB;"}
1
+ {"version":3,"file":"dropdown-menu.js","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"sourcesContent":["import * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { AvatarData } from \"@/design-system/components/Avatar/avatar\"\nimport { MenuItem } from \"@/design-system/components/Menu/menu-item\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { OVERLAY_SIDE_OFFSET, OVERLAY_COLLISION_PADDING } from \"@/design-system/tokens/elevation/overlay-geometry\"\nimport {\n RowSizeProvider,\n useRowSize,\n ICON_SIZE as ROW_ICON_SIZE,\n type RowSize,\n} from \"@/design-system/patterns/element-anatomy/item-anatomy\"\nimport { overlayMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n/**\n * DropdownMenu — Radix DropdownMenu + MenuItem visual layer\n *\n * 架構分工:\n * - Radix primitives:behavior(keyboard nav, focus management, aria roles)\n * - MenuItem:visual(layout, padding, icon alignment, typography)\n *\n * Radix primitive 是外層容器,控制 `data-[highlighted]:bg-neutral-hover`。\n * MenuItem 內層只負責佈局,不加互動樣式。\n *\n * ── Hover / highlight canonical(2026-04-22 修正)──\n * 用 Radix 官方的 `data-[highlighted]` attribute,**不用 `:focus-visible` / `:hover` /\n * `:focus`**:\n * - Radix 在 **mouse hover、keyboard arrow nav、focus move in** 時自動 set `data-highlighted`\n * - mouse leave / focus move out / menu close 時自動清掉\n * - 不會在 click 後留殘影(Radix 內部已處理)\n * - 跨瀏覽器一致(不依賴 `:focus-visible` 的 heuristic)\n *\n * 曾經用過 `focus-visible:bg-neutral-hover` 的理由:避免 click 後殘影。但實測:mouse hover\n * 觸發 Radix 程式化 `.focus()`,Chromium / Safari / Firefox 對 programmatic focus 是否 fire\n * `:focus-visible` 行為不一致,導致 mouse hover 有時無 bg。改用 `data-[highlighted]:` 後行為\n * 一致 —— 世界級 canonical(shadcn / Radix docs / Ariakit 皆此)。\n */\n\n// ── Floating layer 共用樣式 ──\n/** @internal — menu 形浮層共用 class(DropdownMenu/SelectMenu/Command 內部);consumer 不 hand-craft menu surface,用元件本身。root barrel 排除(subpath 仍可 wrap 後用)。 */\nconst floatingLayerClass = [\n 'z-50 overflow-hidden rounded-lg border border-border bg-surface-raised',\n overlayMotion,\n 'data-[state=open]:animate-in data-[state=closed]:animate-out',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n 'origin-[var(--radix-dropdown-menu-content-transform-origin)]',\n].join(' ')\n\n// ── Size:統一用 RowSizeContext(item-layout module),消除本地 SizeContext 漂移 ──\ntype SizeKey = RowSize\n// Re-export for backward compat(內部命名)\nconst ICON_SIZE = ROW_ICON_SIZE\n\n// ── Shared item classes on Radix primitive ──\n// Highlight(hover + keyboard nav): 用 Radix `data-[highlighted]` canonical(見 docblock)\nconst radixItemClass = [\n 'relative cursor-pointer select-none outline-none',\n 'transition-colors duration-150',\n 'data-[highlighted]:bg-neutral-hover',\n 'data-[disabled]:pointer-events-none data-[disabled]:text-fg-disabled data-[disabled]:cursor-default',\n].join(' ')\n\n// ── Root ──\nconst DropdownMenu = DropdownMenuPrimitive.Root\nconst DropdownMenuTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n // 2026-07-05:非 asChild 裸用需可見 focus 指示(WCAG 2.4.7)——原 `outline-none` 讓裸 Trigger\n // 完全無 focus ring。消費 Button focus canonical(button.tsx buttonVariants base)。\n // asChild+Button 場景同名 class 重複,無影響。\n className={cn(\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n className,\n )}\n {...props}\n />\n))\nDropdownMenuTrigger.displayName = DropdownMenuPrimitive.Trigger.displayName\n// DropdownMenuGroup — 對齊 MenuGroup 的 group separation 設計語言\n//\n// 設計語言(跨 Menu-like 元件統一,SSOT 見 item-anatomy.spec.md\n// 「Group auto-separation」):\n// 每個 group 上下各 8px padding,相鄰 group 之間用 border-divider 分隔\n// 兩個 group 之間視覺 gap = 8px(上一個 bottom)+ 8px(下一個 top)= 16px + border\n//\n// MenuGroup(menu-item.tsx)實作:`py-2 [&+&]:border-t [&+&]:border-divider`\n// (在 Command.List 下提供 Content 邊界 8px + group 間 16px gap)\n//\n// DropdownMenuGroup(本元件)實作:`[&+&]:mt-2 [&+&]:pt-2 [&+&]:border-t\n// [&+&]:border-divider`(因為 DropdownMenuContent 已有 py-2 提供 Content 邊界\n// 的 8px,只需在第二個起的 group 加 8+8 = 16px gap + border)\n//\n// **視覺結果等同**:兩種實作的 visual output 一致,只是「padding 住在哪層」\n// 不同。不強制統一 CSS 表達式——DropdownMenuContent 的 py-2 是純視覺 Content\n// 邊界 padding(Radix roving focus 依 collection refs + element.focus(),不讀\n// CSS padding;對齊 spec.md「為什麼不直接套 py-2」段)。Group 若再套 py-2 會\n// double padding,故 Group 用 mt-2/pt-2 只加在第二個起。\nconst DropdownMenuGroup = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Group\n ref={ref}\n className={cn('[&+&]:border-t [&+&]:border-divider [&+&]:mt-2 [&+&]:pt-2', className)}\n {...props}\n />\n))\nDropdownMenuGroup.displayName = 'DropdownMenuGroup'\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\n// ── Content ──\ninterface DropdownMenuContentProps\n // asChild Omit(2026-07-18 決策2):Content 恆注入固定 <RowSizeProvider> + <ScrollArea>\n // wrapper 於 Portal 內 → <Content asChild> 會把 Content props slot-merge 到非-DOM Provider 上而壞。\n // children 保留(合法渲染於固定 wrapper 內)。\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>, 'asChild'> {\n size?: SizeKey\n /** 最小寬度(px),預設 `max(180px, 觸發元件寬度)`——窄 trigger 時吃 180px 地板 */\n minWidth?: number\n /**\n * 最大高度(px)——**可選更低上限**。預設已 viewport-adaptive(夾到 trigger→視窗碰撞邊界的剩餘高度、\n * 離窗 ≥8px、超過即捲動);傳 `maxHeight` 只在「想比視窗剩餘更矮」時用(取 `min(視窗剩餘, maxHeight)`)。\n */\n maxHeight?: number\n}\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n DropdownMenuContentProps\n>(({ className, size = 'md', sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, align = 'start', minWidth, maxHeight, children, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n align={align}\n // Density:繼承 page density(2026-06-15 canonical)。menu item 高度 = field-height-{size},而\n // field-height 隨 density 變(md 28/32/36 → lg 32/36/40)→ 鎖 data-density=\"md\" 會把選單釘在 md-scale,\n // lg page 上對不上 lg 觸發點。原 data-density 是 409b91da a11y 批次順手加(對齊 Popover),非設計\n // 決策 → 移除,item 隨 page density 與觸發點一致(tier 仍由 size prop 決定)。\n // Focus return on close:不 override `onCloseAutoFocus` — 用 Radix 內建 default\n // (close 時 focus 還 trigger;outside-interaction 例外由 Radix `hasInteractedOutsideRef` 自管)。\n // W3C APG menubar「Escape: …return focus to the element…from which the menu was opened」\n // (w3.org/WAI/ARIA/apg/patterns/menubar/);shadcn dropdown-menu 同樣不 override。\n // 2026-06-11 移除 2026-04-08 的 `(e) => e.preventDefault()` hardcode:它跑在 Radix\n // composeEventHandlers 內建 handler 之前,defaultPrevented → trigger focus 被 skip →\n // Esc 關閉後 focus 掉到 body(APG violation)。原動機「mouse close 後 trigger 殘留\n // focus ring」不構成 override 理由:實測(2026-06-12 playwright)pointer item-click\n // close 後 programmatic refocus 在 Chromium 仍可 match `:focus-visible`(UA heuristic),\n // 但與 shadcn 官方 live demo 同流程 DOM 比對 IDENTICAL — Radix 生態一致接受此\n // tradeoff:APG keyboard focus-return 優先於 cosmetic ring。\n className={cn(floatingLayerClass, 'flex flex-col min-h-0', className)}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(180px, var(--radix-dropdown-menu-trigger-width))',\n // 2026-07-18 viewport-adaptive max-h(對齊 HoverCard/Popover 既有 canonical):夾到「trigger 到\n // 碰撞邊界(已內縮 collisionPadding=8px)的剩餘高度」→ 高選單不溢出視窗、離視窗邊 ≥8px、\n // 短選單仍貼內容;consumer `maxHeight` 為**可選更低上限**(min 取小者)。inline style 非 class,\n // 故動態 maxHeight 不觸 Tailwind 掃描陷阱。\n maxHeight: maxHeight\n ? `min(var(--radix-dropdown-menu-content-available-height, 100vh), ${maxHeight}px)`\n : 'var(--radix-dropdown-menu-content-available-height, 100vh)',\n }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {/* body 恆在 flex-1 ScrollArea 內:超過 clamped 高度即跨-OS 一致捲動(不吃寬度);\n 短選單 flex-1 貼內容不塌(同 HoverCard/Popover/DataTable panel 已驗)。py-2 在內層,整 padded 區可捲。 */}\n <ScrollArea className=\"flex-1 min-h-0\">\n <div className=\"py-2\">{children}</div>\n </ScrollArea>\n </RowSizeProvider>\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\n// ── SubContent ──\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n sideOffset={OVERLAY_SIDE_OFFSET}\n className={cn(floatingLayerClass, 'py-2', className)}\n style={{ boxShadow: 'var(--elevation-200)', minWidth: 180 }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {children}\n </RowSizeProvider>\n </DropdownMenuPrimitive.SubContent>\n )\n})\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName\n\n// ── Helper: build endContent from badge + endIcon + shortcut ──\nfunction buildEndContent(\n size: SizeKey,\n badge?: React.ReactNode,\n endIcon?: LucideIcon,\n shortcut?: string,\n): React.ReactNode | undefined {\n const EndIcon = endIcon\n if (!badge && !EndIcon && !shortcut) return undefined\n const iconPx = ICON_SIZE[size]\n return (\n <>\n {badge}\n {EndIcon && <EndIcon size={iconPx} className=\"text-fg-muted\" aria-hidden />}\n {shortcut && <span className=\"text-caption text-fg-muted tracking-shortcut\">{shortcut}</span>}\n </>\n )\n}\n\n// ── Item ──\ninterface DropdownMenuItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 左側頭像資料(AvatarData),與 startIcon 互斥 */\n avatar?: AvatarData\n /** 次要說明文字 */\n description?: React.ReactNode\n /** 後綴 Tag(ReactNode) */\n tag?: React.ReactNode\n /** 後綴 Badge(ReactNode) */\n badge?: React.ReactNode\n /** 後綴指示型 icon(LucideIcon),fg-muted */\n endIcon?: LucideIcon\n /** 鍵盤快捷鍵提示(canonical)——渲染進 MenuItem `endContent` 正規後綴 slot。\n * 替代方案是 `<DropdownMenuShortcut>` child(composition escape-hatch);**同 item 勿混用**。 */\n shortcut?: string\n /** 單選選中(bg-neutral-selected,持續選中狀態)*/\n selected?: boolean\n}\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n DropdownMenuItemProps\n>(({ className, children, startIcon, avatar, description, tag, badge, endIcon, shortcut, selected, disabled, ...props }, ref) => {\n const size = useRowSize()\n const endContent = buildEndContent(size, badge, endIcon, shortcut)\n\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n disabled={disabled}\n // 2026-07-05:typeahead 只比對 label——children 為字串時 auto-forward textValue,避免 Radix\n // fallback 拿整段 textContent(label+description+badge+shortcut 串接)比對。consumer 顯式\n // textValue 在 {...props} 較後 spread,仍優先。\n textValue={typeof children === 'string' ? children : undefined}\n className={cn(\n radixItemClass,\n // 2026-07-04 Q2 拍板:selected bg 勝 hover/highlighted(bg 是唯一選中指示器,被 hover 洗掉\n // = 選中資訊消失)。對齊 MenuItem(menu-item.tsx selected 時關 hover bg)+ Ant Menu\n // `:not(-item-selected)` + VS Code list `:hover:not(.selected)`;Radix highlighted 同時是\n // keyboard cursor,selected 項停留時維持 selected bg(DS bg 通道單一職責)。\n // 2026-07-05 D4 補完 Q2 附帶條件:selected×highlighted(鍵盤 cursor 停留)用加深一階\n // bg-neutral-selected-active(neutral-3;M23 nearest canonical = Button toggle active 同\n // family)— 深化而非洗掉 selection,cursor 可感知(WCAG 2.4.7),對齊 MUI selectedOpacity+hover idiom。\n selected && 'bg-neutral-selected data-[highlighted]:bg-neutral-selected-active',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n avatar={avatar}\n description={description}\n tag={tag}\n endContent={endContent}\n disabled={disabled}\n // Pure visual — Radix parent handles role/aria/interaction\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Item>\n )\n})\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\n// ── SubTrigger(子選單觸發器,自動附加 ChevronRight)──\ninterface DropdownMenuSubTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 子選單目前狀態值文字(如 \"深色\") */\n value?: string\n /** 子選單狀態 badge */\n badge?: React.ReactNode\n}\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n DropdownMenuSubTriggerProps\n>(({ className, children, startIcon, value, badge, ...props }, ref) => {\n const size = useRowSize()\n const iconPx = ICON_SIZE[size]\n\n // SubTrigger suffix: [value?] [badge?] [ChevronRight] with gap-1\n const endContent = (\n <div className=\"flex items-center gap-1\">\n {value && <span className=\"text-fg-muted\">{value}</span>}\n {badge}\n <ChevronRight size={iconPx} className=\"text-fg-muted\" />\n </div>\n )\n\n return (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(endContent 的 value/badge 不進比對)。\n textValue={typeof children === 'string' ? children : undefined}\n className={cn(\n radixItemClass,\n 'data-[state=open]:bg-neutral-hover',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n endContent={endContent}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.SubTrigger>\n )\n})\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName\n\n// ── CheckboxItem ──\ninterface DropdownMenuCheckboxItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n DropdownMenuCheckboxItemProps\n>(({ className, children, startIcon, description, checked, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n checked={checked}\n disabled={disabled}\n // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(M10 同 pattern:帶 description)。\n textValue={typeof children === 'string' ? children : undefined}\n onSelect={(e) => e.preventDefault()}\n className={cn(radixItemClass, className)}\n {...props}\n >\n <MenuItem\n size={size}\n checkbox\n checked={!!checked}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.CheckboxItem>\n )\n})\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName\n\n// ── Label(群組標題)──\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn('outline-none', className)}\n {...props}\n >\n <MenuItem\n size={size}\n header\n role=\"presentation\"\n className=\"pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Label>\n )\n})\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\n// ── Separator ──\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"my-2 h-px bg-divider\", className)}\n {...props}\n />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\n// ── RadioItem(單選,排序方式等)──\n// Radix handles checked state;checked 底色套在外層 Radix RadioItem 本身(parent-bg\n// pattern,詳下方 2026-05-31 #10 註解),內層 MenuItem 恆 !bg-transparent 讓它透出。\ninterface DropdownMenuRadioItemProps\n // children Omit + redeclare required(2026-07-18 決策2 一致性):對齊 sibling Item/SubTrigger/\n // CheckboxItem — menu item 必有 label,children 為 required 非 Radix optional。RadioItem 恆渲染\n // 固定 <MenuItem>{children}</MenuItem>,children 是合法 label(非 lie),此為 required 化一致性修。\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>, 'children'> {\n children: React.ReactNode\n /** Prefix icon(LucideIcon) */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n DropdownMenuRadioItemProps\n>(({ className, children, startIcon, description, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n disabled={disabled}\n // 2026-07-05:同 DropdownMenuItem——typeahead 只比對 label(M10 同 pattern:帶 description)。\n textValue={typeof children === 'string' ? children : undefined}\n onSelect={(e) => e.preventDefault()}\n // 2026-05-31 #10:selected 底色套在外層 Radix RadioItem 本身(非 `[&>*]` 子 MenuItem),\n // 因內層 MenuItem 自帶 `!bg-transparent` 會蓋掉子層 bg → 選中底色從不顯示。\n // 改 parent-bg pattern(對齊 DropdownMenuItem selected):RadioItem 上底色,MenuItem 透明讓它透出。\n // 2026-07-04 Q2:checked 亦勝 highlighted(同 DropdownMenuItem selected 規則)\n className={cn(radixItemClass, 'data-[state=checked]:bg-neutral-selected data-[state=checked]:data-[highlighted]:bg-neutral-selected-active', className)}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.RadioItem>\n )\n})\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\n// ── Shortcut(鍵盤快捷鍵提示 child;escape-hatch,不靠右)──\n// **Canonical 是 `<DropdownMenuItem shortcut=\"⌘C\">`** prop —— 走 MenuItem `endContent` 正規後綴 slot\n// (跟 badge / endIcon 同槽、gap/對齊一致,真正靠右對齊列尾)。本 child 是 **composition\n// escape-hatch**(相容 shadcn DropdownMenuShortcut idiom = `ml-auto` span,source:\n// https://ui.shadcn.com/docs/components/dropdown-menu;npm published API 不砍)。⚠️ 在\n// DropdownMenuItem 內 children 會進 MenuItem ItemContent 的 label `<span>`(inline context,\n// 非 flex row),故 `ml-auto` **不生效、不會靠右**——只會 inline 附加在 label 文字尾;`ml-auto`\n// 僅在 consumer 自組 flex row 時生效。要靠右一律用 canonical `shortcut` prop。**同一 item\n// 只用一種,勿混用**(見 spec.md「快捷鍵提示 API」+ 禁止事項)。\n// 字體樣式統一:text-caption + tracking-shortcut + fg-muted(對齊 prop 後綴 + CommandShortcut);位置不等價。\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn('ml-auto text-caption text-fg-muted tracking-shortcut', className)}\n {...props}\n />\n)\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dropdownMenuMeta = {\n component: 'DropdownMenu',\n family: 1, // 對齊 dropdown-menu.spec.md frontmatter L3(Family 1 消費者;DS 慣例 = meta 對齊 spec frontmatter,原 null 是 Phase 1 boilerplate 殘留)\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = 單選/checked item 持續選中(bg-neutral-selected);'active' 保留 — highlight-on-selected\n // 走 bg-neutral-selected-active(active 階 token,M23 nearest canonical;2026-07-07 詞彙統一補修)。\n states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-neutral-selected-active', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: ['ring-ring'], // 2026-07-05:Trigger focus canonical ring(對齊 Checkbox/Tabs 等 meta 慣例)\n },\n} as const\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuShortcut,\n floatingLayerClass,\n}\nexport type { SizeKey, DropdownMenuItemProps }\n"],"names":["ROW_ICON_SIZE"],"mappings":"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"}
@@ -13,8 +13,14 @@ export type FieldControlLayout = 'inline' | 'block';
13
13
  * - `'toolbar'`— Toolbar / Action bar inline Field(future,目前無 consumer)
14
14
  * - `'table-cell'`— DataTable cell-as-input substrate(取代 variant='naked' cell-detection
15
15
  * heuristic;canonical metrics 由 surface 推導,不再 hardcode in Field consumer)
16
+ * - `'inline-edit'`(2026-07-17)— InlineEdit view 委派 surface。**唯一語義 = 委派控件 view 態
17
+ * 左 px 統一 `--field-px`(不繼承 tagPadding / avatar inset / bare-span 0px)**,讓 InlineEdit
18
+ * 固定 `-mx-[--field-px]` 對齊盒精準抵銷 → 值/tag/avatar 左緣落 label 左緣(x=0)。edit 態**不套**
19
+ * 本 surface(edit 完全繼承原 field,含 tagPadding — Jira 式)。對齊 Atlassian inline-edit
20
+ * read-view「fixed negative margin + read=edit geometry by construction」。只 InlineEdit display
21
+ * box 注入;DataTable cell(table-cell)/ 純表單(form)的 tagPadding + bare-span 完全不受影響。
16
22
  */
17
- type FieldSurface = 'form' | 'toolbar' | 'table-cell';
23
+ type FieldSurface = 'form' | 'toolbar' | 'table-cell' | 'inline-edit';
18
24
  export interface FieldContextValue {
19
25
  id: string;
20
26
  /** a11y(2026-04-25):FieldLabel 渲染時若元素為 div-based role(combobox/slider
@@ -24,7 +30,7 @@ export interface FieldContextValue {
24
30
  descriptionId: string;
25
31
  errorId: string;
26
32
  mode: FieldMode;
27
- /** 視覺外殼透傳(2026-05-05)。default = 含 border+bg;naked = cell-as-input(edit×naked 自畫 border-based state machine;display/readonly/disabled×naked 用 transparent border 由 host cell 供邊框);`bare` 2026-07-09 退役;naked 2026-07-14 型別收窄至
33
+ /** 視覺外殼透傳(2026-05-05)。default = 含 border+bg;naked = cell-as-input(edit×naked 自畫 border-based state machine;view×naked 用 transparent border 由 host cell 供邊框);`bare` 2026-07-09 退役;naked 2026-07-14 型別收窄至
28
34
  * FieldVariantInternal(@internal)— 公開 `<Field variant>` 只收 default,故本欄位維持 FieldVariant。
29
35
  * child Field control 自動繼承,per-control prop override 可覆寫。詳 field-types.ts。 */
30
36
  variant: FieldVariant;
@@ -90,13 +96,13 @@ export declare function useResolvedFieldSize<T extends string = FieldSize>(sizeP
90
96
  */
91
97
  export declare function useResolvedFieldDisabled(disabledProp?: boolean | null): boolean;
92
98
  /**
93
- * Resolve Field control 的 **mode**(display / readonly / disabled / edit)— 2026-06-08 SSOT,統一兩派散落:
99
+ * Resolve Field control 的 **mode**(view / readonly / disabled / edit)— 2026-06-08 SSOT,統一兩派散落:
94
100
  * 舊 Input 派 `modeProp ?? fieldCtx?.mode ?? (...)` → `<Field disabled>` 時 ctx.mode 仍 'edit',漏 disabled chrome。
95
- * 舊 picker 派 `disabled ? 'disabled' : mode`(mode 預設 'edit')→ 完全不讀 fieldCtx.mode,`<Field mode="display">` 失效。
101
+ * 舊 picker 派 `disabled ? 'disabled' : mode`(mode 預設 'edit')→ 完全不讀 fieldCtx.mode,`<Field mode="view">` 失效。
96
102
  * 統一優先序(world-class:MUI FormControl disabled 完整 cascade + 顯式 prop 永遠最優先):
97
- * 1. 顯式 mode prop(caller / DataTable cell displayOrDisabled)→ **永遠最優先**,故表格等顯式場景 inert
103
+ * 1. 顯式 mode prop(caller / DataTable cell mode="view")→ **永遠最優先**,故表格等顯式場景 inert
98
104
  * 2. 有效 disabled(prop 或 `<Field disabled>`)→ 'disabled'(完整 disabled chrome)
99
- * 3. FieldContext.mode(`<Field mode="display"/"readonly">`)→ 讓 mode cascade 真正生效
105
+ * 3. FieldContext.mode(`<Field mode="view"/"readonly">`)→ 讓 mode cascade 真正生效
100
106
  * 4. 本地 readOnly → 'readonly'
101
107
  * 5. 'edit'
102
108
  * cell:mode prop 必有 → step 1 命中、fieldCtx=null → 完全 inert(Δ=0)。`disabled` 傳已 resolve 的 boolean 或未預設 prop。
@@ -135,16 +141,16 @@ export declare function FieldSurfaceEditableProvider({ isEditable, children, }:
135
141
  *
136
142
  * | 情境 | 空值顯示 |
137
143
  * |------------------------------------------------|----------|
138
- * | table-cell **可編輯** 的 display 靜止態 | **空白 `''`**(不佔位,affordance = hover outline)|
144
+ * | table-cell **可編輯** 的 view 靜止態 | **空白 `''`**(不佔位,affordance = hover outline)|
139
145
  * | table-cell **不可編輯**(readonly cell) | **半形 `-`** |
140
- * | standalone display / readonly / form / toolbar | **半形 `-`** |
146
+ * | standalone view / readonly / form / toolbar | **半形 `-`** |
141
147
  *
142
148
  * 收斂式:`surface==='table-cell' && isEditable ? '' : EMPTY_DISPLAY`。
143
149
  * 可編輯 form / edit 輸入框走 native placeholder(不經此 hook)。boolean → unchecked / disabled →
144
150
  * 同上文字 + text-fg-disabled(M24),各控件自理。
145
151
  * 世界級對照:table-cell blank = MUI X / AG Grid / Ant core / Notion / Airtable grid 域共識;
146
152
  * 非 table `-` = Ant ProTable `columnEmptyText`(見 field-wrapper.tsx EMPTY_DISPLAY 註)。
147
- * SSOT 條文 → field-controls.spec.md「null / undefined 值」;全 Field family display/readonly/disabled
153
+ * SSOT 條文 → field-controls.spec.md「null / undefined 值」;全 Field family view/readonly/disabled
148
154
  * 空值渲染必經此 hook,禁直接引 EMPTY_DISPLAY 常數(genre 分流會漏)。
149
155
  */
150
156
  export declare function useFieldEmptyDisplay(): string;
@@ -157,7 +163,7 @@ export declare function useFieldEmptyDisplay(): string;
157
163
  * 提示」的裝飾語意;空值符號不是提示、是被檢視的值狀態)。
158
164
  * disabled 態維持 `text-fg-disabled`(M24 disabled 顯著性 > foreground/muted,不可被蓋)。
159
165
  *
160
- * SSOT:全 Field family display/readonly/disabled 空值 span 消費此 helper,傳入已 resolve 的
166
+ * SSOT:全 Field family view/readonly/disabled 空值 span 消費此 helper,傳入已 resolve 的
161
167
  * resolvedMode。純函式(非 hook)—— resolvedMode 已由 useResolvedFieldMode 解析(含控件自身
162
168
  * disabled prop,context hook 讀不到);故以 resolvedMode 為入參,可條件呼叫、不受 Rules of Hooks 限制。
163
169
  * 世界級對照:Ant read-only / Carbon read-only value = 正常前景色;placeholder 才 muted。
@@ -1 +1 @@
1
- {"version":3,"file":"field-context.d.ts","sourceRoot":"","sources":["../../../src/components/Field/field-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field-context.d.ts","sourceRoot":"","sources":["../../../src/components/Field/field-context.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"field-context.js","sources":["../../../src/components/Field/field-context.ts"],"sourcesContent":["import * as React from 'react'\nimport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from './field-types'\nimport { EMPTY_DISPLAY } from './field-wrapper'\n\n// ── Types ──\nexport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth }\nexport type FieldOrientation = 'vertical' | 'horizontal'\nexport type FieldSize = 'sm' | 'md' | 'lg'\nexport type FieldControlLayout = 'inline' | 'block'\n\n/**\n * Field surface(2026-05-12 codex Q2 verdict — Stream C 4-issues Cluster B fix):\n * 替代散落的 `variant === 'naked'` 判 cell heuristic,讓 Field family components\n * 顯式 query「我在哪 surface」derive cell-specific padding / alignment / overflow。\n *\n * - `'form'`(default)— Standalone Field control(對齊 Polaris / Material Form idiom)\n * - `'toolbar'`— Toolbar / Action bar inline Field(future,目前無 consumer)\n * - `'table-cell'`— DataTable cell-as-input substrate(取代 variant='naked' cell-detection\n * heuristic;canonical metrics 由 surface 推導,不再 hardcode in Field consumer)\n */\ntype FieldSurface = 'form' | 'toolbar' | 'table-cell'\n\n// ── Context ──\nexport interface FieldContextValue {\n id: string\n /** a11y(2026-04-25):FieldLabel 渲染時若元素為 div-based role(combobox/slider\n * 等非 native form control),`<label for>` 無效,需 aria-labelledby 指向 labelId。\n * FieldLabel 自動設 id={labelId},下游 control 可讀此值寫入 aria-labelledby。 */\n labelId: string\n descriptionId: string\n errorId: string\n mode: FieldMode\n /** 視覺外殼透傳(2026-05-05)。default = 含 border+bg;naked = cell-as-input(edit×naked 自畫 border-based state machine;display/readonly/disabled×naked 用 transparent border 由 host cell 供邊框);`bare` 2026-07-09 退役;naked 2026-07-14 型別收窄至\n * FieldVariantInternal(@internal)— 公開 `<Field variant>` 只收 default,故本欄位維持 FieldVariant。\n * child Field control 自動繼承,per-control prop override 可覆寫。詳 field-types.ts。 */\n variant: FieldVariant\n disabled: boolean\n required: boolean\n invalid: boolean\n size: FieldSize\n orientation: FieldOrientation\n controlLayout: FieldControlLayout\n hasFieldWrapper: true\n /** 2026-05-12 Stream C Cluster B fix:surface 上下文(form / toolbar / table-cell)\n * 從 host(Field wrapper / DataTable cell registry)propagate 給 child controls。\n * Optional — 未設視同 `'form'`(backward compat)。 */\n surface?: FieldSurface\n}\n\nexport const FieldContext = React.createContext<FieldContextValue | null>(null)\n\n/** 讓 primitive(Checkbox/Switch/Radio/Button/Input 等)讀 Field context */\nexport function useFieldContext(): FieldContextValue | null {\n return React.useContext(FieldContext)\n}\n\n/**\n * Surface context — **獨立於 FieldContext** 的 surface signal(2026-05-12 v2 fix)。\n *\n * 原 v1 設計(`FieldSurfaceProvider` 透過 FieldContext.Provider 注入)誤把 stub\n * `hasFieldWrapper: true` 傳給沒有真正 Field wrapper 的場景(例 DataTable cell 內 standalone\n * DatePicker),導致 cells 誤以為有 Field 父 → 視覺 regression(2026-05-12 visual-audit 抓 3\n * stories 超過 baseline budget:DatePicker range-picker / FileViewer open-snapshot /\n * DataTable pinned-columns)。\n *\n * v2 修:**完全分離** FieldSurfaceContext;FieldContext 不被污染;non-FieldWrapper hosts\n * 只能影響 surface signal,不能假冒 Field wrapper 身份。\n */\nconst FieldSurfaceContext = React.createContext<FieldSurface | null>(null)\n\n/**\n * Helper: 取當前 surface,fall back 'form'。\n *\n * Stream C Cluster B canonical entry point — Field family controls 用此 helper 替代\n * 散落的 `variant === 'naked'` cell heuristic。對齊 codex Q2 verdict「surface context」\n * canonical(替代「inCellContext per-prop」反 pattern)。\n *\n * 優先順序:FieldSurfaceContext(host-provided) → FieldContext.surface(Field-wrapper-provided)\n * → fallback 'form'。\n */\nexport function useFieldSurface(): FieldSurface {\n const surfaceCtx = React.useContext(FieldSurfaceContext)\n if (surfaceCtx) return surfaceCtx\n const fieldCtx = React.useContext(FieldContext)\n return fieldCtx?.surface ?? 'form'\n}\n\n/**\n * Standalone FieldSurface provider — 給 host(non-Field-wrapper, 如 DataTable cell)\n * **僅**設 surface signal,**不**污染 FieldContext。\n *\n * Usage(per cell-registry.tsx,2026-05-12):\n * <FieldSurfaceProvider surface=\"table-cell\"><Field control /></FieldSurfaceProvider>\n *\n * 與 FieldContext.Provider 差別:\n * - FieldContext.Provider — 完整 Field wrapper 用,provides mode/variant/disabled/...\n * - FieldSurfaceProvider — host **僅**注 surface signal,Field controls 透過\n * `useFieldSurface()` 讀;`useFieldContext()` 仍 returns null(沒被假冒)。\n */\nexport function FieldSurfaceProvider({\n surface,\n children,\n}: {\n surface: FieldSurface\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(FieldSurfaceContext.Provider, { value: surface }, children)\n}\n\n/**\n * 2026-05-13 (c) scroll-defer perf SSOT(per codex DataTable perf brief V3 + user 拍板\n * 「Roadmap still >50ms 進 (c)」):\n * Host(DataTable)在 virtualizer.isScrolling=true 期間 wrap children with `TableScrollProvider`,\n * 重 cell 組件(Avatar HoverCard / PeoplePicker / etc.)讀此 signal 跳過昂貴 subtree(如 HoverCard\n * Portal + useDocumentTheme observer),scroll 結束 unset → 重渲染為完整 affordance。\n *\n * 對齊 codex Profile Plan step 6(Scripting vs Layout 判分):scrolling 期間 cell 渲染壓縮 →\n * 240ms/frame catastrophe → close to 50ms target。世界級對齊:AG Grid `deferRender` for slow\n * React cell components / MUI X DataGrid scroll-defer hover affordance。\n *\n * 不放在 FieldSurfaceContext 內(避免 value object identity drift on scroll → 全 cell 重渲)\n * — 用獨立 Context,value = boolean primitive(stable when unchanged)。\n */\nconst TableScrollContext = React.createContext<boolean>(false)\n\nexport function useTableIsScrolling(): boolean {\n return React.useContext(TableScrollContext)\n}\n\nexport function TableScrollProvider({\n isScrolling,\n children,\n}: {\n isScrolling: boolean\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(TableScrollContext.Provider, { value: isScrolling }, children)\n}\n\n/**\n * Surface size signal — 獨立於 FieldContext + FieldSurfaceContext 的純視覺 size context(2026-06-08)。\n * 讓 host(DataTable cell substrate)把「這片 surface 的 density size」propagate 給 child Field controls,\n * 未來新 cell 漏傳 size prop 也自動繼承 → 字級一致(根治 StringCell/NumberCell 漏傳 size class)。\n *\n * 不放進 FieldSurfaceContext value(避免 string→object identity drift on scroll → 全 cell 重渲,\n * 同 TableScrollContext L119-121 canonical)— 獨立 Context,value = FieldSize primitive(stable when unchanged)。\n * 絕不污染 FieldContext:不碰 hasFieldWrapper/mode/invalid/disabled,useFieldContext() 在 cell 內仍 null。\n */\nconst FieldSurfaceSizeContext = React.createContext<FieldSize | null>(null)\n\n/**\n * Resolve Field control size — 控件用此 helper 取代散落的 `sizeProp ?? fieldCtx?.size ?? 'md'`。\n * 優先序:prop(caller 顯式)> FieldContext.size(真 <Field> wrapper)> surface-size(host 如 cell)> 'md'。\n * 真 <Field> 永遠勝 host surface(安全序);cell 無 Field wrapper(fieldCtx=null)→ 自動接 surface-size,\n * 漏傳 size 的新 cell 也字級一致。\n */\nexport function useResolvedFieldSize<T extends string = FieldSize>(sizeProp?: T | null, fallback?: T): T {\n const fieldCtx = React.useContext(FieldContext)\n const surfaceSize = React.useContext(FieldSurfaceSizeContext)\n // generic T(預設 FieldSize)讓非-input 控件(SegmentedControl/Rating 的 'xs'|'sm'|'md'|'lg' 超集、各自 fallback)\n // 也走同一 SSOT resolution。fieldCtx.size/surfaceSize 為 FieldSize(T 的子集)→ widen cast 安全。\n // fallback 未傳預設 'md'(input-class 控件向後相容);Rating 傳 'xs'、SegmentedControl 傳 'md'。\n return (sizeProp ?? (fieldCtx?.size as T | undefined) ?? (surfaceSize as T | undefined) ?? fallback ?? ('md' as T))\n}\n\n/**\n * Resolve Field control 的 **disabled** — 取代散落的 `disabledProp ?? fieldCtx?.disabled`(2026-06-08 SSOT)。\n * 優先序:顯式 prop > FieldContext.disabled(`<Field disabled>` / `<Field mode=\"disabled\">`)> false。\n * **caller 必傳「未預設」的原始 prop**(沒傳 = undefined),否則 `disabled=false` 預設會吃掉 `??` fallback。\n * DataTable cell 無 FieldContext(fieldCtx=null)→ 回 prop 或 false,行為與現況完全一致(inert)。\n */\nexport function useResolvedFieldDisabled(disabledProp?: boolean | null): boolean {\n const fieldCtx = React.useContext(FieldContext)\n return disabledProp ?? fieldCtx?.disabled ?? false\n}\n\n/**\n * Resolve Field control 的 **mode**(display / readonly / disabled / edit)— 2026-06-08 SSOT,統一兩派散落:\n * 舊 Input 派 `modeProp ?? fieldCtx?.mode ?? (...)` → `<Field disabled>` 時 ctx.mode 仍 'edit',漏 disabled chrome。\n * 舊 picker 派 `disabled ? 'disabled' : mode`(mode 預設 'edit')→ 完全不讀 fieldCtx.mode,`<Field mode=\"display\">` 失效。\n * 統一優先序(world-class:MUI FormControl disabled 完整 cascade + 顯式 prop 永遠最優先):\n * 1. 顯式 mode prop(caller / DataTable cell 的 displayOrDisabled)→ **永遠最優先**,故表格等顯式場景 inert\n * 2. 有效 disabled(prop 或 `<Field disabled>`)→ 'disabled'(完整 disabled chrome)\n * 3. FieldContext.mode(`<Field mode=\"display\"/\"readonly\">`)→ 讓 mode cascade 真正生效\n * 4. 本地 readOnly → 'readonly'\n * 5. 'edit'\n * cell:mode prop 必有 → step 1 命中、fieldCtx=null → 完全 inert(Δ=0)。`disabled` 傳已 resolve 的 boolean 或未預設 prop。\n */\nexport function useResolvedFieldMode({\n mode,\n disabled,\n readOnly,\n}: {\n mode?: FieldMode | null\n disabled?: boolean | null\n readOnly?: boolean\n}): FieldMode {\n const fieldCtx = React.useContext(FieldContext)\n if (mode) return mode\n if ((disabled ?? fieldCtx?.disabled) === true) return 'disabled'\n if (fieldCtx?.mode) return fieldCtx.mode\n if (readOnly) return 'readonly'\n return 'edit'\n}\n\n/**\n * Resolve Field control 的 **variant**(default / naked 視覺外殼;`bare` 2026-07-09 退役)— 2026-06-08 SSOT。\n * 優先序:顯式 prop > FieldContext.variant > 'default'。與既有各控件公式一字不差(Δ=0),統一以利 gate 強制。\n * 2026-07-14 API 策展 E:入參 / 回傳 widen 為 FieldVariantInternal — naked 僅 DataTable cell-registry\n * 經 @internal 通道傳入(公開 prop 型別 = FieldVariant,不含 naked)。\n */\nexport function useResolvedFieldVariant(variantProp?: FieldVariantInternal | null): FieldVariantInternal {\n const fieldCtx = React.useContext(FieldContext)\n return variantProp ?? fieldCtx?.variant ?? 'default'\n}\n\n/**\n * Resolve Field control 的 **error/invalid** — 取代散落的 `errorProp || (fieldCtx?.invalid ?? false)`(2026-06-08 SSOT)。\n * 自身 error prop **或** FieldContext.invalid(`<Field invalid>`)任一為真即 error。與既有公式一致(Δ=0)。\n */\nexport function useResolvedFieldInvalid(errorProp?: boolean): boolean {\n const fieldCtx = React.useContext(FieldContext)\n return Boolean(errorProp) || (fieldCtx?.invalid ?? false)\n}\n\n/**\n * Host(non-Field-wrapper,如 DataTable cell)注 surface density size 給 child Field controls。\n * 僅注 size 視覺訊號,不污染 FieldContext。Usage:cell-registry buildCellWithSurface。\n */\nexport function FieldSurfaceSizeProvider({\n size,\n children,\n}: {\n size: FieldSize\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(FieldSurfaceSizeContext.Provider, { value: size }, children)\n}\n\n/**\n * Table-cell 可編輯訊號(2026-07-08 user 拍板)— 獨立於 FieldSurfaceContext / FieldSurfaceSizeContext\n * 的純 boolean context,由 host(DataTable cell registry)注入該 cell 是否可編輯,讓 useFieldEmptyDisplay\n * 分流「可編輯 cell 空 display → 空白」vs「不可編輯 cell 空 display → '-'」。\n * value = boolean primitive(stable when unchanged),不破壞 cell memo identity(同 TableScrollContext /\n * FieldSurfaceSizeContext L119-121 canonical);絕不污染 FieldContext(useFieldContext() 在 cell 內仍 null)。\n */\nconst FieldSurfaceEditableContext = React.createContext<boolean>(false)\n\nexport function FieldSurfaceEditableProvider({\n isEditable,\n children,\n}: {\n isEditable: boolean\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(FieldSurfaceEditableContext.Provider, { value: isEditable }, children)\n}\n\n/**\n * 空值顯示分流(2026-07-08 user 拍板,verbatim「...都用\"-\"...我從頭到尾哪裡有說要用全形的」):\n *\n * | 情境 | 空值顯示 |\n * |------------------------------------------------|----------|\n * | table-cell **可編輯** 的 display 靜止態 | **空白 `''`**(不佔位,affordance = hover outline)|\n * | table-cell **不可編輯**(readonly cell) | **半形 `-`** |\n * | standalone display / readonly / form / toolbar | **半形 `-`** |\n *\n * 收斂式:`surface==='table-cell' && isEditable ? '' : EMPTY_DISPLAY`。\n * 可編輯 form / edit 輸入框走 native placeholder(不經此 hook)。boolean → unchecked / disabled →\n * 同上文字 + text-fg-disabled(M24),各控件自理。\n * 世界級對照:table-cell blank = MUI X / AG Grid / Ant core / Notion / Airtable grid 域共識;\n * 非 table `-` = Ant ProTable `columnEmptyText`(見 field-wrapper.tsx EMPTY_DISPLAY 註)。\n * SSOT 條文 → field-controls.spec.md「null / undefined 值」;全 Field family display/readonly/disabled\n * 空值渲染必經此 hook,禁直接引 EMPTY_DISPLAY 常數(genre 分流會漏)。\n */\nexport function useFieldEmptyDisplay(): string {\n const surface = useFieldSurface()\n const isEditable = React.useContext(FieldSurfaceEditableContext)\n return surface === 'table-cell' && isEditable ? '' : EMPTY_DISPLAY\n}\n\n/**\n * 空值符號「-」顏色分流(2026-07-09 user 拍板 verbatim「「-」代表的是不可編輯只拿來供檢視的值\n * 所以應該跟readonly 的value同樣顏色吧」):\n *\n * 「-」是「不可編輯、供檢視的值」→ 同 readonly value 色 = `text-foreground`\n * (field-wrapper.tsx base `text-foreground`),**非** `text-fg-muted`(muted 是「可編輯 placeholder\n * 提示」的裝飾語意;空值符號不是提示、是被檢視的值狀態)。\n * disabled 態維持 `text-fg-disabled`(M24 disabled 顯著性 > foreground/muted,不可被蓋)。\n *\n * SSOT:全 Field family display/readonly/disabled 空值 span 消費此 helper,傳入已 resolve 的\n * resolvedMode。純函式(非 hook)—— resolvedMode 已由 useResolvedFieldMode 解析(含控件自身\n * disabled prop,context hook 讀不到);故以 resolvedMode 為入參,可條件呼叫、不受 Rules of Hooks 限制。\n * 世界級對照:Ant read-only / Carbon read-only value = 正常前景色;placeholder 才 muted。\n */\nexport function fieldEmptyColorClass(resolvedMode: FieldMode): string {\n return resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-foreground'\n}\n"],"names":[],"mappings":";;AAiDO,MAAM,eAAe,MAAM,cAAwC,IAAI;AAGvE,SAAS,kBAA4C;AAC1D,SAAO,MAAM,WAAW,YAAY;AACtC;AAcA,MAAM,sBAAsB,MAAM,cAAmC,IAAI;AAYlE,SAAS,kBAAgC;AAC9C,QAAM,aAAa,MAAM,WAAW,mBAAmB;AACvD,MAAI,WAAY,QAAO;AACvB,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,UAAO,qCAAU,YAAW;AAC9B;AAcO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,oBAAoB,UAAU,EAAE,OAAO,QAAA,GAAW,QAAQ;AACvF;AAgBA,MAAM,qBAAqB,MAAM,cAAuB,KAAK;AAEtD,SAAS,sBAA+B;AAC7C,SAAO,MAAM,WAAW,kBAAkB;AAC5C;AAEO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,mBAAmB,UAAU,EAAE,OAAO,YAAA,GAAe,QAAQ;AAC1F;AAWA,MAAM,0BAA0B,MAAM,cAAgC,IAAI;AAQnE,SAAS,qBAAmD,UAAqB,UAAiB;AACvG,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,QAAM,cAAc,MAAM,WAAW,uBAAuB;AAI5D,SAAQ,aAAa,qCAAU,SAA2B,eAAiC,YAAa;AAC1G;AAQO,SAAS,yBAAyB,cAAwC;AAC/E,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,SAAO,iBAAgB,qCAAU,aAAY;AAC/C;AAcO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,GAIc;AACZ,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,MAAI,KAAM,QAAO;AACjB,OAAK,aAAY,qCAAU,eAAc,KAAM,QAAO;AACtD,MAAI,qCAAU,KAAM,QAAO,SAAS;AACpC,MAAI,SAAU,QAAO;AACrB,SAAO;AACT;AAQO,SAAS,wBAAwB,aAAiE;AACvG,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,SAAO,gBAAe,qCAAU,YAAW;AAC7C;AAMO,SAAS,wBAAwB,WAA8B;AACpE,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,SAAO,QAAQ,SAAS,OAAM,qCAAU,YAAW;AACrD;AAMO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,wBAAwB,UAAU,EAAE,OAAO,KAAA,GAAQ,QAAQ;AACxF;AASA,MAAM,8BAA8B,MAAM,cAAuB,KAAK;AAE/D,SAAS,6BAA6B;AAAA,EAC3C;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,4BAA4B,UAAU,EAAE,OAAO,WAAA,GAAc,QAAQ;AAClG;AAmBO,SAAS,uBAA+B;AAC7C,QAAM,UAAU,gBAAA;AAChB,QAAM,aAAa,MAAM,WAAW,2BAA2B;AAC/D,SAAO,YAAY,gBAAgB,aAAa,KAAK;AACvD;AAgBO,SAAS,qBAAqB,cAAiC;AACpE,SAAO,iBAAiB,aAAa,qBAAqB;AAC5D;"}
1
+ {"version":3,"file":"field-context.js","sources":["../../../src/components/Field/field-context.ts"],"sourcesContent":["import * as React from 'react'\nimport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from './field-types'\nimport { EMPTY_DISPLAY } from './field-wrapper'\n\n// ── Types ──\nexport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth }\nexport type FieldOrientation = 'vertical' | 'horizontal'\nexport type FieldSize = 'sm' | 'md' | 'lg'\nexport type FieldControlLayout = 'inline' | 'block'\n\n/**\n * Field surface(2026-05-12 codex Q2 verdict — Stream C 4-issues Cluster B fix):\n * 替代散落的 `variant === 'naked'` 判 cell heuristic,讓 Field family components\n * 顯式 query「我在哪 surface」derive cell-specific padding / alignment / overflow。\n *\n * - `'form'`(default)— Standalone Field control(對齊 Polaris / Material Form idiom)\n * - `'toolbar'`— Toolbar / Action bar inline Field(future,目前無 consumer)\n * - `'table-cell'`— DataTable cell-as-input substrate(取代 variant='naked' cell-detection\n * heuristic;canonical metrics 由 surface 推導,不再 hardcode in Field consumer)\n * - `'inline-edit'`(2026-07-17)— InlineEdit view 委派 surface。**唯一語義 = 委派控件 view 態\n * 左 px 統一 `--field-px`(不繼承 tagPadding / avatar inset / bare-span 0px)**,讓 InlineEdit\n * 固定 `-mx-[--field-px]` 對齊盒精準抵銷 → 值/tag/avatar 左緣落 label 左緣(x=0)。edit 態**不套**\n * 本 surface(edit 完全繼承原 field,含 tagPadding — Jira 式)。對齊 Atlassian inline-edit\n * read-view「fixed negative margin + read=edit geometry by construction」。只 InlineEdit display\n * box 注入;DataTable cell(table-cell)/ 純表單(form)的 tagPadding + bare-span 完全不受影響。\n */\ntype FieldSurface = 'form' | 'toolbar' | 'table-cell' | 'inline-edit'\n\n// ── Context ──\nexport interface FieldContextValue {\n id: string\n /** a11y(2026-04-25):FieldLabel 渲染時若元素為 div-based role(combobox/slider\n * 等非 native form control),`<label for>` 無效,需 aria-labelledby 指向 labelId。\n * FieldLabel 自動設 id={labelId},下游 control 可讀此值寫入 aria-labelledby。 */\n labelId: string\n descriptionId: string\n errorId: string\n mode: FieldMode\n /** 視覺外殼透傳(2026-05-05)。default = 含 border+bg;naked = cell-as-input(edit×naked 自畫 border-based state machine;view×naked 用 transparent border 由 host cell 供邊框);`bare` 2026-07-09 退役;naked 2026-07-14 型別收窄至\n * FieldVariantInternal(@internal)— 公開 `<Field variant>` 只收 default,故本欄位維持 FieldVariant。\n * child Field control 自動繼承,per-control prop override 可覆寫。詳 field-types.ts。 */\n variant: FieldVariant\n disabled: boolean\n required: boolean\n invalid: boolean\n size: FieldSize\n orientation: FieldOrientation\n controlLayout: FieldControlLayout\n hasFieldWrapper: true\n /** 2026-05-12 Stream C Cluster B fix:surface 上下文(form / toolbar / table-cell)\n * 從 host(Field wrapper / DataTable cell registry)propagate 給 child controls。\n * Optional — 未設視同 `'form'`(backward compat)。 */\n surface?: FieldSurface\n}\n\nexport const FieldContext = React.createContext<FieldContextValue | null>(null)\n\n/** 讓 primitive(Checkbox/Switch/Radio/Button/Input 等)讀 Field context */\nexport function useFieldContext(): FieldContextValue | null {\n return React.useContext(FieldContext)\n}\n\n/**\n * Surface context — **獨立於 FieldContext** 的 surface signal(2026-05-12 v2 fix)。\n *\n * 原 v1 設計(`FieldSurfaceProvider` 透過 FieldContext.Provider 注入)誤把 stub\n * `hasFieldWrapper: true` 傳給沒有真正 Field wrapper 的場景(例 DataTable cell 內 standalone\n * DatePicker),導致 cells 誤以為有 Field 父 → 視覺 regression(2026-05-12 visual-audit 抓 3\n * stories 超過 baseline budget:DatePicker range-picker / FileViewer open-snapshot /\n * DataTable pinned-columns)。\n *\n * v2 修:**完全分離** FieldSurfaceContext;FieldContext 不被污染;non-FieldWrapper hosts\n * 只能影響 surface signal,不能假冒 Field wrapper 身份。\n */\nconst FieldSurfaceContext = React.createContext<FieldSurface | null>(null)\n\n/**\n * Helper: 取當前 surface,fall back 'form'。\n *\n * Stream C Cluster B canonical entry point — Field family controls 用此 helper 替代\n * 散落的 `variant === 'naked'` cell heuristic。對齊 codex Q2 verdict「surface context」\n * canonical(替代「inCellContext per-prop」反 pattern)。\n *\n * 優先順序:FieldSurfaceContext(host-provided) → FieldContext.surface(Field-wrapper-provided)\n * → fallback 'form'。\n */\nexport function useFieldSurface(): FieldSurface {\n const surfaceCtx = React.useContext(FieldSurfaceContext)\n if (surfaceCtx) return surfaceCtx\n const fieldCtx = React.useContext(FieldContext)\n return fieldCtx?.surface ?? 'form'\n}\n\n/**\n * Standalone FieldSurface provider — 給 host(non-Field-wrapper, 如 DataTable cell)\n * **僅**設 surface signal,**不**污染 FieldContext。\n *\n * Usage(per cell-registry.tsx,2026-05-12):\n * <FieldSurfaceProvider surface=\"table-cell\"><Field control /></FieldSurfaceProvider>\n *\n * 與 FieldContext.Provider 差別:\n * - FieldContext.Provider — 完整 Field wrapper 用,provides mode/variant/disabled/...\n * - FieldSurfaceProvider — host **僅**注 surface signal,Field controls 透過\n * `useFieldSurface()` 讀;`useFieldContext()` 仍 returns null(沒被假冒)。\n */\nexport function FieldSurfaceProvider({\n surface,\n children,\n}: {\n surface: FieldSurface\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(FieldSurfaceContext.Provider, { value: surface }, children)\n}\n\n/**\n * 2026-05-13 (c) scroll-defer perf SSOT(per codex DataTable perf brief V3 + user 拍板\n * 「Roadmap still >50ms 進 (c)」):\n * Host(DataTable)在 virtualizer.isScrolling=true 期間 wrap children with `TableScrollProvider`,\n * 重 cell 組件(Avatar HoverCard / PeoplePicker / etc.)讀此 signal 跳過昂貴 subtree(如 HoverCard\n * Portal + useDocumentTheme observer),scroll 結束 unset → 重渲染為完整 affordance。\n *\n * 對齊 codex Profile Plan step 6(Scripting vs Layout 判分):scrolling 期間 cell 渲染壓縮 →\n * 240ms/frame catastrophe → close to 50ms target。世界級對齊:AG Grid `deferRender` for slow\n * React cell components / MUI X DataGrid scroll-defer hover affordance。\n *\n * 不放在 FieldSurfaceContext 內(避免 value object identity drift on scroll → 全 cell 重渲)\n * — 用獨立 Context,value = boolean primitive(stable when unchanged)。\n */\nconst TableScrollContext = React.createContext<boolean>(false)\n\nexport function useTableIsScrolling(): boolean {\n return React.useContext(TableScrollContext)\n}\n\nexport function TableScrollProvider({\n isScrolling,\n children,\n}: {\n isScrolling: boolean\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(TableScrollContext.Provider, { value: isScrolling }, children)\n}\n\n/**\n * Surface size signal — 獨立於 FieldContext + FieldSurfaceContext 的純視覺 size context(2026-06-08)。\n * 讓 host(DataTable cell substrate)把「這片 surface 的 density size」propagate 給 child Field controls,\n * 未來新 cell 漏傳 size prop 也自動繼承 → 字級一致(根治 StringCell/NumberCell 漏傳 size class)。\n *\n * 不放進 FieldSurfaceContext value(避免 string→object identity drift on scroll → 全 cell 重渲,\n * 同 TableScrollContext L119-121 canonical)— 獨立 Context,value = FieldSize primitive(stable when unchanged)。\n * 絕不污染 FieldContext:不碰 hasFieldWrapper/mode/invalid/disabled,useFieldContext() 在 cell 內仍 null。\n */\nconst FieldSurfaceSizeContext = React.createContext<FieldSize | null>(null)\n\n/**\n * Resolve Field control size — 控件用此 helper 取代散落的 `sizeProp ?? fieldCtx?.size ?? 'md'`。\n * 優先序:prop(caller 顯式)> FieldContext.size(真 <Field> wrapper)> surface-size(host 如 cell)> 'md'。\n * 真 <Field> 永遠勝 host surface(安全序);cell 無 Field wrapper(fieldCtx=null)→ 自動接 surface-size,\n * 漏傳 size 的新 cell 也字級一致。\n */\nexport function useResolvedFieldSize<T extends string = FieldSize>(sizeProp?: T | null, fallback?: T): T {\n const fieldCtx = React.useContext(FieldContext)\n const surfaceSize = React.useContext(FieldSurfaceSizeContext)\n // generic T(預設 FieldSize)讓非-input 控件(SegmentedControl/Rating 的 'xs'|'sm'|'md'|'lg' 超集、各自 fallback)\n // 也走同一 SSOT resolution。fieldCtx.size/surfaceSize 為 FieldSize(T 的子集)→ widen cast 安全。\n // fallback 未傳預設 'md'(input-class 控件向後相容);Rating 傳 'xs'、SegmentedControl 傳 'md'。\n return (sizeProp ?? (fieldCtx?.size as T | undefined) ?? (surfaceSize as T | undefined) ?? fallback ?? ('md' as T))\n}\n\n/**\n * Resolve Field control 的 **disabled** — 取代散落的 `disabledProp ?? fieldCtx?.disabled`(2026-06-08 SSOT)。\n * 優先序:顯式 prop > FieldContext.disabled(`<Field disabled>` / `<Field mode=\"disabled\">`)> false。\n * **caller 必傳「未預設」的原始 prop**(沒傳 = undefined),否則 `disabled=false` 預設會吃掉 `??` fallback。\n * DataTable cell 無 FieldContext(fieldCtx=null)→ 回 prop 或 false,行為與現況完全一致(inert)。\n */\nexport function useResolvedFieldDisabled(disabledProp?: boolean | null): boolean {\n const fieldCtx = React.useContext(FieldContext)\n return disabledProp ?? fieldCtx?.disabled ?? false\n}\n\n/**\n * Resolve Field control 的 **mode**(view / readonly / disabled / edit)— 2026-06-08 SSOT,統一兩派散落:\n * 舊 Input 派 `modeProp ?? fieldCtx?.mode ?? (...)` → `<Field disabled>` 時 ctx.mode 仍 'edit',漏 disabled chrome。\n * 舊 picker 派 `disabled ? 'disabled' : mode`(mode 預設 'edit')→ 完全不讀 fieldCtx.mode,`<Field mode=\"view\">` 失效。\n * 統一優先序(world-class:MUI FormControl disabled 完整 cascade + 顯式 prop 永遠最優先):\n * 1. 顯式 mode prop(caller / DataTable cell 傳 mode=\"view\")→ **永遠最優先**,故表格等顯式場景 inert\n * 2. 有效 disabled(prop 或 `<Field disabled>`)→ 'disabled'(完整 disabled chrome)\n * 3. FieldContext.mode(`<Field mode=\"view\"/\"readonly\">`)→ 讓 mode cascade 真正生效\n * 4. 本地 readOnly → 'readonly'\n * 5. 'edit'\n * cell:mode prop 必有 → step 1 命中、fieldCtx=null → 完全 inert(Δ=0)。`disabled` 傳已 resolve 的 boolean 或未預設 prop。\n */\nexport function useResolvedFieldMode({\n mode,\n disabled,\n readOnly,\n}: {\n mode?: FieldMode | null\n disabled?: boolean | null\n readOnly?: boolean\n}): FieldMode {\n const fieldCtx = React.useContext(FieldContext)\n if (mode) return mode\n if ((disabled ?? fieldCtx?.disabled) === true) return 'disabled'\n if (fieldCtx?.mode) return fieldCtx.mode\n if (readOnly) return 'readonly'\n return 'edit'\n}\n\n/**\n * Resolve Field control 的 **variant**(default / naked 視覺外殼;`bare` 2026-07-09 退役)— 2026-06-08 SSOT。\n * 優先序:顯式 prop > FieldContext.variant > 'default'。與既有各控件公式一字不差(Δ=0),統一以利 gate 強制。\n * 2026-07-14 API 策展 E:入參 / 回傳 widen 為 FieldVariantInternal — naked 僅 DataTable cell-registry\n * 經 @internal 通道傳入(公開 prop 型別 = FieldVariant,不含 naked)。\n */\nexport function useResolvedFieldVariant(variantProp?: FieldVariantInternal | null): FieldVariantInternal {\n const fieldCtx = React.useContext(FieldContext)\n return variantProp ?? fieldCtx?.variant ?? 'default'\n}\n\n/**\n * Resolve Field control 的 **error/invalid** — 取代散落的 `errorProp || (fieldCtx?.invalid ?? false)`(2026-06-08 SSOT)。\n * 自身 error prop **或** FieldContext.invalid(`<Field invalid>`)任一為真即 error。與既有公式一致(Δ=0)。\n */\nexport function useResolvedFieldInvalid(errorProp?: boolean): boolean {\n const fieldCtx = React.useContext(FieldContext)\n return Boolean(errorProp) || (fieldCtx?.invalid ?? false)\n}\n\n/**\n * Host(non-Field-wrapper,如 DataTable cell)注 surface density size 給 child Field controls。\n * 僅注 size 視覺訊號,不污染 FieldContext。Usage:cell-registry buildCellWithSurface。\n */\nexport function FieldSurfaceSizeProvider({\n size,\n children,\n}: {\n size: FieldSize\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(FieldSurfaceSizeContext.Provider, { value: size }, children)\n}\n\n/**\n * Table-cell 可編輯訊號(2026-07-08 user 拍板)— 獨立於 FieldSurfaceContext / FieldSurfaceSizeContext\n * 的純 boolean context,由 host(DataTable cell registry)注入該 cell 是否可編輯,讓 useFieldEmptyDisplay\n * 分流「可編輯 cell 空 view → 空白」vs「不可編輯 cell 空 view → '-'」。\n * value = boolean primitive(stable when unchanged),不破壞 cell memo identity(同 TableScrollContext /\n * FieldSurfaceSizeContext L119-121 canonical);絕不污染 FieldContext(useFieldContext() 在 cell 內仍 null)。\n */\nconst FieldSurfaceEditableContext = React.createContext<boolean>(false)\n\nexport function FieldSurfaceEditableProvider({\n isEditable,\n children,\n}: {\n isEditable: boolean\n children: React.ReactNode\n}): React.ReactElement {\n return React.createElement(FieldSurfaceEditableContext.Provider, { value: isEditable }, children)\n}\n\n/**\n * 空值顯示分流(2026-07-08 user 拍板,verbatim「...都用\"-\"...我從頭到尾哪裡有說要用全形的」):\n *\n * | 情境 | 空值顯示 |\n * |------------------------------------------------|----------|\n * | table-cell **可編輯** 的 view 靜止態 | **空白 `''`**(不佔位,affordance = hover outline)|\n * | table-cell **不可編輯**(readonly cell) | **半形 `-`** |\n * | standalone view / readonly / form / toolbar | **半形 `-`** |\n *\n * 收斂式:`surface==='table-cell' && isEditable ? '' : EMPTY_DISPLAY`。\n * 可編輯 form / edit 輸入框走 native placeholder(不經此 hook)。boolean → unchecked / disabled →\n * 同上文字 + text-fg-disabled(M24),各控件自理。\n * 世界級對照:table-cell blank = MUI X / AG Grid / Ant core / Notion / Airtable grid 域共識;\n * 非 table `-` = Ant ProTable `columnEmptyText`(見 field-wrapper.tsx EMPTY_DISPLAY 註)。\n * SSOT 條文 → field-controls.spec.md「null / undefined 值」;全 Field family view/readonly/disabled\n * 空值渲染必經此 hook,禁直接引 EMPTY_DISPLAY 常數(genre 分流會漏)。\n */\nexport function useFieldEmptyDisplay(): string {\n const surface = useFieldSurface()\n const isEditable = React.useContext(FieldSurfaceEditableContext)\n return surface === 'table-cell' && isEditable ? '' : EMPTY_DISPLAY\n}\n\n/**\n * 空值符號「-」顏色分流(2026-07-09 user 拍板 verbatim「「-」代表的是不可編輯只拿來供檢視的值\n * 所以應該跟readonly 的value同樣顏色吧」):\n *\n * 「-」是「不可編輯、供檢視的值」→ 同 readonly value 色 = `text-foreground`\n * (field-wrapper.tsx base `text-foreground`),**非** `text-fg-muted`(muted 是「可編輯 placeholder\n * 提示」的裝飾語意;空值符號不是提示、是被檢視的值狀態)。\n * disabled 態維持 `text-fg-disabled`(M24 disabled 顯著性 > foreground/muted,不可被蓋)。\n *\n * SSOT:全 Field family view/readonly/disabled 空值 span 消費此 helper,傳入已 resolve 的\n * resolvedMode。純函式(非 hook)—— resolvedMode 已由 useResolvedFieldMode 解析(含控件自身\n * disabled prop,context hook 讀不到);故以 resolvedMode 為入參,可條件呼叫、不受 Rules of Hooks 限制。\n * 世界級對照:Ant read-only / Carbon read-only value = 正常前景色;placeholder 才 muted。\n */\nexport function fieldEmptyColorClass(resolvedMode: FieldMode): string {\n return resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-foreground'\n}\n"],"names":[],"mappings":";;AAuDO,MAAM,eAAe,MAAM,cAAwC,IAAI;AAGvE,SAAS,kBAA4C;AAC1D,SAAO,MAAM,WAAW,YAAY;AACtC;AAcA,MAAM,sBAAsB,MAAM,cAAmC,IAAI;AAYlE,SAAS,kBAAgC;AAC9C,QAAM,aAAa,MAAM,WAAW,mBAAmB;AACvD,MAAI,WAAY,QAAO;AACvB,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,UAAO,qCAAU,YAAW;AAC9B;AAcO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,oBAAoB,UAAU,EAAE,OAAO,QAAA,GAAW,QAAQ;AACvF;AAgBA,MAAM,qBAAqB,MAAM,cAAuB,KAAK;AAEtD,SAAS,sBAA+B;AAC7C,SAAO,MAAM,WAAW,kBAAkB;AAC5C;AAEO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,mBAAmB,UAAU,EAAE,OAAO,YAAA,GAAe,QAAQ;AAC1F;AAWA,MAAM,0BAA0B,MAAM,cAAgC,IAAI;AAQnE,SAAS,qBAAmD,UAAqB,UAAiB;AACvG,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,QAAM,cAAc,MAAM,WAAW,uBAAuB;AAI5D,SAAQ,aAAa,qCAAU,SAA2B,eAAiC,YAAa;AAC1G;AAQO,SAAS,yBAAyB,cAAwC;AAC/E,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,SAAO,iBAAgB,qCAAU,aAAY;AAC/C;AAcO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,GAIc;AACZ,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,MAAI,KAAM,QAAO;AACjB,OAAK,aAAY,qCAAU,eAAc,KAAM,QAAO;AACtD,MAAI,qCAAU,KAAM,QAAO,SAAS;AACpC,MAAI,SAAU,QAAO;AACrB,SAAO;AACT;AAQO,SAAS,wBAAwB,aAAiE;AACvG,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,SAAO,gBAAe,qCAAU,YAAW;AAC7C;AAMO,SAAS,wBAAwB,WAA8B;AACpE,QAAM,WAAW,MAAM,WAAW,YAAY;AAC9C,SAAO,QAAQ,SAAS,OAAM,qCAAU,YAAW;AACrD;AAMO,SAAS,yBAAyB;AAAA,EACvC;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,wBAAwB,UAAU,EAAE,OAAO,KAAA,GAAQ,QAAQ;AACxF;AASA,MAAM,8BAA8B,MAAM,cAAuB,KAAK;AAE/D,SAAS,6BAA6B;AAAA,EAC3C;AAAA,EACA;AACF,GAGuB;AACrB,SAAO,MAAM,cAAc,4BAA4B,UAAU,EAAE,OAAO,WAAA,GAAc,QAAQ;AAClG;AAmBO,SAAS,uBAA+B;AAC7C,QAAM,UAAU,gBAAA;AAChB,QAAM,aAAa,MAAM,WAAW,2BAA2B;AAC/D,SAAO,YAAY,gBAAgB,aAAa,KAAK;AACvD;AAgBO,SAAS,qBAAqB,cAAiC;AACpE,SAAO,iBAAiB,aAAa,qBAAqB;AAC5D;"}
@@ -1,4 +1,4 @@
1
- export type FieldMode = 'edit' | 'display' | 'readonly' | 'disabled';
1
+ export type FieldMode = 'edit' | 'view' | 'readonly' | 'disabled';
2
2
  export type FieldVariant = 'default';
3
3
  /**
4
4
  * @internal Substrate union — 公開 FieldVariant + `naked`(cell-as-input)。
@@ -19,6 +19,6 @@ export type WithFieldVariantInternal<C> = C extends (props: infer P) => infer R
19
19
  variant?: FieldVariantInternal;
20
20
  }) => R) & Pick<C, keyof C> : never;
21
21
  export type FieldWidth = 'fill' | 'hug';
22
- /** CommandList 最小高度 = field-height × rows + 16px(CommandGroup py-2 上下 padding */
22
+ /** @internal — CommandList 最小高度 = field-height × rows + 16px(CommandGroup py-2 上下 padding);Command/Select 內部 helper,consumer 不直接 import。root barrel 排除(subpath 仍可用)。 */
23
23
  export declare function getMenuListMinHeight(size: string, rows?: number): string;
24
24
  //# sourceMappingURL=field-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../src/components/Field/field-types.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;AA6BpE,MAAM,MAAM,YAAY,GAAG,SAAS,CAAA;AAEpC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,OAAO,CAAA;AAEzD;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,GAC3E,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,oBAAoB,CAAA;CAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAC1F,KAAK,CAAA;AAgBT,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,CAAA;AAYvC,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,MAAM,CAG3E"}
1
+ {"version":3,"file":"field-types.d.ts","sourceRoot":"","sources":["../../../src/components/Field/field-types.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAA;AA6BjE,MAAM,MAAM,YAAY,GAAG,SAAS,CAAA;AAEpC;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,OAAO,CAAA;AAEzD;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,GAC3E,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,oBAAoB,CAAA;CAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAC1F,KAAK,CAAA;AAgBT,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,CAAA;AAYvC,wKAAwK;AACxK,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU,GAAG,MAAM,CAG3E"}
@@ -1 +1 @@
1
- {"version":3,"file":"field-types.js","sources":["../../../src/components/Field/field-types.ts"],"sourcesContent":["// ── Field Mode ───────────────────────────────────────────────────────────────\n//\n// 4 模式 canonical(2026-05-05 expand to 4):\n// edit — 一般可編輯 input(預設 variant:border + bg)\n// display **純展示**( input chrome / 無互動 affordance);語意「這是 read-only 內容,展示給人看」\n// 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode\n// readonly — input chrome + non-editable(保留 underline / border subtle 給 a11y signal「這是 input 但鎖了」)\n// 對齊 Carbon read-only-with-underline。差異:`display` 完全無 chrome;`readonly` 保留 input affordance signal\n// disabled — input chrome + disabled state(灰底,不可互動,語意「不適用」)\n//\n// `display` vs `readonly` 判別:\n// - 該位置語意上是「純展示資料」(DataTable cell read mode / ProfileCard meta) → `display`\n// - 該位置是「表單欄位但目前不可改」(form 鎖部分欄位) → `readonly`\n//\n// World-class refs(M22 verified):\n// Carbon: https://carbondesignsystem.com/patterns/read-only-states-pattern/\n// PatternFly: https://www.patternfly.org/components/inline-edit/design-guidelines/\n// Cloudscape: https://cloudscape.design/patterns/general/disabled-and-read-only-states/\nexport type FieldMode = 'edit' | 'display' | 'readonly' | 'disabled'\n\n// ── Field Variant ────────────────────────────────────────────────────────────\n//\n// 視覺外殼(公開 1 + internal 1;2026-07-09 user 拍板退役 `bare`,2026-07-14 API 策展 E user 拍板\n// 「全部收窄」把 naked 從公開 union 拆出 — 見下方 note):\n// default — 含 border + bg(一般 form input,= Ant outlined / MUI outlined)。公開 FieldVariant 唯一值\n// naked — @internal(FieldVariantInternal)。cell-as-input(host cell substrate)。edit×naked 自畫\n// border-based state machine(rest → hover → focus-within:border-primary → error 紅框);\n// display/readonly/disabled×naked 用 transparent border,由 host cell 提供視覺邊框。\n// 對齊 Airtable / Notion / Excel cell editing。唯一合法消費者 = DataTable cell-registry\n// (field-controls.spec.md「軸二 variant」明文)— consumer 直傳 `<Input variant=\"naked\">`\n// 會繞過 canonical chrome / focus,故公開型別排除\n//\n// **`bare` 退役紀錄(2026-07-09)**:原「透明外殼、hover/focus 才顯 border」variant,2026-05-05 加入時\n// 宣稱用途 = toolbar inline editing(FileViewer zoom / config toolbar)。實測全 repo(DS + apps + stories)\n// `variant=\"bare\"` **零真實 JSX 消費者**:唯一出現處是一個 ❌ 反例 story + 過時註解(含錯誤宣稱 FileViewer\n// 縮放用 bare — 實為 `<Input size=\"sm\" autoWidth>` default variant)。其目標場景已被 default(toolbar,\n// 對齊「邊框給互動元素、輸入框就該有邊框」原則)與 naked(cell-as-input)覆蓋 → 純冗餘,移除。\n// ⚠️ 注意:被大量 import 的 `bareInputStyles` 常數(field-wrapper.tsx)是**內層 <input> 的重置樣式**\n// (透明底 / 無 border / 繼承字體),與本 variant 同名但不同物,**保留不動**(8 個控件共用)。\n//\n// 透傳機制:Field 透過 FieldContext.variant 一次宣告,所有 child Field control 自動繼承。\n// per-control prop override 可覆寫 context。\n//\n// Benchmark(M22 verified 2026-07-09):Ant `Input variant` 有 borderless(https://ant.design/components/input/)\n// → 「無邊框輸入框」概念世界級合法;但 Material M3 只有 filled/outlined 無無邊框變體\n// (https://m3.material.io/components/text-fields/guidelines),MUI standard(underline)已 deprecated。\n// 我們的取捨:無真實消費場景 → 不保留純預留 variant(對齊自家「消費既有、不憑空造」原則)。\nexport type FieldVariant = 'default'\n\n/**\n * @internal Substrate union — 公開 FieldVariant + `naked`(cell-as-input)。\n * 2026-07-14 API 策展 E(user 拍板「全部收窄」):naked 從公開 union 拆出,型別層防 consumer\n * `<Input variant=\"naked\">` 繞過 canonical chrome / focus。內部實作(field-wrapper cva、各控件\n * useResolvedFieldVariant 分支)吃本型別;唯一合法 naked 傳入點 = DataTable cell-registry\n * (field-controls.spec.md「軸二 variant」明文)。\n */\nexport type FieldVariantInternal = FieldVariant | 'naked'\n\n/**\n * @internal 型別層 naked 通道 — 把 Field control 的公開 `variant?: FieldVariant` widen 為\n * FieldVariantInternal(純型別、零 runtime;元件 runtime 本就支援 naked)。消費者:DataTable\n * cell-registry(naked 起點)+ PeoplePicker(wrapper forward 給 Select / Combobox,M30 全 surface\n * forward)。保留原 call signature 與 statics(`Pick<C, keyof C>`),widen 方向與函式參數\n * contravariance 相容,故單次 assertion 可過(非 `as unknown as` 雙跳,型別仍受檢)。\n */\nexport type WithFieldVariantInternal<C> = C extends (props: infer P) => infer R\n ? ((props: Omit<P, 'variant'> & { variant?: FieldVariantInternal }) => R) & Pick<C, keyof C>\n : never\n\n// ── Field Width ──────────────────────────────────────────────────────────────\n//\n// 寬度軸(2026-07-08 user 拍板,正交於 mode / variant / size — 只改寬度,chrome 與互動不變):\n// fill — `w-full` 填滿容器(default;form 直欄對齊 / DataTable cell 滿格)\n// hug — `w-fit max-w-full` 依內容收縮:value 寬 + 各 slot 元素寬 + gap + field 內 padding\n// (detail pane inline metadata / toolbar 內 field 場景)\n//\n// Benchmark(M22 verified):\n// shadcn v4 SelectTrigger 預設 `w-fit`\n// https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/registry/new-york-v4/ui/select.tsx\n// Radix Select Trigger intrinsic sizing\n// https://www.radix-ui.com/primitives/docs/components/select\n//\n// SSOT:fieldWrapperStyles `width` variant(field-wrapper.tsx)+ field-controls.spec.md「寬度軸」。\nexport type FieldWidth = 'fill' | 'hug'\n\n// ── Menu List Min Height ─────────────────────────────────────────────────────\n// SelectMenu / Select / Combobox 共用的 CommandList minHeight 計算。\n// 確保空狀態有足夠高度讓 Empty 垂直置中(有框容器 → 置中原則)。\n\nconst FIELD_HEIGHT_TOKEN: Record<string, string> = {\n sm: 'var(--field-height-sm)',\n md: 'var(--field-height-md)',\n lg: 'var(--field-height-lg)',\n}\n\n/** CommandList 最小高度 = field-height × rows + 16px(CommandGroup py-2 上下 padding */\nexport function getMenuListMinHeight(size: string, rows: number = 3): string {\n const token = FIELD_HEIGHT_TOKEN[size] ?? FIELD_HEIGHT_TOKEN.md\n return `calc(${token} * ${rows} + 16px)`\n}\n\n"],"names":[],"mappings":"AAyFA,MAAM,qBAA6C;AAAA,EACjD,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAGO,SAAS,qBAAqB,MAAc,OAAe,GAAW;AAC3E,QAAM,QAAQ,mBAAmB,IAAI,KAAK,mBAAmB;AAC7D,SAAO,QAAQ,KAAK,MAAM,IAAI;AAChC;"}
1
+ {"version":3,"file":"field-types.js","sources":["../../../src/components/Field/field-types.ts"],"sourcesContent":["// ── Field Mode ───────────────────────────────────────────────────────────────\n//\n// 4 模式 canonical(2026-05-05 expand;2026-07-16 round16 `display`→`view` 更名 + Model A 幾何):\n// edit — 一般可編輯 input(預設 variant:border + bg)\n// view **純展示值(非表單)**;語意「要呈現給人看的值本體」(cell / InlineEdit / 詳情)。\n// **Model A**:view = edit 幾何減 chrome(透明 bg/border,**保留 px 內距 + 高度**) read↔edit\n// 零跳(view edit 同一顆控件、只差 chrome)。對齊 Atlassian inline-edit(read=edit 幾何)\n// + Bootstrap `.form-control-plaintext`(留 padding)。詳 field-controls.spec.md「軸一 view mode」。\n// readonly — input chrome + non-editable(bg-readonly + a11y ring「這是 input 但鎖了」);表單語境、值仍算數可選取\n// disabled — input chrome + disabled state(灰底,不可互動,不送出,語意「目前不適用」);表單專屬\n//\n// `view` vs `readonly` 判別:\n// - 「純展示值、非表單」(DataTable cell / InlineEdit view / ProfileCard meta) → `view`\n// - 「表單欄位但目前不可改、值仍送出」(form 鎖部分欄位) → `readonly`\n//\n// 命名(命名 3-test 全過):MUI X cellMode=view / Atlassian readVIEW 字根 / 與 edit 成對。\n// World-class refs(M22 verified):\n// Carbon: https://carbondesignsystem.com/patterns/read-only-states-pattern/\n// PatternFly: https://www.patternfly.org/components/inline-edit/design-guidelines/\n// Cloudscape: https://cloudscape.design/patterns/general/disabled-and-read-only-states/\nexport type FieldMode = 'edit' | 'view' | 'readonly' | 'disabled'\n\n// ── Field Variant ────────────────────────────────────────────────────────────\n//\n// 視覺外殼(公開 1 + internal 1;2026-07-09 user 拍板退役 `bare`,2026-07-14 API 策展 E user 拍板\n// 「全部收窄」把 naked 從公開 union 拆出 — 見下方 note):\n// default — 含 border + bg(一般 form input,= Ant outlined / MUI outlined)。公開 FieldVariant 唯一值\n// naked — @internal(FieldVariantInternal)。cell-as-input(host cell substrate)。edit×naked 自畫\n// border-based state machine(rest → hover → focus-within:border-primary → error 紅框);\n// view×naked 用 transparent border,由 host cell 提供視覺邊框。\n// 對齊 Airtable / Notion / Excel cell editing。唯一合法消費者 = DataTable cell-registry\n// (field-controls.spec.md「軸二 variant」明文)— consumer 直傳 `<Input variant=\"naked\">`\n// 會繞過 canonical chrome / focus,故公開型別排除\n//\n// **`bare` 退役紀錄(2026-07-09)**:原「透明外殼、hover/focus 才顯 border」variant,2026-05-05 加入時\n// 宣稱用途 = toolbar inline editing(FileViewer zoom / config toolbar)。實測全 repo(DS + apps + stories)\n// `variant=\"bare\"` **零真實 JSX 消費者**:唯一出現處是一個 ❌ 反例 story + 過時註解(含錯誤宣稱 FileViewer\n// 縮放用 bare — 實為 `<Input size=\"sm\" autoWidth>` default variant)。其目標場景已被 default(toolbar,\n// 對齊「邊框給互動元素、輸入框就該有邊框」原則)與 naked(cell-as-input)覆蓋 → 純冗餘,移除。\n// ⚠️ 注意:被大量 import 的 `bareInputStyles` 常數(field-wrapper.tsx)是**內層 <input> 的重置樣式**\n// (透明底 / 無 border / 繼承字體),與本 variant 同名但不同物,**保留不動**(8 個控件共用)。\n//\n// 透傳機制:Field 透過 FieldContext.variant 一次宣告,所有 child Field control 自動繼承。\n// per-control prop override 可覆寫 context。\n//\n// Benchmark(M22 verified 2026-07-09):Ant `Input variant` 有 borderless(https://ant.design/components/input/)\n// → 「無邊框輸入框」概念世界級合法;但 Material M3 只有 filled/outlined 無無邊框變體\n// (https://m3.material.io/components/text-fields/guidelines),MUI standard(underline)已 deprecated。\n// 我們的取捨:無真實消費場景 → 不保留純預留 variant(對齊自家「消費既有、不憑空造」原則)。\nexport type FieldVariant = 'default'\n\n/**\n * @internal Substrate union — 公開 FieldVariant + `naked`(cell-as-input)。\n * 2026-07-14 API 策展 E(user 拍板「全部收窄」):naked 從公開 union 拆出,型別層防 consumer\n * `<Input variant=\"naked\">` 繞過 canonical chrome / focus。內部實作(field-wrapper cva、各控件\n * useResolvedFieldVariant 分支)吃本型別;唯一合法 naked 傳入點 = DataTable cell-registry\n * (field-controls.spec.md「軸二 variant」明文)。\n */\nexport type FieldVariantInternal = FieldVariant | 'naked'\n\n/**\n * @internal 型別層 naked 通道 — 把 Field control 的公開 `variant?: FieldVariant` widen 為\n * FieldVariantInternal(純型別、零 runtime;元件 runtime 本就支援 naked)。消費者:DataTable\n * cell-registry(naked 起點)+ PeoplePicker(wrapper forward 給 Select / Combobox,M30 全 surface\n * forward)。保留原 call signature 與 statics(`Pick<C, keyof C>`),widen 方向與函式參數\n * contravariance 相容,故單次 assertion 可過(非 `as unknown as` 雙跳,型別仍受檢)。\n */\nexport type WithFieldVariantInternal<C> = C extends (props: infer P) => infer R\n ? ((props: Omit<P, 'variant'> & { variant?: FieldVariantInternal }) => R) & Pick<C, keyof C>\n : never\n\n// ── Field Width ──────────────────────────────────────────────────────────────\n//\n// 寬度軸(2026-07-08 user 拍板,正交於 mode / variant / size — 只改寬度,chrome 與互動不變):\n// fill — `w-full` 填滿容器(default;form 直欄對齊 / DataTable cell 滿格)\n// hug — `w-fit max-w-full` 依內容收縮:value 寬 + 各 slot 元素寬 + gap + field 內 padding\n// (detail pane inline metadata / toolbar 內 field 場景)\n//\n// Benchmark(M22 verified):\n// shadcn v4 SelectTrigger 預設 `w-fit`\n// https://raw.githubusercontent.com/shadcn-ui/ui/main/apps/v4/registry/new-york-v4/ui/select.tsx\n// Radix Select Trigger intrinsic sizing\n// https://www.radix-ui.com/primitives/docs/components/select\n//\n// SSOT:fieldWrapperStyles `width` variant(field-wrapper.tsx)+ field-controls.spec.md「寬度軸」。\nexport type FieldWidth = 'fill' | 'hug'\n\n// ── Menu List Min Height ─────────────────────────────────────────────────────\n// SelectMenu / Select / Combobox 共用的 CommandList minHeight 計算。\n// 確保空狀態有足夠高度讓 Empty 垂直置中(有框容器 → 置中原則)。\n\nconst FIELD_HEIGHT_TOKEN: Record<string, string> = {\n sm: 'var(--field-height-sm)',\n md: 'var(--field-height-md)',\n lg: 'var(--field-height-lg)',\n}\n\n/** @internal — CommandList 最小高度 = field-height × rows + 16px(CommandGroup py-2 上下 padding);Command/Select 內部 helper,consumer 不直接 import。root barrel 排除(subpath 仍可用)。 */\nexport function getMenuListMinHeight(size: string, rows: number = 3): string {\n const token = FIELD_HEIGHT_TOKEN[size] ?? FIELD_HEIGHT_TOKEN.md\n return `calc(${token} * ${rows} + 16px)`\n}\n\n"],"names":[],"mappings":"AA2FA,MAAM,qBAA6C;AAAA,EACjD,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAGO,SAAS,qBAAqB,MAAc,OAAe,GAAW;AAC3E,QAAM,QAAQ,mBAAmB,IAAI,KAAK,mBAAmB;AAC7D,SAAO,QAAQ,KAAK,MAAM,IAAI;AAChC;"}