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

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 (548) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/components/Alert/alert.d.ts +5 -0
  3. package/dist/components/Alert/alert.d.ts.map +1 -1
  4. package/dist/components/Alert/alert.js +4 -1
  5. package/dist/components/Alert/alert.js.map +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts +1 -1
  7. package/dist/components/AppShell/app-shell.js.map +1 -1
  8. package/dist/components/AspectRatio/aspect-ratio.d.ts +2 -1
  9. package/dist/components/AspectRatio/aspect-ratio.d.ts.map +1 -1
  10. package/dist/components/AspectRatio/aspect-ratio.js +1 -1
  11. package/dist/components/AspectRatio/aspect-ratio.js.map +1 -1
  12. package/dist/components/Avatar/avatar.d.ts +3 -2
  13. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  14. package/dist/components/Avatar/avatar.js +8 -3
  15. package/dist/components/Avatar/avatar.js.map +1 -1
  16. package/dist/components/Badge/badge.d.ts.map +1 -1
  17. package/dist/components/Badge/badge.js +2 -1
  18. package/dist/components/Badge/badge.js.map +1 -1
  19. package/dist/components/Breadcrumb/breadcrumb.d.ts +14 -1
  20. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  21. package/dist/components/Breadcrumb/breadcrumb.js +9 -3
  22. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  23. package/dist/components/BulkActionBar/bulk-action-bar.d.ts +4 -1
  24. package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
  25. package/dist/components/BulkActionBar/bulk-action-bar.js +2 -2
  26. package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
  27. package/dist/components/Button/button.d.ts +4 -4
  28. package/dist/components/Button/button.d.ts.map +1 -1
  29. package/dist/components/Button/button.js +73 -44
  30. package/dist/components/Button/button.js.map +1 -1
  31. package/dist/components/Button/index.d.ts +1 -0
  32. package/dist/components/Button/index.d.ts.map +1 -1
  33. package/dist/components/Button/index.js +3 -0
  34. package/dist/components/Button/index.js.map +1 -1
  35. package/dist/components/Calendar/calendar.d.ts +3 -1
  36. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  37. package/dist/components/Calendar/calendar.js +40 -17
  38. package/dist/components/Calendar/calendar.js.map +1 -1
  39. package/dist/components/Carousel/carousel.d.ts +1 -1
  40. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  41. package/dist/components/Carousel/carousel.js +3 -1
  42. package/dist/components/Carousel/carousel.js.map +1 -1
  43. package/dist/components/Chart/chart.d.ts.map +1 -1
  44. package/dist/components/Chart/chart.js +1 -1
  45. package/dist/components/Chart/chart.js.map +1 -1
  46. package/dist/components/Checkbox/checkbox.d.ts +6 -3
  47. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  48. package/dist/components/Checkbox/checkbox.js +26 -4
  49. package/dist/components/Checkbox/checkbox.js.map +1 -1
  50. package/dist/components/Checkbox/index.d.ts +1 -0
  51. package/dist/components/Checkbox/index.d.ts.map +1 -1
  52. package/dist/components/Checkbox/index.js +3 -0
  53. package/dist/components/Checkbox/index.js.map +1 -1
  54. package/dist/components/Chip/chip.d.ts +9 -3
  55. package/dist/components/Chip/chip.d.ts.map +1 -1
  56. package/dist/components/Chip/chip.js +46 -13
  57. package/dist/components/Chip/chip.js.map +1 -1
  58. package/dist/components/Coachmark/coachmark.d.ts +5 -3
  59. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  60. package/dist/components/Coachmark/coachmark.js +10 -7
  61. package/dist/components/Coachmark/coachmark.js.map +1 -1
  62. package/dist/components/Combobox/combobox.d.ts +3 -1
  63. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  64. package/dist/components/Combobox/combobox.js +11 -7
  65. package/dist/components/Combobox/combobox.js.map +1 -1
  66. package/dist/components/Command/command.d.ts +2 -2
  67. package/dist/components/Command/command.d.ts.map +1 -1
  68. package/dist/components/Command/command.js +4 -2
  69. package/dist/components/Command/command.js.map +1 -1
  70. package/dist/components/DataTable/active-editor-controller.d.ts +4 -3
  71. package/dist/components/DataTable/active-editor-controller.d.ts.map +1 -1
  72. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  73. package/dist/components/DataTable/cell-registry.js +3 -0
  74. package/dist/components/DataTable/cell-registry.js.map +1 -1
  75. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  76. package/dist/components/DataTable/data-table-interaction-layer.d.ts +4 -3
  77. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  78. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  79. package/dist/components/DataTable/data-table.d.ts +38 -7
  80. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  81. package/dist/components/DataTable/data-table.js +213 -63
  82. package/dist/components/DataTable/data-table.js.map +1 -1
  83. package/dist/components/DataTable/filter-tree.d.ts +5 -0
  84. package/dist/components/DataTable/filter-tree.d.ts.map +1 -1
  85. package/dist/components/DateGrid/date-grid.d.ts +3 -3
  86. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  87. package/dist/components/DateGrid/date-grid.js +52 -6
  88. package/dist/components/DateGrid/date-grid.js.map +1 -1
  89. package/dist/components/DatePicker/date-picker.d.ts +2 -8
  90. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  91. package/dist/components/DatePicker/date-picker.js +139 -109
  92. package/dist/components/DatePicker/date-picker.js.map +1 -1
  93. package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
  94. package/dist/components/DescriptionList/description-list.js +1 -1
  95. package/dist/components/DescriptionList/description-list.js.map +1 -1
  96. package/dist/components/Dialog/dialog.d.ts +1 -1
  97. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  98. package/dist/components/Dialog/dialog.js +2 -1
  99. package/dist/components/Dialog/dialog.js.map +1 -1
  100. package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -4
  101. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  102. package/dist/components/DropdownMenu/dropdown-menu.js +25 -8
  103. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  104. package/dist/components/Empty/empty.d.ts.map +1 -1
  105. package/dist/components/Empty/empty.js +1 -1
  106. package/dist/components/Empty/empty.js.map +1 -1
  107. package/dist/components/Field/field-wrapper.d.ts +1 -0
  108. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  109. package/dist/components/Field/field-wrapper.js +27 -7
  110. package/dist/components/Field/field-wrapper.js.map +1 -1
  111. package/dist/components/Field/field.d.ts +1 -1
  112. package/dist/components/Field/field.d.ts.map +1 -1
  113. package/dist/components/Field/field.js +2 -1
  114. package/dist/components/Field/field.js.map +1 -1
  115. package/dist/components/Field/use-form-validation.d.ts +5 -1
  116. package/dist/components/Field/use-form-validation.d.ts.map +1 -1
  117. package/dist/components/Field/use-form-validation.js +31 -21
  118. package/dist/components/Field/use-form-validation.js.map +1 -1
  119. package/dist/components/FieldControlGroup/field-control-group.d.ts +3 -3
  120. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  121. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  122. package/dist/components/FileItem/file-item.js +3 -2
  123. package/dist/components/FileItem/file-item.js.map +1 -1
  124. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  125. package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
  126. package/dist/components/FileUpload/file-upload.js +22 -10
  127. package/dist/components/FileUpload/file-upload.js.map +1 -1
  128. package/dist/components/FileViewer/can-render-image.d.ts +6 -0
  129. package/dist/components/FileViewer/can-render-image.d.ts.map +1 -0
  130. package/dist/components/FileViewer/can-render-image.js +11 -0
  131. package/dist/components/FileViewer/can-render-image.js.map +1 -0
  132. package/dist/components/FileViewer/file-viewer.d.ts +2 -2
  133. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  134. package/dist/components/FileViewer/file-viewer.js +24 -16
  135. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  136. package/dist/components/FileViewer/image-renderer.d.ts +0 -5
  137. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  138. package/dist/components/FileViewer/image-renderer.js +1 -9
  139. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  140. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  141. package/dist/components/HoverCard/hover-card.js +3 -1
  142. package/dist/components/HoverCard/hover-card.js.map +1 -1
  143. package/dist/components/Input/input.d.ts +26 -3
  144. package/dist/components/Input/input.d.ts.map +1 -1
  145. package/dist/components/Input/input.js +12 -8
  146. package/dist/components/Input/input.js.map +1 -1
  147. package/dist/components/LinkInput/link-input.d.ts +3 -3
  148. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  149. package/dist/components/LinkInput/link-input.js +29 -28
  150. package/dist/components/LinkInput/link-input.js.map +1 -1
  151. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  152. package/dist/components/Menu/menu-item.js +5 -4
  153. package/dist/components/Menu/menu-item.js.map +1 -1
  154. package/dist/components/Notice/notice.d.ts +5 -0
  155. package/dist/components/Notice/notice.d.ts.map +1 -1
  156. package/dist/components/Notice/notice.js +11 -5
  157. package/dist/components/Notice/notice.js.map +1 -1
  158. package/dist/components/NumberInput/number-input.d.ts +1 -1
  159. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  160. package/dist/components/NumberInput/number-input.js +17 -8
  161. package/dist/components/NumberInput/number-input.js.map +1 -1
  162. package/dist/components/OverflowIndicator/overflow-indicator.d.ts +1 -1
  163. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  164. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -1
  165. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  166. package/dist/components/Pagination/index.d.ts +2 -0
  167. package/dist/components/Pagination/index.d.ts.map +1 -0
  168. package/dist/components/Pagination/index.js +6 -0
  169. package/dist/components/Pagination/index.js.map +1 -0
  170. package/dist/components/Pagination/pagination.d.ts +49 -0
  171. package/dist/components/Pagination/pagination.d.ts.map +1 -0
  172. package/dist/components/Pagination/pagination.js +182 -0
  173. package/dist/components/Pagination/pagination.js.map +1 -0
  174. package/dist/components/PeoplePicker/people-picker.d.ts +3 -3
  175. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  176. package/dist/components/PeoplePicker/people-picker.js +38 -36
  177. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  178. package/dist/components/PeoplePicker/person-display.d.ts +4 -0
  179. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  180. package/dist/components/PeoplePicker/person-display.js +3 -3
  181. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  182. package/dist/components/Popover/popover.d.ts.map +1 -1
  183. package/dist/components/Popover/popover.js +58 -32
  184. package/dist/components/Popover/popover.js.map +1 -1
  185. package/dist/components/ProfileCard/profile-card.d.ts +11 -3
  186. package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
  187. package/dist/components/ProfileCard/profile-card.js +14 -4
  188. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  189. package/dist/components/RadioGroup/radio-group.d.ts +1 -1
  190. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  191. package/dist/components/RadioGroup/radio-group.js +28 -3
  192. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  193. package/dist/components/Rating/rating.js +3 -3
  194. package/dist/components/Rating/rating.js.map +1 -1
  195. package/dist/components/ScrollArea/scroll-area.d.ts +1 -1
  196. package/dist/components/ScrollArea/scroll-area.js +2 -2
  197. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  198. package/dist/components/SegmentedControl/segmented-control.d.ts +2 -2
  199. package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
  200. package/dist/components/SegmentedControl/segmented-control.js +23 -10
  201. package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
  202. package/dist/components/Select/select.d.ts +10 -4
  203. package/dist/components/Select/select.d.ts.map +1 -1
  204. package/dist/components/Select/select.js +57 -14
  205. package/dist/components/Select/select.js.map +1 -1
  206. package/dist/components/SelectMenu/index.js +4 -2
  207. package/dist/components/SelectMenu/select-menu.d.ts +31 -6
  208. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  209. package/dist/components/SelectMenu/select-menu.js +169 -85
  210. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  211. package/dist/components/Separator/separator.js +1 -1
  212. package/dist/components/Separator/separator.js.map +1 -1
  213. package/dist/components/Sheet/sheet.d.ts +1 -1
  214. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  215. package/dist/components/Sheet/sheet.js +3 -2
  216. package/dist/components/Sheet/sheet.js.map +1 -1
  217. package/dist/components/Sidebar/sidebar.d.ts +2 -2
  218. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  219. package/dist/components/Sidebar/sidebar.js +28 -10
  220. package/dist/components/Sidebar/sidebar.js.map +1 -1
  221. package/dist/components/Skeleton/skeleton.d.ts +1 -1
  222. package/dist/components/Skeleton/skeleton.js +2 -2
  223. package/dist/components/Skeleton/skeleton.js.map +1 -1
  224. package/dist/components/Slider/slider.d.ts +1 -1
  225. package/dist/components/Slider/slider.d.ts.map +1 -1
  226. package/dist/components/Slider/slider.js +7 -3
  227. package/dist/components/Slider/slider.js.map +1 -1
  228. package/dist/components/Steps/steps.d.ts.map +1 -1
  229. package/dist/components/Steps/steps.js +6 -3
  230. package/dist/components/Steps/steps.js.map +1 -1
  231. package/dist/components/Switch/switch.d.ts +2 -2
  232. package/dist/components/Switch/switch.d.ts.map +1 -1
  233. package/dist/components/Switch/switch.js +47 -9
  234. package/dist/components/Switch/switch.js.map +1 -1
  235. package/dist/components/Tabs/tabs.d.ts +8 -5
  236. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  237. package/dist/components/Tabs/tabs.js +45 -33
  238. package/dist/components/Tabs/tabs.js.map +1 -1
  239. package/dist/components/Tag/tag.d.ts.map +1 -1
  240. package/dist/components/Tag/tag.js +20 -4
  241. package/dist/components/Tag/tag.js.map +1 -1
  242. package/dist/components/Textarea/textarea.d.ts +2 -1
  243. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  244. package/dist/components/Textarea/textarea.js +24 -13
  245. package/dist/components/Textarea/textarea.js.map +1 -1
  246. package/dist/components/TimePicker/time-picker.d.ts +2 -2
  247. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  248. package/dist/components/TimePicker/time-picker.js +23 -12
  249. package/dist/components/TimePicker/time-picker.js.map +1 -1
  250. package/dist/components/Toast/toast.d.ts +1 -1
  251. package/dist/components/Toast/toast.d.ts.map +1 -1
  252. package/dist/components/Toast/toast.js +7 -3
  253. package/dist/components/Toast/toast.js.map +1 -1
  254. package/dist/components/Tooltip/tooltip.js +1 -1
  255. package/dist/components/Tooltip/tooltip.js.map +1 -1
  256. package/dist/components/TreeView/tree-view.d.ts +1 -1
  257. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  258. package/dist/components/TreeView/tree-view.js +11 -5
  259. package/dist/components/TreeView/tree-view.js.map +1 -1
  260. package/dist/hooks/use-controllable.d.ts.map +1 -1
  261. package/dist/hooks/use-controllable.js +4 -2
  262. package/dist/hooks/use-controllable.js.map +1 -1
  263. package/dist/hooks/use-is-touch-device.d.ts.map +1 -1
  264. package/dist/hooks/use-is-touch-device.js +3 -1
  265. package/dist/hooks/use-is-touch-device.js.map +1 -1
  266. package/dist/index.d.ts +2 -0
  267. package/dist/index.d.ts.map +1 -1
  268. package/dist/index.js +14 -0
  269. package/dist/index.js.map +1 -1
  270. package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -2
  271. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  272. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  273. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  274. package/dist/tokens/categorical-color.d.ts +1 -1
  275. package/dist/tokens/categorical-color.js.map +1 -1
  276. package/dist/tokens/uiSize/icon-size.d.ts +2 -2
  277. package/ds-canonical/fork/governance.lock +1 -1
  278. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +10 -1
  279. package/ds-canonical/hooks/check_audit_post_report_validator.sh +13 -0
  280. package/ds-canonical/hooks/check_full_story_visual_interaction_sweep.sh +4 -3
  281. package/ds-canonical/hooks/check_opacity_token_usage.sh +10 -1
  282. package/ds-canonical/hooks/check_plugin_fork_health.sh +30 -22
  283. package/ds-canonical/hooks/check_story_invariants.sh +17 -5
  284. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +5 -2
  285. package/ds-canonical/references/principle-dim-map.json +100 -0
  286. package/ds-canonical/references/story-baseline-registry.json +4 -3
  287. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +1 -1
  288. package/ds-canonical/skills/design-system-audit/SKILL.md +2 -2
  289. package/ds-story-manifest.json +41 -18
  290. package/llms-full.txt +11 -1
  291. package/llms.txt +3 -1
  292. package/package.json +1 -1
  293. package/src/components/Alert/alert.anatomy.stories.tsx +9 -6
  294. package/src/components/Alert/alert.spec.md +3 -3
  295. package/src/components/Alert/alert.tsx +9 -1
  296. package/src/components/AppShell/app-shell.anatomy.stories.tsx +3 -0
  297. package/src/components/AppShell/app-shell.spec.md +3 -2
  298. package/src/components/AppShell/app-shell.tsx +1 -1
  299. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -2
  300. package/src/components/AspectRatio/aspect-ratio.tsx +3 -2
  301. package/src/components/Avatar/avatar.principles.stories.tsx +1 -1
  302. package/src/components/Avatar/avatar.spec.md +10 -30
  303. package/src/components/Avatar/avatar.stories.tsx +1 -1
  304. package/src/components/Avatar/avatar.tsx +30 -16
  305. package/src/components/Badge/badge.anatomy.stories.tsx +2 -2
  306. package/src/components/Badge/badge.spec.md +5 -4
  307. package/src/components/Badge/badge.tsx +4 -1
  308. package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +2 -2
  309. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +15 -10
  310. package/src/components/Breadcrumb/breadcrumb.spec.md +5 -3
  311. package/src/components/Breadcrumb/breadcrumb.tsx +10 -4
  312. package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +3 -2
  313. package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -14
  314. package/src/components/BulkActionBar/bulk-action-bar.tsx +10 -4
  315. package/src/components/Button/button.anatomy.stories.tsx +6 -6
  316. package/src/components/Button/button.spec.md +3 -2
  317. package/src/components/Button/button.tsx +67 -27
  318. package/src/components/Button/index.ts +1 -0
  319. package/src/components/Calendar/calendar.anatomy.stories.tsx +5 -3
  320. package/src/components/Calendar/calendar.principles.stories.tsx +3 -1
  321. package/src/components/Calendar/calendar.spec.md +9 -2
  322. package/src/components/Calendar/calendar.stories.tsx +3 -1
  323. package/src/components/Calendar/calendar.tsx +53 -19
  324. package/src/components/Carousel/carousel.principles.stories.tsx +1 -1
  325. package/src/components/Carousel/carousel.tsx +4 -1
  326. package/src/components/Chart/chart.spec.md +1 -1
  327. package/src/components/Chart/chart.tsx +2 -1
  328. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  329. package/src/components/Checkbox/checkbox.spec.md +1 -1
  330. package/src/components/Checkbox/checkbox.tsx +41 -7
  331. package/src/components/Checkbox/index.ts +1 -0
  332. package/src/components/Chip/chip.anatomy.stories.tsx +9 -9
  333. package/src/components/Chip/chip.principles.stories.tsx +3 -3
  334. package/src/components/Chip/chip.spec.md +7 -4
  335. package/src/components/Chip/chip.tsx +60 -15
  336. package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +1 -1
  337. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -5
  338. package/src/components/CircularProgress/circular-progress.spec.md +2 -0
  339. package/src/components/CircularProgress/circular-progress.stories.tsx +3 -11
  340. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  341. package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
  342. package/src/components/Coachmark/coachmark.spec.md +3 -3
  343. package/src/components/Coachmark/coachmark.tsx +23 -13
  344. package/src/components/Combobox/combobox.anatomy.stories.tsx +4 -4
  345. package/src/components/Combobox/combobox.spec.md +1 -1
  346. package/src/components/Combobox/combobox.tsx +30 -17
  347. package/src/components/Command/command.principles.stories.tsx +10 -12
  348. package/src/components/Command/command.spec.md +5 -5
  349. package/src/components/Command/command.stories.tsx +4 -4
  350. package/src/components/Command/command.tsx +3 -2
  351. package/src/components/DataTable/active-editor-controller.ts +4 -3
  352. package/src/components/DataTable/cell-registry.tsx +8 -0
  353. package/src/components/DataTable/data-table-filter-panel.tsx +8 -2
  354. package/src/components/DataTable/data-table-interaction-layer.tsx +8 -7
  355. package/src/components/DataTable/data-table.anatomy.stories.tsx +4 -4
  356. package/src/components/DataTable/data-table.css +2 -2
  357. package/src/components/DataTable/data-table.spec.md +72 -115
  358. package/src/components/DataTable/data-table.stories.tsx +51 -3
  359. package/src/components/DataTable/data-table.tsx +348 -63
  360. package/src/components/DataTable/filter-operators.spec.md +4 -4
  361. package/src/components/DataTable/filter-tree.ts +192 -42
  362. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +1 -1
  363. package/src/components/DateGrid/date-grid.spec.md +11 -8
  364. package/src/components/DateGrid/date-grid.tsx +63 -8
  365. package/src/components/DatePicker/date-picker.principles.stories.tsx +1 -1
  366. package/src/components/DatePicker/date-picker.spec.md +1 -0
  367. package/src/components/DatePicker/date-picker.stories.tsx +19 -0
  368. package/src/components/DatePicker/date-picker.tsx +52 -15
  369. package/src/components/DescriptionList/description-list.spec.md +1 -1
  370. package/src/components/DescriptionList/description-list.tsx +3 -1
  371. package/src/components/Dialog/dialog.spec.md +1 -1
  372. package/src/components/Dialog/dialog.tsx +2 -1
  373. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +3 -1
  374. package/src/components/DropdownMenu/dropdown-menu.spec.md +8 -5
  375. package/src/components/DropdownMenu/dropdown-menu.tsx +33 -7
  376. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  377. package/src/components/Empty/empty.spec.md +2 -2
  378. package/src/components/Empty/empty.stories.tsx +4 -15
  379. package/src/components/Empty/empty.tsx +4 -1
  380. package/src/components/Field/field-controls.spec.md +2 -1
  381. package/src/components/Field/field-wrapper.tsx +28 -8
  382. package/src/components/Field/field.anatomy.stories.tsx +7 -7
  383. package/src/components/Field/field.principles.stories.tsx +1 -1
  384. package/src/components/Field/field.spec.md +9 -7
  385. package/src/components/Field/field.tsx +2 -1
  386. package/src/components/Field/form-validation.spec.md +6 -3
  387. package/src/components/Field/use-form-validation.ts +49 -21
  388. package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +11 -10
  389. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
  390. package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
  391. package/src/components/FieldControlGroup/field-control-group.tsx +3 -3
  392. package/src/components/FileItem/file-item.anatomy.stories.tsx +6 -4
  393. package/src/components/FileItem/file-item.tsx +3 -2
  394. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +1 -1
  395. package/src/components/FileUpload/file-upload.spec.md +4 -1
  396. package/src/components/FileUpload/file-upload.tsx +36 -10
  397. package/src/components/FileViewer/can-render-image.ts +15 -0
  398. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +6 -4
  399. package/src/components/FileViewer/file-viewer.spec.md +2 -2
  400. package/src/components/FileViewer/file-viewer.tsx +47 -21
  401. package/src/components/FileViewer/image-renderer.tsx +3 -8
  402. package/src/components/HoverCard/hover-card.spec.md +1 -1
  403. package/src/components/HoverCard/hover-card.tsx +3 -1
  404. package/src/components/Input/input.anatomy.stories.tsx +4 -4
  405. package/src/components/Input/input.principles.stories.tsx +1 -1
  406. package/src/components/Input/input.spec.md +1 -1
  407. package/src/components/Input/input.stories.tsx +11 -5
  408. package/src/components/Input/input.tsx +11 -9
  409. package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -0
  410. package/src/components/LinkInput/link-input.tsx +49 -34
  411. package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
  412. package/src/components/Menu/menu-item.spec.md +4 -3
  413. package/src/components/Menu/menu-item.tsx +15 -4
  414. package/src/components/Notice/notice.anatomy.stories.tsx +10 -5
  415. package/src/components/Notice/notice.spec.md +4 -3
  416. package/src/components/Notice/notice.tsx +22 -5
  417. package/src/components/NumberInput/number-input.anatomy.stories.tsx +5 -4
  418. package/src/components/NumberInput/number-input.principles.stories.tsx +2 -1
  419. package/src/components/NumberInput/number-input.spec.md +2 -1
  420. package/src/components/NumberInput/number-input.tsx +25 -9
  421. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +3 -3
  422. package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
  423. package/src/components/OverflowIndicator/overflow-indicator.tsx +2 -1
  424. package/src/components/Pagination/index.ts +5 -0
  425. package/src/components/Pagination/pagination.anatomy.stories.tsx +143 -0
  426. package/src/components/Pagination/pagination.principles.stories.tsx +81 -0
  427. package/src/components/Pagination/pagination.spec.md +150 -0
  428. package/src/components/Pagination/pagination.stories.tsx +98 -0
  429. package/src/components/Pagination/pagination.tsx +263 -0
  430. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +1 -1
  431. package/src/components/PeoplePicker/people-picker.spec.md +4 -4
  432. package/src/components/PeoplePicker/people-picker.stories.tsx +1 -1
  433. package/src/components/PeoplePicker/people-picker.tsx +65 -55
  434. package/src/components/PeoplePicker/person-display.tsx +10 -4
  435. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  436. package/src/components/Popover/popover.spec.md +13 -14
  437. package/src/components/Popover/popover.tsx +88 -39
  438. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +4 -4
  439. package/src/components/ProfileCard/profile-card.principles.stories.tsx +2 -2
  440. package/src/components/ProfileCard/profile-card.spec.md +1 -2
  441. package/src/components/ProfileCard/profile-card.stories.tsx +1 -1
  442. package/src/components/ProfileCard/profile-card.tsx +29 -8
  443. package/src/components/RadioGroup/radio-group.principles.stories.tsx +1 -1
  444. package/src/components/RadioGroup/radio-group.spec.md +2 -2
  445. package/src/components/RadioGroup/radio-group.stories.tsx +3 -1
  446. package/src/components/RadioGroup/radio-group.tsx +37 -3
  447. package/src/components/Rating/rating.anatomy.stories.tsx +11 -8
  448. package/src/components/Rating/rating.principles.stories.tsx +1 -1
  449. package/src/components/Rating/rating.spec.md +2 -2
  450. package/src/components/Rating/rating.stories.tsx +1 -1
  451. package/src/components/Rating/rating.tsx +3 -3
  452. package/src/components/ScrollArea/scroll-area.spec.md +1 -0
  453. package/src/components/ScrollArea/scroll-area.tsx +1 -1
  454. package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +3 -3
  455. package/src/components/SegmentedControl/segmented-control.spec.md +7 -3
  456. package/src/components/SegmentedControl/segmented-control.tsx +29 -11
  457. package/src/components/Select/select.anatomy.stories.tsx +2 -2
  458. package/src/components/Select/select.spec.md +3 -2
  459. package/src/components/Select/select.tsx +62 -19
  460. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +1 -1
  461. package/src/components/SelectMenu/select-menu.principles.stories.tsx +1 -1
  462. package/src/components/SelectMenu/select-menu.spec.md +12 -8
  463. package/src/components/SelectMenu/select-menu.tsx +176 -20
  464. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
  465. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  466. package/src/components/SelectionControl/selection-item.spec.md +2 -1
  467. package/src/components/SelectionControl/selection-item.stories.tsx +1 -1
  468. package/src/components/Separator/separator.anatomy.stories.tsx +2 -2
  469. package/src/components/Separator/separator.tsx +1 -1
  470. package/src/components/Sheet/sheet.spec.md +2 -1
  471. package/src/components/Sheet/sheet.tsx +4 -2
  472. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +1 -1
  473. package/src/components/Sidebar/sidebar.spec.md +2 -1
  474. package/src/components/Sidebar/sidebar.stories.tsx +6 -4
  475. package/src/components/Sidebar/sidebar.tsx +41 -13
  476. package/src/components/Skeleton/skeleton.principles.stories.tsx +1 -1
  477. package/src/components/Skeleton/skeleton.spec.md +1 -2
  478. package/src/components/Skeleton/skeleton.tsx +1 -1
  479. package/src/components/Slider/slider.anatomy.stories.tsx +4 -4
  480. package/src/components/Slider/slider.spec.md +19 -85
  481. package/src/components/Slider/slider.tsx +13 -5
  482. package/src/components/Steps/steps.anatomy.stories.tsx +1 -1
  483. package/src/components/Steps/steps.spec.md +6 -4
  484. package/src/components/Steps/steps.tsx +11 -3
  485. package/src/components/Switch/switch.spec.md +4 -3
  486. package/src/components/Switch/switch.tsx +59 -10
  487. package/src/components/Tabs/tabs.anatomy.stories.tsx +7 -6
  488. package/src/components/Tabs/tabs.principles.stories.tsx +5 -3
  489. package/src/components/Tabs/tabs.spec.md +15 -48
  490. package/src/components/Tabs/tabs.tsx +70 -34
  491. package/src/components/Tag/tag.principles.stories.tsx +2 -2
  492. package/src/components/Tag/tag.spec.md +11 -43
  493. package/src/components/Tag/tag.tsx +30 -4
  494. package/src/components/Textarea/textarea.principles.stories.tsx +2 -1
  495. package/src/components/Textarea/textarea.spec.md +2 -1
  496. package/src/components/Textarea/textarea.tsx +20 -10
  497. package/src/components/TimePicker/time-picker.anatomy.stories.tsx +2 -1
  498. package/src/components/TimePicker/time-picker.spec.md +2 -2
  499. package/src/components/TimePicker/time-picker.tsx +35 -16
  500. package/src/components/Toast/toast.anatomy.stories.tsx +5 -6
  501. package/src/components/Toast/toast.principles.stories.tsx +10 -11
  502. package/src/components/Toast/toast.spec.md +5 -5
  503. package/src/components/Toast/toast.stories.tsx +5 -1
  504. package/src/components/Toast/toast.tsx +13 -7
  505. package/src/components/Tooltip/tooltip.spec.md +3 -3
  506. package/src/components/Tooltip/tooltip.tsx +1 -1
  507. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -1
  508. package/src/components/TreeView/tree-view.principles.stories.tsx +2 -2
  509. package/src/components/TreeView/tree-view.spec.md +2 -2
  510. package/src/components/TreeView/tree-view.tsx +27 -5
  511. package/src/hooks/use-controllable.ts +7 -2
  512. package/src/hooks/use-is-touch-device.ts +8 -1
  513. package/src/index.ts +2 -1
  514. package/src/patterns/element-anatomy/element-anatomy.spec.md +3 -3
  515. package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
  516. package/src/patterns/element-anatomy/item-anatomy.spec.md +5 -14
  517. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +1 -0
  518. package/src/patterns/element-anatomy/item-anatomy.tsx +5 -6
  519. package/src/patterns/header-canonical/header-canonical.css +1 -2
  520. package/src/patterns/header-canonical/header-canonical.spec.md +5 -5
  521. package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +5 -4
  522. package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +137 -0
  523. package/src/patterns/overlay-surface/overlay-surface.spec.md +8 -7
  524. package/src/patterns/overlay-surface/overlay-surface.stories.tsx +116 -0
  525. package/src/patterns/overlay-surface/overlay-surface.tsx +5 -5
  526. package/src/patterns/resize-handle/resize-handle.spec.md +1 -1
  527. package/src/tokens/categorical-color.ts +1 -1
  528. package/src/tokens/color/color.spec.md +11 -13
  529. package/src/tokens/color/color.stories.tsx +1 -1
  530. package/src/tokens/color/primitives.css +7 -5
  531. package/src/tokens/color/semantic.css +8 -2
  532. package/src/tokens/density/density.spec.md +2 -0
  533. package/src/tokens/elevation/elevation.spec.md +1 -1
  534. package/src/tokens/layoutSpace/layoutSpace.spec.md +2 -1
  535. package/src/tokens/layoutSpace/layoutSpace.stories.tsx +3 -3
  536. package/src/tokens/motion/motion.spec.md +1 -1
  537. package/src/tokens/motion/motion.stories.tsx +142 -0
  538. package/src/tokens/opacity/opacity.spec.md +1 -1
  539. package/src/tokens/opacity/opacity.stories.tsx +7 -11
  540. package/src/tokens/radius/radius.spec.md +1 -1
  541. package/src/tokens/typography/typography.css +1 -1
  542. package/src/tokens/typography/typography.spec.md +1 -1
  543. package/src/tokens/uiSize/icon-size.ts +2 -2
  544. package/src/tokens/uiSize/uiSize.spec.md +12 -11
  545. package/src/tokens/uiSize/uiSize.stories.tsx +103 -0
  546. package/src/tokens/utility-registry.json +5 -4
  547. package/dist/node_modules/react-hook-form/dist/index.esm.js +0 -1950
  548. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +0 -1
@@ -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: null, // non-family composite / overlay / layout\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>(({ 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;"}
@@ -89,10 +89,10 @@ export declare const segmentedControlMeta: {
89
89
  readonly purpose: "touch / prominent CTA";
90
90
  };
91
91
  };
92
- readonly states: readonly ["default", "hover", "active", "focus-visible", "disabled"];
92
+ readonly states: readonly ["default", "hover", "selected", "focus-visible", "disabled"];
93
93
  readonly tokens: {
94
94
  readonly bg: readonly ["bg-surface"];
95
- readonly fg: readonly ["text-fg-disabled", "text-fg-secondary", "text-foreground"];
95
+ readonly fg: readonly ["text-fg-disabled", "text-fg-secondary", "text-foreground", "text-primary"];
96
96
  readonly ring: readonly ["ring-ring"];
97
97
  };
98
98
  readonly defaultSize: "md";
@@ -1 +1 @@
1
- {"version":3,"file":"segmented-control.d.ts","sourceRoot":"","sources":["../../../src/components/SegmentedControl/segmented-control.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,oBAAoB,MAAM,8BAA8B,CAAA;AAEpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAM9C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,KAAK,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAiBrD,QAAA,MAAM,wBAAwB;;8EAa7B,CAAA;AAED,MAAM,WAAW,qBACf,SAAQ,IAAI,CACR,KAAK,CAAC,wBAAwB,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,EAChE,MAAM,GAAG,cAAc,GAAG,OAAO,GAAG,eAAe,CACpD;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,QAAA,MAAM,gBAAgB,8FAmDrB,CAAA;AAmBD,QAAA,MAAM,YAAY;;;8EA0CjB,CAAA;AAMD,MAAM,WAAW,yBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC;IACxE,qDAAqD;IACrD,SAAS,CAAC,EAAE,UAAU,CAAA;IACtB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,QAAA,MAAM,oBAAoB,qGA4DxB,CAAA;AAMF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiBvB,CAAA;AAEV,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,YAAY,IAAI,4BAA4B,EAAE,CAAA;AACzH,YAAY,EAAE,oBAAoB,EAAE,CAAA"}
1
+ {"version":3,"file":"segmented-control.d.ts","sourceRoot":"","sources":["../../../src/components/SegmentedControl/segmented-control.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,oBAAoB,MAAM,8BAA8B,CAAA;AAEpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAM9C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,KAAK,oBAAoB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAiBrD,QAAA,MAAM,wBAAwB;;8EAa7B,CAAA;AAED,MAAM,WAAW,qBACf,SAAQ,IAAI,CACR,KAAK,CAAC,wBAAwB,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,EAChE,MAAM,GAAG,cAAc,GAAG,OAAO,GAAG,eAAe,CACpD;IACH,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED,QAAA,MAAM,gBAAgB,8FA4DrB,CAAA;AAmBD,QAAA,MAAM,YAAY;;;8EAiDjB,CAAA;AAMD,MAAM,WAAW,yBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC;IACxE,qDAAqD;IACrD,SAAS,CAAC,EAAE,UAAU,CAAA;IACtB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,QAAA,MAAM,oBAAoB,qGA4DxB,CAAA;AAMF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmBvB,CAAA;AAEV,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,YAAY,IAAI,4BAA4B,EAAE,CAAA;AACzH,YAAY,EAAE,oBAAoB,EAAE,CAAA"}
@@ -41,6 +41,8 @@ const SegmentedControl = React.forwardRef(
41
41
  }, ref) => {
42
42
  const resolvedSize = useResolvedFieldSize(sizeProp, "md");
43
43
  const resolvedDisabled = useResolvedFieldDisabled(disabled);
44
+ const [internalValue, setInternalValue] = React.useState(defaultValue ?? "");
45
+ const resolvedValue = value !== void 0 ? value : internalValue;
44
46
  const ctxValue = React.useMemo(
45
47
  () => ({ size: resolvedSize, fullWidth, iconOnly, disabled: resolvedDisabled }),
46
48
  [resolvedSize, fullWidth, iconOnly, resolvedDisabled]
@@ -50,10 +52,11 @@ const SegmentedControl = React.forwardRef(
50
52
  {
51
53
  ref,
52
54
  type: "single",
53
- value,
54
- defaultValue,
55
+ value: resolvedValue,
55
56
  onValueChange: (v) => {
56
- if (v) onValueChange == null ? void 0 : onValueChange(v);
57
+ if (!v) return;
58
+ if (value === void 0) setInternalValue(v);
59
+ onValueChange == null ? void 0 : onValueChange(v);
57
60
  },
58
61
  disabled: resolvedDisabled,
59
62
  className: cn(segmentedControlVariants({ fullWidth }), className),
@@ -79,12 +82,19 @@ const itemVariants = cva(
79
82
  "disabled:hover:text-fg-disabled disabled:hover:border-border",
80
83
  // hover(未選):border 加深一階 + 文字轉深,對齊 Input / Chip hover
81
84
  "hover:text-foreground hover:border-border-hover hover:z-[5]",
82
- // selected: 文字 + 邊框都用 primary-hover,底色維持 bg-surface 不變 (跟 Chip 一致)
83
- // ── 這是 pill 風格元件 (Chip / SegmentedControl) 的 canonical 選中規則:
84
- // primary-hover 同時染文字和線條;底色不改 (不用 primary-subtle)。
85
- // Tabs underline 風格,規則不同 (文字 foreground + 底線 primary-hover)。
85
+ // selected: 文字 + 邊框都用 primary base,底色維持 bg-surface 不變 (跟 Chip 一致)
86
+ // ── 這是 pill 風格元件 (Chip / SegmentedControl) 的 canonical 選中規則(SSOT = semantic.css「選中狀態」段):
87
+ // primary base 同時染文字和線條;底色不改 (不用 primary-subtle)。
88
+ // 2026-07-06 user 拍板:持續選中 hover base(三家世界級「瞬時弱、持續強」實錘)。
89
+ // Tabs 是 underline 風格,規則不同 (文字 foreground + 底線 primary)。
86
90
  // z-10 讓 border 浮在相鄰 item 之上
87
- "data-[state=on]:text-primary-hover data-[state=on]:border-primary-hover data-[state=on]:z-10",
91
+ "data-[state=on]:text-primary data-[state=on]:border-primary data-[state=on]:z-10",
92
+ // 選中之上的 hover = 同色相升 hover 階(2026-07-06 user 拍板;Button pressed hover 家族
93
+ // + Ant active hover 同派;跟 Chip 一致,顯式宣告解掉 CSS 平手 source-order 脆弱性)
94
+ "data-[state=on]:hover:text-primary-hover data-[state=on]:hover:border-primary-hover",
95
+ // disabled × selected 疊加(2026-07-07 user 拍板統一;M24 disabled 全滅,同 Chip)
96
+ "data-[state=on]:disabled:border-border data-[state=on]:disabled:text-fg-disabled",
97
+ "data-[state=on]:disabled:hover:border-border data-[state=on]:disabled:hover:text-fg-disabled",
88
98
  // item 連體:除第一個外,-ml-px 讓相鄰 border 重疊
89
99
  "first:rounded-l-md last:rounded-r-md",
90
100
  "[&:not(:first-child)]:-ml-px"
@@ -151,10 +161,13 @@ const segmentedControlMeta = {
151
161
  md: { fieldHeight: 32, typography: "body", iconSize: 16, purpose: "預設 — form field 對齊" },
152
162
  lg: { fieldHeight: 36, typography: "body-lg", iconSize: 20, purpose: "touch / prominent CTA" }
153
163
  },
154
- states: ["default", "hover", "active", "focus-visible", "disabled"],
164
+ // 2026-07-04 audit 對齊:cva 無 active(按下)專屬樣式,移除 'active'
165
+ // 'selected' = data-[state=on](2026-07-07 meta 詞彙統一補列:持續選中一律 'selected';cva 無按壓 active 樣式)
166
+ states: ["default", "hover", "selected", "focus-visible", "disabled"],
155
167
  tokens: {
156
168
  bg: ["bg-surface"],
157
- fg: ["text-fg-disabled", "text-fg-secondary", "text-foreground"],
169
+ fg: ["text-fg-disabled", "text-fg-secondary", "text-foreground", "text-primary"],
170
+ // selected 態 cva data-[state=on]:text-primary 實際消費(2026-07-06 選中改 base)
158
171
  ring: ["ring-ring"]
159
172
  },
160
173
  defaultSize: "md"
@@ -1 +1 @@
1
- {"version":3,"file":"segmented-control.js","sources":["../../../src/components/SegmentedControl/segmented-control.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 ToggleGroupPrimitive from '@radix-ui/react-toggle-group'\nimport { cva } from 'class-variance-authority'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { useResolvedFieldSize, useResolvedFieldDisabled } from '@/design-system/components/Field/field-context'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * SegmentedControl — 互斥多選一的 value 選擇器\n *\n * 基於 Radix ToggleGroup(寫死 type=\"single\"),橋接設計系統 token。\n * 內部 item 結構鏡射 Button,但兩者型別獨立。\n *\n * ── 定位 ──\n * 切 value(不是切 view)。塞得進 Field,Tabs 不行。\n * 切 view → Tabs;單一 on/off → Button pressed;>5 個選項 → Select。\n *\n * ── Size(對齊 field-height / Button 系列)──\n * xs h-field-xs(24 固定)\n * sm h-field-sm(28/32)\n * md h-field-md(32/36)★ 預設(跟 Button / Input / 所有 field-height 系列一致)\n * lg h-field-lg(36/40)\n *\n * ── fullWidth ──\n * false ★ 預設,item 寬度由內容決定(hug content)\n * true SegmentedControl 撐滿父容器寬度,所有 item 等分\n *\n * ── Item 結構 ──\n * [startIcon?] [<span px-1>label</span>] [<span gap-1>suffix?</span>]\n * suffix 目前只支援 badge;endIcon 為保留 slot、未開放\n *\n * ── iconOnly(group-level)──\n * 整組同時 icon-only 或全帶 label,不可混搭。\n */\n\ntype SegmentedControlSize = 'xs' | 'sm' | 'md' | 'lg'\n\ninterface SegmentedControlContextValue {\n size: SegmentedControlSize\n fullWidth: boolean\n iconOnly: boolean\n disabled: boolean\n}\n\nconst SegmentedControlContext = React.createContext<SegmentedControlContextValue>({\n size: 'md',\n fullWidth: false,\n iconOnly: false,\n disabled: false,\n})\n\n// ── Root ──\nconst segmentedControlVariants = cva(\n 'flex items-stretch rounded-md',\n {\n variants: {\n fullWidth: {\n true: 'w-full',\n false: 'w-fit',\n },\n },\n defaultVariants: {\n fullWidth: false,\n },\n }\n)\n\nexport interface SegmentedControlProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root>,\n 'type' | 'defaultValue' | 'value' | 'onValueChange'\n > {\n size?: SegmentedControlSize\n /** 撐滿父容器寬度,所有 item 等分。預設 false(hug content) */\n fullWidth?: boolean\n iconOnly?: boolean\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n}\n\nconst SegmentedControl = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Root>,\n SegmentedControlProps\n>(\n (\n {\n className,\n size: sizeProp,\n fullWidth = false,\n iconOnly = false,\n disabled,\n children,\n value,\n defaultValue,\n onValueChange,\n ...props\n },\n ref\n ) => {\n // Field 內自動讀 size + disabled,跟 Button 同機制(SSOT resolver hook)\n const resolvedSize = useResolvedFieldSize<SegmentedControlSize>(sizeProp, 'md') // SSOT:統一 size resolution(原散落 ?? fieldCtx?.size ?? 'md')\n const resolvedDisabled = useResolvedFieldDisabled(disabled)\n\n // Memoize provider value(2026-04-22 D3 perf audit):避免每次父 render 重建 4-field object\n // 讓 children SegmentedControlItem 不必要 re-render\n const ctxValue = React.useMemo(\n () => ({ size: resolvedSize, fullWidth, iconOnly, disabled: resolvedDisabled }),\n [resolvedSize, fullWidth, iconOnly, resolvedDisabled],\n )\n\n return (\n <SegmentedControlContext.Provider value={ctxValue}>\n <ToggleGroupPrimitive.Root\n ref={ref}\n type=\"single\"\n value={value}\n defaultValue={defaultValue}\n onValueChange={(v) => {\n // Radix 的 single ToggleGroup 允許 deselect(返回空字串)\n // SegmentedControl 是 radio 語意,不允許空值——忽略 deselect\n if (v) onValueChange?.(v)\n }}\n disabled={resolvedDisabled}\n className={cn(segmentedControlVariants({ fullWidth }), className)}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Root>\n </SegmentedControlContext.Provider>\n )\n }\n)\nSegmentedControl.displayName = 'SegmentedControl'\n\n// ── Item ──\n\n/**\n * Icon-only base — Polaris/Atlassian idiom:`aspect-square + p-0 + flex-center`。\n * 0 magic-number、0 公式、0 border-deduction;flex centering(已在 itemVariants base)\n * 自動將 SVG 視覺置中,任何 size / icon 都自然正方形。\n *\n * 對齊 `Button.tsx` 的 `ICON_ONLY_BASE`(2026-04-25 從 padding-formula 派改 padding-free)。\n * Rule-of-3:目前 2 處 consumer(Button + SegmentedControl),尚未抽 utility/token;\n * 第 3 個 host 加入時抽到 `packages/design-system/src/utils/`。\n *\n * 舊公式 `(field-height - icon)/2` 沒扣 border 2px,造成 SegmentedControl item 從\n * 設計 spec「自然正方形」漂移為 34×32 長方形(2026-04-25 audit 發現)。\n */\nconst ICON_ONLY_BASE = 'aspect-square p-0 min-w-0 gap-0'\n\nconst itemVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'whitespace-nowrap font-medium',\n 'border border-border bg-surface text-fg-secondary',\n 'transition-colors duration-150',\n 'cursor-pointer select-none',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:z-20',\n // disabled:cursor-not-allowed + 鎖住 hover 色(不用 pointer-events-none,否則 cursor 無法變)\n // button[disabled] 本身擋 click,不需靠 pointer-events: none\n 'disabled:cursor-not-allowed disabled:text-fg-disabled',\n 'disabled:hover:text-fg-disabled disabled:hover:border-border',\n // hover(未選):border 加深一階 + 文字轉深,對齊 Input / Chip hover\n 'hover:text-foreground hover:border-border-hover hover:z-[5]',\n // selected: 文字 + 邊框都用 primary-hover,底色維持 bg-surface 不變 (跟 Chip 一致)\n // ── 這是 pill 風格元件 (Chip / SegmentedControl) 的 canonical 選中規則:\n // primary-hover 同時染文字和線條;底色不改 (不用 primary-subtle)。\n // Tabs 是 underline 風格,規則不同 (文字 foreground + 底線 primary-hover)。\n // z-10 讓 border 浮在相鄰 item 之上\n 'data-[state=on]:text-primary-hover data-[state=on]:border-primary-hover data-[state=on]:z-10',\n // item 連體:除第一個外,-ml-px 讓相鄰 border 重疊\n 'first:rounded-l-md last:rounded-r-md',\n '[&:not(:first-child)]:-ml-px',\n ],\n {\n variants: {\n size: {\n xs: 'h-field-xs px-2 text-caption leading-compact gap-0',\n sm: 'h-field-sm px-3 text-body leading-compact gap-1',\n md: 'h-field-md px-3 text-body leading-compact gap-1',\n lg: 'h-field-lg px-3 text-body-lg leading-compact gap-1',\n },\n fullWidth: {\n true: 'flex-1 min-w-0',\n false: '',\n },\n },\n defaultVariants: {\n size: 'md',\n fullWidth: false,\n },\n }\n)\n\n// iconOnly 是 group-level 決策(由 <SegmentedControl iconOnly> 控制),\n// 不是 per-item 決策,所以 Item 型別不用 discriminated union 強制 iconOnly。\n// 但當 group iconOnly = true 時,每個 item 必須提供 startIcon + aria-label——\n// 這是語意契約,在 render 階段檢查並在 dev mode 發出警告(TS 層做不到這層檢查)。\nexport interface SegmentedControlItemProps\n extends React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> {\n /** 左側 icon(LucideIcon)。group iconOnly = true 時必填。 */\n startIcon?: LucideIcon\n /** 右側 suffix badge(通常是計數指示器)。group iconOnly = true 時會被忽略。 */\n badge?: React.ReactNode\n /**\n * 無障礙名稱。帶 label 時可選(label 已傳達語意),\n * 但 group iconOnly = true 時必填——同時驅動 screen reader 和自動 tooltip。\n */\n 'aria-label'?: string\n children?: React.ReactNode\n}\n\nconst SegmentedControlItem = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Item>,\n SegmentedControlItemProps\n>(({ className, startIcon: StartIcon, badge, children, 'aria-label': ariaLabel, ...restProps }, ref) => {\n const { size, fullWidth, iconOnly: groupIconOnly } = React.useContext(SegmentedControlContext)\n // 2026-05-18 改 per user 拍板「沒機械化導致偏移」+「做完」approval(對齊 Button xs / uiSize Icon Tier):\n // 原 xs=14 跟 Button xs=16 不一致,xs/sm/md 統一 16 對齊 uiSize.spec.md(xs/sm/md→16, lg→20)。\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n // 2026-05-31 #14:ICON_SIZE 只有 sm/md/lg key,size='xs' 會落 undefined → 對齊上方註解「xs/sm/md 統一 16」讓 xs 取 sm(16px)。\n const iconSize = size === 'xs' ? ICON_SIZE.sm : ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const hasSuffix = badge != null\n\n // Dev-mode 語意契約檢查:group iconOnly = true 時,item 必須有 startIcon + aria-label\n if (import.meta.env?.DEV && groupIconOnly) {\n if (!StartIcon) {\n console.warn(\n '[SegmentedControl] iconOnly 群組內的 item 必須提供 startIcon。'\n )\n }\n if (!ariaLabel) {\n console.warn(\n '[SegmentedControl] iconOnly 群組內的 item 必須提供 aria-label(作為無障礙名稱與自動 tooltip 來源)。'\n )\n }\n }\n\n const effectiveIconOnly = groupIconOnly\n\n const itemEl = (\n <ToggleGroupPrimitive.Item\n ref={ref}\n className={cn(\n itemVariants({ size, fullWidth }),\n effectiveIconOnly && ICON_ONLY_BASE,\n className,\n )}\n aria-label={ariaLabel}\n {...restProps}\n >\n {StartIcon && <StartIcon size={iconSize} aria-hidden />}\n {!effectiveIconOnly && children != null && <span className=\"px-1\">{children}</span>}\n {/* suffix wrapper:即使目前只有 badge,wrapper 從第一天就存在,\n 未來若開放 endIcon,直接加入此 span 內 gap-1 自動生效 */}\n {hasSuffix && !effectiveIconOnly && (\n <span className=\"inline-flex items-center gap-1\">{badge}</span>\n )}\n </ToggleGroupPrimitive.Item>\n )\n\n // icon-only 自動包 Tooltip(與 Button icon-only 一致)\n if (effectiveIconOnly && typeof ariaLabel === 'string') {\n return (\n <Tooltip>\n <TooltipTrigger asChild>{itemEl}</TooltipTrigger>\n <TooltipContent>{ariaLabel}</TooltipContent>\n </Tooltip>\n )\n }\n\n return itemEl\n})\nSegmentedControlItem.displayName = 'SegmentedControlItem'\n\n// Story auto-compile metadata — Phase 2 fill(2026-05-15)\n// Sizes 真實 cva keys = xs/sm/md/lg(itemVariants.size),映射 field-height-* token tier\n// SegmentedControl 無語意 variant(選中 vs 未選 是 state 不是 variant)— 與 Button.variant 刻意不同(spec「與 Button 的血緣」段)\nexport const segmentedControlMeta = {\n component: 'SegmentedControl',\n family: 3, // Family 3(Pill Layout)— 對齊 spec frontmatter family:3 + body L42\n variants: {},\n sizes: {\n xs: { fieldHeight: 24, typography: 'caption', iconSize: 16, purpose: 'row-embedded inline action / DataTable inline filter' },\n sm: { fieldHeight: 28, typography: 'body', iconSize: 16, purpose: 'compact chrome bar / dense toolbar' },\n md: { fieldHeight: 32, typography: 'body', iconSize: 16, purpose: '預設 — form field 對齊' },\n lg: { fieldHeight: 36, typography: 'body-lg', iconSize: 20, purpose: 'touch / prominent CTA' },\n },\n states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-surface'],\n fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground'],\n ring: ['ring-ring'],\n },\n defaultSize: 'md',\n} as const\n\nexport { SegmentedControl, SegmentedControlItem, segmentedControlVariants, itemVariants as segmentedControlItemVariants }\nexport type { SegmentedControlSize }\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"segmented-control.js","sources":["../../../src/components/SegmentedControl/segmented-control.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 ToggleGroupPrimitive from '@radix-ui/react-toggle-group'\nimport { cva } from 'class-variance-authority'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { useResolvedFieldSize, useResolvedFieldDisabled } from '@/design-system/components/Field/field-context'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * SegmentedControl — 互斥多選一的 value 選擇器\n *\n * 基於 Radix ToggleGroup(寫死 type=\"single\"),橋接設計系統 token。\n * 內部 item 結構鏡射 Button,但兩者型別獨立。\n *\n * ── 定位 ──\n * 切 value(不是切 view)。塞得進 Field,Tabs 不行。\n * 切 view → Tabs;單一 on/off → Button pressed;>5 個選項 → Select。\n *\n * ── Size(對齊 field-height / Button 系列)──\n * xs h-field-xs(24 固定)\n * sm h-field-sm(28/32)\n * md h-field-md(32/36)★ 預設(跟 Button / Input / 所有 field-height 系列一致)\n * lg h-field-lg(36/40)\n *\n * ── fullWidth ──\n * false ★ 預設,item 寬度由內容決定(hug content)\n * true SegmentedControl 撐滿父容器寬度,所有 item 等分\n *\n * ── Item 結構 ──\n * [startIcon?] [<span px-1>label</span>] [<span gap-1>suffix?</span>]\n * suffix 目前只支援 badge;endIcon 為保留 slot、未開放\n *\n * ── iconOnly(group-level)──\n * 整組同時 icon-only 或全帶 label,不可混搭。\n */\n\ntype SegmentedControlSize = 'xs' | 'sm' | 'md' | 'lg'\n\ninterface SegmentedControlContextValue {\n size: SegmentedControlSize\n fullWidth: boolean\n iconOnly: boolean\n disabled: boolean\n}\n\nconst SegmentedControlContext = React.createContext<SegmentedControlContextValue>({\n size: 'md',\n fullWidth: false,\n iconOnly: false,\n disabled: false,\n})\n\n// ── Root ──\nconst segmentedControlVariants = cva(\n 'flex items-stretch rounded-md',\n {\n variants: {\n fullWidth: {\n true: 'w-full',\n false: 'w-fit',\n },\n },\n defaultVariants: {\n fullWidth: false,\n },\n }\n)\n\nexport interface SegmentedControlProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root>,\n 'type' | 'defaultValue' | 'value' | 'onValueChange'\n > {\n size?: SegmentedControlSize\n /** 撐滿父容器寬度,所有 item 等分。預設 false(hug content) */\n fullWidth?: boolean\n iconOnly?: boolean\n value?: string\n defaultValue?: string\n onValueChange?: (value: string) => void\n}\n\nconst SegmentedControl = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Root>,\n SegmentedControlProps\n>(\n (\n {\n className,\n size: sizeProp,\n fullWidth = false,\n iconOnly = false,\n disabled,\n children,\n value,\n defaultValue,\n onValueChange,\n ...props\n },\n ref\n ) => {\n // Field 內自動讀 size + disabled,跟 Button 同機制(SSOT resolver hook)\n const resolvedSize = useResolvedFieldSize<SegmentedControlSize>(sizeProp, 'md') // SSOT:統一 size resolution(原散落 ?? fieldCtx?.size ?? 'md')\n const resolvedDisabled = useResolvedFieldDisabled(disabled)\n\n // 2026-07-04:恆 controlled mirror(見下方 onValueChange 註解)— uncontrolled consumer 的\n // defaultValue 收進 internal state,Radix 永遠拿 controlled value → deselect 空值可攔。\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? '') // '' = controlled 無選取(恆 controlled 從 mount 起,避免 un→controlled 切換)\n const resolvedValue = value !== undefined ? value : internalValue\n\n // Memoize provider value(2026-04-22 D3 perf audit):避免每次父 render 重建 4-field object\n // 讓 children SegmentedControlItem 不必要 re-render\n const ctxValue = React.useMemo(\n () => ({ size: resolvedSize, fullWidth, iconOnly, disabled: resolvedDisabled }),\n [resolvedSize, fullWidth, iconOnly, resolvedDisabled],\n )\n\n return (\n <SegmentedControlContext.Provider value={ctxValue}>\n <ToggleGroupPrimitive.Root\n ref={ref}\n type=\"single\"\n value={resolvedValue}\n onValueChange={(v) => {\n // Radix 的 single ToggleGroup 允許 deselect(返回空字串)\n // SegmentedControl 是 radio 語意,不允許空值——忽略 deselect。\n // 2026-07-04 修:原 uncontrolled(只傳 defaultValue)時 Radix 內部 state 直通,\n // 點已選中項照樣 deselect 成空值(spec:236「恆有一值」失守)→ wrapper 恆 controlled\n // (internalValue mirror),空字串一律不進 state 也不呼叫 consumer callback。\n if (!v) return\n if (value === undefined) setInternalValue(v)\n onValueChange?.(v)\n }}\n disabled={resolvedDisabled}\n className={cn(segmentedControlVariants({ fullWidth }), className)}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Root>\n </SegmentedControlContext.Provider>\n )\n }\n)\nSegmentedControl.displayName = 'SegmentedControl'\n\n// ── Item ──\n\n/**\n * Icon-only base — Polaris/Atlassian idiom:`aspect-square + p-0 + flex-center`。\n * 0 magic-number、0 公式、0 border-deduction;flex centering(已在 itemVariants base)\n * 自動將 SVG 視覺置中,任何 size / icon 都自然正方形。\n *\n * 對齊 `Button.tsx` 的 `ICON_ONLY_BASE`(2026-04-25 從 padding-formula 派改 padding-free)。\n * Rule-of-3:目前 2 處 consumer(Button + SegmentedControl),尚未抽 utility/token;\n * 第 3 個 host 加入時抽到 `packages/design-system/src/utils/`。\n *\n * 舊公式 `(field-height - icon)/2` 沒扣 border 2px,造成 SegmentedControl item 從\n * 設計 spec「自然正方形」漂移為 34×32 長方形(2026-04-25 audit 發現)。\n */\nconst ICON_ONLY_BASE = 'aspect-square p-0 min-w-0 gap-0'\n\nconst itemVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'whitespace-nowrap font-medium',\n 'border border-border bg-surface text-fg-secondary',\n 'transition-colors duration-150',\n 'cursor-pointer select-none',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:z-20',\n // disabled:cursor-not-allowed + 鎖住 hover 色(不用 pointer-events-none,否則 cursor 無法變)\n // button[disabled] 本身擋 click,不需靠 pointer-events: none\n 'disabled:cursor-not-allowed disabled:text-fg-disabled',\n 'disabled:hover:text-fg-disabled disabled:hover:border-border',\n // hover(未選):border 加深一階 + 文字轉深,對齊 Input / Chip hover\n 'hover:text-foreground hover:border-border-hover hover:z-[5]',\n // selected: 文字 + 邊框都用 primary base,底色維持 bg-surface 不變 (跟 Chip 一致)\n // ── 這是 pill 風格元件 (Chip / SegmentedControl) 的 canonical 選中規則(SSOT = semantic.css「選中狀態」段):\n // primary base 同時染文字和線條;底色不改 (不用 primary-subtle)。\n // 2026-07-06 user 拍板:持續選中 hover 階 → base(三家世界級「瞬時弱、持續強」實錘)。\n // Tabs 是 underline 風格,規則不同 (文字 foreground + 底線 primary)。\n // z-10 讓 border 浮在相鄰 item 之上\n 'data-[state=on]:text-primary data-[state=on]:border-primary data-[state=on]:z-10',\n // 選中之上的 hover = 同色相升 hover 階(2026-07-06 user 拍板;Button pressed hover 家族\n // + Ant active hover 同派;跟 Chip 一致,顯式宣告解掉 CSS 平手 source-order 脆弱性)\n 'data-[state=on]:hover:text-primary-hover data-[state=on]:hover:border-primary-hover',\n // disabled × selected 疊加(2026-07-07 user 拍板統一;M24 disabled 全滅,同 Chip)\n 'data-[state=on]:disabled:border-border data-[state=on]:disabled:text-fg-disabled',\n 'data-[state=on]:disabled:hover:border-border data-[state=on]:disabled:hover:text-fg-disabled',\n // item 連體:除第一個外,-ml-px 讓相鄰 border 重疊\n 'first:rounded-l-md last:rounded-r-md',\n '[&:not(:first-child)]:-ml-px',\n ],\n {\n variants: {\n size: {\n xs: 'h-field-xs px-2 text-caption leading-compact gap-0',\n sm: 'h-field-sm px-3 text-body leading-compact gap-1',\n md: 'h-field-md px-3 text-body leading-compact gap-1',\n lg: 'h-field-lg px-3 text-body-lg leading-compact gap-1',\n },\n fullWidth: {\n true: 'flex-1 min-w-0',\n false: '',\n },\n },\n defaultVariants: {\n size: 'md',\n fullWidth: false,\n },\n }\n)\n\n// iconOnly 是 group-level 決策(由 <SegmentedControl iconOnly> 控制),\n// 不是 per-item 決策,所以 Item 型別不用 discriminated union 強制 iconOnly。\n// 但當 group iconOnly = true 時,每個 item 必須提供 startIcon + aria-label——\n// 這是語意契約,在 render 階段檢查並在 dev mode 發出警告(TS 層做不到這層檢查)。\nexport interface SegmentedControlItemProps\n extends React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> {\n /** 左側 icon(LucideIcon)。group iconOnly = true 時必填。 */\n startIcon?: LucideIcon\n /** 右側 suffix badge(通常是計數指示器)。group iconOnly = true 時會被忽略。 */\n badge?: React.ReactNode\n /**\n * 無障礙名稱。帶 label 時可選(label 已傳達語意),\n * 但 group iconOnly = true 時必填——同時驅動 screen reader 和自動 tooltip。\n */\n 'aria-label'?: string\n children?: React.ReactNode\n}\n\nconst SegmentedControlItem = React.forwardRef<\n React.ElementRef<typeof ToggleGroupPrimitive.Item>,\n SegmentedControlItemProps\n>(({ className, startIcon: StartIcon, badge, children, 'aria-label': ariaLabel, ...restProps }, ref) => {\n const { size, fullWidth, iconOnly: groupIconOnly } = React.useContext(SegmentedControlContext)\n // 2026-05-18 改 per user 拍板「沒機械化導致偏移」+「做完」approval(對齊 Button xs / uiSize Icon Tier):\n // 原 xs=14 跟 Button xs=16 不一致,xs/sm/md 統一 16 對齊 uiSize.spec.md(xs/sm/md→16, lg→20)。\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n // 2026-05-31 #14:ICON_SIZE 只有 sm/md/lg key,size='xs' 會落 undefined → 對齊上方註解「xs/sm/md 統一 16」讓 xs 取 sm(16px)。\n const iconSize = size === 'xs' ? ICON_SIZE.sm : ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const hasSuffix = badge != null\n\n // Dev-mode 語意契約檢查:group iconOnly = true 時,item 必須有 startIcon + aria-label\n if (import.meta.env?.DEV && groupIconOnly) {\n if (!StartIcon) {\n console.warn(\n '[SegmentedControl] iconOnly 群組內的 item 必須提供 startIcon。'\n )\n }\n if (!ariaLabel) {\n console.warn(\n '[SegmentedControl] iconOnly 群組內的 item 必須提供 aria-label(作為無障礙名稱與自動 tooltip 來源)。'\n )\n }\n }\n\n const effectiveIconOnly = groupIconOnly\n\n const itemEl = (\n <ToggleGroupPrimitive.Item\n ref={ref}\n className={cn(\n itemVariants({ size, fullWidth }),\n effectiveIconOnly && ICON_ONLY_BASE,\n className,\n )}\n aria-label={ariaLabel}\n {...restProps}\n >\n {StartIcon && <StartIcon size={iconSize} aria-hidden />}\n {!effectiveIconOnly && children != null && <span className=\"px-1\">{children}</span>}\n {/* suffix wrapper:即使目前只有 badge,wrapper 從第一天就存在,\n 未來若開放 endIcon,直接加入此 span 內 gap-1 自動生效 */}\n {hasSuffix && !effectiveIconOnly && (\n <span className=\"inline-flex items-center gap-1\">{badge}</span>\n )}\n </ToggleGroupPrimitive.Item>\n )\n\n // icon-only 自動包 Tooltip(與 Button icon-only 一致)\n if (effectiveIconOnly && typeof ariaLabel === 'string') {\n return (\n <Tooltip>\n <TooltipTrigger asChild>{itemEl}</TooltipTrigger>\n <TooltipContent>{ariaLabel}</TooltipContent>\n </Tooltip>\n )\n }\n\n return itemEl\n})\nSegmentedControlItem.displayName = 'SegmentedControlItem'\n\n// Story auto-compile metadata — Phase 2 fill(2026-05-15)\n// Sizes 真實 cva keys = xs/sm/md/lg(itemVariants.size),映射 field-height-* token tier\n// SegmentedControl 無語意 variant(選中 vs 未選 是 state 不是 variant)— 與 Button.variant 刻意不同(spec「與 Button 的血緣」段)\nexport const segmentedControlMeta = {\n component: 'SegmentedControl',\n family: 3, // Family 3(Pill Layout)— 對齊 spec frontmatter family:3 + body L42\n variants: {},\n sizes: {\n xs: { fieldHeight: 24, typography: 'caption', iconSize: 16, purpose: 'row-embedded inline action / DataTable inline filter' },\n sm: { fieldHeight: 28, typography: 'body', iconSize: 16, purpose: 'compact chrome bar / dense toolbar' },\n md: { fieldHeight: 32, typography: 'body', iconSize: 16, purpose: '預設 — form field 對齊' },\n lg: { fieldHeight: 36, typography: 'body-lg', iconSize: 20, purpose: 'touch / prominent CTA' },\n },\n // 2026-07-04 audit 對齊:cva 無 active(按下)專屬樣式,移除 'active'\n // 'selected' = data-[state=on](2026-07-07 meta 詞彙統一補列:持續選中一律 'selected';cva 無按壓 active 樣式)\n states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-surface'],\n fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground', 'text-primary'], // selected 態 cva data-[state=on]:text-primary 實際消費(2026-07-06 選中改 base)\n ring: ['ring-ring'],\n },\n defaultSize: 'md',\n} as const\n\nexport { SegmentedControl, SegmentedControlItem, segmentedControlVariants, itemVariants as segmentedControlItemVariants }\nexport type { SegmentedControlSize }\n"],"names":[],"mappings":"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"}
@@ -51,9 +51,10 @@ export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectE
51
51
  startIcon?: LucideIcon;
52
52
  /** 啟用搜尋(desktop 時 field 變 input,打字即篩選) */
53
53
  searchable?: boolean;
54
- /** Loading state(2026-05-15 audit B fix per user verbatim「dropdown 隨時可開,讀取在 panel 中間 CircularProgress」)。
55
- * Forward 給 SelectMenu primitive SSOT;dropdown 開啟時取代 options CircularProgress + loadingText。
56
- * Trigger 不變(chevron 保留 user 隨時可點開)。對齊 Field family loading SSOT + Empty 元件 `<Empty icon={CircularProgress}/>` compose。*/
54
+ /** Loading state(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)。
55
+ * Forward 給 SelectMenu primitive SSOT;spinner 只在**無可顯示選項時**佔 empty slot(cmdk CommandEmpty
56
+ * 機制)— 已有 stale options 時保留顯示不清空(對齊 MUI Autocomplete「only if there are no suggestions」)。
57
+ * Trigger 不變(chevron 保留 user 隨時可點開)。*/
57
58
  loading?: boolean;
58
59
  /** Menu list 最小列數(空狀態 / 選項少時的視覺一致 reserve)。預設 3 — 選項 < 3 時顯式縮(如 And/Or 兩選項) */
59
60
  minRows?: number;
@@ -84,6 +85,11 @@ export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectE
84
85
  * (avatar + name)而非純文字 label。對齊 PeoplePicker = Select wrapper SSOT。
85
86
  */
86
87
  selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode;
88
+ /** 搜尋無結果提示(2026-07-04 Q4 拍板接線 — 原 spec 宣稱可覆寫但 prop 從未轉發)。
89
+ * Forward 給 SelectMenu primitive SSOT(default 在 SelectMenu 層「沒有符合的選項」);
90
+ * 僅 Custom path(searchable dropdown)生效,Native path 無搜尋空狀態。
91
+ * 對齊 MUI noOptionsText / Ant notFoundContent / Polaris emptyState 三家公開覆寫點共識。 */
92
+ emptyText?: string;
87
93
  }
88
94
  declare const Select: React.ForwardRefExoticComponent<SelectProps & React.RefAttributes<HTMLDivElement | HTMLSelectElement>>;
89
95
  export declare const selectMeta: {
@@ -91,7 +97,7 @@ export declare const selectMeta: {
91
97
  readonly family: 4;
92
98
  readonly variants: {};
93
99
  readonly sizes: {};
94
- readonly states: readonly ["default", "hover", "active", "focus-visible", "disabled"];
100
+ readonly states: readonly ["default", "hover", "focus-visible", "disabled"];
95
101
  readonly tokens: {
96
102
  readonly bg: readonly [];
97
103
  readonly fg: readonly ["text-fg-disabled", "text-fg-muted"];
@@ -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,EAAc,KAAK,gBAAgB,EAAE,MAAM,mDAAmD,CAAA;AAcrG;;;;;;;;;;;;;;;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;;+HAE2H;IAC3H,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;CACtE;AAohBD,QAAA,MAAM,MAAM,wGAUX,CAAA;AAKD,eAAO,MAAM,UAAU;;;;;;;;;;;CAeb,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,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"}
@@ -6,7 +6,7 @@ import { bareInputStyles, fieldWrapperStyles, EMPTY_DISPLAY, fieldDisplayTextCla
6
6
  import { Tag } from "../Tag/tag.js";
7
7
  import { ItemPrefix, ItemSuffix, ICON_SIZE, ItemInlineAction } from "../../patterns/element-anatomy/item-anatomy.js";
8
8
  import { useFieldContext, useResolvedFieldInvalid, useResolvedFieldDisabled, useResolvedFieldSize, useResolvedFieldMode, useResolvedFieldVariant } from "../Field/field-context.js";
9
- import { SelectMenu } from "../SelectMenu/select-menu.js";
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";
12
12
  const tagPadding = {
@@ -52,6 +52,9 @@ function CustomSelectTriggerContent({
52
52
  search,
53
53
  setSearch,
54
54
  inputRef,
55
+ activeDescendantId,
56
+ ariaLabel,
57
+ labelId,
55
58
  selectedItemRenderer
56
59
  }) {
57
60
  if (searchable && open) {
@@ -65,6 +68,9 @@ function CustomSelectTriggerContent({
65
68
  ref: inputRef,
66
69
  value: search,
67
70
  onChange: (e) => setSearch(e.target.value),
71
+ "aria-activedescendant": activeDescendantId,
72
+ "aria-label": ariaLabel ?? "搜尋選項",
73
+ "aria-labelledby": ariaLabel ? void 0 : labelId,
68
74
  placeholder: showSelectedOverlay ? "" : triggerEmptyPlaceholder,
69
75
  className: cn(bareInputStyles, "cursor-text"),
70
76
  autoFocus: true
@@ -166,7 +172,37 @@ function ReadonlyDisplay({
166
172
  ] });
167
173
  }
168
174
  const NativeSelect = React.forwardRef(
169
- ({ mode, variant: variantProp, error: errorProp = false, size: sizeProp, options, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, clearable = false, display = "plain", startIcon: StartIcon, showDisplayEndIcon, id: idProp, "aria-describedby": ariaDescribedByProp, "aria-errormessage": ariaErrorMessageProp, ...props }, ref) => {
175
+ ({
176
+ mode,
177
+ variant: variantProp,
178
+ error: errorProp = false,
179
+ size: sizeProp,
180
+ options,
181
+ value: valueProp,
182
+ defaultValue,
183
+ onChange,
184
+ placeholder,
185
+ className,
186
+ disabled: disabledProp,
187
+ clearable = false,
188
+ display = "plain",
189
+ startIcon: StartIcon,
190
+ showDisplayEndIcon,
191
+ id: idProp,
192
+ "aria-describedby": ariaDescribedByProp,
193
+ "aria-errormessage": ariaErrorMessageProp,
194
+ // 2026-07-04:Custom-path-only props 顯式 destructure 丟棄(不 spread 到原生 <select> 消 React
195
+ // unknown-prop warning);各 prop docblock 已註「僅 Custom path 生效」語意
196
+ searchable: _searchable,
197
+ groups: _groups,
198
+ loading: _loading,
199
+ minRows: _minRows,
200
+ emptyText: _emptyText,
201
+ defaultOpen: _defaultOpen,
202
+ onOpenChange: _onOpenChange,
203
+ selectedItemRenderer: _selectedItemRenderer,
204
+ ...props
205
+ }, ref) => {
170
206
  const fieldCtx = useFieldContext();
171
207
  const error = useResolvedFieldInvalid(errorProp);
172
208
  const disabled = useResolvedFieldDisabled(disabledProp);
@@ -207,7 +243,7 @@ const NativeSelect = React.forwardRef(
207
243
  ...props,
208
244
  children: [
209
245
  placeholder && /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: placeholder }),
210
- options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
246
+ options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))
211
247
  ]
212
248
  }
213
249
  );
@@ -222,10 +258,9 @@ const NativeSelect = React.forwardRef(
222
258
  "div",
223
259
  {
224
260
  className: cn(
225
- fieldWrapperStyles({ mode: "edit", variant, size }),
261
+ fieldWrapperStyles({ mode: "edit", variant, size, error }),
226
262
  value && tagPadding[size],
227
263
  "relative",
228
- error && ["border-error hover:border-error-hover", "focus-within:border-error focus-within:hover:border-error"],
229
264
  className
230
265
  ),
231
266
  style: { paddingRight: "var(--field-px)" },
@@ -245,8 +280,7 @@ const NativeSelect = React.forwardRef(
245
280
  "div",
246
281
  {
247
282
  className: cn(
248
- fieldWrapperStyles({ mode: "edit", variant, size }),
249
- error && ["border-error hover:border-error-hover", "focus-within:border-error focus-within:hover:border-error"],
283
+ fieldWrapperStyles({ mode: "edit", variant, size, error }),
250
284
  className
251
285
  ),
252
286
  "data-field-mode": "edit",
@@ -264,7 +298,7 @@ const NativeSelect = React.forwardRef(
264
298
  );
265
299
  NativeSelect.displayName = "NativeSelect";
266
300
  const CustomSelect = React.forwardRef(
267
- ({ 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, defaultOpen = false, onOpenChange, selectedItemRenderer, showDisplayEndIcon, id: idProp, "aria-describedby": ariaDescribedByProp, "aria-errormessage": ariaErrorMessageProp, "aria-label": ariaLabel }, ref) => {
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) => {
268
302
  const fieldCtx = useFieldContext();
269
303
  const error = useResolvedFieldInvalid(errorProp);
270
304
  const disabled = useResolvedFieldDisabled(disabledProp);
@@ -282,6 +316,8 @@ const CustomSelect = React.forwardRef(
282
316
  const [open, setOpen] = React.useState(defaultOpen);
283
317
  const [search, setSearch] = React.useState("");
284
318
  const inputRef = React.useRef(null);
319
+ const listboxId = React.useId();
320
+ const activeOptionId = useActiveDescendant(listboxId, open);
285
321
  React.useEffect(() => {
286
322
  if (!open) setSearch("");
287
323
  }, [open]);
@@ -339,6 +375,9 @@ const CustomSelect = React.forwardRef(
339
375
  search,
340
376
  setSearch,
341
377
  inputRef,
378
+ activeDescendantId: activeOptionId,
379
+ ariaLabel,
380
+ labelId: fieldCtx == null ? void 0 : fieldCtx.labelId,
342
381
  selectedItemRenderer
343
382
  }
344
383
  );
@@ -350,20 +389,20 @@ const CustomSelect = React.forwardRef(
350
389
  role: "combobox",
351
390
  "aria-expanded": open,
352
391
  "aria-haspopup": "listbox",
392
+ "aria-controls": listboxId,
353
393
  "aria-label": ariaLabel,
394
+ "aria-labelledby": ariaLabel ? void 0 : fieldCtx == null ? void 0 : fieldCtx.labelId,
354
395
  "aria-invalid": error || void 0,
355
396
  "aria-required": (fieldCtx == null ? void 0 : fieldCtx.required) || void 0,
356
397
  "aria-describedby": ariaDescribedByProp ?? (fieldCtx == null ? void 0 : fieldCtx.descriptionId),
357
398
  "aria-errormessage": ariaErrorMessageProp ?? (error ? fieldCtx == null ? void 0 : fieldCtx.errorId : void 0),
358
399
  tabIndex: 0,
359
400
  className: cn(
360
- fieldWrapperStyles({ mode: "edit", variant, size }),
401
+ fieldWrapperStyles({ mode: "edit", variant, size, error }),
361
402
  !isTextDisplay && value && !searchable && tagPadding[size],
362
403
  // 2026-05-06 v13.3 SSOT retire:per-control `open && 'border-primary'` 移除。Field default
363
- // state machine `data-[state=open]:border-border-hover`(灰深)處理 open;若 trigger focused
364
- // (Radix focus on open),focus-within:!border-primary 強制勝出顯藍。focus dominates everything
365
- // 全 DS 一致(Material/Polaris/Ant 共識),改 Field default 一處全 control 自動跟動。
366
- error && ["border-error hover:border-error-hover", "focus-within:border-error focus-within:hover:border-error"],
404
+ // state machine `data-[state=open]:border-border-hover`(灰深)處理 open
405
+ // 2026-07-04 Q1:error border 同樣收進 fieldWrapperStyles error variant(error 勝 focus 色)。
367
406
  "cursor-pointer",
368
407
  className
369
408
  ),
@@ -371,6 +410,7 @@ const CustomSelect = React.forwardRef(
371
410
  "data-field-mode": "edit",
372
411
  "data-error": error ? "" : void 0,
373
412
  onKeyDown: (e) => {
413
+ if (open && forwardKeyToListbox(listboxId, e)) return;
374
414
  if (e.key === "Enter" || e.key === " ") {
375
415
  if (!open) {
376
416
  e.preventDefault();
@@ -399,6 +439,7 @@ const CustomSelect = React.forwardRef(
399
439
  onValueChange: handleValueChange,
400
440
  searchable: false,
401
441
  loading,
442
+ emptyText,
402
443
  size,
403
444
  minRows,
404
445
  open,
@@ -406,6 +447,7 @@ const CustomSelect = React.forwardRef(
406
447
  setOpen(o);
407
448
  onOpenChange == null ? void 0 : onOpenChange(o);
408
449
  },
450
+ contentId: listboxId,
409
451
  renderLabel,
410
452
  onOpenAutoFocus: searchable ? (e) => {
411
453
  var _a;
@@ -433,7 +475,8 @@ const selectMeta = {
433
475
  family: 4,
434
476
  variants: {},
435
477
  sizes: {},
436
- states: ["default", "hover", "active", "focus-visible", "disabled"],
478
+ // 'active' 移除 — trigger 走 Field chrome、menu row 走 MenuItem(其 meta 無 active)(2026-07-07 詞彙統一 DS-wide 按壓訊號盤點:檔內 0 active: utility / 0 *-active token)。
479
+ states: ["default", "hover", "focus-visible", "disabled"],
437
480
  tokens: {
438
481
  bg: [],
439
482
  fg: ["text-fg-disabled", "text-fg-muted"],