@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
@@ -31,7 +31,7 @@ TimePicker 是**單一時間**(時/分/秒)輸入與顯示元件,對齊 Ant Desi
31
31
 
32
32
  ## Controlled-only rationale(Dim 26)
33
33
 
34
- 本元件刻意採 **controlled-only** 模式:`value` + `onChange` 必傳,不支援 `defaultValue` uncontrolled fallback。
34
+ 本元件刻意採 **controlled-only** 模式:`value` + `onChange` 成對傳入(型別宣告 optional、無 runtime 強制,對齊 DatePicker / SelectMenu 同慣例),不支援 `defaultValue` uncontrolled fallback。
35
35
 
36
36
  **為什麼**:
37
37
  - 內部狀態(panel `open` + 暫存 `draft` 值)若要跟外部 `value` 雙向 sync 會產生 race condition
@@ -249,7 +249,7 @@ TimePicker 視覺分兩層,各自繼承不同 SSOT:
249
249
 
250
250
  trigger 的互動狀態(focus / invalid / disabled / readonly)完全繼承 `../Field/field-controls.spec.md` SSOT「Mode 狀態」;panel 內 column item 的 hover / selected / disabled 走 `patterns/element-anatomy/item-anatomy.spec.md`「選擇 / 狀態視覺規則」。TimePicker 層級無自有 state 行為,panel 開 / 關由 Popover primitive 處理。重寫 StateBehavior = 與兩個 SSOT 同時漂移。
251
251
 
252
- 對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix`(兩層整合展示) + `SizeMatrix` + 元件特有 `ModeMatrix` / `PrecisionMatrix`。Field-level state 見 Input `StateBehavior` + field-controls.spec.md;panel item-level state 見 SelectMenu / MenuItem 的對應 story。
252
+ 對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix`(兩層整合展示) + `SizeMatrix` + `Accessibility` + 元件特有 `ModeMatrix` / `PrecisionMatrix`。Field-level state 見 Input `StateBehavior` + field-controls.spec.md;panel item-level state 見 SelectMenu / MenuItem 的對應 story。
253
253
 
254
254
  ---
255
255
 
@@ -63,6 +63,21 @@ export interface TimeFormatOptions {
63
63
  locale?: string
64
64
  }
65
65
 
66
+ // 2026-07-06 D3 perf:Intl.DateTimeFormat 是標準庫最貴 constructor 之一(locale data
67
+ // resolution)。formatTime 無 cheap path,display/readonly/edit 每 render 必建;DataTable time 欄
68
+ // 轉發 config 時每 cell × 每次表格 re-render 重建。module-level cache 重用實例(DateTimeFormat
69
+ // 無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。
70
+ const dtfCache = new Map<string, Intl.DateTimeFormat>()
71
+ function getDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {
72
+ const key = locale + '|' + JSON.stringify(options)
73
+ let fmt = dtfCache.get(key)
74
+ if (!fmt) {
75
+ fmt = new Intl.DateTimeFormat(locale, options)
76
+ dtfCache.set(key, fmt)
77
+ }
78
+ return fmt
79
+ }
80
+
66
81
  function formatTime(
67
82
  iso: string,
68
83
  options: TimeFormatOptions = {},
@@ -76,7 +91,7 @@ function formatTime(
76
91
  // 借用 Date 讓 Intl.DateTimeFormat 處理 locale / 12h-24h
77
92
  const d = new Date()
78
93
  d.setHours(parts.hours, parts.minutes, parts.seconds, 0)
79
- return new Intl.DateTimeFormat(locale, formatOptions).format(d)
94
+ return getDateTimeFormatter(locale, formatOptions).format(d)
80
95
  }
81
96
 
82
97
  // ── Disabled time callback ──────────────────────────────────────────────────
@@ -218,15 +233,20 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
218
233
 
219
234
  const handleNow = () => {
220
235
  const now = new Date()
221
- // 按照 minuteStep / secondStep 對齊
222
- const m = Math.round(now.getMinutes() / minuteStep) * minuteStep
223
- const s = showSeconds
224
- ? Math.round(now.getSeconds() / secondStep) * secondStep
225
- : 0
236
+ // 按照 minuteStep / secondStep 對齊。
237
+ // 2026-07-05 邊界修正(deep-audit A.1b):Math.round 進位到 60 時,舊 `Math.min(round, 59)`
238
+ // 會產生 59 這種「不對齊 step、也不在 column values 內」的值(如 53 分 @ step=15 → 59)
239
+ // panel 該欄失去 selected 高亮。改 clamp 到 ≤59 的最大 step 對齊值(= buildRange 產出的
240
+ // 最後一格,如 45),不跨小時進位 — 保持「此刻 = commit 當前時間(step 對齊)」語意
241
+ // (time-picker.spec.md「每次欄位選取當下即 commit」段)。
242
+ const alignToStep = (raw: number, step: number) => {
243
+ const rounded = Math.round(raw / step) * step
244
+ return rounded > 59 ? Math.floor(59 / step) * step : rounded
245
+ }
226
246
  const next: TimeParts = {
227
247
  hours: now.getHours(),
228
- minutes: Math.min(m, 59),
229
- seconds: Math.min(s, 59),
248
+ minutes: alignToStep(now.getMinutes(), minuteStep),
249
+ seconds: showSeconds ? alignToStep(now.getSeconds(), secondStep) : 0,
230
250
  }
231
251
  commitDraft(next)
232
252
  setOpen(false)
@@ -307,7 +327,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
307
327
  (@radix-ui/react-popover index.js:145),不 inject 任何 onKeyDown。原生 <button>
308
328
  靠瀏覽器在 Enter/Space 自動派發 click 才能開;但本 trigger 是 <div role=combobox>,
309
329
  div 不會自動派發 click → 鍵盤使用者打不開 panel。故顯式加 onKeyDown
310
- (對齊 select.tsx:593-598 既有 canonical + WAI-ARIA APG combobox required keys)。 */}
330
+ (對齊 select.tsx:609-619 既有 canonical + WAI-ARIA APG combobox required keys)。 */}
311
331
  <PopoverTrigger asChild>
312
332
  <div
313
333
  ref={ref}
@@ -335,13 +355,9 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
335
355
  data-field-mode="edit"
336
356
  data-error={error ? '' : undefined}
337
357
  className={cn(
338
- fieldWrapperStyles({ mode: 'edit', variant: variant, size }),
358
+ fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }),
339
359
  'text-left cursor-pointer',
340
360
  'focus-visible:outline-none',
341
- error && [
342
- 'border-error hover:border-error-hover',
343
- 'focus-within:border-error focus-within:hover:border-error',
344
- ],
345
361
  className,
346
362
  )}
347
363
  {...props}
@@ -426,9 +442,12 @@ export const timePickerMeta = {
426
442
  sizes: {
427
443
 
428
444
  },
429
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
445
+ // states 對齊真實視覺態:trigger panel item 皆無 'active'(按下)專屬視覺態(2026-07-04 audit 對齊);
446
+ // 'selected' = panel item 持續選中(bg-neutral-selected,time-columns.tsx isSelected;2026-07-07 詞彙統一補列)。
447
+ states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],
430
448
  tokens: {
431
- bg: ['bg-neutral-hover', 'bg-primary', 'bg-transparent'],
449
+ // panel selected 走 bg-neutral-selected(time-columns.tsx isSelected;spec「欄內 item 狀態」),非 bg-primary
450
+ bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-transparent'],
432
451
  fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],
433
452
  ring: [],
434
453
  },
@@ -51,7 +51,7 @@ export const Overview: Story = {
51
51
  ['title', 'string', '必填', '主要訊息'],
52
52
  ['description', 'string', '—', '補充說明,自然換行'],
53
53
  ['action', '{ label: string; onClick: () => void }', '—', 'tertiary xs 按鈕(Undo pattern)'],
54
- ['duration', 'number', '4000', '自動關閉時間(ms)'],
54
+ ['duration', 'number', 'action ? 10000 : 4000', '自動關閉時間(ms);含 action 預設加長(2026-07-05 D4)'],
55
55
  ].map(([p, t, d, desc]) => (
56
56
  <tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
57
57
  ))}
@@ -100,7 +100,7 @@ export const Inspector: Story = {
100
100
  description: { control: 'text', description: '補充說明,自然換行' },
101
101
  duration: {
102
102
  control: { type: 'range', min: 2000, max: 10000, step: 1000 },
103
- description: '自動關閉時間(ms),action toast 建議加長至 6000',
103
+ description: '自動關閉時間(ms);預設 action 存在 10000,否則 4000(2026-07-05 D4)',
104
104
  },
105
105
  withAction: { control: 'boolean', description: '附 undo action button(樂觀 UI 反悔窗口)' },
106
106
  },
@@ -171,7 +171,7 @@ export const ColorMatrix: Story = {
171
171
  render: () => (
172
172
  <div className="flex flex-col gap-6">
173
173
  <div>
174
- <H3>跟 Alert 共用同一套 variant × theme</H3>
174
+ <H3>與 Alert 共用 Notice primitive 與三層 theme 架構(variant × theme 映射各自不同)</H3>
175
175
  <div className="overflow-x-auto">
176
176
  <table className="text-caption border-collapse">
177
177
  <thead><tr><Th>Variant</Th><Th>Bg</Th><Th>data-theme</Th><Th>視覺</Th></tr></thead>
@@ -222,7 +222,7 @@ export const StateBehavior: Story = {
222
222
  <thead><tr><Th>階段</Th><Th>觸發</Th><Th>行為</Th><Th>Token / 數值</Th></tr></thead>
223
223
  <tbody>
224
224
  <tr><Td mono>進場</Td><Td mono>toast()</Td><Td>從右下滑入 + fade-in + stacking shift</Td><Td mono>~400ms(sonner 內建)</Td></tr>
225
- <tr><Td mono>自動關閉</Td><Td>`duration` 到期</Td><Td>fade-out + 向右滑出</Td><Td mono>duration ?? 4000ms</Td></tr>
225
+ <tr><Td mono>自動關閉</Td><Td>`duration` 到期</Td><Td>fade-out + 向右滑出</Td><Td mono>duration ?? (action ? 10000 : 4000) ms</Td></tr>
226
226
  <tr><Td mono>手動 dismiss</Td><Td>X button / 向右滑動</Td><Td>立即 fade-out + 滑出</Td><Td mono>滑動 ≥ 45px 或速度夠快(velocity {'>'} 0.11)</Td></tr>
227
227
  <tr><Td mono>Pause on hover</Td><Td>Hover 任一 toast</Td><Td>倒數暫停;離開後 resume</Td><Td mono>sonner 預設開啟</Td></tr>
228
228
  </tbody>
@@ -284,13 +284,12 @@ export const StateBehavior: Story = {
284
284
  label: '復原',
285
285
  onClick: () => toast({ variant: 'info', title: '已復原' }),
286
286
  },
287
- duration: 6000,
288
287
  })}
289
288
  >
290
289
  觸發含 Undo action 的 toast
291
290
  </Button>
292
291
  <p className="text-footnote text-fg-muted mt-3">
293
- Action toast 通常 `duration` 加長至 6000ms,給使用者足夠時間反悔(預設 4000ms 較短)
292
+ action 的 toast DS 預設 `duration` 自動加長至 10000ms( action 4000ms;2026-07-05 D4)——給使用者(含鍵盤 Tab 路徑)足夠時間反悔,無需手動指定。
294
293
  </p>
295
294
  </div>
296
295
 
@@ -99,7 +99,7 @@ export const UsageGuidance: Story = {
99
99
  >
100
100
  ❌ 刪除確認用 Toast
101
101
  </Button>
102
- <Label warn>↑ 破壞性動作靠 Toast action 確認 → 4 秒消失 / 點錯只能救 → 用 Dialog 聚焦確認</Label>
102
+ <Label warn>↑ 破壞性動作靠 Toast action 確認 → 10 秒後仍消失 / 點錯只能救 → 用 Dialog 聚焦確認</Label>
103
103
  </Rule>
104
104
  </div>
105
105
  <Toaster />
@@ -113,7 +113,7 @@ export const ActionRule: Story = {
113
113
  <div>
114
114
  <Rule
115
115
  title="Toast action — 讓使用者短暫內復原"
116
- note="Undo pattern:執行後 toast 出現 + 「復原」按鈕,使用者有一段時間(預設 4 )可以反悔。Gmail / Linear / Slack 都用這個 pattern"
116
+ note="Undo pattern:執行後 toast 出現 + 「復原」按鈕,使用者有一段時間(含 action 預設 10 秒;2026-07-05 D4)可以反悔。Gmail / Linear / Slack 都用這個 pattern"
117
117
  >
118
118
  <Button
119
119
  variant="tertiary"
@@ -126,12 +126,12 @@ export const ActionRule: Story = {
126
126
  >
127
127
  刪除訊息(帶 Undo)
128
128
  </Button>
129
- <Label>↑ 點「刪除」不彈 Dialog 直接刪 + Toast,4 秒內可「復原」</Label>
129
+ <Label>↑ 點「刪除」不彈 Dialog 直接刪 + Toast,10 秒內可「復原」</Label>
130
130
  </Rule>
131
131
 
132
132
  <Rule
133
133
  title="Undo 的好處:避免每個動作都 Dialog 阻斷"
134
- note="傳統做法每個刪除都彈 Dialog 確認 → 使用者被反覆阻斷。Undo pattern 讓「未確認即執行」變得安全——錯了 4 秒內可救"
134
+ note="傳統做法每個刪除都彈 Dialog 確認 → 使用者被反覆阻斷。Undo pattern 讓「未確認即執行」變得安全——錯了 10 秒內可救"
135
135
  >
136
136
  <Label>✅ 減少 Dialog 阻斷、提升速度、錯誤仍可挽救</Label>
137
137
  </Rule>
@@ -153,7 +153,7 @@ export const VariantRule: Story = {
153
153
  <div>
154
154
  <Rule
155
155
  title="success / info / warning / error 各自對應場景"
156
- note=" Alert 共用同一套 variant × theme 策略。語意由事件類型決定——不靠 consumer 用顏色暗示"
156
+ note=" Alert 共用 Notice primitive 與三層 theme 架構(variant × theme 映射各自不同,如 success:Toast 反轉 surface / Alert solid 綠底)。語意由事件類型決定——不靠 consumer 用顏色暗示"
157
157
  >
158
158
  <div className="flex flex-col gap-2">
159
159
  <Button variant="tertiary" onClick={() => toast({ variant: 'success', title: '已儲存' })}>success</Button>
@@ -179,8 +179,8 @@ export const DurationRule: Story = {
179
179
  render: () => (
180
180
  <div>
181
181
  <Rule
182
- title="預設 4000ms — 足夠讀完一行但不拖累"
183
- note="多數場景用預設。使用者有足夠時間讀「已儲存」+ 考慮是否按「復原」。過短 = 來不及看,過長 = 堆積"
182
+ title="預設 4000ms(無 action)— 足夠讀完一行但不拖累"
183
+ note="多數場景用預設。使用者有足夠時間讀「已儲存」。過短 = 來不及看,過長 = 堆積"
184
184
  >
185
185
  <Button variant="tertiary" onClick={() => toast({ variant: 'success', title: '已儲存' })}>
186
186
  預設 4000ms
@@ -188,8 +188,8 @@ export const DurationRule: Story = {
188
188
  </Rule>
189
189
 
190
190
  <Rule
191
- title="較長(7000-10000ms)— 內容較多或有 action"
192
- note="兩行描述 + undo action 需要更多時間。使用者讀完 + 判斷 + 點擊 action"
191
+ title=" action → 預設自動加長為 10000ms"
192
+ note="兩行描述 + undo action 需要更多時間——含鍵盤使用者 Tab action 的路徑(鍵盤焦點不暫停倒數;2026-07-05 D4)。DS 在 action 存在時預設 duration = 10000ms,無需手動指定;內容特多可自行覆寫"
193
193
  >
194
194
  <Button
195
195
  variant="tertiary"
@@ -198,10 +198,9 @@ export const DurationRule: Story = {
198
198
  title: '已刪除 3 個項目',
199
199
  description: '已移至回收桶,可以在 7 天內復原',
200
200
  action: { label: '復原', onClick: () => {} },
201
- duration: 7000,
202
201
  })}
203
202
  >
204
- 較長 duration(有 description + action)
203
+ 較長 duration(有 description + action,預設 10000ms)
205
204
  </Button>
206
205
  </Rule>
207
206
 
@@ -44,7 +44,7 @@ Toast 是**短暫的浮動通知,自動消失**。用於操作結果回饋(
44
44
 
45
45
  - **操作結果短暫回饋**:儲存成功、訊息已送出、已複製到剪貼簿、刪除失敗
46
46
  - **背景非同步動作完成**:檔案上傳完成、資料同步完成、通知已寄出
47
- - **需要「復原」按鈕的操作**:刪除後 4 秒內可 Undo(sonner action 模式)
47
+ - **需要「復原」按鈕的操作**:刪除後 10 秒內可 Undo(sonner action 模式;含 `action` 的 toast 預設 `duration` = 10000ms,見「API」段)
48
48
  - **非關鍵資訊**:訊息即使使用者沒看到也不影響流程
49
49
 
50
50
  ## 何時不用
@@ -82,6 +82,7 @@ Toast 是**短暫的浮動通知,自動消失**。用於操作結果回饋(
82
82
  - **由本 DS 在 outer wrapper 上設定**:上述 `role` / `aria-live` 是 Toast 自己依 variant 算出 `isCritical` 後手動設在自家 wrapper div 上的(見 `.tsx` L49-51 / L59 / L72),**非** sonner 內建——sonner runtime 本身 0 個 `role`,只有外層 `<section>` 容器固定的 `aria-live="polite"`(包住所有 toast 的 region,不隨 variant 變)。consumer 無需額外處理。
83
83
  - 已知結構限制:`error` / `warning` 的 `aria-live="assertive"` div 巢狀在 sonner `<section>` 的 `aria-live="polite"` 內(nested live region)。screen reader 對巢狀 live region 的處理依實作而異;若需嚴格保證 assertive 立即朗讀,consumer 應在 app 層另設獨立 live region。
84
84
  - **關閉按鈕**:畫面上可見的 X 關閉鈕由 Notice 渲染(`<Button dismiss aria-label="關閉通知">`,見 `../Notice/notice.tsx`),已自帶 `aria-label`,不是 sonner 內建 close 鈕(本 DS 用 `sonner.custom` + `unstyled`,未開 `closeButton`)。若 consumer 自訂 `action`,務必為自訂互動元素提供 `aria-label`。
85
+ - **自動關閉計時器與鍵盤暫停路徑**(2026-07-05 D4):sonner 計時器只在滑鼠 hover / pointer 按住 / 分頁切離時暫停——**Tab 鍵盤焦點進入 toast 不暫停倒數**(sonner 2.x 行為,`expanded / interacting` 只由 mouse / pointer 事件設定)。鍵盤替代路徑是 sonner 內建 hotkey **⌥T(Option+T / Alt+T)**:焦點跳入 toast 區域並展開 stack(`expanded=true`)→ 倒數暫停,焦點離開後恢復。為給鍵盤使用者足夠反應時間,含 `action` 的 toast 預設 `duration` 加長為 10000ms(見「API」段;WCAG 2.2.1 Timing Adjustable)。
85
86
 
86
87
  ---
87
88
 
@@ -117,14 +118,13 @@ toast({
117
118
  title: '操作成功',
118
119
  description: '變更已儲存至伺服器',
119
120
  action: { label: '復原', onClick: handleUndo },
120
- duration: 4000,
121
121
  })
122
122
  ```
123
123
 
124
124
  - `title`(必填):主要訊息
125
125
  - `description`(選填):補充說明,自然換行
126
126
  - `action`(選填):tertiary xs 按鈕
127
- - `duration`(選填):自動關閉時間,預設 4000ms
127
+ - `duration`(選填):自動關閉時間;預設依 `action` 而定——有 `action` → **10000ms**(鍵盤焦點不暫停 sonner 倒數,10 秒給鍵盤使用者足夠時間按到「復原 / 重試」,見「A11y 預設」段;2026-07-05 D4 user 拍板,對齊 Polaris toast with action ≥10s / Carbon actionable notification 預設 persistent),無 `action` → **4000ms**
128
128
 
129
129
  ## 寬度(元件級常數)
130
130
 
@@ -146,9 +146,9 @@ Toast 固定 **360px 寬**(見 `.tsx` 的 `w-[360px]`)——跨通知視覺
146
146
  Toast 的 public API 是 `toast()` 函式(imperative 觸發)+ `<Toaster />` Provider(app-level,置於 main.tsx 一次)。**兩者皆非「forwardRef + ...props + asChild」canonical shadcn 元件 pattern**:
147
147
 
148
148
  - `toast(...)` 是 imperative API(sonner 負責 queue / stack / animation / auto-dismiss),consumer 不需持有 DOM ref
149
- - `<Toaster />` 是 Provider(sonner 內部管理 portal container),consumer 在 app root 放一次,無 consumer-facing DOM operation
149
+ - `<Toaster />` 是 Provider(sonner 內部管理 viewport container),consumer 在 app root 放一次,無 consumer-facing DOM operation
150
150
 
151
- **sonner 已實作 portal / stack / viewport 內部**(Toaster Root portal-renders + viewport DOM spread + 外層 `<section aria-live="polite">` region)。多則同時觸發時 sonner 不是 queue 到 DOM 外,而是全部渲染進 DOM、預設只顯示最近 3 則(`visibleToasts=3`)、超出項以 `data-visible=false` 隱藏堆疊。我們不再 wrap 一層,保持與 sonner 直接對應,避免 API 膨脹。per-variant 的 `role=status/alert` 由本 DS 在自家 toast wrapper 上設(見「A11y 預設」段),非 sonner 提供。
151
+ **sonner 已實作 stack / viewport 內部**(Toaster 就地渲染外層 `<section aria-live="polite">` viewport region,以 CSS `position: fixed` 覆蓋 viewport——非 portal,sonner 不用 `createPortal`)。多則同時觸發時 sonner 不是 queue 到 DOM 外,而是全部渲染進 DOM、預設只顯示最近 3 則(`visibleToasts=3`)、超出項以 `data-visible=false` 隱藏堆疊。我們不再 wrap 一層,保持與 sonner 直接對應,避免 API 膨脹。per-variant 的 `role=status/alert` 由本 DS 在自家 toast wrapper 上設(見「A11y 預設」段),非 sonner 提供。
152
152
 
153
153
  若 consumer 需要程式化控制(`toast.dismiss(id)` / `toast.promise(...)`),使用 sonner 原生 API,Toast 元件層不重新暴露。
154
154
 
@@ -9,21 +9,25 @@ const meta: Meta = {
9
9
  }
10
10
  export default meta
11
11
 
12
- const VARIANTS: NoticeVariant[] = ['neutral', 'success', 'warning', 'error']
12
+ // 2026-07-04 audit:補 'info'(NoticeVariant 5 值,原漏 → 靜態雙 theme 對照未涵蓋全 variant)
13
+ const VARIANTS: NoticeVariant[] = ['neutral', 'info', 'success', 'warning', 'error']
13
14
  const LABELS: Record<string, string> = {
14
15
  neutral: '檔案已複製到剪貼簿',
16
+ info: '新版本 v2.4 可用',
15
17
  success: '專案已儲存',
16
18
  warning: '即將到期的訂閱',
17
19
  error: '無法連線伺服器',
18
20
  }
19
21
  const ACTIONS: Record<string, string> = {
20
22
  neutral: '復原',
23
+ info: '查看更新',
21
24
  success: '查看',
22
25
  warning: '續訂',
23
26
  error: '重試',
24
27
  }
25
28
  const DESCRIPTIONS: Record<string, string> = {
26
29
  neutral: '「Q3 營收報表.xlsx」已加入剪貼簿',
30
+ info: '重新整理頁面即可套用新功能',
27
31
  success: '變更已同步到所有成員',
28
32
  warning: '7 天後將停用,請前往結算頁續訂',
29
33
  error: '請檢查網路後再試一次',
@@ -38,8 +38,11 @@ function ToastInner({
38
38
  const isInverse = variant === 'neutral' || variant === 'success'
39
39
  const dismiss = () => sonnerToast.dismiss(id)
40
40
 
41
+ // action 執行後自動 dismiss:關閉「復原/重試」的重複觸發窗口(double-undo / double-retry),
42
+ // 對齊 Polaris Toast action / Material Snackbar action 按後即收慣例;
43
+ // toast.anatomy.stories.tsx「點 action button 執行 callback + 關閉 toast」宣稱自此為真。
41
44
  const actionButton = action ? (
42
- <Button variant="tertiary" size="xs" onClick={action.onClick}>{action.label}</Button>
45
+ <Button variant="tertiary" size="xs" onClick={() => { action.onClick(); dismiss() }}>{action.label}</Button>
43
46
  ) : undefined
44
47
 
45
48
  // ── Live region 由 outer Toast wrapper 擁有(WAI-ARIA + toast.spec.md「A11y 預設」段 canonical) ──
@@ -80,7 +83,9 @@ function ToastInner({
80
83
  }
81
84
 
82
85
  export function toast(options: ToastOptions) {
83
- const { duration = 4000, ...rest } = options
86
+ // 2026-07-05 D4:sonner 計時器不因鍵盤 Tab 焦點暫停(僅 hover / pointer 按住 / ⌥T hotkey)——
87
+ // 含 action(復原/重試)預設 10s,給鍵盤使用者足夠時間 Tab 到 action(user 拍板;對齊 Polaris toast with action ≥10s)
88
+ const { duration = options.action ? 10000 : 4000, ...rest } = options
84
89
  return sonnerToast.custom((id) => <ToastInner id={id} {...rest} />, { duration })
85
90
  }
86
91
 
@@ -88,10 +93,10 @@ export interface ToasterProps {
88
93
  position?: 'top-left' | 'top-right' | 'top-center' | 'bottom-left' | 'bottom-right' | 'bottom-center'
89
94
  }
90
95
 
91
- // shadcn canonical:forwardRef + displayName 統一。Sonner Toaster portal-renders
92
- // document body,ref Toaster 本身無實際 DOM 節點可接(portal 逃逸),
93
- // 但保留 forwardRef 簽名以符合 DS 統一 API(consumer typecheck ref,
94
- // 與其他 DS 元件一致)。
96
+ // shadcn canonical:forwardRef + displayName 統一。SonnerToaster 不轉發 ref,
97
+ // ref 無實際 DOM 節點可接;viewport 為就地渲染的 <section>,以 CSS position:fixed
98
+ // 覆蓋 viewport(非 portal sonner 全庫 0 createPortal,2026-07-04 grep 驗證)。
99
+ // 保留 forwardRef 簽名以符合 DS 統一 API(consumer 可 typecheck 傳 ref)。
95
100
  const Toaster = React.forwardRef<HTMLDivElement, ToasterProps>(
96
101
  ({ position = 'bottom-right', ...props }, _ref) => {
97
102
  return (
@@ -118,7 +123,8 @@ export const toastMeta = {
118
123
  error: { when: '錯誤(匯入失敗 / 權限不足);action prop 可加重試' },
119
124
  },
120
125
  sizes: {},
121
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
126
+ // 2026-07-04 audit 對齊:Toast disabled state(spec 邊界案例明文;對齊 noticeMeta ['default'] 已修 pattern)
127
+ states: ['default'],
122
128
  tokens: {
123
129
  bg: ['bg-error', 'bg-info', 'bg-surface-raised', 'bg-warning'],
124
130
  fg: ['text-foreground'],
@@ -75,9 +75,9 @@ Tooltip 底色是深色,子元素永遠套用 dark theme token(透過 `data-
75
75
 
76
76
  ## Edge collision(避免貼 viewport 邊)
77
77
 
78
- **`collisionPadding` default = 8**( HoverCard 一致),Radix `avoidCollisions` 預設 true 會自動翻邊,但 padding 0 會讓 tooltip **貼齊 viewport 邊緣**(視覺上擠)。default 8 讓 tooltip 跟 viewport 邊保留最少 8px 呼吸距離。consumer 需自訂傳 prop 即可覆寫。
78
+ **`collisionPadding` default = 8**(消費 `OVERLAY_COLLISION_PADDING` overlay 家族 canonical,與 Popover 一致;HoverCard 是 documented exception 特意用 12 補 Radix rounding,見 `hover-card.tsx` 註解),Radix `avoidCollisions` 預設 true 會自動翻邊,但 padding 0 會讓 tooltip **貼齊 viewport 邊緣**(視覺上擠)。default 8 讓 tooltip 跟 viewport 邊保留最少 8px 呼吸距離。consumer 需自訂傳 prop 即可覆寫。
79
79
 
80
- 世界級對照:Material Tooltip `margin: 14px` default / Polaris Tooltip 8-12px — 本 DS 選 8 對齊 HoverCard。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
80
+ 世界級對照:Material Tooltip `margin: 14px` default / Polaris Tooltip 8-12px — 本 DS 選 8 對齊 overlay 家族共用常數。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
81
81
 
82
82
  ## 最大寬度(元件級常數)
83
83
 
@@ -109,7 +109,7 @@ Tooltip 是**單一職責 hover 提示 primitive**(一句話補充),刻意無變
109
109
  - **無 SizeMatrix**:Tooltip 無 size prop,尺寸由內容 + max-width 常數決定(本 spec「最大寬度」段)。不提供 sm/md/lg tier——短文字 hint 不需要尺寸變體。
110
110
  - **無 StateBehavior**:Tooltip 是 passive 出現 / 消失(hover / focus 觸發,見「出現時機」段),無 hover / selected / active / disabled 這類互動元件 state。開 / 關行為由 Radix primitive 處理,寫在 `Overview` 的 usage 說明已足夠。
111
111
 
112
- 對應 anatomy story:保留 `Overview` + `Inspector`(delay / placement 互動試玩) + 元件特有 `PlacementReference`(12 種 side × align 對照)。
112
+ 對應 anatomy story:保留 `Overview` + `Inspector`(delay / placement 互動試玩) + 元件特有 `PlacementReference`(4 種 side 方向對照 + side-aware 動畫表;Tooltip 走 `align="center"` 輕量浮層例外,見「Align 對齊」段,無 align 軸矩陣)。
113
113
 
114
114
  ---
115
115
 
@@ -24,7 +24,7 @@ const TooltipContent = React.forwardRef<
24
24
  React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
25
25
  >(({ className, sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, style, children, ...props }, ref) => (
26
26
  // collisionPadding default 8px:避免 tooltip 貼 viewport 邊(Radix avoidCollisions 預設 true 但 padding 0 會貼邊)
27
- // 對齊 HoverCard canonical 避免 viewport edge clipping
27
+ // 消費 OVERLAY_COLLISION_PADDING overlay 家族 canonical(與 Popover 一致;HoverCard 特例 12 補 rounding)避免 viewport edge clipping
28
28
  <TooltipPrimitive.Portal>
29
29
  <TooltipPrimitive.Content
30
30
  ref={ref}
@@ -66,8 +66,11 @@ export const Overview: Story = {
66
66
  [' selectionMode', "'single' | 'multiple' | 'none'", "'single'", "multiple 模式自動顯示 checkbox;none=純展開展示不可選(JSON viewer)"],
67
67
  [' expandedIds / onExpandedChange', 'Set<string> / handler', '—', '展開狀態受控'],
68
68
  [' defaultExpandedIds', 'string[]', '—', '初始展開(uncontrolled)'],
69
+ [' defaultSelectedIds', 'string[]', '—', '初始選取(uncontrolled)'],
70
+ [' expandOnSelect', 'boolean', 'false', '點 label 同時 select + expand(stepper 適用;預設 chevron 是展開唯一控件)'],
69
71
  [' draggable / onDragEnd', 'boolean / handler', 'false', '拖曳重排'],
70
- [' size', "'sm' | 'md' | 'lg'", "'md'", 'font-size tier'],
72
+ [' size', "'sm' | 'md' | 'lg'", "'md'", '尺寸 tier(node 高度 / icon 大小 / indent 寬度連動)'],
73
+ [' context', "'sidebar' | 'menu'", "'sidebar'", '使用脈絡,決定水平 padding(sidebar=--layout-space-loose / menu=12px 對齊 MenuItem)'],
71
74
  ['TreeItem', '', '', ''],
72
75
  [' id', 'string', '必填', '唯一識別碼'],
73
76
  [' label', 'ReactNode', '必填', 'node 名稱'],
@@ -75,6 +78,8 @@ export const Overview: Story = {
75
78
  [' checkbox', 'ReactNode', '—', 'multiple 模式 selection checkbox(通常自動帶入)'],
76
79
  [' inlineActions', 'InlineActionConfig[]', '—', '右側 inline actions(宣告式 config,內部用 <ItemInlineAction> 渲染)'],
77
80
  [' inlineActionsSlot', 'ReactNode', '—', 'escape-hatch slot(放自訂元素,跟 inlineActions 互斥)'],
81
+ [' actionsReveal', "false | 'hover'", "'hover'", 'inline actions 顯示時機(hover reveal;false=常駐)'],
82
+ [' indicator', 'ReactNode', '—', '取代 icon 位置(chevron 永存);stepper 狀態視覺由此傳入'],
78
83
  [' disabled', 'boolean', 'false', '停用該 node'],
79
84
  ].map(([p, t, d, desc]) => (
80
85
  <tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
@@ -30,7 +30,7 @@ const Label = ({ children, warn }: { children: React.ReactNode; warn?: boolean }
30
30
 
31
31
  const Section = ({ title, children }: { title: string; children: React.ReactNode }) => (
32
32
  <section className="mb-12">
33
- <h2 className="text-heading-3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
33
+ <h2 className="text-h3 font-bold text-foreground mb-4 pb-2 border-b border-border">{title}</h2>
34
34
  {children}
35
35
  </section>
36
36
  )
@@ -77,7 +77,7 @@ export const UsageGuidance: Story = {
77
77
 
78
78
  <Section title="何時不用 + 替代方案">
79
79
  <Rule
80
- title="❌ 互斥展開(同時只開一個):用 Accordion(未來)"
80
+ title='❌ 互斥展開(同時只開一個):用 Accordion type="single"'
81
81
  note="TreeView 預設允許任意多個節點同時展開。若需要「選一個展開其他自動收」的互斥語意,用 Accordion(非 TreeView)"
82
82
  >
83
83
  <Label warn>(範例省略)TreeView 是多展開,Accordion 是單展開,語意不同</Label>
@@ -116,7 +116,7 @@ Chevron 是**展開/收合控件**,不是 prefix icon:`fg-muted`(指示色,hover
116
116
 
117
117
  **預設 label 不 expand**——chevron 是展開唯一控件。理由:select / expand 語意獨立(sidebar「Documents」點 label 進頁面,點 chevron 才展開子列表)。Consumer `expandOnSelect` prop 可讓整行同時 select + expand(適合 stepper)。
118
118
 
119
- **動畫**:children 用 Radix `Collapsible` height animation(0 → auto);chevron `transition-transform duration-150 rotate-0 → rotate-90`。
119
+ **動畫**:children 用 Radix `Collapsible` height animation(0 → auto);chevron `transition-all duration-150 rotate-0 → rotate-90`(`transition-all` 同時涵蓋 hover 色彩過渡)。
120
120
 
121
121
  ---
122
122
 
@@ -258,7 +258,7 @@ TreeItem 右側 `actions` slot 只在 hover 該列時出現(opacity 0→1 transi
258
258
 
259
259
  ### Inline action 視覺 + API
260
260
 
261
- Icon 尺寸跟 size tier(sm/md=16, lg=20);色 `fg-muted` → hover `foreground`;hover bg `neutral-hover`;action 間距 / 高度對齊規則見 `item-anatomy.spec.md`「Inline Action 設計規格」(canonical SSOT)。
261
+ Icon 尺寸跟 size tier(sm/md=16, lg=20);色 `fg-muted` → hover `foreground`;hover bg `neutral-hover`;action 間距 / 高度對齊規則見 `../../patterns/element-anatomy/inline-action.spec.md`(canonical SSOT,2026-04-24 自 item-anatomy 抽出)。
262
262
 
263
263
  **宣告式 API**(對齊 `inline-action.spec.md` + `SidebarMenuButton.inlineActions`):`inlineActions: InlineActionConfig[]` + `actionsReveal: false | "hover"`(預設 `"hover"`,鍵盤 focus-visible 也顯)。內部用 `<ItemInlineAction>` helper 渲染,consumer **不可手刻 button JSX**(canonical 在 `item-anatomy.tsx`,共用 size 查表 / Tooltip / hover bg / aria)。Hover-reveal 用 `group-has-[:focus-visible]/tree-item:opacity-100` 而非 `group-focus-within`(後者 mouse click 會永久顯示)。
264
264
 
@@ -383,14 +383,19 @@ const TreeView = React.forwardRef<HTMLDivElement, TreeViewProps>(
383
383
  if (parentId && parentId !== String(active.id)) {
384
384
  const parentDepth = Number(parentTreeItem?.getAttribute('aria-level') ?? 1) - 1
385
385
  finalDepth = parentDepth
386
- setDropTarget({ id: parentId, position: 'after', depth: parentDepth })
386
+ setDropTarget(prev => prev && prev.id === parentId && prev.position === 'after' && prev.depth === parentDepth ? prev : { id: parentId, position: 'after', depth: parentDepth })
387
387
  return
388
388
  }
389
389
  }
390
390
  }
391
391
  }
392
392
 
393
- setDropTarget({ id: String(over.id), position, depth: finalDepth })
393
+ // 2026-07-06 D3 perf:bail-out — drop target 未變( dragOver row 才變)時回傳 prev,
394
+ // React 對相同 reference bail out,避免每次 over 事件重建 contextValue → 全樹 TreeItem re-render。
395
+ setDropTarget(prev => {
396
+ const id = String(over.id)
397
+ return prev && prev.id === id && prev.position === position && prev.depth === finalDepth ? prev : { id, position, depth: finalDepth }
398
+ })
394
399
 
395
400
  // Auto-expand collapsed folder after 500ms hover (Figma behavior)
396
401
  if (position === 'inside' && hasChildren && !expandedIds.has(String(over.id))) {
@@ -904,6 +909,9 @@ const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
904
909
  aria-selected={selectionMode !== 'none' ? isSelected : undefined}
905
910
  aria-level={depth + 1}
906
911
  aria-disabled={disabled || undefined}
912
+ // 2026-07-05 D4 修:aria-roledescription 移到 treeitem(有 role 的元素才合法)—
913
+ // 供 AT 提示此 node 可拖曳;值消費 dnd-kit attributes SSOT(預設 'draggable')。
914
+ aria-roledescription={draggable && !disabled ? dragAttrs['aria-roledescription'] : undefined}
907
915
  data-tree-id={id}
908
916
  data-tree-parent-id={parentId ?? ''}
909
917
  data-tree-has-children={hasChildren}
@@ -954,7 +962,14 @@ const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
954
962
  paddingRight: 'var(--tree-px)',
955
963
  }}
956
964
  onClick={handleRowClick}
957
- {...(draggable ? { ...dragListeners, ...dragAttrs } : {})}
965
+ // 2026-07-05 D4 修:只 spread listeners,不 spread dnd-kit attributes —
966
+ // useDraggable 預設 attributes 注入 role="button" + tabIndex=0 + aria-pressed +
967
+ // aria-describedby(鍵盤拖曳指示):role=button 污染 treeitem 語意、tabIndex=0
968
+ // 讓每 row 變 DOM tab stop 破壞單一 tab stop 虛擬焦點模型(DOM focus 在 row 上按
969
+ // Enter 會 bubble 到容器 handleKeyDown 但 handler 操作 state focusedId → 錯位);
970
+ // sensors 僅 PointerSensor(無 KeyboardSensor),這些 attrs 換不到鍵盤拖曳能力。
971
+ // aria-roledescription 保留在上方 treeitem 元素(有 role 才合法)。
972
+ {...(draggable ? dragListeners : {})}
958
973
  {...props}
959
974
  >
960
975
  {chevronSlot}
@@ -968,7 +983,12 @@ const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
968
983
  * 對齊 cite:menu-item.tsx:194-195(MenuItem selected bg)+ select-menu.tsx:352-354(SelectMenu multi=checkbox) */}
969
984
  {(checkbox || selectionMode === 'multiple') && (
970
985
  <ItemPrefix className="pointer-events-none">
971
- {checkbox || <Checkbox checked={isSelected} disabled={disabled} aria-hidden="true" />}
986
+ {/* 2026-07-05 D4 修:auto-render Checkbox tabIndex={-1} Radix Checkbox root
987
+ * 是原生 button(預設可聚焦),aria-hidden + 可聚焦 = axe aria-hidden-focus,且
988
+ * 破壞 tree 單一 tab stop 虛擬焦點模型(tree-view.spec.md「Focus」段);
989
+ * ItemPrefix pointer-events-none 只擋滑鼠不擋鍵盤。選取語意由 treeitem
990
+ * aria-selected 承載,checkbox 純視覺反映。 */}
991
+ {checkbox || <Checkbox checked={isSelected} disabled={disabled} aria-hidden="true" tabIndex={-1} />}
972
992
  </ItemPrefix>
973
993
  )}
974
994
 
@@ -1056,7 +1076,9 @@ export const treeViewMeta = {
1056
1076
  sizes: {
1057
1077
 
1058
1078
  },
1059
- states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
1079
+ // 'selected' = single-selection 持續選中(bg-neutral-selected + aria-selected);'active' 移除
1080
+ // 全檔無 Tailwind 按壓 utility,無按壓專屬視覺態(2026-07-07 詞彙統一對抗稽核補修)。
1081
+ states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],
1060
1082
  tokens: {
1061
1083
  bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-surface'],
1062
1084
  fg: ['text-fg-disabled', 'text-fg-muted', 'text-fg-secondary', 'text-foreground'],
@@ -25,15 +25,20 @@ export function useControllable<T>({
25
25
  onChangeRef.current = onChange
26
26
 
27
27
  const current = isControlled ? (value as T) : internal
28
+ // 2026-07-07 C14:setter identity 穩定化——原 deps 含 `current`,值一變 setter 就換 identity
29
+ // (consumer 放 useEffect deps / useCallback 鏈會白重跑)。對齊 Radix useControllableState:
30
+ // current 走 ref、setter 只依 isControlled,行為 Δ=0(functional updater 讀最近 render 值,同 Radix)。
31
+ const currentRef = useRef(current)
32
+ currentRef.current = current
28
33
 
29
34
  const setValue = useCallback(
30
35
  (next: T | ((prev: T) => T)) => {
31
36
  const computed =
32
- typeof next === 'function' ? (next as (prev: T) => T)(current) : next
37
+ typeof next === 'function' ? (next as (prev: T) => T)(currentRef.current) : next
33
38
  if (!isControlled) setInternal(computed)
34
39
  onChangeRef.current?.(computed)
35
40
  },
36
- [isControlled, current]
41
+ [isControlled]
37
42
  )
38
43
 
39
44
  return [current, setValue]
@@ -7,10 +7,17 @@ import { useState, useEffect } from 'react'
7
7
  * 用途:Select 等元件在 mobile 退回原生 picker。
8
8
  */
9
9
  export function useIsTouchDevice() {
10
- const [isMobile, setIsMobile] = useState(false)
10
+ // 2026-07-06 D3 perf 修:初值固定 false 讓觸控裝置首次 commit 先掛 desktop 分支
11
+ // (CustomSelect/CustomCombobox trigger subtree),effect flip true 後整棵 unmount 換 Native
12
+ // → 每 instance 白付一次 double-mount。改 lazy initializer 同步讀 matchMedia,
13
+ // 首次 render 即正確分支;effect 保留 change listener(行為不變)。
14
+ const [isMobile, setIsMobile] = useState(
15
+ () => typeof window !== 'undefined' && window.matchMedia('(pointer: coarse)').matches,
16
+ )
11
17
 
12
18
  useEffect(() => {
13
19
  const mq = window.matchMedia('(pointer: coarse)')
20
+ // 同步一次防 initializer 與 effect 之間 mq 變化(同值時 React Object.is bail-out,無多餘 render)
14
21
  setIsMobile(mq.matches)
15
22
  const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches)
16
23
  mq.addEventListener('change', handler)
package/src/index.ts CHANGED
@@ -33,8 +33,10 @@ export * from './components/FileViewer/index'
33
33
  export * from './components/Input/index'
34
34
  export * from './components/LinkInput/index'
35
35
  export * from './components/NumberInput/index'
36
+ export * from './components/Pagination/index'
36
37
  export * from './components/PeoplePicker/index'
37
38
  export * from './components/Popover/index'
39
+ export * from './components/ProfileCard/index'
38
40
  export * from './components/ProgressBar/index'
39
41
  export * from './components/RadioGroup/index'
40
42
  export * from './components/Rating/index'
@@ -71,7 +73,6 @@ export * from './patterns/resize-handle/index'
71
73
  // - components/Menu
72
74
  // - components/Notice
73
75
  // - components/OverflowIndicator
74
- // - components/ProfileCard
75
76
  // - components/SelectMenu
76
77
  // - components/SelectionControl
77
78
  // - patterns/horizontal-overflow