@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
@@ -19,19 +19,15 @@ benchmark:
19
19
 
20
20
  ## 定位
21
21
 
22
- DataTable 是基於 TanStack Table 的資料表格元件,提供排序、篩選、選取、欄位操作、虛擬捲動等完整能力。
23
- TanStack Table 負責邏輯,DataTable 負責視覺與互動。
22
+ DataTable 是基於 TanStack Table 的資料表格元件,提供排序、篩選、選取、欄位操作、虛擬捲動等完整能力。TanStack Table 負責邏輯,DataTable 負責視覺與互動。
24
23
 
25
- 簡單展示場景也用 DataTable(最少 config),不另外維護靜態 Table。
26
24
  底層使用 `<div>` + ARIA role,不用語義 `<table>`——虛擬捲動需要絕對定位 row,且未來 frozen column 需要獨立 scroll 區域,`<table>` 的佈局模型兩者都不支援。
27
25
 
28
- **預設不是試算表**——預設模式不做公式計算、不做跨 cell 選取(定位是「資料展示 + row 操作」,非 Excel)。但提供 **opt-in `spreadsheetMode` prop**:啟用後支援方向鍵跨 cell 導覽 + cell editing(見「鍵盤」段 L504-507),給確實需要 Excel-like 編輯的 productivity 場景。預設關閉以保持單純。(2026-06-01:原定位「不是試算表」與已 ship 的 `spreadsheetMode` opt-in 自相矛盾,改為「預設不是 + 可 opt-in」對齊 code;user 拍板保留功能)
26
+ **預設不是試算表**——預設模式不做公式計算、不做跨 cell 選取(定位是「資料展示 + row 操作」,非 Excel)。但提供 **opt-in `spreadsheetMode` prop**:啟用後支援方向鍵跨 cell 導覽 + cell editing(見「A11y 預設」Keyboard 行為段),給確實需要 Excel-like 編輯的 productivity 場景。預設關閉以保持單純。(2026-06-01 user 拍板:原「不是試算表」與已 ship 的 opt-in 矛盾,改「預設不是 + 可 opt-in」對齊 code、保留功能)
29
27
 
30
28
  **Layout Family**:非上述 family — composite / multi-section(多區塊組合,自 own layout)。
31
29
 
32
- ### 檔案結構
33
-
34
- 檔案拆分架構(12 file split matrix)與工程決策史屬 code home — 詳 `data-table.tsx` 檔頭 docblock(2026-06-11 遷移,Level 4;spec 只管設計語言)。
30
+ **檔案結構**:檔案拆分架構(12 file split matrix)與工程決策史屬 code home — 詳 `data-table.tsx` 檔頭 docblock(2026-06-11 遷移,Level 4;spec 只管設計語言)。
35
31
 
36
32
  ---
37
33
 
@@ -56,9 +52,7 @@ TanStack Table 負責邏輯,DataTable 負責視覺與互動。
56
52
 
57
53
  ---
58
54
 
59
- ## 層級架構
60
-
61
- 每一層建立在前一層之上,可獨立啟用。
55
+ ## 層級架構(每一層建立在前一層之上,可獨立啟用)
62
56
 
63
57
  | 層級 | 能力 | 狀態 |
64
58
  |------|------|------|
@@ -66,7 +60,7 @@ TanStack Table 負責邏輯,DataTable 負責視覺與互動。
66
60
  | **L2 選取** | row selection、checkbox、單/多選、bulk action 整合 | ✅ 完成(本文件 L2 段)|
67
61
  | **L3 欄位互動** | 排序(本文件 L3)、resize、reorder、pin、顯示隱藏 | ✅ 完成(sort + resize + reorder + pin + visibility 全 props 支援:`enableColumnResize` / `enableColumnReorder` / `columnVisibility` / `pinnedLeftColumns` / `pinnedRightColumns`,見 `data-table.tsx` `DataTableProps` 宣告)|
68
62
  | **L4 資料操作 + Cell 能力** | 進階篩選(本文件 L4 Filter)、inline edit、nested rows、row drag(本文件 L4 段)| ✅ Filter / Inline edit / Nested rows / Row drag v3 完成(Jira canonical + virtualization fix) |
69
- | **L5 進階** | 分組、搜尋、tree data v2 enhancements、export CSV/Excel | v2 |
63
+ | **L5 進階** | **分頁(本文件 L5 段)**、分組、搜尋、tree data v2 enhancements、export CSV/Excel | 分頁完成(2026-07-06);其餘待 v2 |
70
64
 
71
65
  ---
72
66
 
@@ -74,45 +68,26 @@ TanStack Table 負責邏輯,DataTable 負責視覺與互動。
74
68
 
75
69
  ### 一、Table Size
76
70
 
77
- DataTable 有三種尺寸(`sm`、`md`、`lg`),透過 `size` prop 控制。
78
-
79
- **Size 不等於 density。** Size 是這張表格的結構決策(需要多緊湊),density 是全域的使用者偏好。同一頁可以有不同 size 的表格,density 全頁一致。
71
+ DataTable 有三種尺寸(`sm`、`md`、`lg`),透過 `size` prop 控制。**Size 不等於 density。** Size 是這張表格的結構決策(需要多緊湊),density 是全域的使用者偏好。同一頁可以有不同 size 的表格,density 全頁一致。
80
72
 
81
73
  水平 padding 固定,不隨 size 或 density 變化(具體 token 見 `data-table.tsx`);垂直方向由行高模式決定(見第四節)。
82
74
 
83
75
  **Cell / header 字級隨 size,對齊 Field family。** 三種 size 的 cell 與 header 預設字級**必與同 size 的 Field 一致**:`sm`/`md` → `text-body`(14px)、`lg` → `text-body-lg`(16px),SSOT = `fieldDisplayTextClass()`(`field-wrapper.tsx`)。`lg` 行高放大時字級同步放大,避免「字小、行高大」失衡。**機制**:typed cell 由各自 Field 控件吃 `size` 自動套用(cell-registry 各 cell display mode 必傳 `size`,**禁漏傳**否則 fallback md 卡 14px);header 與非-Field 內容(consumer 自訂 cell)由 cell wrapper / header `cn(fieldDisplayTextClass(size))` 套用。`1lh`-based cell-py 公式(見第四節)隨字級自動吸收,row 高不變。
84
76
 
85
- ### 二、高度模式
86
-
87
- **有高度約束 vs 無高度約束,決定可用的功能組合。**
88
-
89
- **有高度約束**(指定高度值,或父容器提供高度限制)
90
- - 行為:資料少→outer 內容高度;資料多→撐到上限後內部 scroll(虛擬捲動啟用,header 固定)
91
- - 高度來源:固定 `height="400px"` 等具體 px/rem,或 `height="100%"` 由父 flex 提供約束(Linear 做法)— 都走相同 cap 行為,無 dead surface
77
+ ### 二、高度模式(有高度約束 vs 無高度約束,決定可用的功能組合)
92
78
 
93
- **無高度約束**(auto)
94
- - 內容決定高度,table 框只包住內容
95
- - 適合少量資料、預覽、嵌入式表格
96
- - 犧牲:無虛擬捲動(全部渲染)、header 隨頁面捲走、水平捲軸在 table 最底部
97
- - 這些不是 bug,是模式的取捨
79
+ - **有高度約束**(固定 `height="400px"` 等具體 px/rem,或 `height="100%"` 由父 flex 提供約束,Linear 做法——都走相同 cap 行為,無 dead surface):資料少→outer 內容高度;資料多→撐到上限後內部 scroll(虛擬捲動啟用,header 固定)
80
+ - **無高度約束**(auto):內容決定高度,table 框只包住內容;適合少量資料、預覽、嵌入式表格。犧牲:無虛擬捲動(全部渲染)、header 隨頁面捲走、水平捲軸在 table 最底部——這些不是 bug,是模式的取捨
98
81
 
99
82
  ### 三、三區域架構(AG Grid 模式)
100
83
 
101
84
  Table 分三層:
102
- - **Header**(固定頂部,不在 scroll 容器內):含 left / center / right 三區,center 區與 body center 的水平捲動 JS 同步 scrollLeft
103
- - **Body viewport**(垂直可捲,橫向由 center 區負責):含 left / center / right 三區,只有 center 可水平捲動,left / right 不隨水平捲動
104
- - **Left / Right 區**:寬度由凍結欄加總,不吃水平捲動;**Center 區**:flex-1,水平 overflow 自行處理
85
+ - **Header**(固定頂部,結構性地在 scroll 容器外、body 上方——不用 CSS sticky,永遠固定在頂部):含 left / center / right 三區,center 區與 body center 的水平捲動 JS 同步 scrollLeft(center-header 跟隨 center-body 捲動位置,同步機制見「捲軸」段 + `data-table.tsx`)。Header bg 用 `--muted`(code `HEADER_BG = 'bg-muted'`,比 surface 深一階,同 anatomy ColorMatrix)
86
+ - **Body viewport**:含 left / center / right 三區;center-body 是唯一的水平 scroll container、也是垂直 scroll container(`overflow-y-auto`),left / right body 不自行捲動(`overflow-hidden`,`scrollTop` 由 center 的 `onScroll` 同步兩側;AR44:V scroll 移進 region 自身,讓水平捲軸落在可視視窗底部,不必捲到內容底才看到)
87
+ - **Left / Right 區**:寬度由凍結欄加總,不吃水平捲動;frozen 邊界線用 `.dtPanelBoundaryRight/Left` 的 `box-shadow: inset` 1px `var(--divider)`(paint-only 不佔 box model;2026-05-12 自 `border-divider` 改制 — Windows 常駐捲軸會蓋掉 outer-edge border),header + body panel 各套,視覺整欄高度;**Center 區**:flex-1,水平 overflow 自行處理
105
88
 
106
89
  完整 class / overflow 規則見 `data-table.tsx`。
107
90
 
108
- **Header 在 scroll 容器外面。** 不用 CSS sticky——header 結構性地在 body 上方,永遠固定在頂部。Header bg 用 `--muted`(code `HEADER_BG = 'bg-muted'`,比 surface 深一階,同 anatomy ColorMatrix)。
109
-
110
- **三個 region 共享垂直捲動。** body-viewport 是唯一的垂直 scroll container,三個 body region 是它的 flex children。垂直捲動天然同步,不需 JS。
111
-
112
- **只有 center 水平捲動。** center-body 是唯一的水平 scroll container,center-header 跟隨其捲動位置同步(同步機制見「捲軸」段 + `data-table.tsx`)。
113
-
114
- **Left / right 不水平捲動。** Frozen 邊界用 `border-divider`(全高度)。
115
-
116
91
  **固定行高確保跨 region 對齊。** 所有 row 用 `h-table-row-{size}`,三個 region 的 row 精確同高。
117
92
 
118
93
  **Header/body region 寬度同步。** Header region 寬度由內容決定(columns + actions),body region 量測 header 寬度同步(機制見 `data-table.tsx`)。
@@ -121,15 +96,8 @@ Table 分三層:
121
96
 
122
97
  Table 層級的模式切換,不是 column 層級。跟 AG Grid / Airtable 的做法一致。
123
98
 
124
- **固定行高(預設)**——適合大多數場景
125
- - 所有 row 同高,內容垂直置中
126
- - 文字、tag、badge、avatar 等不同高度的元件都自然居中,不需要處理對齊
127
- - 文字一律截斷,不換行(column 的 `wrap: true` 被忽略)
128
-
129
- **自動行高**——適合有描述、備註等需要完整顯示的欄位
130
- - Row 高度由最高的 cell 決定,內容頂部對齊
131
- - 垂直 padding 由目標行高推導,單行時製造置中效果,多行時保持頂部對齊
132
- - `wrap: true` 的欄位可換行撐高 row
99
+ - **固定行高(預設,適合大多數場景)**:所有 row 同高、內容垂直置中——文字、tag、badge、avatar 等不同高度的元件都自然居中,不需處理對齊;文字一律截斷不換行(column 的 `wrap: true` 被忽略)
100
+ - **自動行高(適合描述、備註等需完整顯示的欄位)**:row 高度由最高的 cell 決定、內容頂部對齊;垂直 padding 由目標行高推導(單行時製造置中效果,多行時保持頂部對齊);`wrap: true` 的欄位可換行撐高 row
133
101
 
134
102
  ### 五、Header vs Body 的視覺區隔
135
103
 
@@ -140,31 +108,19 @@ Table 層級的模式切換,不是 column 層級。跟 AG Grid / Airtable 的
140
108
  | Header 分隔線 | 僅 header 區域(上下留 padding) | 一般非 frozen 欄位之間 |
141
109
  | Frozen 邊界線 | **整欄高度**(table 頂部到底部) | frozen column 與 scrollable area 的交界 |
142
110
 
143
- 一般 column 只在 header 有短線——body 的欄位邊界由 header 引導,不需額外視覺噪音。但 frozen column 的邊界是結構性的分隔(固定區域 vs 捲動區域),需要全高度的線來明確標示。
144
-
145
- Row actions 欄本質上是 frozen right column,左邊界也使用 full-height 分隔線。
111
+ 一般 column 只在 header 有短線——body 的欄位邊界由 header 引導,不需額外視覺噪音。但 frozen column 的邊界是結構性的分隔(固定區域 vs 捲動區域),需要全高度的線來明確標示。Row actions 欄本質上是 frozen right column,左邊界也使用 full-height 分隔線。
146
112
 
147
113
  **Header 文字弱化。** Header 是結構標籤,不是資訊本體。字體與 body 相同但使用次要文字色,搭配 muted 背景拉出層級,讓視覺重心留在 body 的資料上。
148
114
 
149
115
  ### 六、外框規則
150
116
 
151
- **邊框標記「這裡有使用者看不到的內容」。** 沒有邊框時,使用者無法判斷內容是否有溢出。
152
-
153
- 加框的條件(滿足任一即加):
154
- - **垂直捲動**(有高度約束,內容超出容器)
155
- - **水平溢出**(欄位總寬超過容器)
156
- - **有 frozen column**(固定欄與捲動區域的陰影分界需要外框歸屬)
157
- - **全表 inline edit**(可編輯容器需要邊界提示)
158
-
159
- 不加框時,最後一行保留底線自然收尾。加框時最後一行底線去掉,避免與外框 double border。
117
+ **邊框標記「這裡有使用者看不到的內容」。** 沒有邊框時,使用者無法判斷內容是否有溢出。加框的條件(滿足任一即加):**垂直捲動**(有高度約束,內容超出容器)/**水平溢出**(欄位總寬超過容器)/**有 frozen column**(固定欄與捲動區域的分界線需要外框歸屬)/**全表 inline edit**(可編輯容器需要邊界提示)。
160
118
 
161
- **Prop**:`bordered`(boolean,預設 `true`)。多數場景(有高度約束的虛擬捲動 / frozen column / inline edit 表)都應保持預設;只在**資料量極少、無溢出、嵌在 Card / Section 內已有外框**的展示型場景傳 `bordered={false}` 讓最外層視覺收尾。
119
+ 不加框時,最後一行保留底線自然收尾。加框時最後一行底線去掉,避免與外框 double border。**Prop**:`bordered`(boolean,預設 `true`)。多數場景(有高度約束的虛擬捲動 / frozen column / inline edit 表)都應保持預設;只在**資料量極少、無溢出、嵌在 Card / Section 內已有外框**的展示型場景傳 `bordered={false}` 讓最外層視覺收尾。
162
120
 
163
121
  ### 六之二、Column 寬度 API + 不變條件(2026-05-06 v14.3)
164
122
 
165
- **命名**:`meta.width` / `meta.minWidth` / `meta.maxWidth`(px)。**不用 TanStack `size`** — DS 內 `size` 既定為 `'sm'|'md'|'lg'` density(49+ 處),避 namespace 衝突。內部 pre-process copy 到 TanStack root,resize feature 正常。No-resize default:`width` = reserve(cell ≥ width,flex 可 grow,不可 shrink)。`enableColumnResize=true`:`width` = 初始,`minWidth` = 拖拉下限(default 80)。
166
-
167
- **不變條件(invariants,L2 test + hook 守)**:(1) cell width = column width(跟 padding/state/mode 無關)(2) display↔edit cell width 0 delta (3) display↔edit cell height 0 delta(textarea `field-sizing:content`)(4) Field 填滿 cell 高度(1px 容差於 cell.border-r)(5) No-resize column ≥ meta.width。對應 `scripts/data-table-invariants.mjs`。改 `columnSizeStyle` / 切 layout 必跑 invariant test 才 commit。
123
+ **命名**:`meta.width` / `meta.minWidth` / `meta.maxWidth`(px)。**不用 TanStack `size`** — DS 內 `size` 既定為 `'sm'|'md'|'lg'` density(49+ 處),避 namespace 衝突。內部 pre-process copy 到 TanStack root,resize feature 正常。No-resize default:`width` = reserve(cell ≥ width,flex 可 grow,不可 shrink)。`enableColumnResize=true`:`width` = 初始,`minWidth` = 拖拉下限(default 80)。**不變條件(invariants,L2 test + hook 守)**:(1) cell width = column width(跟 padding/state/mode 無關)(2) display↔edit cell width 0 delta (3) display↔edit cell height 0 delta(textarea `field-sizing:content`)(4) Field 填滿 cell 高度(1px 容差於 cell.border-r)(5) No-resize column ≥ meta.width。對應 `scripts/data-table-invariants.mjs`。改 `columnSizeStyle` / 切 layout 必跑 invariant test 才 commit
168
124
 
169
125
  ### 六之三、Runtime perf budget canonical(2026-05-14 codex+Layer A)
170
126
 
@@ -179,7 +135,7 @@ Row actions 欄本質上是 frozen right column,左邊界也使用 full-height
179
135
 
180
136
  **2026-06-12 重建後實測**(static build,3 runs/case):1x CPU — A 16.56/16.8/0、B 24.0/33.4/0、C 16.67/16.8/0 全過 gate;script 預設 4x throttle 在有背景負載機器上全 case 一致 ~2.3x 超標(含未動過的對照組 A)→ 本表門檻實證對應 1x/閒置條件,4x 門檻是否重校 = user 決策(本表數字不動)。
181
137
 
182
- **Anti-pattern**:`RoadmapAllInOne`(全 features stack)≈117ms 不達 B,因 SortableRowProvider/column reorder/resize/selection/overlay 同時開。Consumer:13+ cols rich-cell → 拆 detail drawer / column visibility 預設 hide,不該期 60fps + 全 feature stack。Cite + Phase 1/2 history → `cell-registry.tsx:480-499` JSDoc + commits log。
138
+ **Anti-pattern**:`RoadmapAllInOne`(全 features stack)≈117ms 不達 B,因 SortableRowProvider/column reorder/resize/selection/overlay 同時開。Consumer:13+ cols rich-cell → 拆 detail drawer / column visibility 預設 hide,不該期 60fps + 全 feature stack。Cite + Phase 1/2 history → `cell-registry.tsx` perf-fix JSDoc(`buildCellWithSurface` 上方)+ commits log。
183
139
 
184
140
  ### 七、Column Type
185
141
 
@@ -192,24 +148,13 @@ Row actions 欄本質上是 frozen right column,左邊界也使用 full-height
192
148
 
193
149
  ### 九、Row Actions
194
150
 
195
- 每列最右側可配置操作(編輯、刪除、複製等)。位於 right-pinned region(全高 `border-divider` 分隔),不參與水平捲動,**常駐顯示**(對齊 dense data ops 派)。
196
-
197
- **Canonical**:Row actions 一律 `Button iconOnly variant="text" size="xs"`(固定 24px),不隨 row tier 放大,**不套 `dismiss` prop**(Trash/Delete = `onRemove` 語意,不是 dismiss)。
151
+ 每列最右側可配置操作(編輯、刪除、複製等)。位於 right-pinned region(全高 1px `var(--divider)` 分隔線,機制見「三區域架構」frozen 邊界),不參與水平捲動,**常駐顯示**(對齊 dense data ops 派)。
198
152
 
199
- **Why 固定 24**:row actions 是「dense utility affordance」(輔助 ≠ 資料本體),固定 24 讓資料 cell 為視覺重心;放大會違反「data 本體 / action 輔助」階層。對照 `patterns/element-anatomy/inline-action.spec.md` Real case 表「DataTable row dedicated action column」row。
153
+ **Canonical**:Row actions 一律 `Button iconOnly variant="text" size="xs"`(固定 24px),不隨 row tier 放大,**不套 `dismiss` prop**(Trash/Delete = `onRemove` 語意,不是 dismiss)。**Why 固定 24**:row actions 是「dense utility affordance」(輔助 ≠ 資料本體),固定 24 讓資料 cell 為視覺重心;放大會違反「data 本體 / action 輔助」階層。對照 `patterns/element-anatomy/inline-action.spec.md` Real case 表「DataTable row dedicated action column」row。
200
154
 
201
155
  **世界級對照**:Material DataGrid `GridActionsCellItem`(IconButton small)/ Polaris IndexTable hover 20-24px / Atlassian Dynamic Table small iconOnly / Apple HIG Finder list row ≤24px — 全派固定不放大。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
202
156
 
203
- **收納邏輯:**
204
-
205
- | 數量 | 顯示方式 |
206
- |------|----------|
207
- | 1-2 | icon buttons 並排(全 size="xs")|
208
- | 3+ | 前 1-2 個 inline + MoreVertical dropdown(全 size="xs") |
209
-
210
- MoreVertical dropdown 包含所有操作,確保鍵盤可存取全部。
211
-
212
- **Header/body 寬度同步:** Header 渲染 invisible buttons 佔位(同 size="xs"),確保 header 和 body 的 right region 同寬。
157
+ **收納邏輯**:1-2 個 → icon buttons 並排(全 size="xs");3+ → 前 1-2 個 inline + MoreVertical dropdown(全 size="xs";dropdown 包含所有操作,確保鍵盤可存取全部)。**Header/body 寬度同步**:header 渲染 invisible buttons 佔位(同 size="xs"),確保 header 和 body 的 right region 同寬。
213
158
 
214
159
  ### 九之二、Cell action primitive 分類(2026-04-29 codified)
215
160
 
@@ -227,13 +172,11 @@ SSOT → `patterns/element-anatomy/inline-action.spec.md`「Real case 表」+ Pr
227
172
 
228
173
  ### 十、與 Toolbar 的關係
229
174
 
230
- DataTable 不內建 toolbar。Toolbar 是外部用 action-bar pattern 組合的,保持職責分離。
231
-
232
- 篩選、排序、分組走統一入口(toolbar 按鈕),不做在表頭的 per-column filter。這些按鈕的 variant 規則見 `action-bar.spec.md`。
175
+ DataTable 不內建 toolbar。Toolbar 是外部用 action-bar pattern 組合的,保持職責分離。篩選、排序、分組走統一入口(toolbar 按鈕),不做在表頭的 per-column filter。這些按鈕的 variant 規則見 `action-bar.spec.md`。
233
176
 
234
177
  ### 十一之一、Cell 垂直對齊 + icon canonical
235
178
 
236
- Cell 已 `flex items-center`,consumer render 直接 inline-flex + gap-2。Icon size:sm/md→16 / lg→20(對齊 Field family,禁 14/18/24 自由挑)。`renderCellContent` 對 `React.isValidElement(content)` true / `isKnownCompound`(select/multiSelect/person/url)bypass TruncateCell;primitive 才走 truncate + hover tooltip。❌ consumer wrapper 加 `leading-none` / `h-full` / `align-middle` 治標 — 根因常在 TruncateCell 包覆。
179
+ Cell 已 `flex items-center`,consumer render 直接 inline-flex + gap-2。Icon size:sm/md→16 / lg→20(對齊 Field family,禁 14/18/24 自由挑)。`renderCellContent` 對 `React.isValidElement(content)` true / `isKnownCompound`(select/multiSelect/person/multiPerson/url/date/time)bypass TruncateCell;primitive 才走 truncate + hover tooltip。❌ consumer wrapper 加 `leading-none` / `h-full` / `align-middle` 治標 — 根因常在 TruncateCell 包覆。
237
180
 
238
181
  ### 十一、Cell 單行截斷原則
239
182
 
@@ -249,9 +192,7 @@ DataTable 只管「column + data」;Loading / Error / Disabled-整表由 consume
249
192
 
250
193
  ## 捲軸(pinned header / column + scroll canonical)
251
194
 
252
- 3-panel(left-pinned / center-scroll / right-pinned),center body 用 **native `overflow-x-auto`**(非 ScrollArea),header 透過 JS `onScroll` 同步 scrollLeft。對齊 Linear / Notion / Airbnb / Ant ProTable / TanStack 主流。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
253
-
254
- **不用 `<ScrollArea>` 的理由**:Radix viewport nested div 會 break scrollLeft 同步;pinned column 需「左右獨立 scroll + 中央共享 scroll state」,單一 viewport 不適配。
195
+ 3-panel(left-pinned / center-scroll / right-pinned),center body 用 **native `overflow-x-auto`**(非 ScrollArea),header 透過 JS `onScroll` 同步 scrollLeft。對齊 Linear / Notion / Airbnb / Ant ProTable / TanStack 主流。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL --> **不用 `<ScrollArea>` 的理由**:Radix viewport nested div 會 break scrollLeft 同步;pinned column 需「左右獨立 scroll + 中央共享 scroll state」,單一 viewport 不適配。
255
196
 
256
197
  **Tech debt**:macOS auto-hide vs Windows/Linux 常駐 scrollbar,cross-OS 視覺寬度差異 — consumer 可 override `::-webkit-scrollbar` 樣式;ScrollArea 重構列 post-v1。
257
198
 
@@ -259,9 +200,7 @@ DataTable 只管「column + data」;Loading / Error / Disabled-整表由 consume
259
200
 
260
201
  ## L2:選取(Selection)
261
202
 
262
- DataTable 的 row selection layer。提供 controlled/uncontrolled state + 視覺 + 鍵盤,搭配獨立 `BulkActionBar` primitive 完成批次 workflow。
263
-
264
- **世界級對照**:Material DataGrid `rowSelectionModel` / Polaris IndexTable `selectedResources` / Linear / Notion 全 controlled-first + uncontrolled fallback。AG Grid 的 imperative `gridRef.api` 不採(違背 React idiom + 既有 Field/Switch/Checkbox controllable 慣例)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
203
+ DataTable 的 row selection layer。提供 controlled/uncontrolled state + 視覺 + 鍵盤,搭配獨立 `BulkActionBar` primitive 完成批次 workflow。**世界級對照**:Material DataGrid `rowSelectionModel` / Polaris IndexTable `selectedResources` / Linear / Notion 全 controlled-first + uncontrolled fallbackAG Grid 的 imperative `gridRef.api` 不採(違背 React idiom + 既有 Field/Switch/Checkbox controllable 慣例)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
265
204
 
266
205
  ### 一、State 模式(discriminated union,2026-06-22 支援反向選取 inverted)
267
206
 
@@ -275,7 +214,7 @@ selection?: string[] | DataTableSelection // controlled;傳 string[] = i
275
214
  defaultSelection?: string[] | DataTableSelection // uncontrolled
276
215
  onSelectionChange?: (next: DataTableSelection) => void // 一律 emit union
277
216
  totalCount?: number // 全集筆數 M(server-side / filter 後);all 模式計數用
278
- selectable?: boolean | 'single' | 'multi' // default 'multi';single 永遠 include
217
+ selectable?: boolean | 'single' | 'multi' // default false(不啟用);true 等同 'multi';single 永遠 include
279
218
  isRowSelectable?: (row: TData) => boolean
280
219
  preserveSelectionOnFilter?: boolean // default false
281
220
  ```
@@ -285,7 +224,7 @@ preserveSelectionOnFilter?: boolean // default false
285
224
  ### 二、Checkbox column
286
225
 
287
226
  - **位置**:最左,自動 left-pin(不論 consumer pin 哪些 cols)
288
- - **寬度**:對稱 row-actions 區寬
227
+ - **寬度**:固定 40px(system col;不可 resize、不可隱藏)
289
228
  - **顯示時機**:**always visible**(對齊 Linear 2024 / Polaris / Material consensus,不允 hover-show) <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
290
229
  - **Header tri-state**:none / indeterminate / all,使用既有 Checkbox `indeterminate` prop
291
230
 
@@ -322,12 +261,12 @@ preserveSelectionOnFilter?: boolean // default false
322
261
 
323
262
  ### 七、BulkActionBar 整合(inline composition canonical)
324
263
 
325
- `BulkActionBar` 是獨立 primitive(`../BulkActionBar/`),不內建。Consumer flex-column 容器 inline composition,**toolbar 永遠保留**(filter / sort / search selection 期間仍可用,additive 派 — 對齊 Linear / Notion / Apple Mail / iOS Files)。Hint banner 用 `<Alert variant="info" placement="fixed">` + ReactNode title。4 layout use case 詳 `../BulkActionBar/bulk-action-bar.spec.md`。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
264
+ `BulkActionBar` 是獨立 primitive(`../BulkActionBar/`),不內建。Consumer flex-column 容器 inline composition,**toolbar 永遠保留**(filter / sort / search selection 期間仍可用,additive 派 — 對齊 Linear / Notion / Apple Mail / iOS Files)。Hint banner 用 `<Alert variant="neutral" placement="fixed">` + ReactNode title(資訊性 hint 非 info hue,canonical 見 `data-table.stories.tsx` WithBulkActions)。4 layout use case 詳 `../BulkActionBar/bulk-action-bar.spec.md`。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
326
265
 
327
266
  ### 八、a11y 預設
328
267
 
329
268
  - 每個 row checkbox 必有 `aria-label`:consumer 提供 `getRowAriaLabel?: (row) => string`,fallback `"Select row"`
330
- - header checkbox `aria-label="Select all visible rows"`
269
+ - header checkbox `aria-label="全選可見列"`
331
270
  - 鍵盤:`Space` toggle / `Shift+Space` 擴 range / `Cmd/Ctrl+A` 選全可見 / `Esc` clear
332
271
  - Selection 變更可選 `aria-live="polite"` 通知(consumer-implemented)
333
272
  - **Multi mode 用 Checkbox / Single mode 用 Radio**(對齊 Material DataGrid / Polaris IndexTable 共識,same-row consistency 全 sm)。Single mode 內部 wrap `RadioGroupPrimitive.Root` 提供 context,header checkbox 抑制(single 無「全選」概念)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
@@ -344,9 +283,7 @@ preserveSelectionOnFilter?: boolean // default false
344
283
 
345
284
  ## L4:Advanced Filter(進階篩選 panel)
346
285
 
347
- DataTable toolbar 的「篩選」按鈕展開 `<DataTableFilterPanel>` — flat 或 1-level nested boolean expression builder。實作 sub-file `data-table-filter-panel.tsx`(同 SortManager 對齊 sub-file pattern,**不另開 5-file**:spec / stories 都消費本檔)。
348
-
349
- **M8 對標**:ClickUp 為主 + Notion / Airtable / Coda / Linear。Image ref:user 提供 ClickUp-style 截圖(2026-05-02)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
286
+ DataTable toolbar 的「篩選」按鈕展開 `<DataTableFilterPanel>` — flat 或 1-level nested boolean expression builder。實作 sub-file `data-table-filter-panel.tsx`(同 SortManager 對齊 sub-file pattern,**不另開 5-file**:spec / stories 都消費本檔)。**M8 對標**:ClickUp 為主 + Notion / Airtable / Coda / LinearImage ref:user 提供 ClickUp-style 截圖(2026-05-02)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
350
287
 
351
288
  ### 一、Mode
352
289
 
@@ -364,7 +301,7 @@ type FilterTreeNested = { mode: 'nested'; conjunction: 'and'|'or'; children: Fil
364
301
 
365
302
  ### 二、求值策略
366
303
 
367
- 採 TanStack `globalFilter` + 自訂 `globalFilterFn(row, _, tree) => evaluateTree(tree, row.original)`,**棄 `columnFilters`**(N 條同 column 不能 OR)。`evaluateTree` SSOT 在 `filter-tree.ts`。**ms-precision when `meta.includeTime=true`**:datetime 比對檢查 column meta,`true` ms 比對(避開 Airtable day-precision 漏邊界地雷)。
304
+ 採 TanStack `globalFilter` + 自訂 `globalFilterFn(row, _, tree) => evaluateTree(tree, row.original)`,**棄 `columnFilters`**(N 條同 column 不能 OR)。`evaluateTree` SSOT 在 `filter-tree.ts`。**比對精度(2026-07-04 Q6 實作)**:panel 建 condition 時把 `meta.includeTime` 固化為 `condition.datePrecision`('ms' / 'day';`evaluateTree` 簽名不變)— date ops 預設 day 級(本地 `startOfDay` 截斷,AG Grid / MUI X 慣例;`is` 同步走日期比對非字串),`includeTime=true` ms 全精度(避開 Airtable day-precision 漏邊界地雷)。
368
305
 
369
306
  ### 三、Operator × ValueShape SSOT
370
307
 
@@ -382,14 +319,14 @@ ValueShape ↔ DS picker 對照(canonical 2026-05-02):
382
319
  | `datetime_single` | `<DatePicker showTime>` | `meta.includeTime=true` 時 promote |
383
320
  | `datetime_range` | `<DatePickerRange showTime>` | 同上 |
384
321
  | `select_multi` | `<Combobox>` | |
385
- | `person_multi` | `<PeoplePicker multi>` 預留 | v1 fallback Input |
322
+ | `person_multi` | `<PeoplePicker>` 多選 | 2026-05-07 升級為真 picker,吃 `column.meta.people` |
386
323
 
387
324
  ### 四、UI canonical
388
325
 
389
326
  - 第 1 row conjunction 是靜態 `Where` label(`px-[var(--field-px)]` 對齊下方 Field value 起點 = 12px)
390
- - field 未選 → operator + value picker disabled;同 group 共用 conjunction(toggle 任一 flip group)
391
- - **空狀態**:無 condition → 只顯 inline `+ 加篩選` CTA(對齊 Notion / Airtable / Linear,**禁止** auto-create 空 row) <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
392
- - **CTA 位置**:緊貼最後一條 row(text variant 輕量,**廢 SurfaceFooter**),條件與「加入」屬同一語境
327
+ - field 未選 → operator + value picker disabled;同 group 共用 conjunction( 2 row 是唯一可改的 And/Or Select,改動連動整 group;第 3 條起唯讀顯示當前 conjunction — A6 canonical)
328
+ - **空狀態(兩態,G fix 2026-05-04 v2)**:initial mount 且 value 空 → auto-add 1 條空 condition row(field 未選 operator / value 自動 disabled;讓 user 直接看到 row shape,不必先點 CTA;useRef gate 只 mount 一次);user 手動刪光 → 只顯 inline `+ 加篩選` CTA、不 re-add(尊重清空意圖,對齊 Notion / Airtable / Linear) <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
329
+ - **CTA 位置**:緊貼最後一條 row(**廢 SurfaceFooter**),條件與「加入」屬同一語境;root-level「加篩選 / 加入篩選器」用 `tertiary`(輕量但有邊界,符合 root-CTA 重量),group 內「加入巢狀篩選」才用 `text`(更輕,inline group )
393
330
  - **Trash / 刪除**:row 是 form-control row → text Button(non Inline Action,違 item-anatomy canonical)
394
331
  - **And/Or Select** `minRows={2}`(2 選項顯式縮 menu 高度);**Where padding** `px-[var(--field-px)]` align Field
395
332
  - Header refresh icon:`value !== defaultValue` 顯;ButtonDivider 串接 close X(對齊欄位顯示 chrome canonical)
@@ -400,7 +337,7 @@ ValueShape ↔ DS picker 對照(canonical 2026-05-02):
400
337
 
401
338
  | 條件 | 是否出現 |
402
339
  |------|---------|
403
- | Display column(無 `accessorKey`)| ❌ TanStack 內建限制 |
340
+ | Display column(無 `accessorKey`)| ❌ 慣例排除 — 判定實際只看 `meta.type`(display column 不設 type 即不出現;非 TanStack 機械限制,設了 type 仍會列入)|
404
341
  | Accessor + 有 `meta.type` | ✅ 預設 |
405
342
  | Accessor + `meta.filterable: false` | ❌ opt-out |
406
343
  | Accessor + 無 `meta.type` | ❌ 無 type 無法決定 op set |
@@ -418,13 +355,11 @@ ValueShape ↔ DS picker 對照(canonical 2026-05-02):
418
355
 
419
356
  ---
420
357
 
421
- ## L4 Inline Edit / Nested rows / Row drag(2026-05-04)
422
-
423
- 對齊 Notion / Airtable type-aware editor + Jira drag idiom @benchmark-unverified。
358
+ ## L4 Inline Edit / Nested rows / Row drag(2026-05-04;對齊 Notion / Airtable type-aware editor + Jira drag idiom @benchmark-unverified)
424
359
 
425
360
  ### Inline Edit — per-column opt-in
426
361
 
427
- `columnDef.meta.editable: boolean | (row) => boolean`(`true` / fn-true 才開)。Commit:blur or Enter → `onCellCommit(rowId, colId, value)`;Cancel:Esc。
362
+ `columnDef.meta.editable: boolean | (row) => boolean`(`true` / fn-true 才開)。Commit:blur or Enter → `onCellCommit(rowId, colId, value)`;Cancel:Esc。例外:autoRowHeight string cell 的 edit 是 `Textarea` — Enter 換行、`Cmd/Ctrl+Enter` commit(blur 同 commit)
428
363
 
429
364
  | ColumnType | Trigger | Edit mode |
430
365
  |--|--|--|
@@ -458,13 +393,36 @@ Row drag + column reorder + TreeView 共用 `lib/drag-visual.ts`:source `opacity
458
393
 
459
394
  - **Handle**:Button tertiary iconOnly xs(GripVertical)24px chip,所有 state(idle / hover / aria-disabled)統一 `bg-surface-raised`(border / shadow 已 retire,2026-05-12 per user「我有叫你加 elevation 嗎」),fixed-position 浮層貼 row 左緣、不佔 column 空間(位置 JS 計算,實作見 `data-table.tsx`);**hover-reveal** 由 JS 控 visibility / opacity(row 或 handle hover 顯示;drag 中 source 強制顯示、其他列隱藏)。對齊 Jira backlog(@benchmark-unverified,M22)。Tertiary chip 非 ItemInlineAction 因透明背景撞 table border。
460
395
  - **Sort × Drag 互斥**:sort.length>0 → handle disabled+Tooltip。**Top-level only**(`row.depth>0` 不顯 handle)。**Position**:active vs over 視覺位置 → `'after'`/`'before'` 對齊 `arrayMove`。**Consumer-managed mutation**:`onRowReorder(sourceId, targetId, position)`,DS 不持 row order(Notion/Airtable/Linear)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
461
- - **Virtualization 整合**(v3 2026-05-05):enableRowDrag 自動把 overscan 拉到 `Math.max(overscan, 5)` + drag 期 freeze `measureElement` + `modifiers={[snapToCursorModifier]}`(ghost top-left 對齊 cursor,不鎖軸)。**3-panel mirror sync**:各 region 對同 row id 各呼叫 `useDraggable` + `useDroppable`,mirror 自然取得相同 transform;handle 只 render primary region(left 優先 → center)避雙觸發。**Cross-parent drop 禁止**(已知 limit):nested 只同 top-level 重排,collisionDetection 過濾,顯 invalid signal。
396
+ - **Virtualization 整合**(v3 2026-05-05):enableRowDrag 自動把 overscan 拉到 `Math.max(overscan, 5)` + drag 期 freeze `measureElement` + `modifiers={[snapToCursorModifier]}`(ghost top-left 對齊 cursor,不鎖軸)。**3-panel mirror sync**:primary 永遠 = center region(v15.4 撤銷「left 優先」— multi-instance same-id dnd-kit anti-pattern,且 pinned column 是「鎖定欄」語意非 drag 起點),只有 center 掛 `useDraggable`;mirror region(left / right pinned)只掛 `useDroppable`,drag 期以 `useDndContext` 同步 source 半透視覺(Path B source 留原位,無 row transform);handle 只 render primary(center)避雙觸發。**Cross-parent drop 禁止**(已知 limit):nested 只同 top-level 重排,collisionDetection 過濾,顯 invalid signal。
397
+
398
+ ---
399
+
400
+ ## L5:分頁(Pagination,2026-07-06)
401
+
402
+ **共用模式(user 拍板)**:`Pagination` 獨立公開元件是頁碼視覺 SSOT(`../Pagination/pagination.spec.md`,細節不在此重述);DataTable 加 `pagination` prop 內建消費它——內部接 TanStack `getPaginationRowModel()`,表格下方 render 分頁列。對齊 Ant Table(消費 Pagination)/ Atlassian DynamicTable(`rowsPerPage`)/ MUI DataGrid 內建派;shadcn data-table 教學同款「分頁 render 在 DataTable 元件 JSX 內」。
403
+
404
+ **API**:`pagination?: boolean | DataTablePaginationOptions`——`{ pageSize?(**uncontrolled 初始值**,預設 20;之後變更由使用者操作選單驅動、經 onPageSizeChange 回報,動態改此欄位不生效——controlled 需求列 v2。注意與 Pagination 元件的 pageSize prop 語意不同:那是 controlled)、pageSizeOptions?(傳了才渲染「每頁 N 筆」Select sm)、showTotal?(傳 true 才渲染 range 資訊,= Ant showTotal opt-in 邏輯)、page? / defaultPage? / onPageChange?(1-based dual-mode)、onPageSizeChange? }`。v1 = **client-side only**(全量 data 進來由 TanStack 切頁);server-side(`manualPagination` + 外部 total)列 v2。
405
+
406
+ **分頁列(bar)規則**:
407
+ - **bar 本體 SSOT 在 `<Pagination>` 完整形態**(2026-07-06 user 拍板「Pagination 元件提供完整功能、Table 按一致定義套用」):showTotal range 資訊、「N 筆/頁」選單、「資訊左、操作右」layout 全在 `../Pagination/pagination.spec.md`「完整形態」段——DataTable **只轉發 config + own TanStack state**(controlled 消費),不自拼分頁列
408
+ - **間距**:表格 → 分頁列 = `--layout-space-tight`(layoutSpace spec 規則 3「跨範疇 functional 交互」;Ant Table margin 16px / shadcn py-4 同量級)
409
+ - **對齊**:純頁碼形態**靠右**(shadcn `justify-end` / Ant Table 預設 `bottomEnd` / MUI TablePagination / Carbon 控制群 4 家實證);完整形態由 Pagination 自帶 w-full justify-between
410
+ - **`total` 數源** = `getPrePaginationRowModel().rows.length`(filter 後全集)——**不是** selection all-mode 的 `totalCount` prop(那是 server-side 全集數,語意不同)
411
+ - filter / data 縮小時自動 clamp 當前頁(對齊 MUI X);`isEmpty` 時分頁列整條不渲染(Empty 已渲染)
412
+
413
+ **與虛擬滾動互斥**:啟用分頁 → `useVirtual` 強制關閉——TanStack 官方定位兩者為互斥替代策略(分頁 = 每頁固定筆數主動換頁;虛擬滾動 = 連續滾動只渲可視列)。分頁時無虛擬化保護,建議 `pageSize ≤ 100`。
414
+
415
+ **height 預設連動**:未顯式傳 `height` 時,啟用分頁預設 **`'auto'` 自然高度**(hug 當前頁)——頁碼是分頁的唯一導覽通道,再疊 body 內捲動 = 雙重導覽(一頁 20 筆只露 10 筆);對齊 Ant Table 無預設高度罩、`scroll.y` 顯式選配慣例。未分頁維持既有 `'400px'` 預設;**顯式傳 height 一律尊重**(分頁 + 頁內捲動可並用)。
416
+
417
+ **Selection 交互**:header checkbox「全選」= 當頁可見列(page-scoped,rows 即當頁);跨頁已選 ids 翻頁不清除;BulkActionBar 2-step「已選本頁 N → 選全部 M」語意與分頁天然契合(見 L2 段)。`aria-rowcount` = 全集筆數 + 1(非當頁,ARIA 規範)。
418
+
419
+ **禁止**:❌ 分頁 + 期待虛擬滾動同時生效;❌ 繞過 prop 在表格下方自行手排 standalone `<Pagination>`(間距/對齊/互斥全部走本段 canonical)。
462
420
 
463
421
  ---
464
422
 
465
423
  ## Overlay + cell error SSOT(Phase 9)
466
424
 
467
- **Overlay**:viewport `position:fixed inset:0` layer。`getCellRect()` 從 `getBoundingClientRect()` 取 float coords no rounding。Paint:hover/selected/range outer ring `outline outline-offset:-1px` in-place;active editor host portal opaque `<div>` z 3(cell 保持 display)。**Viewport clip**(Issue 6):body panel 加 `data-datatable-panel="left|center|right"`;`getCellGeometry()` return cell+panel rect;`<ClipMask>` panel rect `overflow:hidden`,內部 `toRelRect()` 轉 mask-relative。Range outer ring 按 panel 分組(避 bbox 跨 pin boundary)。Active editor host **不 clip**。對齊 AG Grid `cellsForRangeSet` / Glide / Notion sticky-cell mask。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
425
+ **Overlay**:viewport `position:fixed inset:0` layer。`getCellRect()` 從 `getBoundingClientRect()` 取 float coords no rounding。Paint:hover/selected ring `outline outline-offset:-1px` in-place(range outer ring 已 2026-05-10 retire — range 視覺只剩 cell-bg `--primary-subtle` `[data-range-cell]`,bg 已足以標示範圍、外框冗餘);active editor host portal opaque `<div>` z 3(cell 保持 display)。**Viewport clip**(Issue 6):body panel 加 `data-datatable-panel="left|center|right"`;`getCellGeometry()` return cell+panel rect;`<ClipMask>` panel rect `overflow:hidden`,內部 `toRelRect()` 轉 mask-relative(hover/selected ring 按 panel clip,不畫出 pin boundary)。Active editor host **不 clip**。對齊 AG Grid `cellsForRangeSet` / Glide / Notion sticky-cell mask。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
468
426
 
469
427
  **Cell errors**(Issue 9):`cellErrors?: Record<string, string|string[]>` prop key `${rowId}:${colId}`。Cell display 渲 error 14px `text-error` 下方 gap-1;array→`<ul><li>`;single→`<span>`。`aria-describedby` + `aria-invalid` + `<span role="alert">`。`overflow:visible` 當有 error(搭 `autoRowHeight`)。**Per-row state SSOT** cell-render wrapper(`items-X` 等)必 consume `effectiveAutoRowForCell`,禁 global `autoRowHeight`(audit `audit-data-table-row-mode-ssot.mjs` 強制)。**Edit-clears-own-cell** 自動清視覺,consumer onCellCommit validate 後回填。**a11y caveat**:≥ 5 同時 `role="alert"` 第一次 paint AT 噪音 → consumer 可考 `role="status"` fallback。對齊 AG Grid `cellClassRules='ag-cell-error'` + Material X errorMessage + Airtable validation。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
470
428
 
@@ -486,7 +444,7 @@ Row drag + column reorder + TreeView 共用 `lib/drag-visual.ts`:source `opacity
486
444
 
487
445
  ## Anatomy 結構例外
488
446
 
489
- DataTable 是 composite multi-section 元件,**不套 canonical 5**(Inspector / SizeMatrix / StateBehavior)而採「按區塊 + 按 feature」拆:`Overview` / `ColumnTypes` / `RowHeightMatrix`(對應 row-height tier 而非 component size)/ `AlignmentRule` / `Features` / `ColorMatrix`(row 多 state 集中)/ `EmptyState`(消費 Empty primitive)。理由:單一 Inspector 無法呈現「資料 schema → column type 對應」這類跨 prop 決策。
447
+ DataTable 是 composite multi-section 元件,**不套 SizeMatrix / StateBehavior**( `RowHeightMatrix` / `ColorMatrix` 對應)而採「按區塊 + 按 feature」拆:`Overview` / `Inspector`(props 即時檢閱)/ `ColumnTypes` / `RowHeightMatrix`(對應 row-height tier 而非 component size)/ `AlignmentRule` / `Features` / `ColorMatrix`(row 多 state 集中)/ `EmptyState`(消費 Empty primitive)/ `BorderedProp` / `Accessibility`。理由:單一 Inspector 無法呈現「資料 schema → column type 對應」這類跨 prop 決策,Inspector 之外仍按 feature 拆。
490
448
 
491
449
  ---
492
450
 
@@ -498,12 +456,12 @@ DataTable 是 composite multi-section 元件,**不套 canonical 5**(Inspector /
498
456
  - `../TreeView/tree-view.spec.md` — 階層結構的對應元件
499
457
  - `../../tokens/uiSize/uiSize.spec.md` — `--table-row-*` / `--field-height-*` token
500
458
  - `../../tokens/color/color.spec.md` — 語義色彩
501
- - `../../tokens/elevation/elevation.spec.md` — 固定欄陰影
459
+ - `../../tokens/elevation/elevation.spec.md` — drag ghost 陰影(`--elevation-200`;固定欄分界為 1px divider,無陰影)
502
460
  - `../Field/field-controls.spec.md` — cell editable 時的 Field Control 共用規則
503
461
 
504
462
  ## A11y 預設
505
463
 
506
- **ARIA / Pattern**:DataTable 是 composite tabular widget,**對齊 W3C ARIA Authoring Practices Guide `grid` pattern**(非 `radio-group`):
464
+ **ARIA / Pattern**:DataTable 是 composite tabular widget,**對齊 W3C ARIA Authoring Practices Guide `grid` pattern**(非 `radio-group`——之前 boilerplate 從 RadioGroup spec 誤抄;DataTable 是 multi-row / multi-column composite,not single-choice selection group,a11y semantics 完全不同(grid vs radio-group)<!-- @benchmark-verified: 2026-05-18 D1 rewrite -->):
507
465
 
508
466
  - Root 套 `role="table"`(currently)或 `role="grid"`(future tier,when cell editing 普及)— 詳 [WAI-ARIA APG: grid](https://www.w3.org/WAI/ARIA/apg/patterns/grid/) + [MDN grid role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/grid_role)
509
467
  - Column headers:以 `<div role="columnheader">` 顯式標記(本元件用 div + ARIA role,非語義 `<table>` — 見定位段;不渲染 `<th>` / `scope`)
@@ -513,11 +471,11 @@ DataTable 是 composite multi-section 元件,**不套 canonical 5**(Inspector /
513
471
  - 字 cell hover overlay action:overlay 為 absolute/fixed paint layer(`DataTableInteractionLayer`),trigger 目前**未**套 `aria-haspopup` / `aria-controls`(留待 future tier)
514
472
 
515
473
  **Keyboard 行為**(目前實作 — `tableKeyboardHandler`):
516
- - ↑↓←→:cell-to-cell navigation **僅 `spreadsheetMode` opt-in 時生效**(`spreadsheetMode && selectedCellId != null && editingCellId == null`);預設模式方向鍵無作用
517
- - Enter / F2:spreadsheet 模式下進 cell editing(cell 可編輯 + 非 boolean/url + 非 disabled 時)
474
+ - ↑↓←→:cell-to-cell navigation **僅 `spreadsheetMode` opt-in 時生效**;selection 尚未建立時按方向鍵自動選取第一個 visible cell(鍵盤可直接進入 spreadsheet 導覽,無需滑鼠 click 對齊 Excel / Google Sheets / AG Grid「focus grid → first cell active」,2026-07-05 D4 補);預設模式方向鍵無作用
475
+ - Enter / F2:spreadsheet 模式下進 cell editing(cell 可編輯 + 非 boolean/url + 非 disabled 時);**Enter 確認後維持原格不下移**(2026-07-05 user 拍板;10 家實查:Excel 系 7 家下移、AG Grid 預設維持原格 — 採 AG Grid 派,數據 → `.claude/logs/deep-audit-2026-07-03/enter-commit-navigation-benchmark.json`;未來連續輸入需求可重議 opt-in);**edit 退出(commit / Esc)後 selection 還原至該 cell、焦點還給 table root**(editor unmount 後焦點掉到 body 才收回,不搶 user 點擊的新焦點 — 對齊 spreadsheet RFC Contract 11 + Excel / AG Grid,2026-07-05 D4 補)
518
476
  - Cmd/Ctrl+A:`mode="multi"` selection 時選全可見列(扣 disabled)
519
- - Esc:取消 editing(spreadsheet)/ 清 selection(selection mode)
520
- - Tab:進入表格後操作排序與勾選
477
+ - Esc:取消 editing(spreadsheet)/ 清 selection(selection mode);**IME 組字中的 Enter / Esc 不觸發 commit / cancel**(cell editor 帶 `isComposing` guard,2026-07-05 D4 補 — 中文選字 Enter 不誤提交半截組字)
478
+ - Tab:進入表格後操作排序與勾選;portal edit(`experimentalActiveEditorController`)中 Tab / Shift+Tab = commit 當前 draft + 移至下一個 editable cell 進 edit(2026-07-05 D4 補 commit — 原本換格丟 draft)
521
479
 
522
480
  > APG grid full keyboard model(Home/End、Ctrl+Home/End、PageUp/PageDown、roving cell action)為 `role="grid"` future tier 目標,**尚未實作**。
523
481
 
@@ -525,8 +483,6 @@ DataTable 是 composite multi-section 元件,**不套 canonical 5**(Inspector /
525
483
 
526
484
  **驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
527
485
 
528
- **Why not radio-group**:之前 boilerplate 從 RadioGroup spec 誤抄。DataTable 是 multi-row / multi-column composite,not single-choice selection group;a11y semantics 完全不同(grid vs radio-group)。 <!-- @benchmark-verified: 2026-05-18 D1 rewrite -->
529
-
530
486
  ## 被引用(auto-maintained,Dim 3 reciprocal audit)
531
487
 
532
488
  > 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
@@ -536,5 +492,6 @@ DataTable 是 composite multi-section 元件,**不套 canonical 5**(Inspector /
536
492
  - `circular-progress.spec.md`
537
493
  - `description-list.spec.md`
538
494
  - `opacity.spec.md`
495
+ - `pagination.spec.md`
539
496
  - `scroll-area.spec.md`
540
497
  - `tree-view.spec.md`
@@ -603,10 +603,11 @@ export const InlineEdit: Story = {
603
603
  * 驗證點:
604
604
  * - hover editable cell → 1px overlay 邊框 var(--border-hover)
605
605
  * - sku(readonly)/ inStock(boolean)/ url(openAction)→ 不出現 hover overlay(Contract 15)
606
- * - click 1 → cell selected(2px primary border outline)
606
+ * - click 1 → cell selected(1px `--primary` border outline,`CELL_RING_STYLES.selected`)
607
607
  * - click 2 / Enter / F2 → enter edit(portal Field, cell mode 不變)
608
- * - Shift+click → range(focus = 2px primary outline + 內 cells `--primary-subtle` bg)
609
- * - cell border-box 對齊(0.5px sub-pixel snap;dtCellGrid 4-edge inset divider 共軌)
608
+ * - Shift+click → range(focus = 1px primary outline + 內 cells `--primary-subtle` bg)
609
+ * - cell border-box 對齊(float coords 直接 pass-through round / 不 snap,overlay outline
610
+ * -1px 剛好壓 cell 邊線;dtCellGrid right-edge inset divider + row border-b 共軌)
610
611
  * - Issue 6 viewport clip:H scroll cell out → overlay 被 panel ClipMask 裁切不溢出
611
612
  */
612
613
  export const InlineEditWithSpreadsheetOverlay: Story = {
@@ -1939,3 +1940,50 @@ export const RoadmapPerfBudget: Story = {
1939
1940
  )
1940
1941
  },
1941
1942
  }
1943
+
1944
+ // ── L5 分頁(2026-07-06)─────────────────────────────────────────────────────
1945
+
1946
+ type OrderRow = { id: string; orderNo: string; customer: string; amount: number; placedAt: string }
1947
+
1948
+ // 128 筆訂單(電商後台真實規模;deterministic 生成,無 Math.random)
1949
+ const ORDER_ROWS: OrderRow[] = Array.from({ length: 128 }, (_, i) => ({
1950
+ id: `order-${i + 1}`,
1951
+ orderNo: `SO-2026-${String(1001 + i)}`,
1952
+ customer: ['Acme Corp', 'Globex', 'Initech', 'Umbrella', 'Stark Industries', 'Wayne Enterprises'][i % 6],
1953
+ amount: 1200 + (i * 137) % 8800,
1954
+ placedAt: `2026-0${(i % 6) + 1}-${String((i % 27) + 1).padStart(2, '0')}`,
1955
+ }))
1956
+
1957
+ const orderColumns: ColumnDef<OrderRow>[] = [
1958
+ { accessorKey: 'orderNo', header: '訂單編號' },
1959
+ { accessorKey: 'customer', header: '客戶' },
1960
+ { accessorKey: 'amount', header: '金額', cell: ({ getValue }) => `$${(getValue() as number).toLocaleString()}` },
1961
+ { accessorKey: 'placedAt', header: '成立日期' },
1962
+ ]
1963
+
1964
+ export const WithPagination: Story = {
1965
+ name: '分頁',
1966
+ render: () => (
1967
+ <div className="mx-[var(--layout-space-loose)] my-[var(--layout-space-loose)] flex flex-col gap-8">
1968
+ <div>
1969
+ <p className="text-caption text-fg-muted mb-2">
1970
+ 預設形態:`pagination` 傳 true — 128 筆 / 每頁 20 = 7 頁,分頁列間距 tight、頁碼靠右
1971
+ (spec「L5:分頁」canonical);與虛擬滾動互斥(分頁時 useVirtual 關閉)。
1972
+ </p>
1973
+ <DataTable columns={orderColumns} data={ORDER_ROWS} pagination getRowId={(row) => row.id} />
1974
+ </div>
1975
+ <div>
1976
+ <p className="text-caption text-fg-muted mb-2">
1977
+ 完整形態:「共 N 筆」opt-in(showTotal)+ 每頁筆數 Select(pageSizeOptions)——
1978
+ 左資訊右頁碼 justify-between;換每頁筆數自動回第 1 頁;filter 縮小自動 clamp 當前頁。
1979
+ </p>
1980
+ <DataTable
1981
+ columns={orderColumns}
1982
+ data={ORDER_ROWS}
1983
+ pagination={{ pageSize: 20, pageSizeOptions: [10, 20, 50], showTotal: true }}
1984
+ getRowId={(row) => row.id}
1985
+ />
1986
+ </div>
1987
+ </div>
1988
+ ),
1989
+ }