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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (612) hide show
  1. package/CLAUDE.md +8 -8
  2. package/README.md +3 -2
  3. package/dist/components/Alert/alert.js.map +1 -1
  4. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  5. package/dist/components/AppShell/app-shell.d.ts +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  7. package/dist/components/AppShell/app-shell.js +22 -2
  8. package/dist/components/AppShell/app-shell.js.map +1 -1
  9. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  10. package/dist/components/Avatar/avatar.js +4 -4
  11. package/dist/components/Avatar/avatar.js.map +1 -1
  12. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  13. package/dist/components/Breadcrumb/breadcrumb.js +1 -1
  14. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  15. package/dist/components/Button/button.d.ts +2 -2
  16. package/dist/components/Button/button.d.ts.map +1 -1
  17. package/dist/components/Button/button.js +12 -3
  18. package/dist/components/Button/button.js.map +1 -1
  19. package/dist/components/Calendar/calendar.d.ts +13 -5
  20. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  21. package/dist/components/Calendar/calendar.js +7 -1
  22. package/dist/components/Calendar/calendar.js.map +1 -1
  23. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  24. package/dist/components/Carousel/carousel.js +8 -3
  25. package/dist/components/Carousel/carousel.js.map +1 -1
  26. package/dist/components/Chart/chart.d.ts.map +1 -1
  27. package/dist/components/Chart/chart.js +1 -0
  28. package/dist/components/Chart/chart.js.map +1 -1
  29. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  30. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  31. package/dist/components/Checkbox/checkbox.js +8 -4
  32. package/dist/components/Checkbox/checkbox.js.map +1 -1
  33. package/dist/components/Chip/chip.d.ts +1 -1
  34. package/dist/components/Chip/chip.d.ts.map +1 -1
  35. package/dist/components/Chip/chip.js.map +1 -1
  36. package/dist/components/Coachmark/coachmark.d.ts +10 -3
  37. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  38. package/dist/components/Coachmark/coachmark.js.map +1 -1
  39. package/dist/components/Combobox/combobox.d.ts +5 -1
  40. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  41. package/dist/components/Combobox/combobox.js +30 -13
  42. package/dist/components/Combobox/combobox.js.map +1 -1
  43. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  44. package/dist/components/DataTable/cell-registry.js +53 -62
  45. package/dist/components/DataTable/cell-registry.js.map +1 -1
  46. package/dist/components/DataTable/column-types.d.ts +10 -4
  47. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  48. package/dist/components/DataTable/column-types.js.map +1 -1
  49. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +1 -1
  50. package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
  51. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
  52. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  53. package/dist/components/DataTable/data-table-filter-panel.js +607 -0
  54. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  55. package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
  56. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  57. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  58. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  59. package/dist/components/DataTable/data-table-sort-manager.js +140 -0
  60. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  61. package/dist/components/DataTable/data-table.d.ts +5 -6
  62. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  63. package/dist/components/DataTable/data-table.js +36 -15
  64. package/dist/components/DataTable/data-table.js.map +1 -1
  65. package/dist/components/DataTable/filter-operators.js +140 -0
  66. package/dist/components/DataTable/filter-operators.js.map +1 -0
  67. package/dist/components/DataTable/filter-tree.js +312 -0
  68. package/dist/components/DataTable/filter-tree.js.map +1 -0
  69. package/dist/components/DataTable/index.d.ts +3 -0
  70. package/dist/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/components/DataTable/index.js +12 -1
  72. package/dist/components/DataTable/index.js.map +1 -1
  73. package/dist/components/DataTable/lib/column-meta.js +25 -0
  74. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  75. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  76. package/dist/components/DateGrid/date-grid.js +3 -1
  77. package/dist/components/DateGrid/date-grid.js.map +1 -1
  78. package/dist/components/DatePicker/date-picker.d.ts +7 -1
  79. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  80. package/dist/components/DatePicker/date-picker.js +105 -74
  81. package/dist/components/DatePicker/date-picker.js.map +1 -1
  82. package/dist/components/Dialog/dialog.d.ts +20 -3
  83. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  84. package/dist/components/Dialog/dialog.js +38 -20
  85. package/dist/components/Dialog/dialog.js.map +1 -1
  86. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  87. package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
  88. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  89. package/dist/components/Field/field-context.d.ts +45 -5
  90. package/dist/components/Field/field-context.d.ts.map +1 -1
  91. package/dist/components/Field/field-context.js +19 -0
  92. package/dist/components/Field/field-context.js.map +1 -1
  93. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  94. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  95. package/dist/components/Field/field-edit-keys.js +19 -0
  96. package/dist/components/Field/field-edit-keys.js.map +1 -0
  97. package/dist/components/Field/field-types.d.ts +20 -1
  98. package/dist/components/Field/field-types.d.ts.map +1 -1
  99. package/dist/components/Field/field-types.js.map +1 -1
  100. package/dist/components/Field/field-wrapper.d.ts +3 -2
  101. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  102. package/dist/components/Field/field-wrapper.js +38 -39
  103. package/dist/components/Field/field-wrapper.js.map +1 -1
  104. package/dist/components/Field/field.d.ts +2 -1
  105. package/dist/components/Field/field.d.ts.map +1 -1
  106. package/dist/components/Field/field.js.map +1 -1
  107. package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
  108. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  109. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  110. package/dist/components/FileItem/file-item.js +1 -1
  111. package/dist/components/FileItem/file-item.js.map +1 -1
  112. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  113. package/dist/components/FileUpload/file-upload.js.map +1 -1
  114. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  115. package/dist/components/FileViewer/file-viewer.js +7 -2
  116. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  117. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  118. package/dist/components/FileViewer/image-renderer.js +22 -1
  119. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  120. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  121. package/dist/components/HoverCard/hover-card.js +5 -3
  122. package/dist/components/HoverCard/hover-card.js.map +1 -1
  123. package/dist/components/InlineEdit/index.d.ts +2 -0
  124. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  125. package/dist/components/InlineEdit/index.js +5 -0
  126. package/dist/components/InlineEdit/index.js.map +1 -0
  127. package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
  128. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  129. package/dist/components/InlineEdit/inline-edit.js +164 -0
  130. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  131. package/dist/components/Input/input.d.ts +6 -11
  132. package/dist/components/Input/input.d.ts.map +1 -1
  133. package/dist/components/Input/input.js +43 -13
  134. package/dist/components/Input/input.js.map +1 -1
  135. package/dist/components/LinkInput/link-input.d.ts +2 -2
  136. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  137. package/dist/components/LinkInput/link-input.js +6 -5
  138. package/dist/components/LinkInput/link-input.js.map +1 -1
  139. package/dist/components/Menu/menu-item.d.ts +14 -2
  140. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  141. package/dist/components/Menu/menu-item.js +25 -3
  142. package/dist/components/Menu/menu-item.js.map +1 -1
  143. package/dist/components/NumberInput/number-input.d.ts +3 -3
  144. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  145. package/dist/components/NumberInput/number-input.js +6 -5
  146. package/dist/components/NumberInput/number-input.js.map +1 -1
  147. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  148. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
  149. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  150. package/dist/components/Pagination/pagination.js +1 -1
  151. package/dist/components/Pagination/pagination.js.map +1 -1
  152. package/dist/components/PeoplePicker/index.js +7 -1
  153. package/dist/components/PeoplePicker/index.js.map +1 -1
  154. package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
  155. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  156. package/dist/components/PeoplePicker/people-picker.js +23 -10
  157. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  158. package/dist/components/PeoplePicker/person-display.d.ts +6 -3
  159. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  160. package/dist/components/PeoplePicker/person-display.js +19 -14
  161. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  162. package/dist/components/Popover/popover.d.ts.map +1 -1
  163. package/dist/components/Popover/popover.js +3 -1
  164. package/dist/components/Popover/popover.js.map +1 -1
  165. package/dist/components/ProfileCard/profile-card.d.ts +2 -2
  166. package/dist/components/ProfileCard/profile-card.js +3 -3
  167. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  168. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  169. package/dist/components/RadioGroup/radio-group.js +15 -9
  170. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  171. package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
  172. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  173. package/dist/components/ScrollArea/scroll-area.js +6 -3
  174. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  175. package/dist/components/Select/select.d.ts +46 -5
  176. package/dist/components/Select/select.d.ts.map +1 -1
  177. package/dist/components/Select/select.js +109 -28
  178. package/dist/components/Select/select.js.map +1 -1
  179. package/dist/components/SelectMenu/select-menu.d.ts +3 -1
  180. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  181. package/dist/components/SelectMenu/select-menu.js +8 -1
  182. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  183. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  184. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  185. package/dist/components/SelectionControl/boolean-value.js +23 -0
  186. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  187. package/dist/components/SelectionControl/index.d.ts +1 -0
  188. package/dist/components/SelectionControl/index.d.ts.map +1 -1
  189. package/dist/components/SelectionControl/index.js +2 -0
  190. package/dist/components/SelectionControl/index.js.map +1 -1
  191. package/dist/components/Sheet/index.js +2 -1
  192. package/dist/components/Sheet/sheet.d.ts +7 -1
  193. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  194. package/dist/components/Sheet/sheet.js +5 -2
  195. package/dist/components/Sheet/sheet.js.map +1 -1
  196. package/dist/components/Sidebar/sidebar.d.ts +9 -4
  197. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  198. package/dist/components/Sidebar/sidebar.js +8 -10
  199. package/dist/components/Sidebar/sidebar.js.map +1 -1
  200. package/dist/components/Slider/slider.d.ts.map +1 -1
  201. package/dist/components/Slider/slider.js.map +1 -1
  202. package/dist/components/Switch/switch.d.ts +6 -6
  203. package/dist/components/Switch/switch.d.ts.map +1 -1
  204. package/dist/components/Switch/switch.js +3 -2
  205. package/dist/components/Switch/switch.js.map +1 -1
  206. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  207. package/dist/components/Tabs/tabs.js +15 -1
  208. package/dist/components/Tabs/tabs.js.map +1 -1
  209. package/dist/components/Textarea/textarea.d.ts +2 -2
  210. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  211. package/dist/components/Textarea/textarea.js +50 -42
  212. package/dist/components/Textarea/textarea.js.map +1 -1
  213. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  214. package/dist/components/TimePicker/time-columns.js +42 -38
  215. package/dist/components/TimePicker/time-columns.js.map +1 -1
  216. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  217. package/dist/components/TimePicker/time-picker.js +8 -6
  218. package/dist/components/TimePicker/time-picker.js.map +1 -1
  219. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  220. package/dist/components/Tooltip/tooltip.js +7 -5
  221. package/dist/components/Tooltip/tooltip.js.map +1 -1
  222. package/dist/components/TreeView/tree-view.d.ts +56 -1
  223. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  224. package/dist/components/TreeView/tree-view.js +211 -12
  225. package/dist/components/TreeView/tree-view.js.map +1 -1
  226. package/dist/index.d.ts +115 -58
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +93 -108
  229. package/dist/index.js.map +1 -1
  230. package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
  231. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  232. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  233. package/dist/tokens/categorical-color.js +15 -0
  234. package/dist/tokens/categorical-color.js.map +1 -1
  235. package/dist/tokens/motion/motion.d.ts +7 -6
  236. package/dist/tokens/motion/motion.d.ts.map +1 -1
  237. package/dist/tokens/motion/motion.js +6 -6
  238. package/dist/tokens/motion/motion.js.map +1 -1
  239. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  240. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  241. package/dist/tokens/motion/overlay-motion.js +9 -0
  242. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  243. package/dist/tokens/uiSize/icon-size.js +5 -0
  244. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  245. package/ds-canonical/fork/governance.lock +37 -19
  246. package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
  247. package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
  248. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
  249. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +17 -5
  250. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
  251. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
  252. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
  253. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
  254. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +36 -0
  255. package/ds-canonical/fork/hooks/check_story_determinism.sh +39 -0
  256. package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
  257. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  258. package/ds-canonical/fork/manifest.json +20 -0
  259. package/ds-canonical/fork/preamble.md +22 -18
  260. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +9 -3
  261. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
  262. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +7 -0
  263. package/ds-canonical/fork/skills/performance-audit/SKILL.md +4 -2
  264. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
  265. package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
  266. package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
  267. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
  268. package/ds-canonical/fork/skills/visual-audit/SKILL.md +10 -2
  269. package/ds-canonical/hooks/README.md +1 -1
  270. package/ds-canonical/hooks/block_prototype_imports.py +15 -4
  271. package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
  272. package/ds-canonical/hooks/check_benchmark_citation.sh +5 -3
  273. package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
  274. package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
  275. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +17 -5
  276. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
  277. package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
  278. package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
  279. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
  280. package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
  281. package/ds-canonical/hooks/check_opacity_token_usage.sh +36 -0
  282. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  283. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
  284. package/ds-canonical/hooks/check_propose_without_benchmark.sh +6 -2
  285. package/ds-canonical/hooks/check_ssot_header_declaration.sh +55 -0
  286. package/ds-canonical/hooks/check_story_determinism.sh +39 -0
  287. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
  288. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  289. package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
  290. package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
  291. package/ds-canonical/hooks/stop_self_audit.sh +31 -0
  292. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
  293. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
  294. package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +3 -3
  295. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
  296. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
  297. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
  298. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  299. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
  300. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
  301. package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -1
  302. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +41 -2
  303. package/ds-canonical/hooks/tests/test_check_ssot_header_declaration.sh +72 -0
  304. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  305. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  306. package/ds-canonical/references/build-ui-canonicals.md +2 -1
  307. package/ds-canonical/references/failure-class-registry.json +145 -0
  308. package/ds-canonical/references/governance-audit-coverage.md +33 -0
  309. package/ds-canonical/references/item-anatomy-recipe.md +2 -2
  310. package/ds-canonical/references/scenario-definition.md +6 -9
  311. package/ds-canonical/references/ssot-consultation.md +2 -2
  312. package/ds-canonical/references/ssot-index.md +5 -4
  313. package/ds-canonical/references/story-baseline-registry.json +8 -2
  314. package/ds-canonical/references/structural-token-retention.md +6 -9
  315. package/ds-canonical/rules/meta-patterns.md +8 -8
  316. package/ds-canonical/rules/self-verify.md +4 -3
  317. package/ds-canonical/rules/spec-rules.md +1 -1
  318. package/ds-canonical/rules/story-rules.md +2 -2
  319. package/ds-canonical/rules/ui-development.md +4 -2
  320. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +9 -3
  321. package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
  322. package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
  323. package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
  324. package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
  325. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  326. package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
  327. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +57 -68
  328. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
  329. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
  330. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
  331. package/ds-canonical/skills/delivery-handoff/SKILL.md +5 -0
  332. package/ds-canonical/skills/design-system-audit/SKILL.md +27 -26
  333. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
  334. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
  335. package/ds-canonical/skills/governance-health/SKILL.md +2 -1
  336. package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
  337. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
  338. package/ds-canonical/skills/new-component/SKILL.md +7 -5
  339. package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
  340. package/ds-canonical/skills/performance-audit/SKILL.md +2 -2
  341. package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
  342. package/ds-canonical/skills/propose-options/SKILL.md +3 -3
  343. package/ds-canonical/skills/prototype/SKILL.md +1 -1
  344. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
  345. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
  346. package/ds-canonical/skills/story-writing/SKILL.md +3 -3
  347. package/ds-canonical/skills/visual-audit/SKILL.md +10 -2
  348. package/ds-story-manifest.json +29 -20
  349. package/llms-full.txt +22 -1
  350. package/llms.txt +5 -4
  351. package/package.json +7 -3
  352. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  353. package/src/components/Accordion/accordion.stories.tsx +16 -1
  354. package/src/components/Alert/alert.principles.stories.tsx +1 -1
  355. package/src/components/Alert/alert.spec.md +8 -10
  356. package/src/components/Alert/alert.stories.tsx +28 -28
  357. package/src/components/Alert/alert.tsx +1 -1
  358. package/src/components/AppShell/_demo-helpers.tsx +6 -3
  359. package/src/components/AppShell/app-shell.spec.md +4 -4
  360. package/src/components/AppShell/app-shell.tsx +28 -2
  361. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
  362. package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
  363. package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
  364. package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
  365. package/src/components/Avatar/avatar.spec.md +2 -2
  366. package/src/components/Avatar/avatar.stories.tsx +8 -6
  367. package/src/components/Avatar/avatar.tsx +13 -5
  368. package/src/components/Badge/badge.principles.stories.tsx +1 -1
  369. package/src/components/Badge/badge.spec.md +2 -0
  370. package/src/components/Badge/badge.stories.tsx +4 -2
  371. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
  372. package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
  373. package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
  374. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  375. package/src/components/Button/button.principles.stories.tsx +1 -1
  376. package/src/components/Button/button.spec.md +2 -2
  377. package/src/components/Button/button.tsx +18 -3
  378. package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
  379. package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
  380. package/src/components/Calendar/calendar.spec.md +5 -4
  381. package/src/components/Calendar/calendar.stories.tsx +7 -4
  382. package/src/components/Calendar/calendar.tsx +26 -7
  383. package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
  384. package/src/components/Carousel/carousel.spec.md +2 -2
  385. package/src/components/Carousel/carousel.tsx +11 -3
  386. package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
  387. package/src/components/Chart/chart.principles.stories.tsx +1 -1
  388. package/src/components/Chart/chart.stories.tsx +2 -8
  389. package/src/components/Chart/chart.tsx +3 -0
  390. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
  391. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  392. package/src/components/Checkbox/checkbox.spec.md +11 -26
  393. package/src/components/Checkbox/checkbox.stories.tsx +11 -8
  394. package/src/components/Checkbox/checkbox.tsx +18 -13
  395. package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
  396. package/src/components/Chip/chip.principles.stories.tsx +1 -1
  397. package/src/components/Chip/chip.spec.md +5 -4
  398. package/src/components/Chip/chip.tsx +5 -1
  399. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
  400. package/src/components/CircularProgress/circular-progress.spec.md +3 -3
  401. package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
  402. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  403. package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
  404. package/src/components/Coachmark/coachmark.spec.md +5 -5
  405. package/src/components/Coachmark/coachmark.stories.tsx +3 -1
  406. package/src/components/Coachmark/coachmark.tsx +10 -3
  407. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  408. package/src/components/Combobox/combobox.tsx +53 -19
  409. package/src/components/Command/command.anatomy.stories.tsx +2 -2
  410. package/src/components/Command/command.principles.stories.tsx +1 -1
  411. package/src/components/Command/command.stories.tsx +42 -10
  412. package/src/components/DataTable/cell-registry.tsx +86 -63
  413. package/src/components/DataTable/column-types.ts +10 -1
  414. package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
  415. package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
  416. package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
  417. package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
  418. package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
  419. package/src/components/DataTable/data-table.spec.md +32 -12
  420. package/src/components/DataTable/data-table.stories.tsx +16 -17
  421. package/src/components/DataTable/data-table.tsx +103 -60
  422. package/src/components/DataTable/index.ts +3 -0
  423. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
  424. package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
  425. package/src/components/DateGrid/date-grid.spec.md +1 -1
  426. package/src/components/DateGrid/date-grid.tsx +3 -1
  427. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
  428. package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
  429. package/src/components/DatePicker/date-picker.spec.md +3 -3
  430. package/src/components/DatePicker/date-picker.stories.tsx +4 -5
  431. package/src/components/DatePicker/date-picker.tsx +99 -45
  432. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
  433. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  434. package/src/components/DescriptionList/description-list.spec.md +3 -3
  435. package/src/components/DescriptionList/description-list.stories.tsx +7 -6
  436. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
  437. package/src/components/Dialog/dialog.spec.md +9 -0
  438. package/src/components/Dialog/dialog.stories.tsx +66 -5
  439. package/src/components/Dialog/dialog.tsx +60 -25
  440. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  441. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
  442. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
  443. package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
  444. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  445. package/src/components/Empty/empty.spec.md +2 -2
  446. package/src/components/Field/field-context.ts +69 -5
  447. package/src/components/Field/field-controls.spec.md +62 -8
  448. package/src/components/Field/field-edit-keys.ts +66 -0
  449. package/src/components/Field/field-types.ts +58 -9
  450. package/src/components/Field/field-wrapper.tsx +50 -42
  451. package/src/components/Field/field.anatomy.stories.tsx +9 -6
  452. package/src/components/Field/field.spec.md +1 -1
  453. package/src/components/Field/field.stories.tsx +1 -1
  454. package/src/components/Field/field.tsx +2 -1
  455. package/src/components/Field/form-validation.spec.md +3 -3
  456. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
  457. package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
  458. package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
  459. package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
  460. package/src/components/FileItem/file-item.spec.md +14 -8
  461. package/src/components/FileItem/file-item.stories.tsx +36 -25
  462. package/src/components/FileItem/file-item.tsx +8 -6
  463. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
  464. package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
  465. package/src/components/FileUpload/file-upload.spec.md +1 -1
  466. package/src/components/FileUpload/file-upload.tsx +1 -1
  467. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
  468. package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
  469. package/src/components/FileViewer/file-viewer.spec.md +30 -12
  470. package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
  471. package/src/components/FileViewer/file-viewer.tsx +12 -4
  472. package/src/components/FileViewer/image-renderer.tsx +35 -7
  473. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
  474. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
  475. package/src/components/HoverCard/hover-card.spec.md +5 -5
  476. package/src/components/HoverCard/hover-card.stories.tsx +16 -16
  477. package/src/components/HoverCard/hover-card.tsx +5 -3
  478. package/src/components/InlineEdit/index.ts +5 -0
  479. package/src/components/InlineEdit/inline-edit.spec.md +149 -0
  480. package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
  481. package/src/components/InlineEdit/inline-edit.tsx +287 -0
  482. package/src/components/Input/input.anatomy.stories.tsx +8 -7
  483. package/src/components/Input/input.principles.stories.tsx +2 -12
  484. package/src/components/Input/input.spec.md +13 -18
  485. package/src/components/Input/input.tsx +69 -22
  486. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  487. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  488. package/src/components/LinkInput/link-input.tsx +11 -10
  489. package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
  490. package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
  491. package/src/components/Menu/menu-item.spec.md +22 -15
  492. package/src/components/Menu/menu-item.stories.tsx +39 -11
  493. package/src/components/Menu/menu-item.tsx +38 -4
  494. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  495. package/src/components/Notice/notice.spec.md +16 -3
  496. package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
  497. package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
  498. package/src/components/NumberInput/number-input.spec.md +4 -4
  499. package/src/components/NumberInput/number-input.tsx +19 -12
  500. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
  501. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
  502. package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
  503. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
  504. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
  505. package/src/components/Pagination/pagination.spec.md +1 -1
  506. package/src/components/Pagination/pagination.stories.tsx +2 -6
  507. package/src/components/Pagination/pagination.tsx +1 -1
  508. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
  509. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
  510. package/src/components/PeoplePicker/people-picker.spec.md +6 -6
  511. package/src/components/PeoplePicker/people-picker.tsx +56 -16
  512. package/src/components/PeoplePicker/person-display.tsx +32 -16
  513. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  514. package/src/components/Popover/popover.tsx +3 -1
  515. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
  516. package/src/components/ProfileCard/profile-card.spec.md +3 -3
  517. package/src/components/ProfileCard/profile-card.tsx +6 -6
  518. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
  519. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
  520. package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
  521. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
  522. package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
  523. package/src/components/RadioGroup/radio-group.tsx +41 -15
  524. package/src/components/Rating/rating.spec.md +11 -3
  525. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  526. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  527. package/src/components/ScrollArea/scroll-area.spec.md +7 -3
  528. package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
  529. package/src/components/ScrollArea/scroll-area.tsx +17 -4
  530. package/src/components/Select/select.anatomy.stories.tsx +3 -3
  531. package/src/components/Select/select.spec.md +27 -17
  532. package/src/components/Select/select.stories.tsx +33 -0
  533. package/src/components/Select/select.tsx +190 -36
  534. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
  535. package/src/components/SelectMenu/select-menu.spec.md +15 -7
  536. package/src/components/SelectMenu/select-menu.tsx +16 -3
  537. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  538. package/src/components/SelectionControl/index.ts +1 -0
  539. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
  540. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  541. package/src/components/SelectionControl/selection-item.spec.md +3 -3
  542. package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
  543. package/src/components/Separator/separator.spec.md +2 -2
  544. package/src/components/Separator/separator.stories.tsx +6 -27
  545. package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
  546. package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
  547. package/src/components/Sheet/sheet.spec.md +2 -2
  548. package/src/components/Sheet/sheet.tsx +9 -5
  549. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
  550. package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
  551. package/src/components/Sidebar/sidebar.spec.md +13 -15
  552. package/src/components/Sidebar/sidebar.stories.tsx +8 -7
  553. package/src/components/Sidebar/sidebar.tsx +37 -25
  554. package/src/components/Skeleton/skeleton.stories.tsx +4 -11
  555. package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
  556. package/src/components/Slider/slider.principles.stories.tsx +2 -2
  557. package/src/components/Slider/slider.spec.md +5 -5
  558. package/src/components/Slider/slider.tsx +3 -1
  559. package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
  560. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  561. package/src/components/Steps/steps.spec.md +1 -1
  562. package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
  563. package/src/components/Switch/switch.principles.stories.tsx +4 -4
  564. package/src/components/Switch/switch.spec.md +8 -8
  565. package/src/components/Switch/switch.stories.tsx +9 -24
  566. package/src/components/Switch/switch.tsx +17 -13
  567. package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
  568. package/src/components/Tabs/tabs.spec.md +1 -1
  569. package/src/components/Tabs/tabs.tsx +21 -1
  570. package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
  571. package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
  572. package/src/components/Textarea/textarea.spec.md +4 -4
  573. package/src/components/Textarea/textarea.tsx +72 -46
  574. package/src/components/TimePicker/time-columns.tsx +6 -2
  575. package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
  576. package/src/components/TimePicker/time-picker.spec.md +3 -3
  577. package/src/components/TimePicker/time-picker.tsx +16 -10
  578. package/src/components/Toast/toast.stories.tsx +7 -4
  579. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
  580. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  581. package/src/components/Tooltip/tooltip.spec.md +1 -1
  582. package/src/components/Tooltip/tooltip.stories.tsx +4 -39
  583. package/src/components/Tooltip/tooltip.tsx +8 -6
  584. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
  585. package/src/components/TreeView/tree-view.spec.md +42 -11
  586. package/src/components/TreeView/tree-view.stories.tsx +1 -0
  587. package/src/components/TreeView/tree-view.tsx +322 -17
  588. package/src/index.ts +584 -60
  589. package/src/patterns/action-bar/action-bar.spec.md +9 -0
  590. package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
  591. package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
  592. package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
  593. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
  594. package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
  595. package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
  596. package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
  597. package/src/styles/tokens.css +3 -0
  598. package/src/tokens/README.md +1 -1
  599. package/src/tokens/color/color.spec.md +3 -3
  600. package/src/tokens/color/color.stories.tsx +3 -3
  601. package/src/tokens/density/density.stories.tsx +16 -3
  602. package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
  603. package/src/tokens/motion/motion.css +33 -21
  604. package/src/tokens/motion/motion.spec.md +32 -15
  605. package/src/tokens/motion/motion.stories.tsx +58 -5
  606. package/src/tokens/motion/motion.ts +7 -6
  607. package/src/tokens/motion/overlay-motion.ts +26 -0
  608. package/src/tokens/orphan-tokens.spec.md +4 -4
  609. package/src/tokens/token-system.spec.md +1 -0
  610. package/src/tokens/uiSize/uiSize.spec.md +1 -1
  611. package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
  612. package/src/tokens/utility-registry.json +160 -23
@@ -1 +1 @@
1
- {"version":3,"file":"radio-group.js","sources":["../../../src/components/RadioGroup/radio-group.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 RadioGroupPrimitive from \"@radix-ui/react-radio-group\"\nimport { Circle } from \"lucide-react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { FieldMode, FieldVariant } from \"@/design-system/components/Field/field-types\"\nimport { useFieldContext, useResolvedFieldMode, useResolvedFieldDisabled, useResolvedFieldSize } from \"@/design-system/components/Field/field-context\"\nimport { SelectionItem } from \"@/design-system/components/SelectionControl/selection-item\"\nimport type { LucideIcon } from \"lucide-react\"\nimport type { AvatarData } from \"@/design-system/components/Avatar/avatar\"\nimport { EMPTY_DISPLAY, fieldWrapperStyles } from \"@/design-system/components/Field/field-wrapper\"\n\n// ── RadioGroup display mode ─────────────────────────────────────────────────\n// RadioGroup mode='display' 時:Group 不渲染 Radix primitive(無 radio 視覺),\n// 改由 RadioGroup 本體 walk props.children,找 control.value === selectedValue 的\n// SelectionItem,把它的 label 渲染為單一純文字 span(其他選項不顯示)。\n// 對齊 Carbon read-only single-select(只顯示 selected 內容)+ Airtable / Notion read-only。\n// 實作在 RadioGroup forwardRef 內(見下方 mode === 'display' 分支)。\n\n// ── RadioGroup ──────────────────────────────────────────────────────────────\n\nexport interface RadioGroupProps\n extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {\n /**\n * Field mode(2026-05-05 Phase B3 align):\n * edit — 一般可互動 RadioGroup(預設)\n * display — **純展示**:不渲染 Radix Root / 任何 radio 視覺;RadioGroup 本體 walk\n * children,僅 control.value === group.value 那筆把 label 渲染為純文字 span。\n * 對齊 Carbon read-only / DataTable single-select cell read mode。\n * readonly — standalone:ReadonlyContext 傳 items 鎖互動保留視覺;Field 內:灰框 + 選中項 label(不渲染 radio 群組)\n * disabled — 同 RadioGroupPrimitive.Root disabled 屬性\n */\n mode?: FieldMode\n /**\n * Visual chrome — RadioGroup 本體無 input wrapper variant,本 prop 對主體無視覺影響;\n * 為對齊 Field 4-mode + chrome 透傳契約而保留(M19 一致性)。\n */\n variant?: FieldVariant\n}\n\n// RadioGroup mode='readonly' → 透過 context 把 readOnly 傳給所有 child RadioGroupItem\n// (item 已支援 readOnly prop + data-[readonly] 樣式;Radix Root 無 readOnly,故用 context)。\nconst RadioGroupReadonlyContext = React.createContext(false)\n\n// walk children 找 control.value === selectedValue 的 SelectionItem label(display / readonly-in-Field 共用)\nfunction findSelectedRadioLabel(children: React.ReactNode, selectedValue: string | undefined): React.ReactNode {\n let selectedLabel: React.ReactNode = null\n React.Children.forEach(children, (child) => {\n if (!React.isValidElement(child)) return\n const cProps = child.props as { control?: unknown; label?: React.ReactNode; value?: unknown }\n // 形狀 1:<RadioGroupItem value label>(主用法)— value/label 直掛 props\n if (cProps.value === selectedValue && cProps.value !== undefined) {\n selectedLabel = cProps.label ?? selectedValue\n return\n }\n // 形狀 2:<SelectionItem control={<RadioGroupItem value/>} label>(組合用法)\n const control = cProps.control\n if (React.isValidElement(control)) {\n const controlValue = (control.props as { value?: unknown }).value\n if (controlValue === selectedValue) {\n selectedLabel = cProps.label ?? selectedValue\n }\n }\n })\n return selectedLabel\n}\n\nconst RadioGroup = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Root>,\n RadioGroupProps\n>(({ className, mode, variant: _chrome, value, defaultValue, ...props }, ref) => {\n // 2026-06-08 SSOT cascade:resolvedMode 經 resolver hook 讀 fieldCtx(原 root 完全不讀 → <Field disabled>/<Field mode> 失效)\n const resolvedMode = useResolvedFieldMode({ mode, disabled: (props as { disabled?: boolean }).disabled })\n const fieldCtx = useFieldContext()\n // readonly 灰框 size:走 SSOT resolver(RadioGroup 無 size prop → ctx > 'md')\n const resolvedBoxSize = useResolvedFieldSize(undefined, 'md') as 'sm' | 'md' | 'lg'\n // mode='display' — 純展示 selected option 的 label,不渲染任何 radio control 視覺。\n // 對齊 Carbon read-only single-select(只顯示 selected 內容)+ Airtable / Notion read-only。\n // 實作:walk children 找 control.value === selectedValue 的 SelectionItem,render label plain text。\n // (不用 context dispatch 給 RadioGroupItem — SelectionItem layout wrapper 仍會渲染所有 item label)\n // 2026-07-04 修:display / readonly-in-Field 分支的 <div> 原丟棄剩餘 props(id/data-*/aria-*)\n // 且不轉發 forwardRef ref → 自家 radio-group.stories.tsx display demo 傳的 aria-label 都靜默失效。\n // 抽出 Radix 專屬 non-DOM props,其餘 DOM props 於兩分支 spread 轉發(與 Checkbox/Switch 同修);\n // edit 主路徑不受影響(Root 仍 spread 完整 props)。\n const {\n onValueChange: _onValueChange,\n disabled: _disabled,\n required: _required,\n name: _name,\n orientation: _orientation,\n dir: _dir,\n loop: _loop,\n asChild: _asChild,\n children: _children,\n ...restDomProps\n } = props as RadioGroupProps & { asChild?: boolean }\n\n if (resolvedMode === 'display') {\n const selectedValue = (value ?? defaultValue) as string | undefined\n if (!selectedValue) {\n return <div {...restDomProps} ref={ref as React.Ref<HTMLDivElement>} role=\"group\" className={cn('grid', className)}><span className=\"text-fg-muted\">{EMPTY_DISPLAY}</span></div>\n }\n const selectedLabel = findSelectedRadioLabel(props.children, selectedValue)\n return (\n <div {...restDomProps} ref={ref as React.Ref<HTMLDivElement>} role=\"group\" className={cn('grid', className)}>\n <span className=\"text-foreground\">{selectedLabel ?? selectedValue}</span>\n </div>\n )\n }\n\n // ── mode='readonly' in Field(2026-06-12 user 拍板,與 Checkbox/Switch 灰框模型一致)──\n // Field 內 readonly 單選 = fieldWrapperStyles readonly 灰框 + 選中項 label 文字\n // (= Select readonly 同款呈現:同為「單選資料」,鎖定時呈現一致)。\n // standalone readonly(無 Field)維持原樣鎖互動(ReadonlyContext 路徑)。\n if (resolvedMode === 'readonly' && fieldCtx?.hasFieldWrapper === true) {\n const selectedValue = (value ?? defaultValue) as string | undefined\n const selectedLabel = selectedValue ? findSelectedRadioLabel(props.children, selectedValue) : null\n const boxSize = resolvedBoxSize\n return (\n <div\n {...restDomProps}\n ref={ref as React.Ref<HTMLDivElement>}\n role=\"radiogroup\"\n aria-readonly=\"true\"\n aria-labelledby={fieldCtx?.labelId}\n aria-invalid={fieldCtx?.invalid || undefined}\n data-readonly=\"true\"\n tabIndex={0}\n className={cn(\n fieldWrapperStyles({ size: boxSize, mode: 'readonly', variant: 'default' }),\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n className,\n )}\n >\n {selectedValue\n ? <span className=\"text-foreground\">{selectedLabel ?? selectedValue}</span>\n : <span className=\"text-fg-muted\">{EMPTY_DISPLAY}</span>}\n </div>\n )\n }\n\n // mode='disabled' → Radix Root disabled(原生 propagate 給所有 item);\n // mode='readonly' → context 傳 readOnly 給 items(item 渲染為 data-[readonly] 鎖互動 + aria-readonly)。\n return (\n <RadioGroupReadonlyContext.Provider value={resolvedMode === 'readonly'}>\n <RadioGroupPrimitive.Root\n className={cn(\"grid\", className)}\n value={value}\n defaultValue={defaultValue}\n {...props}\n disabled={resolvedMode === 'disabled'}\n ref={ref}\n />\n </RadioGroupReadonlyContext.Provider>\n )\n})\nRadioGroup.displayName = 'RadioGroup'\n// Field layout 宣告:RadioGroup 是 block primitive(多項堆疊),\n// 進入 <Field> 時 control area 自動切 items-start + padding-top 公式對齊。\n// Convention 詳見 components/Field/field.spec.md「Control area:Inline vs Block」段落。\n;(RadioGroup as unknown as { fieldLayout: 'block' }).fieldLayout = 'block'\n\n// ── RadioGroupItem Variants ─────────────────────────────────────────────────\n// 與 Checkbox 完全對齊:sm/md=16px, lg=20px。差異只有形狀(rounded-full)和指示器(filled dot)。\n\nconst radioItemVariants = cva(\n [\n 'grid place-content-center shrink-0 rounded-full',\n 'border border-border bg-surface',\n 'transition-colors duration-150',\n 'hover:border-border-hover',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n 'data-[state=checked]:border-primary data-[state=checked]:text-primary',\n 'data-[state=checked]:hover:border-primary-hover data-[state=checked]:hover:text-primary-hover',\n 'disabled:cursor-not-allowed disabled:bg-disabled disabled:border-transparent disabled:hover:border-transparent',\n 'disabled:data-[state=checked]:bg-disabled disabled:data-[state=checked]:border-transparent disabled:data-[state=checked]:text-fg-disabled',\n // readOnly:鎖定互動但維持 checked/unchecked 視覺\n 'data-[readonly=true]:pointer-events-none data-[readonly=true]:cursor-default',\n 'data-[readonly=true]:hover:border-border',\n ],\n {\n variants: {\n size: {\n sm: 'h-4 w-4',\n md: 'h-4 w-4',\n lg: 'h-5 w-5',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n }\n)\n\n// ── Dot Size ────────────────────────────────────────────────────────────────\nconst dotSize: Record<string, number> = { sm: 8, md: 8, lg: 10 }\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\ntype RadioItemPrimitiveProps = React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n\nexport interface RadioGroupItemProps\n extends RadioItemPrimitiveProps,\n VariantProps<typeof radioItemVariants> {\n /**\n * Inline label。提供時 RadioGroupItem 自動透過 SelectionItem 包裝,\n * 套用 codified 樣式(text-body / text-foreground / disabled 色)。\n * 在 <Field> context 內時此 prop 仍然生效(Radio 的 label 是每個 item\n * 各自的,不是整組 Field 的;FieldLabel 是 RadioGroup 整體的 label)。\n */\n label?: React.ReactNode\n /**\n * Inline description(secondary 文字)。須與 label 搭配使用。\n * 套用 text-body / text-fg-secondary 樣式。\n */\n description?: React.ReactNode\n /** 可選左側 icon(label 前)— 2026-06-12 M30 修:轉發 SelectionItem 既有 canonical 槽(selection-item.tsx jsDoc SSOT;與 avatar 互斥)*/\n icon?: LucideIcon\n /** 可選左側 avatar(label 前)— 同上 */\n avatar?: AvatarData\n /**\n * readonly 模式:鎖定互動但維持 checked/unchecked 視覺正確。\n * 通常整個 RadioGroup 一起設 readonly(由 parent RadioGroup 的 disabled\n * 或 readonly 行為決定),個別 item 也可設。\n */\n readOnly?: boolean\n}\n\n// ── RadioGroupItem ──────────────────────────────────────────────────────────\n\nconst RadioGroupItem = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Item>,\n RadioGroupItemProps\n>(\n (\n {\n className,\n size,\n label,\n icon,\n avatar,\n description,\n readOnly = false,\n disabled,\n id: idProp,\n ...props\n },\n ref\n ) => {\n const sizeKey = size ?? 'md'\n const dotPx = dotSize[sizeKey]\n\n // 注意:RadioGroup mode='display' 的純文字渲染由 RadioGroup 本體 walk-children 處理\n // (見上方 RadioGroup forwardRef 的 mode === 'display' 分支),RadioGroupItem 在 display\n // mode 下不會被獨立 render,故此處無 display 分支。\n\n // 注意:Radio 的 label 語意與 Checkbox/Switch 不同——\n // Checkbox/Switch 的 label 就是該 control 的唯一 label(被 Field context 接管),\n // RadioGroupItem 的 label 是「該選項」的 label(每 item 各自擁有),\n // FieldLabel 則是整個 RadioGroup 的 label。\n // 因此 RadioGroupItem 的 label 不因 Field context 被忽略。\n const resolvedDisabled = useResolvedFieldDisabled(disabled)\n // group-level readonly(RadioGroup mode='readonly')或 item-level readOnly,任一 true 即鎖互動。\n const groupReadonly = React.useContext(RadioGroupReadonlyContext)\n const effectiveReadonly = readOnly || groupReadonly\n\n const generatedId = React.useId()\n const inputId = idProp ?? generatedId\n\n const rootEl = (\n <RadioGroupPrimitive.Item\n id={inputId}\n ref={ref}\n disabled={resolvedDisabled}\n aria-readonly={effectiveReadonly || undefined}\n data-readonly={effectiveReadonly || undefined}\n tabIndex={effectiveReadonly ? -1 : undefined}\n className={cn(radioItemVariants({ size }), className)}\n {...props}\n // 2026-07-05 D4 修:readOnly 鎖互動補 label 繞道 — <label htmlFor> 的 synthetic click\n // 不是 pointer event,cva pointer-events-none 攔不到,點 label 文字仍選中。\n // preventDefault 讓 Radix composeEventHandlers(checkForDefaultPrevented)跳過內部 onCheck;\n // 必列在 {...props} 之後才不被 consumer onClick 覆蓋(與 Checkbox/Switch 同修)。\n // 同時攔 Radix roving-focus 的 arrow-key onFocus → click() 路徑(同為 click event)。\n onClick={(event) => {\n if (effectiveReadonly) {\n event.preventDefault()\n return\n }\n props.onClick?.(event)\n }}\n >\n <RadioGroupPrimitive.Indicator className=\"grid place-content-center\">\n <Circle\n style={{ width: dotPx, height: dotPx }}\n className=\"fill-current text-current\"\n />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n )\n\n // 無 label → 只渲染 radio 本體\n if (label == null) return rootEl\n\n // 有 label → 透過 SelectionItem 包裝,與 Checkbox 一致(disabled 已於上方 useResolvedFieldDisabled 解析)\n return (\n <SelectionItem\n control={rootEl}\n label={label}\n description={description}\n icon={icon}\n avatar={avatar}\n htmlFor={inputId}\n disabled={resolvedDisabled}\n size={sizeKey}\n />\n )\n }\n)\nRadioGroupItem.displayName = 'RadioGroupItem'\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 radioGroupMeta = {\n component: 'RadioGroup',\n family: null, // self-contained primitive(對齊 spec frontmatter self-contained + body L31;非 Family 4 — field-controls.spec.md 成員名單不含 RadioGroup)\n variants: {\n\n },\n sizes: {\n // iconSize = 渲染指示器尺寸(對齊 checkbox/switch meta 慣例:checkbox iconSize=真 Check glyph 12/12/16)。\n // Radio 指示器是 filled dot 非 glyph,真值 = dotSize 8/8/10(radio-group.tsx:179);\n // 控件框 16/16/20 與 Checkbox 對齊但那是 box 非「指示器」,不入此鍵(避免 metadata 語意 drift)。\n sm: { fieldHeight: 28, iconSize: 8, typography: 'body' },\n md: { fieldHeight: 32, iconSize: 8, typography: 'body' },\n lg: { fieldHeight: 36, iconSize: 10, typography: 'body-lg' },\n },\n // 2026-07-04 修 stale meta:移除 'active' — radioItemVariants cva 無 active-state 樣式,\n // Radix radio 也無 data-state=active(對照 checkboxMeta 正確不含 active)\n // 'checked' = 選定持續態(card 式 = border+text primary;2026-07-07 meta 詞彙統一補列)\n states: ['default', 'hover', 'checked', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-disabled', 'bg-surface'],\n fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground', 'text-primary'],\n ring: ['ring-ring'],\n },\n defaultSize: 'md',\n} as const\n\nexport { RadioGroup, RadioGroupItem, radioItemVariants }\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"radio-group.js","sources":["../../../src/components/RadioGroup/radio-group.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 RadioGroupPrimitive from \"@radix-ui/react-radio-group\"\nimport { Circle } from \"lucide-react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { FieldMode, FieldVariant } from \"@/design-system/components/Field/field-types\"\nimport { useFieldContext, useResolvedFieldMode, useResolvedFieldDisabled, useResolvedFieldSize } from \"@/design-system/components/Field/field-context\"\nimport { SelectionItem } from \"@/design-system/components/SelectionControl/selection-item\"\nimport type { LucideIcon } from \"lucide-react\"\nimport type { AvatarData } from \"@/design-system/components/Avatar/avatar\"\nimport { fieldWrapperStyles } from \"@/design-system/components/Field/field-wrapper\"\nimport { useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\n\n// ── RadioGroup display mode ─────────────────────────────────────────────────\n// RadioGroup mode='display' 時:Group 不渲染 Radix primitive(無 radio 視覺),\n// 改由 RadioGroup 本體 walk props.children,找 control.value === selectedValue 的\n// SelectionItem,把它的 label 渲染為單一純文字 span(其他選項不顯示)。\n// 對齊 Carbon read-only single-select(只顯示 selected 內容)+ Airtable / Notion read-only。\n// 實作在 RadioGroup forwardRef 內(見下方 mode === 'display' 分支)。\n\n// ── RadioGroup ──────────────────────────────────────────────────────────────\n\nexport interface RadioGroupProps\n extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {\n /**\n * Field mode(2026-05-05 Phase B3 align):\n * edit — 一般可互動 RadioGroup(預設)\n * display — **純展示**:不渲染 Radix Root / 任何 radio 視覺;RadioGroup 本體 walk\n * children,僅 control.value === group.value 那筆把 label 渲染為純文字 span。\n * 對齊 Carbon read-only / DataTable single-select cell read mode。\n * readonly — standalone:ReadonlyContext 傳 items 鎖互動保留視覺;Field 內:灰框 + 選中項 label(不渲染 radio 群組)\n * disabled — 同 RadioGroupPrimitive.Root disabled 屬性\n */\n mode?: FieldMode\n /**\n * Visual chrome — RadioGroup 本體無 input wrapper variant,本 prop 對主體無視覺影響;\n * 為對齊 Field 4-mode + chrome 透傳契約而保留(M19 一致性)。\n */\n variant?: FieldVariant\n}\n\n// RadioGroup mode='readonly' → 透過 context 把 readOnly 傳給所有 child RadioGroupItem\n// (item 已支援 readOnly prop + data-[readonly] 樣式;Radix Root 無 readOnly,故用 context)。\nconst RadioGroupReadonlyContext = React.createContext(false)\n\n// group-level disabled(standalone <RadioGroup disabled>)→ context 傳給 items,讓 SelectionItem\n// label/description 也降 text-fg-disabled(Radix Root disabled 只 propagate 給 radio 圈、不碰 label 色)。\n// 對齊本檔 RadioGroupReadonlyContext 既有模式 + spec「Disabled」邊界案例(Group-level disabled 視覺繼承 SelectionItem SSOT text-fg-disabled)。\nconst RadioGroupDisabledContext = React.createContext(false)\n\n// walk children 找 control.value === selectedValue 的 SelectionItem label(display / readonly-in-Field 共用)\nfunction findSelectedRadioLabel(children: React.ReactNode, selectedValue: string | undefined): React.ReactNode {\n let selectedLabel: React.ReactNode = null\n React.Children.forEach(children, (child) => {\n if (!React.isValidElement(child)) return\n const cProps = child.props as { control?: unknown; label?: React.ReactNode; value?: unknown }\n // 形狀 1:<RadioGroupItem value label>(主用法)— value/label 直掛 props\n if (cProps.value === selectedValue && cProps.value !== undefined) {\n selectedLabel = cProps.label ?? selectedValue\n return\n }\n // 形狀 2:<SelectionItem control={<RadioGroupItem value/>} label>(組合用法)\n const control = cProps.control\n if (React.isValidElement(control)) {\n const controlValue = (control.props as { value?: unknown }).value\n if (controlValue === selectedValue) {\n selectedLabel = cProps.label ?? selectedValue\n }\n }\n })\n return selectedLabel\n}\n\nconst RadioGroup = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Root>,\n RadioGroupProps\n>(({ className, mode, variant: _chrome, value, defaultValue, ...props }, ref) => {\n // 2026-06-08 SSOT cascade:resolvedMode 經 resolver hook 讀 fieldCtx(原 root 完全不讀 → <Field disabled>/<Field mode> 失效)\n const resolvedMode = useResolvedFieldMode({ mode, disabled: (props as { disabled?: boolean }).disabled })\n const emptyDisplay = useFieldEmptyDisplay()\n const fieldCtx = useFieldContext()\n // readonly 灰框 size:走 SSOT resolver(RadioGroup 無 size prop → ctx > 'md')\n const resolvedBoxSize = useResolvedFieldSize(undefined, 'md') as 'sm' | 'md' | 'lg'\n // mode='display' — 純展示 selected option 的 label,不渲染任何 radio control 視覺。\n // 對齊 Carbon read-only single-select(只顯示 selected 內容)+ Airtable / Notion read-only。\n // 實作:walk children 找 control.value === selectedValue 的 SelectionItem,render label plain text。\n // (不用 context dispatch 給 RadioGroupItem — SelectionItem layout wrapper 仍會渲染所有 item label)\n // 2026-07-04 修:display / readonly-in-Field 分支的 <div> 原丟棄剩餘 props(id/data-*/aria-*)\n // 且不轉發 forwardRef ref → 自家 radio-group.stories.tsx display demo 傳的 aria-label 都靜默失效。\n // 抽出 Radix 專屬 non-DOM props,其餘 DOM props 於兩分支 spread 轉發(與 Checkbox/Switch 同修);\n // edit 主路徑不受影響(Root 仍 spread 完整 props)。\n const {\n onValueChange: _onValueChange,\n disabled: _disabled,\n required: _required,\n name: _name,\n orientation: _orientation,\n dir: _dir,\n loop: _loop,\n asChild: _asChild,\n children: _children,\n ...restDomProps\n } = props as RadioGroupProps & { asChild?: boolean }\n\n if (resolvedMode === 'display') {\n const selectedValue = (value ?? defaultValue) as string | undefined\n if (!selectedValue) {\n return <div {...restDomProps} ref={ref as React.Ref<HTMLDivElement>} role=\"group\" className={cn('grid', className)}><span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span></div>\n }\n const selectedLabel = findSelectedRadioLabel(props.children, selectedValue)\n return (\n <div {...restDomProps} ref={ref as React.Ref<HTMLDivElement>} role=\"group\" className={cn('grid', className)}>\n <span className=\"text-foreground\">{selectedLabel ?? selectedValue}</span>\n </div>\n )\n }\n\n // ── mode='readonly' in Field(2026-06-12 user 拍板,與 Checkbox/Switch 灰框模型一致)──\n // Field 內 readonly 單選 = fieldWrapperStyles readonly 灰框 + 選中項 label 文字\n // (= Select readonly 同款呈現:同為「單選資料」,鎖定時呈現一致)。\n // standalone readonly(無 Field)維持原樣鎖互動(ReadonlyContext 路徑)。\n if (resolvedMode === 'readonly' && fieldCtx?.hasFieldWrapper === true) {\n const selectedValue = (value ?? defaultValue) as string | undefined\n const selectedLabel = selectedValue ? findSelectedRadioLabel(props.children, selectedValue) : null\n const boxSize = resolvedBoxSize\n return (\n <div\n {...restDomProps}\n ref={ref as React.Ref<HTMLDivElement>}\n role=\"radiogroup\"\n aria-readonly=\"true\"\n aria-labelledby={fieldCtx?.labelId}\n aria-invalid={fieldCtx?.invalid || undefined}\n data-readonly=\"true\"\n tabIndex={0}\n className={cn(\n fieldWrapperStyles({ size: boxSize, mode: 'readonly', variant: 'default' }),\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n className,\n )}\n >\n {selectedValue\n ? <span className=\"text-foreground\">{selectedLabel ?? selectedValue}</span>\n : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>}\n </div>\n )\n }\n\n // mode='disabled' → Radix Root disabled(原生 propagate 給所有 item);\n // mode='readonly' → context 傳 readOnly 給 items(item 渲染為 data-[readonly] 鎖互動 + aria-readonly)。\n return (\n <RadioGroupReadonlyContext.Provider value={resolvedMode === 'readonly'}>\n <RadioGroupDisabledContext.Provider value={resolvedMode === 'disabled'}>\n <RadioGroupPrimitive.Root\n className={cn(\"grid\", className)}\n value={value}\n defaultValue={defaultValue}\n // Field 內:role=\"radiogroup\" 的 <div> 無法被 <label for> 命名 → 自讀 fieldCtx 接\n // aria-labelledby / aria-invalid / aria-describedby(field-context.ts「下游 control 需自讀\n // labelId」;對齊 slider.tsx / Rating / TimePicker 的 fieldLabelId 接線 + 上方 readonly-in-Field 分支)。\n // standalone(無 Field)時皆 undefined,consumer 的 {...props} aria-* 照舊生效(置於 spread 前故可被覆蓋)。\n aria-labelledby={fieldCtx?.labelId}\n aria-invalid={fieldCtx?.invalid || undefined}\n aria-describedby={fieldCtx?.descriptionId}\n // a11y(2026-07-14 dim-10):readonly 語意放 radiogroup(aria-readonly 合法 role;\n // 對齊上方 readonly-in-Field 分支 L133)。item(role=radio)不帶 aria-readonly —\n // axe 實證 radio 不允許該屬性,item 靠 data-readonly + tabIndex + click guard 鎖互動。\n aria-readonly={resolvedMode === 'readonly' || undefined}\n {...props}\n disabled={resolvedMode === 'disabled'}\n ref={ref}\n />\n </RadioGroupDisabledContext.Provider>\n </RadioGroupReadonlyContext.Provider>\n )\n})\nRadioGroup.displayName = 'RadioGroup'\n// Field layout 宣告:RadioGroup 是 block primitive(多項堆疊),\n// 進入 <Field> 時 control area 自動切 items-start(block 分支不設 min-h、不加 padding-top,\n// 高度由內容決定;額外加 paddingTop 會 double padding — field.tsx block control area SSOT)。\n// Convention 詳見 components/Field/field.spec.md「Control area:Inline vs Block」段落。\n;(RadioGroup as unknown as { fieldLayout: 'block' }).fieldLayout = 'block'\n\n// ── RadioGroupItem Variants ─────────────────────────────────────────────────\n// 與 Checkbox 完全對齊:sm/md=16px, lg=20px。差異只有形狀(rounded-full)和指示器(filled dot)。\n\nconst radioItemVariants = cva(\n [\n 'grid place-content-center shrink-0 rounded-full',\n 'border border-border bg-surface',\n 'transition-colors duration-150',\n 'hover:border-border-hover',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n 'data-[state=checked]:border-primary data-[state=checked]:text-primary',\n 'data-[state=checked]:hover:border-primary-hover data-[state=checked]:hover:text-primary-hover',\n 'disabled:cursor-not-allowed disabled:bg-disabled disabled:border-transparent disabled:hover:border-transparent',\n 'disabled:data-[state=checked]:bg-disabled disabled:data-[state=checked]:border-transparent disabled:data-[state=checked]:text-fg-disabled',\n // readOnly:鎖定互動但維持 checked/unchecked 視覺\n 'data-[readonly=true]:pointer-events-none data-[readonly=true]:cursor-default',\n 'data-[readonly=true]:hover:border-border',\n ],\n {\n variants: {\n size: {\n sm: 'h-4 w-4',\n md: 'h-4 w-4',\n lg: 'h-5 w-5',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n }\n)\n\n// ── Dot Size ────────────────────────────────────────────────────────────────\nconst dotSize: Record<string, number> = { sm: 8, md: 8, lg: 10 }\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\ntype RadioItemPrimitiveProps = React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>\n\nexport interface RadioGroupItemProps\n extends RadioItemPrimitiveProps,\n VariantProps<typeof radioItemVariants> {\n /**\n * Inline label。提供時 RadioGroupItem 自動透過 SelectionItem 包裝,\n * 套用 codified 樣式(text-body / text-foreground / disabled 色)。\n * 在 <Field> context 內時此 prop 仍然生效(Radio 的 label 是每個 item\n * 各自的,不是整組 Field 的;FieldLabel 是 RadioGroup 整體的 label)。\n */\n label?: React.ReactNode\n /**\n * Inline description(secondary 文字)。須與 label 搭配使用。\n * 套用 text-body / text-fg-secondary 樣式。\n */\n description?: React.ReactNode\n /** 可選左側 icon(label 前)— 2026-06-12 M30 修:轉發 SelectionItem 既有 canonical 槽(selection-item.tsx jsDoc SSOT;與 avatar 互斥)*/\n icon?: LucideIcon\n /** 可選左側 avatar(label 前)— 同上 */\n avatar?: AvatarData\n /**\n * readonly 模式:鎖定互動但維持 checked/unchecked 視覺正確。\n * 通常整個 RadioGroup 一起設 readonly(由 parent RadioGroup 的 disabled\n * 或 readonly 行為決定),個別 item 也可設。\n */\n readOnly?: boolean\n}\n\n// ── RadioGroupItem ──────────────────────────────────────────────────────────\n\nconst RadioGroupItem = React.forwardRef<\n React.ElementRef<typeof RadioGroupPrimitive.Item>,\n RadioGroupItemProps\n>(\n (\n {\n className,\n size,\n label,\n icon,\n avatar,\n description,\n readOnly = false,\n disabled,\n id: idProp,\n ...props\n },\n ref\n ) => {\n const sizeKey = size ?? 'md'\n const dotPx = dotSize[sizeKey]\n\n // 注意:RadioGroup mode='display' 的純文字渲染由 RadioGroup 本體 walk-children 處理\n // (見上方 RadioGroup forwardRef 的 mode === 'display' 分支),RadioGroupItem 在 display\n // mode 下不會被獨立 render,故此處無 display 分支。\n\n // 注意:Radio 的 label 語意與 Checkbox/Switch 不同——\n // Checkbox/Switch 的 label 就是該 control 的唯一 label(被 Field context 接管),\n // RadioGroupItem 的 label 是「該選項」的 label(每 item 各自擁有),\n // FieldLabel 則是整個 RadioGroup 的 label。\n // 因此 RadioGroupItem 的 label 不因 Field context 被忽略。\n // group-level disabled(standalone <RadioGroup disabled>)也要降 label/description 色 —— Radix Root\n // disabled 只 propagate 給 radio 圈,label 色由 SelectionItem disabled 決定,故讀 RadioGroupDisabledContext 補上。\n const groupDisabled = React.useContext(RadioGroupDisabledContext)\n const resolvedDisabled = useResolvedFieldDisabled(disabled) || groupDisabled\n // group-level readonly(RadioGroup mode='readonly')或 item-level readOnly,任一 true 即鎖互動。\n const groupReadonly = React.useContext(RadioGroupReadonlyContext)\n const effectiveReadonly = readOnly || groupReadonly\n\n const generatedId = React.useId()\n const inputId = idProp ?? generatedId\n\n const rootEl = (\n <RadioGroupPrimitive.Item\n id={inputId}\n ref={ref}\n disabled={resolvedDisabled}\n // a11y(2026-07-14 dim-10):item(role=radio)不帶 aria-readonly — axe 實證 radio 不允許\n // 該屬性(readonly 語意由 Root role=radiogroup 宣告,見上方 L167-170)。item 靠\n // data-readonly + tabIndex + click guard 鎖互動。\n data-readonly={effectiveReadonly || undefined}\n tabIndex={effectiveReadonly ? -1 : undefined}\n className={cn(radioItemVariants({ size }), className)}\n {...props}\n // 2026-07-05 D4 修:readOnly 鎖互動補 label 繞道 — <label htmlFor> 的 synthetic click\n // 不是 pointer event,cva pointer-events-none 攔不到,點 label 文字仍選中。\n // preventDefault 讓 Radix composeEventHandlers(checkForDefaultPrevented)跳過內部 onCheck;\n // 必列在 {...props} 之後才不被 consumer onClick 覆蓋(與 Checkbox/Switch 同修)。\n // 同時攔 Radix roving-focus 的 arrow-key onFocus → click() 路徑(同為 click event)。\n onClick={(event) => {\n if (effectiveReadonly) {\n event.preventDefault()\n return\n }\n props.onClick?.(event)\n }}\n >\n <RadioGroupPrimitive.Indicator className=\"grid place-content-center\">\n <Circle\n style={{ width: dotPx, height: dotPx }}\n className=\"fill-current text-current\"\n />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n )\n\n // 無 label → 只渲染 radio 本體\n if (label == null) return rootEl\n\n // 有 label → 透過 SelectionItem 包裝,與 Checkbox 一致(disabled 已於上方 useResolvedFieldDisabled 解析)\n return (\n <SelectionItem\n control={rootEl}\n label={label}\n description={description}\n icon={icon}\n avatar={avatar}\n htmlFor={inputId}\n disabled={resolvedDisabled}\n size={sizeKey}\n />\n )\n }\n)\nRadioGroupItem.displayName = 'RadioGroupItem'\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 radioGroupMeta = {\n component: 'RadioGroup',\n family: null, // self-contained primitive(對齊 spec frontmatter self-contained + body L31;非 Family 4 — field-controls.spec.md 成員名單不含 RadioGroup)\n variants: {\n\n },\n sizes: {\n // iconSize = 渲染指示器尺寸(對齊 checkbox/switch meta 慣例:checkbox iconSize=真 Check glyph 12/12/16)。\n // Radio 指示器是 filled dot 非 glyph,真值 = 上方 `const dotSize` map 8/8/10;\n // 控件框 16/16/20 與 Checkbox 對齊但那是 box 非「指示器」,不入此鍵(避免 metadata 語意 drift)。\n sm: { fieldHeight: 28, iconSize: 8, typography: 'body' },\n md: { fieldHeight: 32, iconSize: 8, typography: 'body' },\n lg: { fieldHeight: 36, iconSize: 10, typography: 'body-lg' },\n },\n // 2026-07-04 修 stale meta:移除 'active' — radioItemVariants cva 無 active-state 樣式,\n // Radix radio 也無 data-state=active(對照 checkboxMeta 正確不含 active)\n // 'checked' = 選定持續態(card 式 = border+text primary;2026-07-07 meta 詞彙統一補列)\n states: ['default', 'hover', 'checked', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-disabled', 'bg-surface'],\n fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground', 'text-primary'],\n ring: ['ring-ring'],\n },\n defaultSize: 'md',\n} as const\n\nexport { RadioGroup, RadioGroupItem, radioItemVariants }\n"],"names":[],"mappings":";;;;;;;;;AA6CA,MAAM,4BAA4B,MAAM,cAAc,KAAK;AAK3D,MAAM,4BAA4B,MAAM,cAAc,KAAK;AAG3D,SAAS,uBAAuB,UAA2B,eAAoD;AAC7G,MAAI,gBAAiC;AACrC,QAAM,SAAS,QAAQ,UAAU,CAAC,UAAU;AAC1C,QAAI,CAAC,MAAM,eAAe,KAAK,EAAG;AAClC,UAAM,SAAS,MAAM;AAErB,QAAI,OAAO,UAAU,iBAAiB,OAAO,UAAU,QAAW;AAChE,sBAAgB,OAAO,SAAS;AAChC;AAAA,IACF;AAEA,UAAM,UAAU,OAAO;AACvB,QAAI,MAAM,eAAe,OAAO,GAAG;AACjC,YAAM,eAAgB,QAAQ,MAA8B;AAC5D,UAAI,iBAAiB,eAAe;AAClC,wBAAgB,OAAO,SAAS;AAAA,MAClC;AAAA,IACF;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,MAAM,aAAa,MAAM,WAGvB,CAAC,EAAE,WAAW,MAAM,SAAS,SAAS,OAAO,cAAc,GAAG,MAAA,GAAS,QAAQ;AAE/E,QAAM,eAAe,qBAAqB,EAAE,MAAM,UAAW,MAAiC,UAAU;AACxG,QAAM,eAAe,qBAAA;AACrB,QAAM,WAAW,gBAAA;AAEjB,QAAM,kBAAkB,qBAAqB,QAAW,IAAI;AAS5D,QAAM;AAAA,IACJ,eAAe;AAAA,IACf,UAAU;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,IACN,aAAa;AAAA,IACb,KAAK;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,GAAG;AAAA,EAAA,IACD;AAEJ,MAAI,iBAAiB,WAAW;AAC9B,UAAM,gBAAiB,SAAS;AAChC,QAAI,CAAC,eAAe;AAClB,iCAAQ,OAAA,EAAK,GAAG,cAAc,KAAuC,MAAK,SAAQ,WAAW,GAAG,QAAQ,SAAS,GAAG,8BAAC,QAAA,EAAK,WAAW,qBAAqB,YAAY,GAAI,wBAAa,GAAO;AAAA,IAChM;AACA,UAAM,gBAAgB,uBAAuB,MAAM,UAAU,aAAa;AAC1E,+BACG,OAAA,EAAK,GAAG,cAAc,KAAuC,MAAK,SAAQ,WAAW,GAAG,QAAQ,SAAS,GACxG,UAAA,oBAAC,QAAA,EAAK,WAAU,mBAAmB,UAAA,iBAAiB,eAAc,GACpE;AAAA,EAEJ;AAMA,MAAI,iBAAiB,eAAc,qCAAU,qBAAoB,MAAM;AACrE,UAAM,gBAAiB,SAAS;AAChC,UAAM,gBAAgB,gBAAgB,uBAAuB,MAAM,UAAU,aAAa,IAAI;AAC9F,UAAM,UAAU;AAChB,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACE,GAAG;AAAA,QACJ;AAAA,QACA,MAAK;AAAA,QACL,iBAAc;AAAA,QACd,mBAAiB,qCAAU;AAAA,QAC3B,iBAAc,qCAAU,YAAW;AAAA,QACnC,iBAAc;AAAA,QACd,UAAU;AAAA,QACV,WAAW;AAAA,UACT,mBAAmB,EAAE,MAAM,SAAS,MAAM,YAAY,SAAS,WAAW;AAAA,UAC1E;AAAA,UACA;AAAA,QAAA;AAAA,QAGD,UAAA,gBACG,oBAAC,QAAA,EAAK,WAAU,mBAAmB,UAAA,iBAAiB,cAAA,CAAc,IAClE,oBAAC,QAAA,EAAK,WAAW,qBAAqB,YAAY,GAAI,UAAA,aAAA,CAAa;AAAA,MAAA;AAAA,IAAA;AAAA,EAG7E;AAIA,SACE,oBAAC,0BAA0B,UAA1B,EAAmC,OAAO,iBAAiB,YAC1D,UAAA,oBAAC,0BAA0B,UAA1B,EAAmC,OAAO,iBAAiB,YAC1D,UAAA;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,WAAW,GAAG,QAAQ,SAAS;AAAA,MAC/B;AAAA,MACA;AAAA,MAKA,mBAAiB,qCAAU;AAAA,MAC3B,iBAAc,qCAAU,YAAW;AAAA,MACnC,oBAAkB,qCAAU;AAAA,MAI5B,iBAAe,iBAAiB,cAAc;AAAA,MAC7C,GAAG;AAAA,MACJ,UAAU,iBAAiB;AAAA,MAC3B;AAAA,IAAA;AAAA,EAAA,GAEJ,EAAA,CACF;AAEJ,CAAC;AACD,WAAW,cAAc;AAKvB,WAAmD,cAAc;AAKnE,MAAM,oBAAoB;AAAA,EACxB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA,IACN;AAAA,IAEF,iBAAiB;AAAA,MACf,MAAM;AAAA,IAAA;AAAA,EACR;AAEJ;AAGA,MAAM,UAAkC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAA;AAmC5D,MAAM,iBAAiB,MAAM;AAAA,EAI3B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,IAAI;AAAA,IACJ,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,UAAU,QAAQ;AACxB,UAAM,QAAQ,QAAQ,OAAO;AAa7B,UAAM,gBAAgB,MAAM,WAAW,yBAAyB;AAChE,UAAM,mBAAmB,yBAAyB,QAAQ,KAAK;AAE/D,UAAM,gBAAgB,MAAM,WAAW,yBAAyB;AAChE,UAAM,oBAAoB,YAAY;AAEtC,UAAM,cAAc,MAAM,MAAA;AAC1B,UAAM,UAAU,UAAU;AAE1B,UAAM,SACJ;AAAA,MAAC,oBAAoB;AAAA,MAApB;AAAA,QACC,IAAI;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,QAIV,iBAAe,qBAAqB;AAAA,QACpC,UAAU,oBAAoB,KAAK;AAAA,QACnC,WAAW,GAAG,kBAAkB,EAAE,KAAA,CAAM,GAAG,SAAS;AAAA,QACnD,GAAG;AAAA,QAMJ,SAAS,CAAC,UAAU;;AAClB,cAAI,mBAAmB;AACrB,kBAAM,eAAA;AACN;AAAA,UACF;AACA,sBAAM,YAAN,+BAAgB;AAAA,QAClB;AAAA,QAEA,UAAA,oBAAC,oBAAoB,WAApB,EAA8B,WAAU,6BACvC,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO,EAAE,OAAO,OAAO,QAAQ,MAAA;AAAA,YAC/B,WAAU;AAAA,UAAA;AAAA,QAAA,EACZ,CACF;AAAA,MAAA;AAAA,IAAA;AAKJ,QAAI,SAAS,KAAM,QAAO;AAG1B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,UAAU;AAAA,QACV,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAGZ;AACF;AACA,eAAe,cAAc;AAItB,MAAM,iBAAiB;AAAA,EAC5B,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO;AAAA;AAAA;AAAA;AAAA,IAIL,IAAI,EAAE,aAAa,IAAI,UAAU,GAAG,YAAY,OAAA;AAAA,IAChD,IAAI,EAAE,aAAa,IAAI,UAAU,GAAG,YAAY,OAAA;AAAA,IAChD,IAAI,EAAE,aAAa,IAAI,UAAU,IAAI,YAAY,UAAA;AAAA,EAAU;AAAA;AAAA;AAAA;AAAA,EAK7D,QAAQ,CAAC,WAAW,SAAS,WAAW,iBAAiB,UAAU;AAAA,EACnE,QAAQ;AAAA,IACN,IAAI,CAAC,eAAe,YAAY;AAAA,IAChC,IAAI,CAAC,oBAAoB,qBAAqB,mBAAmB,cAAc;AAAA,IAC/E,MAAM,CAAC,WAAW;AAAA,EAAA;AAAA,EAEpB,aAAa;AACf;"}
@@ -27,7 +27,17 @@ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
27
27
  * - 單行 truncate(用 text-overflow:ellipsis 就夠)
28
28
  * - 極短內容(不會捲動 → 不需 wrapper)
29
29
  */
30
- declare const ScrollArea: React.ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
+ declare const ScrollArea: React.ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
31
+ /** 2026-07-08(WM 戰役 R2):Radix Viewport 內建 wrapper `display:table; min-width:100%`
32
+ * 讓垂直捲動容器內的 flex 多欄佈局以 max-content 佈局、突破 100% 寬(WM 兩欄 dialog
33
+ * 右欄被裁 33px 實證)。垂直捲動場景 opt-in 此 prop 中和為 block/w-full(水平捲動
34
+ * 場景勿開 — table wrapper 是其 min-content 溢出機制)。 */
35
+ fillX?: boolean;
36
+ /** Viewport tabIndex(預設 0 = axe scrollable-region-focusable fix)。內容已有自帶
37
+ * focusable 元素(如 TimePicker role=listbox tabIndex=0)時傳 -1 消除雙 tab stop —
38
+ * axe 由內部 focusable 內容滿足,焦點直接落在有 keydown 的 listbox 上。 */
39
+ viewportTabIndex?: 0 | -1;
40
+ } & React.RefAttributes<HTMLDivElement>>;
31
41
  declare const ScrollBar: React.ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaScrollbarProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
32
42
  export declare const scrollAreaMeta: {
33
43
  readonly component: "ScrollArea";
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-area.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollArea/scroll-area.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,mBAAmB,MAAM,6BAA6B,CAAA;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,QAAA,MAAM,UAAU,+JA2Bd,CAAA;AAGF,QAAA,MAAM,SAAS,wKAwBb,CAAA;AAKF,eAAO,MAAM,cAAc;;;;;;;;;;;CAejB,CAAA;AAEV,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAA"}
1
+ {"version":3,"file":"scroll-area.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollArea/scroll-area.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,mBAAmB,MAAM,6BAA6B,CAAA;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,QAAA,MAAM,UAAU;IAGZ;;;qDAGiD;YACzC,OAAO;IACf;;8DAE0D;uBACvC,CAAC,GAAG,CAAC,CAAC;wCA6B3B,CAAA;AAGF,QAAA,MAAM,SAAS,wKAwBb,CAAA;AAKF,eAAO,MAAM,cAAc;;;;;;;;;;;CAejB,CAAA;AAEV,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAA"}
@@ -2,7 +2,7 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
4
4
  import { cn } from "../../lib/utils.js";
5
- const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
5
+ const ScrollArea = React.forwardRef(({ className, children, fillX, viewportTabIndex = 0, ...props }, ref) => /* @__PURE__ */ jsxs(
6
6
  ScrollAreaPrimitive.Root,
7
7
  {
8
8
  ref,
@@ -12,8 +12,11 @@ const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) =>
12
12
  /* @__PURE__ */ jsx(
13
13
  ScrollAreaPrimitive.Viewport,
14
14
  {
15
- tabIndex: 0,
16
- className: "flex-1 min-h-0 w-full rounded-[inherit] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring",
15
+ tabIndex: viewportTabIndex,
16
+ className: cn(
17
+ "flex-1 min-h-0 w-full rounded-[inherit] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring",
18
+ fillX && "[&>div]:!block [&>div]:!w-full [&>div]:!min-w-0"
19
+ ),
17
20
  children
18
21
  }
19
22
  ),
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-area.js","sources":["../../../src/components/ScrollArea/scroll-area.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 ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\nimport { cn } from '@/lib/utils'\n\n/**\n * ScrollArea — 自訂樣式的捲動區(Radix ScrollArea primitive 包裝)\n *\n * 世界級對照:shadcn `ScrollArea` / Ant Design scrollable 容器的 pattern。\n *\n * ── 為什麼需要 ScrollArea ──\n * Native scrollbar 跨 OS 不一致:\n * macOS: overlay(不吃寬度,預設隱藏,滾動時浮出)\n * Windows / Linux: always-visible(永遠吃 ~15-17px 寬度)\n *\n * 結果:同一個 DataTable / Sheet / Dialog 內容在 macOS 看起來對齊,在 Windows\n * 右側被吃 17px 跑版(「Left pinned + Row Actions」那張圖的問題)。\n *\n * ScrollArea 用 Radix 包裝自建 overlay 捲軸 → **跨 OS 一致不吃寬度**,\n * 捲動時浮現(hover / scroll 自動顯示)。\n *\n * ── 何時用 ──\n * - DataTable 橫向捲動(水平跑版最明顯場景)\n * - Sheet / Dialog 垂直內容捲動(body 太長)\n * - Sidebar nav 長列表\n * - 任何「內容可能溢出容器」且「跨 OS 視覺必須一致」的場景\n *\n * ── 何時不用 ──\n * - 全頁捲動(瀏覽器 document scroll,保持 native 即可;ScrollArea 是 sub-region)\n * - 單行 truncate(用 text-overflow:ellipsis 就夠)\n * - 極短內容(不會捲動 → 不需 wrapper)\n */\n\nconst ScrollArea = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n <ScrollAreaPrimitive.Root\n ref={ref}\n className={cn('relative flex flex-col overflow-hidden', className)}\n {...props}\n >\n {/* Viewport canonical(2026-04-23):Root 用 `flex flex-col`,Viewport 用 `flex-1 min-h-0`。\n 原 `h-full` 在 Root 為 flex item(外層 flex-1 min-h-0)時失效 — Chrome 不把\n flex-computed 高度視為 `height: 100%` 的 definite anchor,導致 Viewport 撐成\n content height 而非 parent height → 失去 scroll 能力。\n 改 Root 為 flex container,Viewport 成 flex item,flex algorithm 給 definite height。\n `w-full` 保留(水平維度不受影響)。 */}\n {/* a11y(2026-04-25 axe scrollable-region-focusable fix):Viewport 需 tabIndex=0\n 才能 keyboard focus(Safari 不自動把 scroll container 標 focusable)。focus-\n visible:outline 用 DS focus ring,不破壞視覺。 */}\n <ScrollAreaPrimitive.Viewport\n tabIndex={0}\n className=\"flex-1 min-h-0 w-full rounded-[inherit] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n))\nScrollArea.displayName = 'ScrollArea'\n\nconst ScrollBar = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none transition-colors',\n orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',\n orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',\n className,\n )}\n {...props}\n >\n {/* Thumb 用 scrollbar-thumb / -hover semantic alias(2026-05-09 抽 SSOT,跟 DataTable fake scrollbar\n 共享 token)。Token 值仍 = `--border` / `--border-hover`(neutral-5/-6)— 世界級 SaaS\n (Linear / Notion / Figma / macOS)scrollbar thumb 慣例「很淡、幾乎看不見,hover 略深」。\n 前身直接 `bg-border` borrowing(2026-04 ship)→ semantic alias 收斂 SSOT(避免 thumb 視覺\n 未來演化時誤動 Field/Input/Checkbox border 視覺)。對齊 shadcn ScrollArea source `bg-border`。 */}\n <ScrollAreaPrimitive.ScrollAreaThumb\n className={cn('relative flex-1 rounded-full bg-[var(--scrollbar-thumb)] hover:bg-[var(--scrollbar-thumb-hover)] transition-colors')}\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n))\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.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 scrollAreaMeta = {\n component: 'ScrollArea',\n family: 'self-contained', // 對齊 scroll-area.spec.md frontmatter(SSOT;同 Separator/OverflowIndicator meta 寫法)\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default'],\n tokens: {\n bg: [],\n fg: [],\n ring: [],\n },\n} as const\n\nexport { ScrollArea, ScrollBar }\n"],"names":[],"mappings":";;;;AAiCA,MAAM,aAAa,MAAM,WAGvB,CAAC,EAAE,WAAW,UAAU,GAAG,SAAS,QACpC;AAAA,EAAC,oBAAoB;AAAA,EAApB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,0CAA0C,SAAS;AAAA,IAChE,GAAG;AAAA,IAWJ,UAAA;AAAA,MAAA;AAAA,QAAC,oBAAoB;AAAA,QAApB;AAAA,UACC,UAAU;AAAA,UACV,WAAU;AAAA,UAET;AAAA,QAAA;AAAA,MAAA;AAAA,0BAEF,WAAA,EAAU;AAAA,MACX,oBAAC,oBAAoB,QAApB,CAAA,CAA2B;AAAA,IAAA;AAAA,EAAA;AAC9B,CACD;AACD,WAAW,cAAc;AAEzB,MAAM,YAAY,MAAM,WAGtB,CAAC,EAAE,WAAW,cAAc,YAAY,GAAG,SAAS,QACpD;AAAA,EAAC,oBAAoB;AAAA,EAApB;AAAA,IACC;AAAA,IACA;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA,gBAAgB,cAAc;AAAA,MAC9B,gBAAgB,gBAAgB;AAAA,MAChC;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAOJ,UAAA;AAAA,MAAC,oBAAoB;AAAA,MAApB;AAAA,QACC,WAAW,GAAG,oHAAoH;AAAA,MAAA;AAAA,IAAA;AAAA,EACpI;AACF,CACD;AACD,UAAU,cAAc,oBAAoB,oBAAoB;AAIzD,MAAM,iBAAiB;AAAA,EAC5B,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA,EAGP,QAAQ,CAAC,SAAS;AAAA,EAClB,QAAQ;AAAA,IACN,IAAI,CAAA;AAAA,IACJ,IAAI,CAAA;AAAA,IACJ,MAAM,CAAA;AAAA,EAAC;AAEX;"}
1
+ {"version":3,"file":"scroll-area.js","sources":["../../../src/components/ScrollArea/scroll-area.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 ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\nimport { cn } from '@/lib/utils'\n\n/**\n * ScrollArea — 自訂樣式的捲動區(Radix ScrollArea primitive 包裝)\n *\n * 世界級對照:shadcn `ScrollArea` / Ant Design scrollable 容器的 pattern。\n *\n * ── 為什麼需要 ScrollArea ──\n * Native scrollbar 跨 OS 不一致:\n * macOS: overlay(不吃寬度,預設隱藏,滾動時浮出)\n * Windows / Linux: always-visible(永遠吃 ~15-17px 寬度)\n *\n * 結果:同一個 DataTable / Sheet / Dialog 內容在 macOS 看起來對齊,在 Windows\n * 右側被吃 17px 跑版(「Left pinned + Row Actions」那張圖的問題)。\n *\n * ScrollArea 用 Radix 包裝自建 overlay 捲軸 → **跨 OS 一致不吃寬度**,\n * 捲動時浮現(hover / scroll 自動顯示)。\n *\n * ── 何時用 ──\n * - DataTable 橫向捲動(水平跑版最明顯場景)\n * - Sheet / Dialog 垂直內容捲動(body 太長)\n * - Sidebar nav 長列表\n * - 任何「內容可能溢出容器」且「跨 OS 視覺必須一致」的場景\n *\n * ── 何時不用 ──\n * - 全頁捲動(瀏覽器 document scroll,保持 native 即可;ScrollArea 是 sub-region)\n * - 單行 truncate(用 text-overflow:ellipsis 就夠)\n * - 極短內容(不會捲動 → 不需 wrapper)\n */\n\nconst ScrollArea = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {\n /** 2026-07-08(WM 戰役 R2):Radix Viewport 內建 wrapper `display:table; min-width:100%`\n * 讓垂直捲動容器內的 flex 多欄佈局以 max-content 佈局、突破 100% 寬(WM 兩欄 dialog\n * 右欄被裁 33px 實證)。垂直捲動場景 opt-in 此 prop 中和為 block/w-full(水平捲動\n * 場景勿開 — table wrapper 是其 min-content 溢出機制)。 */\n fillX?: boolean\n /** Viewport tabIndex(預設 0 = axe scrollable-region-focusable fix)。內容已有自帶\n * focusable 元素(如 TimePicker role=listbox tabIndex=0)時傳 -1 消除雙 tab stop —\n * axe 由內部 focusable 內容滿足,焦點直接落在有 keydown 的 listbox 上。 */\n viewportTabIndex?: 0 | -1\n }\n>(({ className, children, fillX, viewportTabIndex = 0, ...props }, ref) => (\n <ScrollAreaPrimitive.Root\n ref={ref}\n className={cn('relative flex flex-col overflow-hidden', className)}\n {...props}\n >\n {/* Viewport canonical(2026-04-23):Root 用 `flex flex-col`,Viewport 用 `flex-1 min-h-0`。\n 原 `h-full` 在 Root 為 flex item(外層 flex-1 min-h-0)時失效 — Chrome 不把\n flex-computed 高度視為 `height: 100%` 的 definite anchor,導致 Viewport 撐成\n content height 而非 parent height → 失去 scroll 能力。\n 改 Root 為 flex container,Viewport 成 flex item,flex algorithm 給 definite height。\n `w-full` 保留(水平維度不受影響)。 */}\n {/* a11y(2026-04-25 axe scrollable-region-focusable fix):Viewport 需 tabIndex=0\n 才能 keyboard focus(Safari 不自動把 scroll container 標 focusable)。focus-\n visible:outline 用 DS focus ring,不破壞視覺。 */}\n <ScrollAreaPrimitive.Viewport\n tabIndex={viewportTabIndex}\n className={cn(\n 'flex-1 min-h-0 w-full rounded-[inherit] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',\n fillX && '[&>div]:!block [&>div]:!w-full [&>div]:!min-w-0',\n )}\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n))\nScrollArea.displayName = 'ScrollArea'\n\nconst ScrollBar = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none transition-colors',\n orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',\n orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',\n className,\n )}\n {...props}\n >\n {/* Thumb 用 scrollbar-thumb / -hover semantic alias(2026-05-09 抽 SSOT,跟 DataTable fake scrollbar\n 共享 token)。Token 值仍 = `--border` / `--border-hover`(neutral-5/-6)— 世界級 SaaS\n (Linear / Notion / Figma / macOS)scrollbar thumb 慣例「很淡、幾乎看不見,hover 略深」。\n 前身直接 `bg-border` borrowing(2026-04 ship)→ semantic alias 收斂 SSOT(避免 thumb 視覺\n 未來演化時誤動 Field/Input/Checkbox border 視覺)。對齊 shadcn ScrollArea source `bg-border`。 */}\n <ScrollAreaPrimitive.ScrollAreaThumb\n className={cn('relative flex-1 rounded-full bg-[var(--scrollbar-thumb)] hover:bg-[var(--scrollbar-thumb-hover)] transition-colors')}\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n))\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.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 scrollAreaMeta = {\n component: 'ScrollArea',\n family: 'self-contained', // 對齊 scroll-area.spec.md frontmatter(SSOT;同 Separator/OverflowIndicator meta 寫法)\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default'],\n tokens: {\n bg: [],\n fg: [],\n ring: [],\n },\n} as const\n\nexport { ScrollArea, ScrollBar }\n"],"names":[],"mappings":";;;;AAiCA,MAAM,aAAa,MAAM,WAavB,CAAC,EAAE,WAAW,UAAU,OAAO,mBAAmB,GAAG,GAAG,MAAA,GAAS,QACjE;AAAA,EAAC,oBAAoB;AAAA,EAApB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,0CAA0C,SAAS;AAAA,IAChE,GAAG;AAAA,IAWJ,UAAA;AAAA,MAAA;AAAA,QAAC,oBAAoB;AAAA,QAApB;AAAA,UACC,UAAU;AAAA,UACV,WAAW;AAAA,YACT;AAAA,YACA,SAAS;AAAA,UAAA;AAAA,UAGV;AAAA,QAAA;AAAA,MAAA;AAAA,0BAEF,WAAA,EAAU;AAAA,MACX,oBAAC,oBAAoB,QAApB,CAAA,CAA2B;AAAA,IAAA;AAAA,EAAA;AAC9B,CACD;AACD,WAAW,cAAc;AAEzB,MAAM,YAAY,MAAM,WAGtB,CAAC,EAAE,WAAW,cAAc,YAAY,GAAG,SAAS,QACpD;AAAA,EAAC,oBAAoB;AAAA,EAApB;AAAA,IACC;AAAA,IACA;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA,gBAAgB,cAAc;AAAA,MAC9B,gBAAgB,gBAAgB;AAAA,MAChC;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAOJ,UAAA;AAAA,MAAC,oBAAoB;AAAA,MAApB;AAAA,QACC,WAAW,GAAG,oHAAoH;AAAA,MAAA;AAAA,IAAA;AAAA,EACpI;AACF,CACD;AACD,UAAU,cAAc,oBAAoB,oBAAoB;AAIzD,MAAM,iBAAiB;AAAA,EAC5B,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA,EAGP,QAAQ,CAAC,SAAS;AAAA,EAClB,QAAQ;AAAA,IACN,IAAI,CAAA;AAAA,IACJ,IAAI,CAAA;AAAA,IACJ,MAAM,CAAA;AAAA,EAAC;AAEX;"}
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { LucideIcon } from 'lucide-react';
3
- import type { FieldMode, FieldVariant } from '../../components/Field/field-types';
3
+ import type { FieldMode, FieldVariant, FieldWidth } from '../../components/Field/field-types';
4
4
  import { type SelectMenuOption } from '../../components/SelectMenu/select-menu';
5
5
  /**
6
6
  * Select 用的 option schema(2026-05-10 Issue 4 + post-prune unify):**explicit extends
@@ -27,10 +27,48 @@ export interface SelectGroupConfig {
27
27
  key: string;
28
28
  label: string;
29
29
  }
30
- export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size' | 'value' | 'defaultValue' | 'onChange'> {
30
+ /**
31
+ * 原生 `<select>` 屬性 allowlist(2026-07-14 API 策展 D,user 拍板「全部收窄」)。
32
+ *
33
+ * 舊契約 `extends Omit<SelectHTMLAttributes, ...>` 型別承諾**全部**原生 select 屬性,但桌機
34
+ * 自訂 combobox(div trigger + hidden input mirror)只有部分真生效 — 其餘 silent no-op /
35
+ * 被元件內部覆寫,「型別承諾 ≠ 實作出口」= footgun。改 `Pick<>` 顯式 allowlist,**只承諾
36
+ * 兩路徑(desktop CustomSelect / mobile NativeSelect)皆有實作出口的屬性**,盤點以 code 為準:
37
+ *
38
+ * - `id` — 桌機 trigger div / mobile `<select>`,皆 `idProp ?? fieldCtx?.id`
39
+ * - `name` / `form` / `required` — 桌機經 hidden input mirror(D2 2026-07-13,見 hiddenInputEl
40
+ * docblock)/ mobile 原生 `<select>` spread
41
+ * - `disabled` — 兩路徑 `useResolvedFieldDisabled` cascade → mode 分流
42
+ * - `className` / `style` — 兩路徑 field wrapper
43
+ * - `onFocus` / `onBlur` — 桌機 rest spread 到 focusable trigger(tabIndex=0)/ mobile spread
44
+ * - `onKeyDown` — 桌機 consumer handler 先跑再走元件導覽(dim-9)/ mobile 原生
45
+ * - `aria-label` / `aria-describedby` / `aria-errormessage` — 兩路徑顯式接線(fieldCtx fallback)
46
+ * - `autoFocus` / `autoComplete` — **mobile-only**:NativeSelect 原生 `<select>` 生效;桌機
47
+ * custom combobox 無原生對應 = documented no-op(桌機 autofill 由 hidden input mirror 的
48
+ * option-match onChange guard 承擔)
49
+ * - `data-*` — 兩路徑 spread passthrough(dim-9 契約;index signature 顯式承諾,見下)
50
+ *
51
+ * 被砍者(原 extends 承諾但從未生效 / 被元件覆寫,不再進型別):`multiple` / `children` /
52
+ * `tabIndex`(桌機恆 0)/ `aria-labelledby`(桌機由 fieldCtx.labelId / aria-label 管理,
53
+ * consumer 值會被覆寫)/ 其餘 generic HTMLAttributes(title / dir / onClick 等)。
54
+ * 需要時先接線(兩路徑等效)再入表,**不回 extends 全開**。
55
+ *
56
+ * 世界級對照:自訂 combobox 皆走 curated 原生屬性子集,無人 extends 全部 SelectHTMLAttributes —
57
+ * Radix Select.Root 顯式列舉 name/disabled/required/form/autoComplete
58
+ * (radix-ui.com/primitives/docs/components/select#root)/ Headless UI Listbox name/form/disabled
59
+ * (headlessui.com/react/listbox#using-with-html-forms)/ React Aria Select name/autoComplete/
60
+ * isDisabled/isRequired(react-spectrum.adobe.com/react-aria/Select.html)。
61
+ */
62
+ export interface SelectProps extends Pick<React.SelectHTMLAttributes<HTMLSelectElement>, 'id' | 'className' | 'style' | 'name' | 'form' | 'required' | 'disabled' | 'onFocus' | 'onBlur' | 'onKeyDown' | 'aria-label' | 'aria-describedby' | 'aria-errormessage' | 'autoFocus' | 'autoComplete'> {
63
+ /** `data-*` passthrough(桌機 trigger div rest spread / mobile `<select>` spread;dim-9 契約)。 */
64
+ [dataAttribute: `data-${string}`]: unknown;
31
65
  mode?: FieldMode;
32
66
  /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */
33
67
  variant?: FieldVariant;
68
+ /** 寬度軸 — `fill` 填滿容器(default)/ `hug` 依內容收縮:value 寬 + slot 元素寬 + gap +
69
+ * field 內 padding;chrome 與互動不變。場景 = detail pane inline metadata。
70
+ * SSOT → field-controls.spec.md「寬度軸(width: fill / hug)」。 */
71
+ width?: FieldWidth;
34
72
  error?: boolean;
35
73
  size?: 'sm' | 'md' | 'lg';
36
74
  options: SelectOption[];
@@ -76,13 +114,16 @@ export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectE
76
114
  */
77
115
  showDisplayEndIcon?: boolean;
78
116
  /**
79
- * Trigger 內「已選項目」客製 render(2026-05-07 v15.5)。
117
+ * 「已選項目」客製 render(2026-05-07 v15.5;2026-07-08 A 案回歸修正擴及 4 mode)。
80
118
  *
81
- * 設了 → trigger 不走純文字 / Tag 預設 path,改用 consumer 提供的 ReactNode(收 selectedOpt)。
119
+ * 設了 → 已選值不走純文字 / Tag 預設 path,改用 consumer 提供的 ReactNode(收 selectedOpt)。
120
+ * **display / readonly / disabled / edit 4 mode 共享同一 renderer**(field-controls.spec.md
121
+ * 共享 contract (a),禁 edit-only)— renderer 輸出屬「值內容」(status icon+text 等語意呈現),
122
+ * display 態照常渲染;A 案撤的是 affordance(chevron/outline),非值內容(field.spec.md L6 分層)。
82
123
  * Searchable+open 仍走 input(搜尋優先)。Empty value(no selection)仍走 placeholder。
83
124
  *
84
125
  * 用例:PeoplePicker 用此 slot 把 single 選中的 person render 成 PersonDisplay
85
- * (avatar + name)而非純文字 label。對齊 PeoplePicker = Select wrapper SSOT。
126
+ * (avatar + name)而非純文字 label;DataTable status 欄 icon+text。對齊 PeoplePicker = Select wrapper SSOT。
86
127
  */
87
128
  selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode;
88
129
  /** 搜尋無結果提示(2026-07-04 Q4 拍板接線 — 原 spec 宣稱可覆寫但 prop 從未轉發)。
@@ -1 +1 @@
1
- {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/components/Select/select.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,8CAA8C,CAAA;AAK3F,OAAO,EAAwD,KAAK,gBAAgB,EAAE,MAAM,mDAAmD,CAAA;AAc/I;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAID,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC;IAC3G,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB;;;;kGAI8F;IAC9F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACzB,SAAS,CAAC,EAAE,UAAU,CAAA;IACtB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;4CAGwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;0EAEsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;yFACqF;IACrF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;;;;OAQG;IACH,oBAAoB,CAAC,EAAE,CAAC,WAAW,EAAE,YAAY,KAAK,KAAK,CAAC,SAAS,CAAA;IACrE;;;qFAGiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwjBD,QAAA,MAAM,MAAM,wGAUX,CAAA;AAKD,eAAO,MAAM,UAAU;;;;;;;;;;;CAgBb,CAAA;AAEV,OAAO,EAAE,MAAM,EAAE,CAAA"}
1
+ {"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/components/Select/select.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAwB,UAAU,EAAE,MAAM,8CAA8C,CAAA;AAK7H,OAAO,EAAwD,KAAK,gBAAgB,EAAE,MAAM,mDAAmD,CAAA;AAc/I;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,WACf,SAAQ,IAAI,CACV,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAE3C,IAAI,GAAG,WAAW,GAAG,OAAO,GAC5B,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,GACzC,SAAS,GAAG,QAAQ,GAAG,WAAW,GAClC,YAAY,GAAG,kBAAkB,GAAG,mBAAmB,GAEvD,WAAW,GAAG,cAAc,CAC/B;IACD,4FAA4F;IAC5F,CAAC,aAAa,EAAE,QAAQ,MAAM,EAAE,GAAG,OAAO,CAAA;IAC1C,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB;;iEAE6D;IAC7D,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC5B,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB;;;;kGAI8F;IAC9F,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACzB,SAAS,CAAC,EAAE,UAAU,CAAA;IACtB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;4CAGwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;0EAEsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;yFACqF;IACrF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;;;;;;;OAWG;IACH,oBAAoB,CAAC,EAAE,CAAC,WAAW,EAAE,YAAY,KAAK,KAAK,CAAC,SAAS,CAAA;IACrE;;;qFAGiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAgqBD,QAAA,MAAM,MAAM,wGAUX,CAAA;AAKD,eAAO,MAAM,UAAU;;;;;;;;;;;CAgBb,CAAA;AAEV,OAAO,EAAE,MAAM,EAAE,CAAA"}
@@ -2,10 +2,10 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { ChevronDown, X } from "lucide-react";
4
4
  import { cn } from "../../lib/utils.js";
5
- import { bareInputStyles, fieldWrapperStyles, EMPTY_DISPLAY, fieldDisplayTextClass, nakedCellRowModeAlign } from "../Field/field-wrapper.js";
5
+ import { bareInputStyles, fieldWrapperStyles, fieldDisplayTextClass, nakedCellRowModeAlign } from "../Field/field-wrapper.js";
6
6
  import { Tag } from "../Tag/tag.js";
7
7
  import { ItemPrefix, ItemSuffix, ICON_SIZE, ItemInlineAction } from "../../patterns/element-anatomy/item-anatomy.js";
8
- import { useFieldContext, useResolvedFieldInvalid, useResolvedFieldDisabled, useResolvedFieldSize, useResolvedFieldMode, useResolvedFieldVariant } from "../Field/field-context.js";
8
+ import { useFieldContext, useResolvedFieldInvalid, useResolvedFieldDisabled, useResolvedFieldSize, useResolvedFieldMode, useResolvedFieldVariant, useFieldEmptyDisplay, fieldEmptyColorClass } from "../Field/field-context.js";
9
9
  import { useActiveDescendant, forwardKeyToListbox, SelectMenu } from "../SelectMenu/select-menu.js";
10
10
  import { useIsTouchDevice } from "../../hooks/use-is-touch-device.js";
11
11
  import { useControllable } from "../../hooks/use-controllable.js";
@@ -114,6 +114,7 @@ CustomSelectTriggerContent.displayName = "CustomSelectTriggerContent";
114
114
  function ReadonlyDisplay({
115
115
  mode,
116
116
  variant: variantProp,
117
+ width,
117
118
  size,
118
119
  options,
119
120
  value,
@@ -121,35 +122,41 @@ function ReadonlyDisplay({
121
122
  startIcon: StartIcon,
122
123
  className,
123
124
  placeholder,
124
- showDisplayEndIcon
125
+ showDisplayEndIcon,
126
+ selectedItemRenderer
125
127
  }) {
126
- var _a;
127
128
  const resolvedMode = mode ?? "readonly";
129
+ const emptyDisplay = useFieldEmptyDisplay();
128
130
  const variant = variantProp ?? "default";
129
131
  const sz = size ?? "md";
130
132
  const iconSize = getIconSize(sz);
131
- const label = ((_a = options == null ? void 0 : options.find((o) => o.value === value)) == null ? void 0 : _a.label) ?? value;
133
+ const selectedOpt = options == null ? void 0 : options.find((o) => o.value === value);
134
+ const label = (selectedOpt == null ? void 0 : selectedOpt.label) ?? value;
132
135
  const iconColor = resolvedMode === "disabled" ? "text-fg-disabled" : "text-fg-muted";
133
136
  const isTextDisplay = display !== "tag";
134
- const emptyColorCls = resolvedMode === "disabled" ? "text-fg-disabled" : "text-fg-muted";
135
- const emptyText = placeholder ?? EMPTY_DISPLAY;
137
+ const emptyColorCls = fieldEmptyColorClass(resolvedMode);
138
+ const emptyText = placeholder ?? emptyDisplay;
136
139
  if (resolvedMode === "display") {
137
140
  if (!showDisplayEndIcon) {
138
- if (!value) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(sz), "text-fg-muted", className), children: emptyText });
141
+ if (!value) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(sz), emptyColorCls, className), children: emptyText });
142
+ if (selectedItemRenderer && selectedOpt) {
143
+ return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(sz), "inline-flex items-center min-w-0", nakedCellRowModeAlign, className), children: selectedItemRenderer(selectedOpt) });
144
+ }
139
145
  if (isTextDisplay) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(sz), "truncate", className), children: label });
140
- const selOpt2 = options == null ? void 0 : options.find((o) => o.value === value);
141
- const tVariant2 = selOpt2 == null ? void 0 : selOpt2.tagVariant;
146
+ const tVariant2 = selectedOpt == null ? void 0 : selectedOpt.tagVariant;
142
147
  return /* @__PURE__ */ jsx(Tag, { size: sz, color: tVariant2, className, children: label });
143
148
  }
144
- const selOpt = options == null ? void 0 : options.find((o) => o.value === value);
145
- const tVariant = selOpt == null ? void 0 : selOpt.tagVariant;
149
+ const tVariant = selectedOpt == null ? void 0 : selectedOpt.tagVariant;
146
150
  return /* @__PURE__ */ jsxs(
147
151
  "div",
148
152
  {
149
- className: cn(fieldWrapperStyles({ mode: "display", variant, size: sz }), value && !isTextDisplay && tagPadding[sz], className),
153
+ className: cn(fieldWrapperStyles({ mode: "display", variant, width, size: sz }), value && !isTextDisplay && tagPadding[sz], className),
150
154
  "data-field-mode": "display",
151
155
  children: [
152
- isTextDisplay ? /* @__PURE__ */ jsx("span", { className: cn(bareInputStyles, "flex-1 min-w-0 truncate", !value && emptyColorCls), children: value ? label : emptyText }) : value ? /* @__PURE__ */ jsx(Tag, { size: sz, color: tVariant, children: label }) : /* @__PURE__ */ jsx("span", { className: cn("flex-1 min-w-0", emptyColorCls), children: emptyText }),
156
+ selectedItemRenderer && value && selectedOpt ? (
157
+ // renderer 優先(同 CustomSelectTriggerContent 優先序):值內容歸 renderer,chevron 歸 D-path chrome
158
+ /* @__PURE__ */ jsx("span", { className: cn("flex-1 min-w-0 inline-flex items-center", nakedCellRowModeAlign), children: selectedItemRenderer(selectedOpt) })
159
+ ) : isTextDisplay ? /* @__PURE__ */ jsx("span", { className: cn(bareInputStyles, "flex-1 min-w-0 truncate", !value && emptyColorCls), children: value ? label : emptyText }) : value ? /* @__PURE__ */ jsx(Tag, { size: sz, color: tVariant, children: label }) : /* @__PURE__ */ jsx("span", { className: cn("flex-1 min-w-0", emptyColorCls), children: emptyText }),
153
160
  /* @__PURE__ */ jsx(ItemSuffix, { children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, className: "text-fg-muted pointer-events-none", "aria-hidden": true }) })
154
161
  ]
155
162
  }
@@ -157,16 +164,22 @@ function ReadonlyDisplay({
157
164
  }
158
165
  const showIndicator = variant === "naked" ? !!showDisplayEndIcon : resolvedMode === "disabled";
159
166
  const ariaDisabled = resolvedMode === "disabled" ? true : void 0;
167
+ if (selectedItemRenderer && value && selectedOpt) {
168
+ return /* @__PURE__ */ jsxs("div", { className: cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), className), "data-field-mode": resolvedMode, "aria-disabled": ariaDisabled, children: [
169
+ StartIcon && /* @__PURE__ */ jsx(ItemPrefix, { children: /* @__PURE__ */ jsx(StartIcon, { size: iconSize, className: cn("pointer-events-none", iconColor), "aria-hidden": true }) }),
170
+ /* @__PURE__ */ jsx("span", { className: cn("flex-1 min-w-0 inline-flex items-center", nakedCellRowModeAlign, resolvedMode === "disabled" && "text-fg-disabled"), children: selectedItemRenderer(selectedOpt) }),
171
+ showIndicator && /* @__PURE__ */ jsx(ItemSuffix, { className: "pointer-events-none", children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, className: cn("shrink-0", iconColor), "aria-hidden": true }) })
172
+ ] });
173
+ }
160
174
  if (isTextDisplay) {
161
- return /* @__PURE__ */ jsxs("div", { className: cn(fieldWrapperStyles({ mode: resolvedMode, variant, size: sz }), className), "data-field-mode": resolvedMode, "aria-disabled": ariaDisabled, children: [
175
+ return /* @__PURE__ */ jsxs("div", { className: cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), className), "data-field-mode": resolvedMode, "aria-disabled": ariaDisabled, children: [
162
176
  StartIcon && /* @__PURE__ */ jsx(ItemPrefix, { children: /* @__PURE__ */ jsx(StartIcon, { size: iconSize, className: cn("pointer-events-none", iconColor), "aria-hidden": true }) }),
163
177
  /* @__PURE__ */ jsx("span", { className: cn("flex-1 min-w-0 truncate", resolvedMode === "disabled" && "text-fg-disabled"), children: value ? label : /* @__PURE__ */ jsx("span", { className: emptyColorCls, children: emptyText }) }),
164
178
  showIndicator && /* @__PURE__ */ jsx(ItemSuffix, { className: "pointer-events-none", children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, className: cn("shrink-0", iconColor), "aria-hidden": true }) })
165
179
  ] });
166
180
  }
167
- const selectedOpt = options == null ? void 0 : options.find((o) => o.value === value);
168
181
  const tagVariant = selectedOpt == null ? void 0 : selectedOpt.tagVariant;
169
- return /* @__PURE__ */ jsxs("div", { className: cn(fieldWrapperStyles({ mode: resolvedMode, variant, size: sz }), value && tagPadding[sz], className), style: { paddingRight: "var(--field-px)" }, "data-field-mode": resolvedMode, "aria-disabled": ariaDisabled, children: [
182
+ return /* @__PURE__ */ jsxs("div", { className: cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), value && tagPadding[sz], className), style: { paddingRight: "var(--field-px)" }, "data-field-mode": resolvedMode, "aria-disabled": ariaDisabled, children: [
170
183
  value ? /* @__PURE__ */ jsx(Tag, { size: sz, color: tagVariant, children: label }) : /* @__PURE__ */ jsx("span", { className: emptyColorCls, children: emptyText }),
171
184
  showIndicator && /* @__PURE__ */ jsx(ItemSuffix, { className: "pointer-events-none", children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, className: cn("shrink-0", iconColor), "aria-hidden": true }) })
172
185
  ] });
@@ -175,6 +188,7 @@ const NativeSelect = React.forwardRef(
175
188
  ({
176
189
  mode,
177
190
  variant: variantProp,
191
+ width,
178
192
  error: errorProp = false,
179
193
  size: sizeProp,
180
194
  options,
@@ -193,6 +207,9 @@ const NativeSelect = React.forwardRef(
193
207
  "aria-errormessage": ariaErrorMessageProp,
194
208
  // 2026-07-04:Custom-path-only props 顯式 destructure 丟棄(不 spread 到原生 <select> 消 React
195
209
  // unknown-prop warning);各 prop docblock 已註「僅 Custom path 生效」語意
210
+ // 2026-07-08 A 案回歸修正:selectedItemRenderer 從丟棄名單移出 — Native path 的
211
+ // ReadonlyDisplay(display/readonly/disabled)同樣消費值內容 renderer(contract (a) 4-mode
212
+ // 共享,值內容不因 pointer type 而異);native <select> edit 路徑仍不消費(原生 option 無法客製 render)。
196
213
  searchable: _searchable,
197
214
  groups: _groups,
198
215
  loading: _loading,
@@ -200,7 +217,7 @@ const NativeSelect = React.forwardRef(
200
217
  emptyText: _emptyText,
201
218
  defaultOpen: _defaultOpen,
202
219
  onOpenChange: _onOpenChange,
203
- selectedItemRenderer: _selectedItemRenderer,
220
+ selectedItemRenderer,
204
221
  ...props
205
222
  }, ref) => {
206
223
  const fieldCtx = useFieldContext();
@@ -225,7 +242,7 @@ const NativeSelect = React.forwardRef(
225
242
  else if (ref) ref.current = el;
226
243
  }, [ref]);
227
244
  if (resolvedMode !== "edit") {
228
- return /* @__PURE__ */ jsx(ReadonlyDisplay, { mode: resolvedMode, variant, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon });
245
+ return /* @__PURE__ */ jsx(ReadonlyDisplay, { mode: resolvedMode, variant, width, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon, selectedItemRenderer });
229
246
  }
230
247
  const selectEl = /* @__PURE__ */ jsxs(
231
248
  "select",
@@ -258,7 +275,7 @@ const NativeSelect = React.forwardRef(
258
275
  "div",
259
276
  {
260
277
  className: cn(
261
- fieldWrapperStyles({ mode: "edit", variant, size, error }),
278
+ fieldWrapperStyles({ mode: "edit", variant, width, size, error }),
262
279
  value && tagPadding[size],
263
280
  "relative",
264
281
  className
@@ -280,7 +297,7 @@ const NativeSelect = React.forwardRef(
280
297
  "div",
281
298
  {
282
299
  className: cn(
283
- fieldWrapperStyles({ mode: "edit", variant, size, error }),
300
+ fieldWrapperStyles({ mode: "edit", variant, width, size, error }),
284
301
  className
285
302
  ),
286
303
  "data-field-mode": "edit",
@@ -298,7 +315,46 @@ const NativeSelect = React.forwardRef(
298
315
  );
299
316
  NativeSelect.displayName = "NativeSelect";
300
317
  const CustomSelect = React.forwardRef(
301
- ({ mode, variant: variantProp, error: errorProp = false, size: sizeProp, options, groups, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, clearable = false, display = "plain", startIcon: StartIcon, searchable = false, loading, minRows, emptyText, defaultOpen = false, onOpenChange, selectedItemRenderer, showDisplayEndIcon, id: idProp, "aria-describedby": ariaDescribedByProp, "aria-errormessage": ariaErrorMessageProp, "aria-label": ariaLabel }, ref) => {
318
+ ({
319
+ mode,
320
+ variant: variantProp,
321
+ width,
322
+ error: errorProp = false,
323
+ size: sizeProp,
324
+ options,
325
+ groups,
326
+ value: valueProp,
327
+ defaultValue,
328
+ onChange,
329
+ placeholder,
330
+ className,
331
+ disabled: disabledProp,
332
+ name,
333
+ required,
334
+ form,
335
+ clearable = false,
336
+ display = "plain",
337
+ startIcon: StartIcon,
338
+ searchable = false,
339
+ loading,
340
+ minRows,
341
+ emptyText,
342
+ defaultOpen = false,
343
+ onOpenChange,
344
+ selectedItemRenderer,
345
+ showDisplayEndIcon,
346
+ id: idProp,
347
+ "aria-describedby": ariaDescribedByProp,
348
+ "aria-errormessage": ariaErrorMessageProp,
349
+ "aria-label": ariaLabel,
350
+ onKeyDown: onKeyDownProp,
351
+ style: styleProp,
352
+ // 2026-07-14 API 策展 D:mobile-only props(allowlist 註記)desktop 顯式丟棄 — div trigger 無原生
353
+ // 對應,不 spread 進 DOM(對稱 NativeSelect 丟棄 custom-path-only props 的既有 pattern)
354
+ autoFocus: _autoFocus,
355
+ autoComplete: _autoComplete,
356
+ ...rest
357
+ }, ref) => {
302
358
  const fieldCtx = useFieldContext();
303
359
  const error = useResolvedFieldInvalid(errorProp);
304
360
  const disabled = useResolvedFieldDisabled(disabledProp);
@@ -354,10 +410,28 @@ const CustomSelect = React.forwardRef(
354
410
  [setValue]
355
411
  );
356
412
  if (resolvedMode !== "edit") {
357
- return /* @__PURE__ */ jsx(ReadonlyDisplay, { mode: resolvedMode, variant, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon });
413
+ return /* @__PURE__ */ jsx(ReadonlyDisplay, { mode: resolvedMode, variant, width, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon, selectedItemRenderer });
358
414
  }
359
415
  const clearEl = showClear ? /* @__PURE__ */ jsx(SelectClearButton, { size: size ?? "md", onClear: () => setValue(""), stopPropagation: true }) : null;
360
416
  const chevronEl = /* @__PURE__ */ jsx(ItemSuffix, { children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, className: cn("text-fg-muted transition-transform", open && "rotate-180"), "aria-hidden": true }) });
417
+ const hiddenInputEl = /* @__PURE__ */ jsx(
418
+ "input",
419
+ {
420
+ type: "text",
421
+ name,
422
+ form,
423
+ value: value ?? "",
424
+ onChange: (e) => {
425
+ const matched = options.find((o) => o.value === e.target.value && !o.disabled);
426
+ if (matched) setValue(matched.value);
427
+ },
428
+ required: required || void 0,
429
+ disabled,
430
+ "aria-hidden": "true",
431
+ tabIndex: -1,
432
+ className: "absolute bottom-0 left-0 w-full opacity-0 pointer-events-none"
433
+ }
434
+ );
361
435
  const triggerContent = /* @__PURE__ */ jsx(
362
436
  CustomSelectTriggerContent,
363
437
  {
@@ -384,6 +458,7 @@ const CustomSelect = React.forwardRef(
384
458
  const trigger = /* @__PURE__ */ jsxs(
385
459
  "div",
386
460
  {
461
+ ...rest,
387
462
  ref,
388
463
  id: idProp ?? (fieldCtx == null ? void 0 : fieldCtx.id),
389
464
  role: "combobox",
@@ -393,23 +468,28 @@ const CustomSelect = React.forwardRef(
393
468
  "aria-label": ariaLabel,
394
469
  "aria-labelledby": ariaLabel ? void 0 : fieldCtx == null ? void 0 : fieldCtx.labelId,
395
470
  "aria-invalid": error || void 0,
396
- "aria-required": (fieldCtx == null ? void 0 : fieldCtx.required) || void 0,
471
+ "aria-required": required || (fieldCtx == null ? void 0 : fieldCtx.required) || void 0,
397
472
  "aria-describedby": ariaDescribedByProp ?? (fieldCtx == null ? void 0 : fieldCtx.descriptionId),
398
473
  "aria-errormessage": ariaErrorMessageProp ?? (error ? fieldCtx == null ? void 0 : fieldCtx.errorId : void 0),
399
474
  tabIndex: 0,
400
475
  className: cn(
401
- fieldWrapperStyles({ mode: "edit", variant, size, error }),
476
+ fieldWrapperStyles({ mode: "edit", variant, width, size, error }),
402
477
  !isTextDisplay && value && !searchable && tagPadding[size],
403
478
  // 2026-05-06 v13.3 SSOT retire:per-control `open && 'border-primary'` 移除。Field default
404
479
  // state machine `data-[state=open]:border-border-hover`(灰深)處理 open。
405
480
  // 2026-07-04 Q1:error border 同樣收進 fieldWrapperStyles error variant(error 勝 focus 色)。
406
- "cursor-pointer",
481
+ // D2(2026-07-13):`relative` = hidden input mirror 的 absolute 定位容器
482
+ // (fieldWrapperStyles base 無 relative;對齊 NativeSelect tag 分支同款顯式加法)。
483
+ "relative cursor-pointer",
407
484
  className
408
485
  ),
409
- style: !isTextDisplay ? { paddingRight: "var(--field-px)" } : void 0,
486
+ style: styleProp || !isTextDisplay ? { ...styleProp, ...!isTextDisplay ? { paddingRight: "var(--field-px)" } : void 0 } : void 0,
410
487
  "data-field-mode": "edit",
411
488
  "data-error": error ? "" : void 0,
412
489
  onKeyDown: (e) => {
490
+ var _a, _b;
491
+ onKeyDownProp == null ? void 0 : onKeyDownProp(e);
492
+ if (e.target !== e.currentTarget && ((_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, "button"))) return;
413
493
  if (open && forwardKeyToListbox(listboxId, e)) return;
414
494
  if (e.key === "Enter" || e.key === " ") {
415
495
  if (!open) {
@@ -426,7 +506,8 @@ const CustomSelect = React.forwardRef(
426
506
  children: [
427
507
  triggerContent,
428
508
  clearEl,
429
- chevronEl
509
+ chevronEl,
510
+ hiddenInputEl
430
511
  ]
431
512
  }
432
513
  );