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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (612) hide show
  1. package/CLAUDE.md +8 -8
  2. package/README.md +3 -2
  3. package/dist/components/Alert/alert.js.map +1 -1
  4. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  5. package/dist/components/AppShell/app-shell.d.ts +1 -1
  6. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  7. package/dist/components/AppShell/app-shell.js +22 -2
  8. package/dist/components/AppShell/app-shell.js.map +1 -1
  9. package/dist/components/Avatar/avatar.d.ts.map +1 -1
  10. package/dist/components/Avatar/avatar.js +4 -4
  11. package/dist/components/Avatar/avatar.js.map +1 -1
  12. package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
  13. package/dist/components/Breadcrumb/breadcrumb.js +1 -1
  14. package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
  15. package/dist/components/Button/button.d.ts +2 -2
  16. package/dist/components/Button/button.d.ts.map +1 -1
  17. package/dist/components/Button/button.js +12 -3
  18. package/dist/components/Button/button.js.map +1 -1
  19. package/dist/components/Calendar/calendar.d.ts +13 -5
  20. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  21. package/dist/components/Calendar/calendar.js +7 -1
  22. package/dist/components/Calendar/calendar.js.map +1 -1
  23. package/dist/components/Carousel/carousel.d.ts.map +1 -1
  24. package/dist/components/Carousel/carousel.js +8 -3
  25. package/dist/components/Carousel/carousel.js.map +1 -1
  26. package/dist/components/Chart/chart.d.ts.map +1 -1
  27. package/dist/components/Chart/chart.js +1 -0
  28. package/dist/components/Chart/chart.js.map +1 -1
  29. package/dist/components/Checkbox/checkbox.d.ts +1 -1
  30. package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
  31. package/dist/components/Checkbox/checkbox.js +8 -4
  32. package/dist/components/Checkbox/checkbox.js.map +1 -1
  33. package/dist/components/Chip/chip.d.ts +1 -1
  34. package/dist/components/Chip/chip.d.ts.map +1 -1
  35. package/dist/components/Chip/chip.js.map +1 -1
  36. package/dist/components/Coachmark/coachmark.d.ts +10 -3
  37. package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
  38. package/dist/components/Coachmark/coachmark.js.map +1 -1
  39. package/dist/components/Combobox/combobox.d.ts +5 -1
  40. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  41. package/dist/components/Combobox/combobox.js +30 -13
  42. package/dist/components/Combobox/combobox.js.map +1 -1
  43. package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
  44. package/dist/components/DataTable/cell-registry.js +53 -62
  45. package/dist/components/DataTable/cell-registry.js.map +1 -1
  46. package/dist/components/DataTable/column-types.d.ts +10 -4
  47. package/dist/components/DataTable/column-types.d.ts.map +1 -1
  48. package/dist/components/DataTable/column-types.js.map +1 -1
  49. package/dist/components/DataTable/data-table-column-visibility-panel.d.ts +1 -1
  50. package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
  51. package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
  52. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  53. package/dist/components/DataTable/data-table-filter-panel.js +607 -0
  54. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
  55. package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
  56. package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
  57. package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
  58. package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
  59. package/dist/components/DataTable/data-table-sort-manager.js +140 -0
  60. package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
  61. package/dist/components/DataTable/data-table.d.ts +5 -6
  62. package/dist/components/DataTable/data-table.d.ts.map +1 -1
  63. package/dist/components/DataTable/data-table.js +36 -15
  64. package/dist/components/DataTable/data-table.js.map +1 -1
  65. package/dist/components/DataTable/filter-operators.js +140 -0
  66. package/dist/components/DataTable/filter-operators.js.map +1 -0
  67. package/dist/components/DataTable/filter-tree.js +312 -0
  68. package/dist/components/DataTable/filter-tree.js.map +1 -0
  69. package/dist/components/DataTable/index.d.ts +3 -0
  70. package/dist/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/components/DataTable/index.js +12 -1
  72. package/dist/components/DataTable/index.js.map +1 -1
  73. package/dist/components/DataTable/lib/column-meta.js +25 -0
  74. package/dist/components/DataTable/lib/column-meta.js.map +1 -0
  75. package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
  76. package/dist/components/DateGrid/date-grid.js +3 -1
  77. package/dist/components/DateGrid/date-grid.js.map +1 -1
  78. package/dist/components/DatePicker/date-picker.d.ts +7 -1
  79. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  80. package/dist/components/DatePicker/date-picker.js +105 -74
  81. package/dist/components/DatePicker/date-picker.js.map +1 -1
  82. package/dist/components/Dialog/dialog.d.ts +20 -3
  83. package/dist/components/Dialog/dialog.d.ts.map +1 -1
  84. package/dist/components/Dialog/dialog.js +38 -20
  85. package/dist/components/Dialog/dialog.js.map +1 -1
  86. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  87. package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
  88. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  89. package/dist/components/Field/field-context.d.ts +45 -5
  90. package/dist/components/Field/field-context.d.ts.map +1 -1
  91. package/dist/components/Field/field-context.js +19 -0
  92. package/dist/components/Field/field-context.js.map +1 -1
  93. package/dist/components/Field/field-edit-keys.d.ts +21 -0
  94. package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
  95. package/dist/components/Field/field-edit-keys.js +19 -0
  96. package/dist/components/Field/field-edit-keys.js.map +1 -0
  97. package/dist/components/Field/field-types.d.ts +20 -1
  98. package/dist/components/Field/field-types.d.ts.map +1 -1
  99. package/dist/components/Field/field-types.js.map +1 -1
  100. package/dist/components/Field/field-wrapper.d.ts +3 -2
  101. package/dist/components/Field/field-wrapper.d.ts.map +1 -1
  102. package/dist/components/Field/field-wrapper.js +38 -39
  103. package/dist/components/Field/field-wrapper.js.map +1 -1
  104. package/dist/components/Field/field.d.ts +2 -1
  105. package/dist/components/Field/field.d.ts.map +1 -1
  106. package/dist/components/Field/field.js.map +1 -1
  107. package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
  108. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  109. package/dist/components/FileItem/file-item.d.ts.map +1 -1
  110. package/dist/components/FileItem/file-item.js +1 -1
  111. package/dist/components/FileItem/file-item.js.map +1 -1
  112. package/dist/components/FileUpload/file-upload.d.ts +1 -1
  113. package/dist/components/FileUpload/file-upload.js.map +1 -1
  114. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  115. package/dist/components/FileViewer/file-viewer.js +7 -2
  116. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  117. package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
  118. package/dist/components/FileViewer/image-renderer.js +22 -1
  119. package/dist/components/FileViewer/image-renderer.js.map +1 -1
  120. package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
  121. package/dist/components/HoverCard/hover-card.js +5 -3
  122. package/dist/components/HoverCard/hover-card.js.map +1 -1
  123. package/dist/components/InlineEdit/index.d.ts +2 -0
  124. package/dist/components/InlineEdit/index.d.ts.map +1 -0
  125. package/dist/components/InlineEdit/index.js +5 -0
  126. package/dist/components/InlineEdit/index.js.map +1 -0
  127. package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
  128. package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
  129. package/dist/components/InlineEdit/inline-edit.js +164 -0
  130. package/dist/components/InlineEdit/inline-edit.js.map +1 -0
  131. package/dist/components/Input/input.d.ts +6 -11
  132. package/dist/components/Input/input.d.ts.map +1 -1
  133. package/dist/components/Input/input.js +43 -13
  134. package/dist/components/Input/input.js.map +1 -1
  135. package/dist/components/LinkInput/link-input.d.ts +2 -2
  136. package/dist/components/LinkInput/link-input.d.ts.map +1 -1
  137. package/dist/components/LinkInput/link-input.js +6 -5
  138. package/dist/components/LinkInput/link-input.js.map +1 -1
  139. package/dist/components/Menu/menu-item.d.ts +14 -2
  140. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  141. package/dist/components/Menu/menu-item.js +25 -3
  142. package/dist/components/Menu/menu-item.js.map +1 -1
  143. package/dist/components/NumberInput/number-input.d.ts +3 -3
  144. package/dist/components/NumberInput/number-input.d.ts.map +1 -1
  145. package/dist/components/NumberInput/number-input.js +6 -5
  146. package/dist/components/NumberInput/number-input.js.map +1 -1
  147. package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
  148. package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
  149. package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
  150. package/dist/components/Pagination/pagination.js +1 -1
  151. package/dist/components/Pagination/pagination.js.map +1 -1
  152. package/dist/components/PeoplePicker/index.js +7 -1
  153. package/dist/components/PeoplePicker/index.js.map +1 -1
  154. package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
  155. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  156. package/dist/components/PeoplePicker/people-picker.js +23 -10
  157. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  158. package/dist/components/PeoplePicker/person-display.d.ts +6 -3
  159. package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
  160. package/dist/components/PeoplePicker/person-display.js +19 -14
  161. package/dist/components/PeoplePicker/person-display.js.map +1 -1
  162. package/dist/components/Popover/popover.d.ts.map +1 -1
  163. package/dist/components/Popover/popover.js +3 -1
  164. package/dist/components/Popover/popover.js.map +1 -1
  165. package/dist/components/ProfileCard/profile-card.d.ts +2 -2
  166. package/dist/components/ProfileCard/profile-card.js +3 -3
  167. package/dist/components/ProfileCard/profile-card.js.map +1 -1
  168. package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
  169. package/dist/components/RadioGroup/radio-group.js +15 -9
  170. package/dist/components/RadioGroup/radio-group.js.map +1 -1
  171. package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
  172. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  173. package/dist/components/ScrollArea/scroll-area.js +6 -3
  174. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  175. package/dist/components/Select/select.d.ts +46 -5
  176. package/dist/components/Select/select.d.ts.map +1 -1
  177. package/dist/components/Select/select.js +109 -28
  178. package/dist/components/Select/select.js.map +1 -1
  179. package/dist/components/SelectMenu/select-menu.d.ts +3 -1
  180. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  181. package/dist/components/SelectMenu/select-menu.js +8 -1
  182. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  183. package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
  184. package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
  185. package/dist/components/SelectionControl/boolean-value.js +23 -0
  186. package/dist/components/SelectionControl/boolean-value.js.map +1 -0
  187. package/dist/components/SelectionControl/index.d.ts +1 -0
  188. package/dist/components/SelectionControl/index.d.ts.map +1 -1
  189. package/dist/components/SelectionControl/index.js +2 -0
  190. package/dist/components/SelectionControl/index.js.map +1 -1
  191. package/dist/components/Sheet/index.js +2 -1
  192. package/dist/components/Sheet/sheet.d.ts +7 -1
  193. package/dist/components/Sheet/sheet.d.ts.map +1 -1
  194. package/dist/components/Sheet/sheet.js +5 -2
  195. package/dist/components/Sheet/sheet.js.map +1 -1
  196. package/dist/components/Sidebar/sidebar.d.ts +9 -4
  197. package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
  198. package/dist/components/Sidebar/sidebar.js +8 -10
  199. package/dist/components/Sidebar/sidebar.js.map +1 -1
  200. package/dist/components/Slider/slider.d.ts.map +1 -1
  201. package/dist/components/Slider/slider.js.map +1 -1
  202. package/dist/components/Switch/switch.d.ts +6 -6
  203. package/dist/components/Switch/switch.d.ts.map +1 -1
  204. package/dist/components/Switch/switch.js +3 -2
  205. package/dist/components/Switch/switch.js.map +1 -1
  206. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  207. package/dist/components/Tabs/tabs.js +15 -1
  208. package/dist/components/Tabs/tabs.js.map +1 -1
  209. package/dist/components/Textarea/textarea.d.ts +2 -2
  210. package/dist/components/Textarea/textarea.d.ts.map +1 -1
  211. package/dist/components/Textarea/textarea.js +50 -42
  212. package/dist/components/Textarea/textarea.js.map +1 -1
  213. package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
  214. package/dist/components/TimePicker/time-columns.js +42 -38
  215. package/dist/components/TimePicker/time-columns.js.map +1 -1
  216. package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
  217. package/dist/components/TimePicker/time-picker.js +8 -6
  218. package/dist/components/TimePicker/time-picker.js.map +1 -1
  219. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  220. package/dist/components/Tooltip/tooltip.js +7 -5
  221. package/dist/components/Tooltip/tooltip.js.map +1 -1
  222. package/dist/components/TreeView/tree-view.d.ts +56 -1
  223. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  224. package/dist/components/TreeView/tree-view.js +211 -12
  225. package/dist/components/TreeView/tree-view.js.map +1 -1
  226. package/dist/index.d.ts +115 -58
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +93 -108
  229. package/dist/index.js.map +1 -1
  230. package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
  231. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  232. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  233. package/dist/tokens/categorical-color.js +15 -0
  234. package/dist/tokens/categorical-color.js.map +1 -1
  235. package/dist/tokens/motion/motion.d.ts +7 -6
  236. package/dist/tokens/motion/motion.d.ts.map +1 -1
  237. package/dist/tokens/motion/motion.js +6 -6
  238. package/dist/tokens/motion/motion.js.map +1 -1
  239. package/dist/tokens/motion/overlay-motion.d.ts +19 -0
  240. package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
  241. package/dist/tokens/motion/overlay-motion.js +9 -0
  242. package/dist/tokens/motion/overlay-motion.js.map +1 -0
  243. package/dist/tokens/uiSize/icon-size.js +5 -0
  244. package/dist/tokens/uiSize/icon-size.js.map +1 -0
  245. package/ds-canonical/fork/governance.lock +37 -19
  246. package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
  247. package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
  248. package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
  249. package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +17 -5
  250. package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
  251. package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
  252. package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
  253. package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
  254. package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +36 -0
  255. package/ds-canonical/fork/hooks/check_story_determinism.sh +39 -0
  256. package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
  257. package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  258. package/ds-canonical/fork/manifest.json +20 -0
  259. package/ds-canonical/fork/preamble.md +22 -18
  260. package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +9 -3
  261. package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
  262. package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +7 -0
  263. package/ds-canonical/fork/skills/performance-audit/SKILL.md +4 -2
  264. package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
  265. package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
  266. package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
  267. package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
  268. package/ds-canonical/fork/skills/visual-audit/SKILL.md +10 -2
  269. package/ds-canonical/hooks/README.md +1 -1
  270. package/ds-canonical/hooks/block_prototype_imports.py +15 -4
  271. package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
  272. package/ds-canonical/hooks/check_benchmark_citation.sh +5 -3
  273. package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
  274. package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
  275. package/ds-canonical/hooks/check_ds_anchor_preflight.sh +17 -5
  276. package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
  277. package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
  278. package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
  279. package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
  280. package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
  281. package/ds-canonical/hooks/check_opacity_token_usage.sh +36 -0
  282. package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
  283. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
  284. package/ds-canonical/hooks/check_propose_without_benchmark.sh +6 -2
  285. package/ds-canonical/hooks/check_ssot_header_declaration.sh +55 -0
  286. package/ds-canonical/hooks/check_story_determinism.sh +39 -0
  287. package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
  288. package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
  289. package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
  290. package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
  291. package/ds-canonical/hooks/stop_self_audit.sh +31 -0
  292. package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
  293. package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
  294. package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +3 -3
  295. package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
  296. package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
  297. package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
  298. package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
  299. package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
  300. package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
  301. package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -1
  302. package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +41 -2
  303. package/ds-canonical/hooks/tests/test_check_ssot_header_declaration.sh +72 -0
  304. package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
  305. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  306. package/ds-canonical/references/build-ui-canonicals.md +2 -1
  307. package/ds-canonical/references/failure-class-registry.json +145 -0
  308. package/ds-canonical/references/governance-audit-coverage.md +33 -0
  309. package/ds-canonical/references/item-anatomy-recipe.md +2 -2
  310. package/ds-canonical/references/scenario-definition.md +6 -9
  311. package/ds-canonical/references/ssot-consultation.md +2 -2
  312. package/ds-canonical/references/ssot-index.md +5 -4
  313. package/ds-canonical/references/story-baseline-registry.json +8 -2
  314. package/ds-canonical/references/structural-token-retention.md +6 -9
  315. package/ds-canonical/rules/meta-patterns.md +8 -8
  316. package/ds-canonical/rules/self-verify.md +4 -3
  317. package/ds-canonical/rules/spec-rules.md +1 -1
  318. package/ds-canonical/rules/story-rules.md +2 -2
  319. package/ds-canonical/rules/ui-development.md +4 -2
  320. package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +9 -3
  321. package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
  322. package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
  323. package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
  324. package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
  325. package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
  326. package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
  327. package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +57 -68
  328. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
  329. package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
  330. package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
  331. package/ds-canonical/skills/delivery-handoff/SKILL.md +5 -0
  332. package/ds-canonical/skills/design-system-audit/SKILL.md +27 -26
  333. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
  334. package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
  335. package/ds-canonical/skills/governance-health/SKILL.md +2 -1
  336. package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
  337. package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
  338. package/ds-canonical/skills/new-component/SKILL.md +7 -5
  339. package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
  340. package/ds-canonical/skills/performance-audit/SKILL.md +2 -2
  341. package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
  342. package/ds-canonical/skills/propose-options/SKILL.md +3 -3
  343. package/ds-canonical/skills/prototype/SKILL.md +1 -1
  344. package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
  345. package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
  346. package/ds-canonical/skills/story-writing/SKILL.md +3 -3
  347. package/ds-canonical/skills/visual-audit/SKILL.md +10 -2
  348. package/ds-story-manifest.json +29 -20
  349. package/llms-full.txt +22 -1
  350. package/llms.txt +5 -4
  351. package/package.json +7 -3
  352. package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
  353. package/src/components/Accordion/accordion.stories.tsx +16 -1
  354. package/src/components/Alert/alert.principles.stories.tsx +1 -1
  355. package/src/components/Alert/alert.spec.md +8 -10
  356. package/src/components/Alert/alert.stories.tsx +28 -28
  357. package/src/components/Alert/alert.tsx +1 -1
  358. package/src/components/AppShell/_demo-helpers.tsx +6 -3
  359. package/src/components/AppShell/app-shell.spec.md +4 -4
  360. package/src/components/AppShell/app-shell.tsx +28 -2
  361. package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
  362. package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
  363. package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
  364. package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
  365. package/src/components/Avatar/avatar.spec.md +2 -2
  366. package/src/components/Avatar/avatar.stories.tsx +8 -6
  367. package/src/components/Avatar/avatar.tsx +13 -5
  368. package/src/components/Badge/badge.principles.stories.tsx +1 -1
  369. package/src/components/Badge/badge.spec.md +2 -0
  370. package/src/components/Badge/badge.stories.tsx +4 -2
  371. package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
  372. package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
  373. package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
  374. package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
  375. package/src/components/Button/button.principles.stories.tsx +1 -1
  376. package/src/components/Button/button.spec.md +2 -2
  377. package/src/components/Button/button.tsx +18 -3
  378. package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
  379. package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
  380. package/src/components/Calendar/calendar.spec.md +5 -4
  381. package/src/components/Calendar/calendar.stories.tsx +7 -4
  382. package/src/components/Calendar/calendar.tsx +26 -7
  383. package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
  384. package/src/components/Carousel/carousel.spec.md +2 -2
  385. package/src/components/Carousel/carousel.tsx +11 -3
  386. package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
  387. package/src/components/Chart/chart.principles.stories.tsx +1 -1
  388. package/src/components/Chart/chart.stories.tsx +2 -8
  389. package/src/components/Chart/chart.tsx +3 -0
  390. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
  391. package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
  392. package/src/components/Checkbox/checkbox.spec.md +11 -26
  393. package/src/components/Checkbox/checkbox.stories.tsx +11 -8
  394. package/src/components/Checkbox/checkbox.tsx +18 -13
  395. package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
  396. package/src/components/Chip/chip.principles.stories.tsx +1 -1
  397. package/src/components/Chip/chip.spec.md +5 -4
  398. package/src/components/Chip/chip.tsx +5 -1
  399. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
  400. package/src/components/CircularProgress/circular-progress.spec.md +3 -3
  401. package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
  402. package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
  403. package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
  404. package/src/components/Coachmark/coachmark.spec.md +5 -5
  405. package/src/components/Coachmark/coachmark.stories.tsx +3 -1
  406. package/src/components/Coachmark/coachmark.tsx +10 -3
  407. package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
  408. package/src/components/Combobox/combobox.tsx +53 -19
  409. package/src/components/Command/command.anatomy.stories.tsx +2 -2
  410. package/src/components/Command/command.principles.stories.tsx +1 -1
  411. package/src/components/Command/command.stories.tsx +42 -10
  412. package/src/components/DataTable/cell-registry.tsx +86 -63
  413. package/src/components/DataTable/column-types.ts +10 -1
  414. package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
  415. package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
  416. package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
  417. package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
  418. package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
  419. package/src/components/DataTable/data-table.spec.md +32 -12
  420. package/src/components/DataTable/data-table.stories.tsx +16 -17
  421. package/src/components/DataTable/data-table.tsx +103 -60
  422. package/src/components/DataTable/index.ts +3 -0
  423. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
  424. package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
  425. package/src/components/DateGrid/date-grid.spec.md +1 -1
  426. package/src/components/DateGrid/date-grid.tsx +3 -1
  427. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
  428. package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
  429. package/src/components/DatePicker/date-picker.spec.md +3 -3
  430. package/src/components/DatePicker/date-picker.stories.tsx +4 -5
  431. package/src/components/DatePicker/date-picker.tsx +99 -45
  432. package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
  433. package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
  434. package/src/components/DescriptionList/description-list.spec.md +3 -3
  435. package/src/components/DescriptionList/description-list.stories.tsx +7 -6
  436. package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
  437. package/src/components/Dialog/dialog.spec.md +9 -0
  438. package/src/components/Dialog/dialog.stories.tsx +66 -5
  439. package/src/components/Dialog/dialog.tsx +60 -25
  440. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  441. package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
  442. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
  443. package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
  444. package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
  445. package/src/components/Empty/empty.spec.md +2 -2
  446. package/src/components/Field/field-context.ts +69 -5
  447. package/src/components/Field/field-controls.spec.md +62 -8
  448. package/src/components/Field/field-edit-keys.ts +66 -0
  449. package/src/components/Field/field-types.ts +58 -9
  450. package/src/components/Field/field-wrapper.tsx +50 -42
  451. package/src/components/Field/field.anatomy.stories.tsx +9 -6
  452. package/src/components/Field/field.spec.md +1 -1
  453. package/src/components/Field/field.stories.tsx +1 -1
  454. package/src/components/Field/field.tsx +2 -1
  455. package/src/components/Field/form-validation.spec.md +3 -3
  456. package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
  457. package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
  458. package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
  459. package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
  460. package/src/components/FileItem/file-item.spec.md +14 -8
  461. package/src/components/FileItem/file-item.stories.tsx +36 -25
  462. package/src/components/FileItem/file-item.tsx +8 -6
  463. package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
  464. package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
  465. package/src/components/FileUpload/file-upload.spec.md +1 -1
  466. package/src/components/FileUpload/file-upload.tsx +1 -1
  467. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
  468. package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
  469. package/src/components/FileViewer/file-viewer.spec.md +30 -12
  470. package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
  471. package/src/components/FileViewer/file-viewer.tsx +12 -4
  472. package/src/components/FileViewer/image-renderer.tsx +35 -7
  473. package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
  474. package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
  475. package/src/components/HoverCard/hover-card.spec.md +5 -5
  476. package/src/components/HoverCard/hover-card.stories.tsx +16 -16
  477. package/src/components/HoverCard/hover-card.tsx +5 -3
  478. package/src/components/InlineEdit/index.ts +5 -0
  479. package/src/components/InlineEdit/inline-edit.spec.md +149 -0
  480. package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
  481. package/src/components/InlineEdit/inline-edit.tsx +287 -0
  482. package/src/components/Input/input.anatomy.stories.tsx +8 -7
  483. package/src/components/Input/input.principles.stories.tsx +2 -12
  484. package/src/components/Input/input.spec.md +13 -18
  485. package/src/components/Input/input.tsx +69 -22
  486. package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
  487. package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
  488. package/src/components/LinkInput/link-input.tsx +11 -10
  489. package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
  490. package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
  491. package/src/components/Menu/menu-item.spec.md +22 -15
  492. package/src/components/Menu/menu-item.stories.tsx +39 -11
  493. package/src/components/Menu/menu-item.tsx +38 -4
  494. package/src/components/Notice/notice.principles.stories.tsx +1 -1
  495. package/src/components/Notice/notice.spec.md +16 -3
  496. package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
  497. package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
  498. package/src/components/NumberInput/number-input.spec.md +4 -4
  499. package/src/components/NumberInput/number-input.tsx +19 -12
  500. package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
  501. package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
  502. package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
  503. package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
  504. package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
  505. package/src/components/Pagination/pagination.spec.md +1 -1
  506. package/src/components/Pagination/pagination.stories.tsx +2 -6
  507. package/src/components/Pagination/pagination.tsx +1 -1
  508. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
  509. package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
  510. package/src/components/PeoplePicker/people-picker.spec.md +6 -6
  511. package/src/components/PeoplePicker/people-picker.tsx +56 -16
  512. package/src/components/PeoplePicker/person-display.tsx +32 -16
  513. package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
  514. package/src/components/Popover/popover.tsx +3 -1
  515. package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
  516. package/src/components/ProfileCard/profile-card.spec.md +3 -3
  517. package/src/components/ProfileCard/profile-card.tsx +6 -6
  518. package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
  519. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
  520. package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
  521. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
  522. package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
  523. package/src/components/RadioGroup/radio-group.tsx +41 -15
  524. package/src/components/Rating/rating.spec.md +11 -3
  525. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  526. package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
  527. package/src/components/ScrollArea/scroll-area.spec.md +7 -3
  528. package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
  529. package/src/components/ScrollArea/scroll-area.tsx +17 -4
  530. package/src/components/Select/select.anatomy.stories.tsx +3 -3
  531. package/src/components/Select/select.spec.md +27 -17
  532. package/src/components/Select/select.stories.tsx +33 -0
  533. package/src/components/Select/select.tsx +190 -36
  534. package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
  535. package/src/components/SelectMenu/select-menu.spec.md +15 -7
  536. package/src/components/SelectMenu/select-menu.tsx +16 -3
  537. package/src/components/SelectionControl/boolean-value.tsx +39 -0
  538. package/src/components/SelectionControl/index.ts +1 -0
  539. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
  540. package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
  541. package/src/components/SelectionControl/selection-item.spec.md +3 -3
  542. package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
  543. package/src/components/Separator/separator.spec.md +2 -2
  544. package/src/components/Separator/separator.stories.tsx +6 -27
  545. package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
  546. package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
  547. package/src/components/Sheet/sheet.spec.md +2 -2
  548. package/src/components/Sheet/sheet.tsx +9 -5
  549. package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
  550. package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
  551. package/src/components/Sidebar/sidebar.spec.md +13 -15
  552. package/src/components/Sidebar/sidebar.stories.tsx +8 -7
  553. package/src/components/Sidebar/sidebar.tsx +37 -25
  554. package/src/components/Skeleton/skeleton.stories.tsx +4 -11
  555. package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
  556. package/src/components/Slider/slider.principles.stories.tsx +2 -2
  557. package/src/components/Slider/slider.spec.md +5 -5
  558. package/src/components/Slider/slider.tsx +3 -1
  559. package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
  560. package/src/components/Steps/steps.principles.stories.tsx +1 -1
  561. package/src/components/Steps/steps.spec.md +1 -1
  562. package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
  563. package/src/components/Switch/switch.principles.stories.tsx +4 -4
  564. package/src/components/Switch/switch.spec.md +8 -8
  565. package/src/components/Switch/switch.stories.tsx +9 -24
  566. package/src/components/Switch/switch.tsx +17 -13
  567. package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
  568. package/src/components/Tabs/tabs.spec.md +1 -1
  569. package/src/components/Tabs/tabs.tsx +21 -1
  570. package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
  571. package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
  572. package/src/components/Textarea/textarea.spec.md +4 -4
  573. package/src/components/Textarea/textarea.tsx +72 -46
  574. package/src/components/TimePicker/time-columns.tsx +6 -2
  575. package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
  576. package/src/components/TimePicker/time-picker.spec.md +3 -3
  577. package/src/components/TimePicker/time-picker.tsx +16 -10
  578. package/src/components/Toast/toast.stories.tsx +7 -4
  579. package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
  580. package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
  581. package/src/components/Tooltip/tooltip.spec.md +1 -1
  582. package/src/components/Tooltip/tooltip.stories.tsx +4 -39
  583. package/src/components/Tooltip/tooltip.tsx +8 -6
  584. package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
  585. package/src/components/TreeView/tree-view.spec.md +42 -11
  586. package/src/components/TreeView/tree-view.stories.tsx +1 -0
  587. package/src/components/TreeView/tree-view.tsx +322 -17
  588. package/src/index.ts +584 -60
  589. package/src/patterns/action-bar/action-bar.spec.md +9 -0
  590. package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
  591. package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
  592. package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
  593. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
  594. package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
  595. package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
  596. package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
  597. package/src/styles/tokens.css +3 -0
  598. package/src/tokens/README.md +1 -1
  599. package/src/tokens/color/color.spec.md +3 -3
  600. package/src/tokens/color/color.stories.tsx +3 -3
  601. package/src/tokens/density/density.stories.tsx +16 -3
  602. package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
  603. package/src/tokens/motion/motion.css +33 -21
  604. package/src/tokens/motion/motion.spec.md +32 -15
  605. package/src/tokens/motion/motion.stories.tsx +58 -5
  606. package/src/tokens/motion/motion.ts +7 -6
  607. package/src/tokens/motion/overlay-motion.ts +26 -0
  608. package/src/tokens/orphan-tokens.spec.md +4 -4
  609. package/src/tokens/token-system.spec.md +1 -0
  610. package/src/tokens/uiSize/uiSize.spec.md +1 -1
  611. package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
  612. package/src/tokens/utility-registry.json +160 -23
@@ -1 +1 @@
1
- {"version":3,"file":"textarea.js","sources":["../../../src/components/Textarea/textarea.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'\nimport { EMPTY_DISPLAY } from '@/design-system/components/Field/field-wrapper'\n\n/**\n * Textarea — 多行文字輸入\n *\n * ── 定位 ────────────────────────────────────────────────────────────────\n * 多行版本的 Input,edit / display / readonly / disabled 四態與 Input 邏輯一致(Phase B1 2026-05-05)。\n * 不同於 Input:\n * - 沒有固定 field-height(高度由 rows 或 min-h 決定)\n * - 沒有 startIcon / endAction(textarea 慣例不放 icon)\n * - readonly 呈現保留邊框與 padding,只改底色,讓多行文字有合理閱讀區\n * - display 渲染 <div> + white-space:pre-wrap 保留多行文本\n *\n * ── Padding 規則 ───────────────────────────────────────────────────────\n * 多行內容必須有上下內距才能閱讀舒適。不沿用 Input 的 items-center,\n * 改用 py-2(8px)固定上下內距 + px-[var(--field-px)](12px token,左右內距 SSOT,與 Input/Field family 一致)。\n *\n * ── Size ────────────────────────────────────────────────────────────────\n * sm / md → text-body(14px)\n * lg → text-body-lg(16px)\n *\n * ── rows / min-h ───────────────────────────────────────────────────────\n * 預設 rows={3}。消費者可透過 rows prop 調整,或透過 min-h-* className 覆寫。\n */\n\n// Phase B1(2026-05-05):新增 chrome variant(default / bare),mode×chrome 的 chrome 規則由\n// compoundVariants 決定,鏡射 fieldWrapperStyles 對齊 canonical(Phase D 將整併進 fieldWrapperStyles)。\nconst textareaVariants = cva(\n [\n 'w-full rounded-md',\n 'text-foreground font-normal',\n 'outline-none resize-y',\n 'placeholder:text-fg-muted',\n // K10 fix(2026-05-04):disabled 時 placeholder + text 切 fg-disabled(parallel 到 bareInputStyles)\n // Textarea 自身 `<textarea disabled>` 帶 disabled HTML attribute,用 `disabled:` variant 直接命中\n 'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled',\n 'px-[var(--field-px)] py-2',\n 'transition-colors duration-150',\n ],\n {\n variants: {\n mode: {\n edit: '',\n display: '',\n readonly: '',\n disabled: '',\n },\n // chrome 對齊 fieldWrapperStyles.variant(default / bare / naked)。\n variant: {\n default: '',\n bare: '',\n naked: '',\n },\n size: {\n sm: 'text-body',\n md: 'text-body',\n lg: 'text-body-lg',\n },\n // error(2026-07-04 Q1,鏡射 fieldWrapperStyles error variant;Phase D 整併時一起收)\n error: {\n true: '',\n false: '',\n },\n },\n compoundVariants: [\n // default chrome × mode\n {\n mode: 'edit',\n variant: 'default',\n className: [\n 'bg-surface border border-border',\n 'hover:border-border-hover',\n ],\n },\n { mode: 'edit', variant: 'default', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },\n {\n mode: 'display',\n variant: 'default',\n // 2026-05-13 Q3 Path Ⅰ:Textarea default display zero chrome,!px-0 !py-0 override base `px-[var(--field-px)] py-2`\n // (跟 Input 同 SSOT,per field-controls.spec.md (d))\n className: 'bg-transparent border border-transparent !px-0 !py-0',\n },\n {\n mode: 'readonly',\n variant: 'default',\n className: 'bg-readonly border border-transparent',\n },\n {\n mode: 'disabled',\n variant: 'default',\n className: 'bg-disabled border border-transparent cursor-not-allowed text-fg-disabled',\n },\n // bare chrome × mode(對齊 fieldWrapperStyles bare 規則)\n {\n mode: 'edit',\n variant: 'bare',\n className: [\n 'bg-transparent border border-transparent',\n 'hover:border-border',\n ],\n },\n { mode: 'edit', variant: 'bare', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },\n {\n mode: 'edit',\n error: true,\n className: 'border-error hover:border-error-hover focus-within:!border-error focus-within:hover:!border-error',\n },\n {\n mode: 'display',\n variant: 'bare',\n className: 'bg-transparent border border-transparent',\n },\n {\n mode: 'readonly',\n variant: 'bare',\n className: 'bg-transparent border border-transparent',\n },\n // 2026-07-05 fix(deep-audit A.1b,鏡射下方 naked 2026-05-13 codex V2 fix):移除 bare×disabled\n // 的 `opacity-disabled` blanket — 同 class 已用具體 `text-fg-disabled` token swap,兩策略同時\n // 套用違 opacity.spec.md「不可混用」canonical(0.45 × token 疊加 = 過度褪色)。\n {\n mode: 'disabled',\n variant: 'bare',\n className: 'bg-transparent border border-transparent cursor-not-allowed text-fg-disabled',\n },\n // naked chrome × mode — cell-as-input substrate(2026-05-06 v14 revert v12)。\n // v12 `!absolute -inset-px` autoRowHeight 不相容 → revert v9 baseline + 保留 v13.3\n // focus !important。focus-visible 用 textarea 自身 selector(focusable element)。\n {\n mode: 'edit',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !resize-none !h-full',\n '!px-[var(--table-cell-px)] !py-[var(--table-cell-py)]',\n 'border border-border',\n 'hover:border-border-hover',\n // textarea UA stylesheet 預設 line-height: normal(1.2-1.5 不定),會跟 display\n // `<div>` text-body line-height: 1.5(21px @ 14px)不一致 → cell 進 edit 後 height\n // shift。顯式 leading 對齊 div 行為。\n '!leading-[1.5]',\n ],\n },\n { mode: 'edit', variant: 'naked', error: false, className: 'focus-visible:!border-primary focus-visible:hover:!border-primary' },\n // 2026-05-13 Q1 R4 verify(per codex Q1 verdict 補 Textarea nuance):\n // Textarea naked display/readonly/disabled 用 `!h-full`,**不**對齊 Field wrapper 的 `!h-auto`。\n // Why divergence:textarea 是 native form element 帶 intrinsic rows-based height,且 cell 內\n // multi-line text 需要撐滿 cell 而非依 line-height intrinsic。`!h-full` 讓 textarea 填滿 cell\n // 高度,文字 anchored to cell.top + cell padding(同視覺結果 Field wrapper autoRow !h-auto)。\n // 此 divergence intentional + documented;非 SSOT violation。\n { mode: 'display', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },\n { mode: 'readonly', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },\n // 2026-05-13 codex V2 fix:移除 `opacity-disabled` blanket(對齊 field-wrapper.tsx naked R3 fix +\n // color.spec.md:729 逃生艙 rule)。Textarea 已用具體 `text-fg-disabled` token,不需要 wrapper opacity。\n { mode: 'disabled', variant: 'naked', className: 'bg-transparent !rounded-none cursor-not-allowed text-fg-disabled !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },\n ],\n defaultVariants: {\n mode: 'edit',\n variant: 'default',\n size: 'md',\n error: false,\n },\n }\n)\n\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n Omit<VariantProps<typeof textareaVariants>, 'mode' | 'variant'> {\n /** Field display mode */\n mode?: FieldMode\n /**\n * Visual chrome(正交於 mode);Phase B1(2026-05-05)新增。透傳自 FieldContext.variant,per-prop override。\n * - `'default'` — 完整 chrome(form 場景)\n * - `'bare'` — 透明 variant,hover/focus 才 reveal inner border(toolbar / inline editing)\n * - `'naked'` — 完全無 chrome,cell-as-input(host cell 提供 border + focus frame,對齊 Airtable / Notion / Excel cell editing)\n */\n variant?: FieldVariant\n /** Error 狀態(正交於 mode)。border-error + aria-invalid。 */\n error?: boolean\n}\n\n// code-quality-allow: long-function — Textarea forwardRef body 含 mode×size×variant×error 4 軸 prop + autoFocus + aria 完整覆蓋,拆 sub-fn 會把 useFieldContext / fieldWrapperStyles 跨檔 drilling\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n (\n {\n mode: modeProp,\n variant: variantProp,\n error: errorProp = false,\n size: sizeProp,\n className,\n disabled,\n readOnly,\n rows = 3,\n value,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref\n ) => {\n // Field context 整合:disabled / mode / chrome / invalid / size / id 都能從 context 繼承\n const fieldCtx = useFieldContext()\n // chrome 透傳:per-prop override context\n const variant: FieldVariant = useResolvedFieldVariant(variantProp)\n const error = useResolvedFieldInvalid(errorProp)\n const size = useResolvedFieldSize(sizeProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')\n const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })\n const isEditable = resolvedMode === 'edit'\n const isDisplay = resolvedMode === 'display'\n const inputId = idProp ?? fieldCtx?.id\n const ariaDescribedBy = ariaDescribedByProp ?? fieldCtx?.descriptionId\n const ariaErrorMessage = ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)\n\n // ── display mode:純展示,渲染 <div> 取代 <textarea>(white-space:pre-wrap 保留多行) ──\n // 對齊 Carbon read-only / Cloudscape display-mode\n if (isDisplay) {\n const displayValue = value != null && value !== '' ? String(value) : null\n return (\n <div\n id={inputId}\n data-field-mode=\"display\"\n aria-describedby={ariaDescribedBy}\n className={cn(\n textareaVariants({ mode: 'display', variant: variant, size }),\n 'whitespace-pre-wrap break-words',\n displayValue == null && 'text-fg-muted',\n className,\n )}\n >\n {displayValue ?? EMPTY_DISPLAY}\n </div>\n )\n }\n\n return (\n <textarea\n ref={ref}\n id={inputId}\n rows={rows}\n value={value as string | number | readonly string[] | undefined}\n disabled={resolvedMode === 'disabled'}\n readOnly={resolvedMode === 'readonly'}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedBy}\n aria-errormessage={ariaErrorMessage}\n data-field-mode={resolvedMode}\n data-error={isEditable && error ? '' : undefined}\n className={cn(\n textareaVariants({ mode: resolvedMode, variant: variant, size, error }),\n className\n )}\n {...props}\n />\n )\n }\n)\nTextarea.displayName = 'Textarea'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// sizes 只反映 Textarea 真實控制的軸:typography(size 只控字體,不綁 field-height、無 icon slot —\n// 高度由 rows + resize-y 決定,per textarea.spec.md L88-92 / L64 / L113)。\nexport const textareaMeta = {\n component: 'Textarea',\n family: 4,\n variants: {\n\n },\n sizes: {\n sm: { typography: 'body' },\n md: { typography: 'body' },\n lg: { typography: 'body-lg' },\n },\n // states 對齊 cva compoundVariants + anatomy ColorMatrix 真實 state 集合;\n // text input 無 'active'(按下)視覺態(Material/Polaris/Carbon TextArea 共識)。\n states: ['default', 'hover', 'focus-visible', 'readonly', 'disabled', 'error'],\n tokens: {\n bg: ['bg-disabled', 'bg-readonly', 'bg-surface'], // 2026-07-04 補:readonly×default cva 實際消費(spec「Readonly 特例」主打 token)\n fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],\n ring: [],\n },\n defaultSize: 'md',\n} as const\n\nexport { Textarea, textareaVariants }\n"],"names":[],"mappings":";;;;;;AAiCA,MAAM,mBAAmB;AAAA,EACvB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,SAAS;AAAA,QACT,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA;AAAA,MAGZ,SAAS;AAAA,QACP,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,MAET,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,MAAA;AAAA;AAAA,MAGN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,kBAAkB;AAAA;AAAA,MAEhB;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,MACF;AAAA,MAEF,EAAE,MAAM,QAAQ,SAAS,WAAW,OAAO,OAAO,WAAW,kEAAA;AAAA,MAC7D;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA;AAAA;AAAA,QAGT,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,MAAA;AAAA;AAAA,MAGb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,MACF;AAAA,MAEF,EAAE,MAAM,QAAQ,SAAS,QAAQ,OAAO,OAAO,WAAW,kEAAA;AAAA,MAC1D;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,QACP,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,MAAA;AAAA,MAEb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,MAAA;AAAA;AAAA;AAAA;AAAA,MAKb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,MAAA;AAAA;AAAA;AAAA;AAAA,MAKb;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,QACT,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA;AAAA;AAAA,UAIA;AAAA,QAAA;AAAA,MACF;AAAA,MAEF,EAAE,MAAM,QAAQ,SAAS,SAAS,OAAO,OAAO,WAAW,oEAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAO3D,EAAE,MAAM,WAAW,SAAS,SAAS,WAAW,yGAAA;AAAA,MAChD,EAAE,MAAM,YAAY,SAAS,SAAS,WAAW,yGAAA;AAAA;AAAA;AAAA,MAGjD,EAAE,MAAM,YAAY,SAAS,SAAS,WAAW,6IAAA;AAAA,IAA6I;AAAA,IAEhM,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,IAAA;AAAA,EACT;AAEJ;AAmBA,MAAM,WAAW,MAAM;AAAA,EACrB,CACE;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO,YAAY;AAAA,IACnB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA,IAAI;AAAA,IACJ,oBAAoB;AAAA,IACpB,qBAAqB;AAAA,IACrB,GAAG;AAAA,EAAA,GAEL,QACG;AAEH,UAAM,WAAW,gBAAA;AAEjB,UAAM,UAAwB,wBAAwB,WAAW;AACjE,UAAM,QAAQ,wBAAwB,SAAS;AAC/C,UAAM,OAAO,qBAAqB,QAAQ;AAE1C,UAAM,eAA0B,qBAAqB,EAAE,MAAM,UAAU,UAAU,UAAU;AAC3F,UAAM,aAAa,iBAAiB;AACpC,UAAM,YAAY,iBAAiB;AACnC,UAAM,UAAU,WAAU,qCAAU;AACpC,UAAM,kBAAkB,wBAAuB,qCAAU;AACzD,UAAM,mBAAmB,yBAAyB,QAAQ,qCAAU,UAAU;AAI9E,QAAI,WAAW;AACb,YAAM,eAAe,SAAS,QAAQ,UAAU,KAAK,OAAO,KAAK,IAAI;AACrE,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAI;AAAA,UACJ,mBAAgB;AAAA,UAChB,oBAAkB;AAAA,UAClB,WAAW;AAAA,YACT,iBAAiB,EAAE,MAAM,WAAW,SAAkB,MAAM;AAAA,YAC5D;AAAA,YACA,gBAAgB,QAAQ;AAAA,YACxB;AAAA,UAAA;AAAA,UAGD,UAAA,gBAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IAGvB;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA,UAAU,iBAAiB;AAAA,QAC3B,UAAU,iBAAiB;AAAA,QAC3B,gBAAc,SAAS;AAAA,QACvB,kBAAe,qCAAU,aAAY;AAAA,QACrC,oBAAkB;AAAA,QAClB,qBAAmB;AAAA,QACnB,mBAAiB;AAAA,QACjB,cAAY,cAAc,QAAQ,KAAK;AAAA,QACvC,WAAW;AAAA,UACT,iBAAiB,EAAE,MAAM,cAAc,SAAkB,MAAM,OAAO;AAAA,UACtE;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AACA,SAAS,cAAc;AAKhB,MAAM,eAAe;AAAA,EAC1B,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO;AAAA,IACL,IAAI,EAAE,YAAY,OAAA;AAAA,IAClB,IAAI,EAAE,YAAY,OAAA;AAAA,IAClB,IAAI,EAAE,YAAY,UAAA;AAAA,EAAU;AAAA;AAAA;AAAA,EAI9B,QAAQ,CAAC,WAAW,SAAS,iBAAiB,YAAY,YAAY,OAAO;AAAA,EAC7E,QAAQ;AAAA,IACN,IAAI,CAAC,eAAe,eAAe,YAAY;AAAA;AAAA,IAC/C,IAAI,CAAC,oBAAoB,iBAAiB,iBAAiB;AAAA,IAC3D,MAAM,CAAA;AAAA,EAAC;AAAA,EAET,aAAa;AACf;"}
1
+ {"version":3,"file":"textarea.js","sources":["../../../src/components/Textarea/textarea.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'\nimport { useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\n\n/**\n * Textarea — 多行文字輸入\n *\n * ── 定位 ────────────────────────────────────────────────────────────────\n * 多行版本的 Input,edit / display / readonly / disabled 四態與 Input 邏輯一致(Phase B1 2026-05-05)。\n * 不同於 Input:\n * - 沒有固定 field-height(高度由 rows 或 min-h 決定)\n * - 沒有 startIcon / endAction(textarea 慣例不放 icon)\n * - readonly 呈現保留邊框與 padding,只改底色,讓多行文字有合理閱讀區\n * - display 渲染 <div> + white-space:pre-wrap 保留多行文本\n *\n * ── Padding 規則 ───────────────────────────────────────────────────────\n * 多行內容必須有上下內距才能閱讀舒適。不沿用 Input 的 items-center,\n * 改用 py-2(8px)固定上下內距 + px-[var(--field-px)](12px token,左右內距 SSOT,與 Input/Field family 一致)。\n *\n * ── Size ────────────────────────────────────────────────────────────────\n * sm / md → text-body(14px)\n * lg → text-body-lg(16px)\n *\n * ── rows / min-h ───────────────────────────────────────────────────────\n * 預設 rows={3}。消費者可透過 rows prop 調整,或透過 min-h-* className 覆寫。\n */\n\n// Phase B1(2026-05-05):chrome variant(default / naked;`bare` 2026-07-09 退役),mode×chrome 的\n// chrome 規則由 compoundVariants 決定,鏡射 fieldWrapperStyles 對齊 canonical(Phase D 將整併進 fieldWrapperStyles)。\nconst textareaVariants = cva(\n [\n 'w-full rounded-md',\n 'text-foreground font-normal',\n 'outline-none resize-y',\n 'placeholder:text-fg-muted',\n // K10 fix(2026-05-04):disabled 時 placeholder + text 切 fg-disabled(parallel 到 bareInputStyles)\n // Textarea 自身 `<textarea disabled>` 帶 disabled HTML attribute,用 `disabled:` variant 直接命中\n 'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled',\n 'px-[var(--field-px)] py-2',\n 'transition-colors duration-150',\n ],\n {\n variants: {\n mode: {\n edit: '',\n display: '',\n readonly: '',\n disabled: '',\n },\n // chrome 對齊 fieldWrapperStyles.variant(default / naked;2026-07-09 `bare` 退役)。\n variant: {\n default: '',\n naked: '',\n },\n size: {\n sm: 'text-body',\n md: 'text-body',\n lg: 'text-body-lg',\n },\n // error(2026-07-04 Q1,鏡射 fieldWrapperStyles error variant;Phase D 整併時一起收)\n error: {\n true: '',\n false: '',\n },\n },\n compoundVariants: [\n // default chrome × mode\n {\n mode: 'edit',\n variant: 'default',\n className: [\n 'bg-surface border border-border',\n 'hover:border-border-hover',\n ],\n },\n { mode: 'edit', variant: 'default', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },\n {\n mode: 'display',\n variant: 'default',\n // 2026-05-13 Q3 Path Ⅰ:Textarea default display zero chrome,!px-0 !py-0 override base `px-[var(--field-px)] py-2`\n // (跟 Input 同 SSOT,per field-controls.spec.md (d))\n className: 'bg-transparent border border-transparent !px-0 !py-0',\n },\n {\n mode: 'readonly',\n variant: 'default',\n // 2026-07-13 a11y(WCAG 2.4.7):readonly 有值渲 native <textarea readOnly> 可鍵盤聚焦,base\n // outline-none + border-transparent 無焦點指示 → 鏡射 field-wrapper.tsx readonly ring idiom\n // (field-controls.spec.md「readonly focus ring」)。textarea 為裸 focusable element,直接用\n // focus-visible:(非 :has()),僅鍵盤聚焦顯示、滑鼠點擊不觸發。\n className: 'bg-readonly border border-transparent focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',\n },\n {\n mode: 'disabled',\n variant: 'default',\n className: 'bg-disabled border border-transparent cursor-not-allowed text-fg-disabled',\n },\n // (2026-07-09 `bare` chrome × mode compounds 退役已移除;error:true 為 variant-agnostic 保留)\n {\n mode: 'edit',\n error: true,\n className: 'border-error hover:border-error-hover focus-within:!border-error focus-within:hover:!border-error',\n },\n // (2026-07-09 display×bare / readonly×bare / disabled×bare compounds 退役已移除)\n // naked chrome × mode — cell-as-input substrate(2026-05-06 v14 revert v12)。\n // v12 `!absolute -inset-px` autoRowHeight 不相容 → revert v9 baseline + 保留 v13.3\n // focus !important。focus-visible 用 textarea 自身 selector(focusable element)。\n {\n mode: 'edit',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !resize-none !h-full',\n '!px-[var(--table-cell-px)] !py-[var(--table-cell-py)]',\n 'border border-border',\n 'hover:border-border-hover',\n // textarea UA stylesheet 預設 line-height: normal(1.2-1.5 不定),會跟 display\n // `<div>` text-body line-height: 1.5(21px @ 14px)不一致 → cell 進 edit 後 height\n // shift。顯式 leading 對齊 div 行為。\n '!leading-[1.5]',\n ],\n },\n { mode: 'edit', variant: 'naked', error: false, className: 'focus-visible:!border-primary focus-visible:hover:!border-primary' },\n // 2026-05-13 Q1 R4 verify(per codex Q1 verdict 補 Textarea nuance):\n // Textarea naked display/readonly/disabled 用 `!h-full`,**不**對齊 Field wrapper 的 `!h-auto`。\n // Why divergence:textarea 是 native form element 帶 intrinsic rows-based height,且 cell 內\n // multi-line text 需要撐滿 cell 而非依 line-height intrinsic。`!h-full` 讓 textarea 填滿 cell\n // 高度,文字 anchored to cell.top + cell padding(同視覺結果 Field wrapper autoRow !h-auto)。\n // 此 divergence intentional + documented;非 SSOT violation。\n { mode: 'display', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },\n { mode: 'readonly', variant: 'naked', className: 'bg-transparent !rounded-none !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },\n // 2026-05-13 codex V2 fix:移除 `opacity-disabled` blanket(對齊 field-wrapper.tsx naked R3 fix +\n // color.spec.md:729 逃生艙 rule)。Textarea 已用具體 `text-fg-disabled` token,不需要 wrapper opacity。\n { mode: 'disabled', variant: 'naked', className: 'bg-transparent !rounded-none cursor-not-allowed text-fg-disabled !h-full !resize-none !px-0 !py-0 border border-transparent !leading-[1.5]' },\n ],\n defaultVariants: {\n mode: 'edit',\n variant: 'default',\n size: 'md',\n error: false,\n },\n }\n)\n\nexport interface TextareaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'size'>,\n Omit<VariantProps<typeof textareaVariants>, 'mode' | 'variant'> {\n /** Field display mode */\n mode?: FieldMode\n /**\n * Visual chrome(正交於 mode);Phase B1(2026-05-05)新增。透傳自 FieldContext.variant,per-prop override。\n * - `'default'` — 完整 chrome(form 場景)\n * (2026-07-09 `bare` variant 退役;naked = cell-as-input substrate,@internal)\n * - `'naked'` — 完全無 chrome,cell-as-input(host cell 提供 border + focus frame,對齊 Airtable / Notion / Excel cell editing)\n */\n variant?: FieldVariant\n /** Error 狀態(正交於 mode)。border-error + aria-invalid。 */\n error?: boolean\n}\n\n// code-quality-allow: long-function — Textarea forwardRef body 含 mode×size×variant×error 4 軸 prop + autoFocus + aria 完整覆蓋,拆 sub-fn 會把 useFieldContext / fieldWrapperStyles 跨檔 drilling\nconst Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(\n (\n {\n mode: modeProp,\n variant: variantProp,\n error: errorProp = false,\n size: sizeProp,\n className,\n disabled,\n readOnly,\n rows = 3,\n value,\n defaultValue,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref\n ) => {\n // Field context 整合:disabled / mode / chrome / invalid / size / id 都能從 context 繼承\n const fieldCtx = useFieldContext()\n // chrome 透傳:per-prop override context\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const error = useResolvedFieldInvalid(errorProp)\n const size = useResolvedFieldSize(sizeProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')\n const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })\n const emptyDisplay = useFieldEmptyDisplay()\n const isEditable = resolvedMode === 'edit'\n const isDisplay = resolvedMode === 'display'\n const inputId = idProp ?? fieldCtx?.id\n const ariaDescribedBy = ariaDescribedByProp ?? fieldCtx?.descriptionId\n const ariaErrorMessage = ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)\n\n // ── display mode(+ readonly 空值)純展示,渲染 <div> 取代 <textarea>(white-space:pre-wrap 保留多行) ──\n // 對齊 Carbon read-only / Cloudscape display-mode\n // 2026-07-08 user 拍板:readonly 空值也走 display-div 顯 '-'(readonly **有值** 仍走下方\n // native <textarea readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)。\n // 2026-07-13 fix:空值判斷需認得 uncontrolled defaultValue — readonly / display 也要顯示內容,\n // 不可誤判為空值渲 '-'(原為靜態 value ?? defaultValue)。\n // 2026-07-14 R2(dual-model consensus,同 Input input.tsx):resolved value 改走 useControllable\n // 內部 SSOT(Radix idiom)。原靜態版有 typing-stale gap:uncontrolled 打字後外部切\n // display / readonly 仍顯示 stale defaultValue。uncontrolled 時 native textarea 由\n // value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),同時修\n // remount-stale(切 display 再回 edit 從 defaultValue attribute 重掛)+ form.reset() stale\n // (HTML 標準 reset 不發 input event,下方 reset bridge 修)。controlled(傳 value)時\n // useControllable 純 passthrough,行為與原本完全一致。consumer onChange 不接進 hook\n // (保留 native event signature),於 <textarea> onChange 另行轉發。\n const isControlled = value !== undefined\n const [resolved, setResolved] = useControllable<string | number | readonly string[]>({\n value: value as string | number | readonly string[] | undefined,\n defaultValue: defaultValue ?? '',\n })\n const displayValue = resolved != null && resolved !== '' ? String(resolved) : null\n const showDisplaySpan = isDisplay || (resolvedMode === 'readonly' && displayValue == null)\n\n // form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →\n // 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:display 分支不掛 native\n // textarea,回 edit 重掛時 effect 重跑補掛 listener。\n const innerRef = React.useRef<HTMLTextAreaElement | null>(null)\n React.useEffect(() => {\n if (isControlled || showDisplaySpan) return\n const form = innerRef.current?.form\n if (!form) return\n const handleReset = () => setResolved(defaultValue ?? '')\n form.addEventListener('reset', handleReset)\n return () => form.removeEventListener('reset', handleReset)\n }, [isControlled, showDisplaySpan, defaultValue, setResolved])\n // Merge refs(LinkInput setRef idiom,link-input.tsx:138)\n const setRef = React.useCallback((el: HTMLTextAreaElement | null) => {\n innerRef.current = el\n if (typeof ref === 'function') ref(el)\n else if (ref) (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = el\n }, [ref])\n\n if (showDisplaySpan) {\n const spanMode = isDisplay ? 'display' : 'readonly'\n return (\n <div\n id={inputId}\n data-field-mode={spanMode}\n aria-describedby={ariaDescribedBy}\n className={cn(\n textareaVariants({ mode: spanMode, variant: variant, size }),\n 'whitespace-pre-wrap break-words',\n displayValue == null && fieldEmptyColorClass(resolvedMode),\n className,\n )}\n >\n {displayValue ?? emptyDisplay}\n </div>\n )\n }\n\n return (\n <textarea\n ref={setRef}\n id={inputId}\n rows={rows}\n value={resolved}\n disabled={resolvedMode === 'disabled'}\n readOnly={resolvedMode === 'readonly'}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedBy}\n aria-errormessage={ariaErrorMessage}\n data-field-mode={resolvedMode}\n data-error={isEditable && error ? '' : undefined}\n className={cn(\n textareaVariants({ mode: resolvedMode, variant: variant, size, error }),\n className\n )}\n {...props}\n // 置於 {...props} 後:寫回 resolved SSOT(controlled 時 useControllable 內部為 no-op)再轉發 consumer onChange。\n onChange={(e) => {\n setResolved(e.target.value)\n props.onChange?.(e)\n }}\n />\n )\n }\n)\nTextarea.displayName = 'Textarea'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// sizes 只反映 Textarea 真實控制的軸:typography(size 只控字體,不綁 field-height、無 icon slot —\n// 高度由 rows + resize-y 決定,per textarea.spec.md L88-92 / L64 / L113)。\nexport const textareaMeta = {\n component: 'Textarea',\n family: 4,\n variants: {\n\n },\n sizes: {\n sm: { typography: 'body' },\n md: { typography: 'body' },\n lg: { typography: 'body-lg' },\n },\n // states 對齊 cva compoundVariants + anatomy ColorMatrix 真實 state 集合;\n // text input 無 'active'(按下)視覺態(Material/Polaris/Carbon TextArea 共識)。\n // 'focus-visible' 泛指 focus 態:default chrome 實為 focus-within(滑鼠+鍵盤皆亮),naked chrome\n // 為 focus-visible(鍵盤限定),readonly 有值態為 focus-visible ring(見上方 compoundVariants)。\n states: ['default', 'hover', 'focus-visible', 'readonly', 'disabled', 'error'],\n tokens: {\n bg: ['bg-disabled', 'bg-readonly', 'bg-surface'], // 2026-07-04 補:readonly×default cva 實際消費(spec「Readonly 特例」主打 token)\n fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],\n ring: [],\n },\n defaultSize: 'md',\n} as const\n\nexport { Textarea, textareaVariants }\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-columns.d.ts","sourceRoot":"","sources":["../../../src/components/TimePicker/time-columns.tsx"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAQH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;AAE3C,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAID,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAapF;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,UAAQ,GAAG,MAAM,CAM/E;AAkID,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,WAAmB,EACnB,UAAc,EACd,UAAc,EACd,QAAQ,EACR,SAAS,EACT,cAAsB,GACvB,EAAE,gBAAgB,2CAkDlB"}
1
+ {"version":3,"file":"time-columns.d.ts","sourceRoot":"","sources":["../../../src/components/TimePicker/time-columns.tsx"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAQH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;AAE3C,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAID,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAapF;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,UAAQ,GAAG,MAAM,CAM/E;AAsID,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,WAAmB,EACnB,UAAc,EACd,UAAc,EACd,QAAQ,EACR,SAAS,EACT,cAAsB,GACvB,EAAE,gBAAgB,2CAkDlB"}
@@ -63,44 +63,48 @@ function TimeColumn({ values, selected, disabledSet, label, onSelect, withDivide
63
63
  }
64
64
  }
65
65
  };
66
- return /* @__PURE__ */ jsx(ScrollArea, { className: cn("flex-1 h-full", withDivider && "border-r border-divider"), children: /* @__PURE__ */ jsx(
67
- "div",
68
- {
69
- ref: listRef,
70
- role: "listbox",
71
- "aria-label": label,
72
- "aria-activedescendant": selected != null ? `${baseId}-opt-${selected}` : void 0,
73
- tabIndex: 0,
74
- onKeyDown: handleKeyDown,
75
- className: "flex flex-col py-2 focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-[-2px]",
76
- children: values.map((v) => {
77
- const isSelected = v === selected;
78
- const isDisabled = (disabledSet == null ? void 0 : disabledSet.has(v)) ?? false;
79
- return /* @__PURE__ */ jsx(
80
- "button",
81
- {
82
- id: `${baseId}-opt-${v}`,
83
- type: "button",
84
- role: "option",
85
- "aria-selected": isSelected,
86
- disabled: isDisabled,
87
- tabIndex: -1,
88
- onClick: () => onSelect(v),
89
- className: cn(
90
- "w-full h-field-sm text-body tabular-nums",
91
- "flex items-center justify-center",
92
- "cursor-pointer transition-colors",
93
- "hover:bg-neutral-hover",
94
- isSelected && "bg-neutral-selected text-foreground hover:bg-neutral-selected",
95
- isDisabled && "text-fg-disabled cursor-not-allowed hover:bg-transparent"
96
- ),
97
- children: String(v).padStart(2, "0")
98
- },
99
- v
100
- );
101
- })
102
- }
103
- ) });
66
+ return (
67
+ // viewportTabIndex=-1:焦點直接落 listbox(有 keydown),消除「Viewport+listbox 雙 tab stop
68
+ // + 焦點落 Viewport 時 ↑/↓ 觸發原生捲動非選值」a11y bug(axe 由 listbox tabIndex=0 滿足)
69
+ /* @__PURE__ */ jsx(ScrollArea, { viewportTabIndex: -1, className: cn("flex-1 h-full", withDivider && "border-r border-divider"), children: /* @__PURE__ */ jsx(
70
+ "div",
71
+ {
72
+ ref: listRef,
73
+ role: "listbox",
74
+ "aria-label": label,
75
+ "aria-activedescendant": selected != null && values.includes(selected) ? `${baseId}-opt-${selected}` : void 0,
76
+ tabIndex: 0,
77
+ onKeyDown: handleKeyDown,
78
+ className: "flex flex-col py-2 focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-[-2px]",
79
+ children: values.map((v) => {
80
+ const isSelected = v === selected;
81
+ const isDisabled = (disabledSet == null ? void 0 : disabledSet.has(v)) ?? false;
82
+ return /* @__PURE__ */ jsx(
83
+ "button",
84
+ {
85
+ id: `${baseId}-opt-${v}`,
86
+ type: "button",
87
+ role: "option",
88
+ "aria-selected": isSelected,
89
+ disabled: isDisabled,
90
+ tabIndex: -1,
91
+ onClick: () => onSelect(v),
92
+ className: cn(
93
+ "w-full h-field-sm text-body tabular-nums",
94
+ "flex items-center justify-center",
95
+ "cursor-pointer transition-colors",
96
+ "hover:bg-neutral-hover",
97
+ isSelected && "bg-neutral-selected text-foreground hover:bg-neutral-selected",
98
+ isDisabled && "text-fg-disabled cursor-not-allowed hover:bg-transparent"
99
+ ),
100
+ children: String(v).padStart(2, "0")
101
+ },
102
+ v
103
+ );
104
+ })
105
+ }
106
+ ) })
107
+ );
104
108
  }
105
109
  function TimeColumns({
106
110
  value,
@@ -1 +1 @@
1
- {"version":3,"file":"time-columns.js","sources":["../../../src/components/TimePicker/time-columns.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\n/**\n * TimeColumns — H/M/S scroll selector primitive(M17 Rule-of-3 SSOT)。\n *\n * 共用消費者:\n * - `TimePicker`(本元件家)\n * - `DatePicker showTime`(date + time)\n * - `DatePickerRange showTime`(range with time)\n *\n * 抽出原因:三個 picker 共用 H/M/S column scroll-pick 行為,公式重覆 = M17 違反。\n * 抽到 TimePicker/(time scroll selector 的 canonical 家)。\n *\n * ── 設計 ──\n * - Value:`TimeParts { hours, minutes, seconds }`(對齊 date-fns / Date getHours()...)\n * - Step:每欄獨立 `minuteStep` / `secondStep`(會議常用 15)\n * - Disabled:`disabledHours / disabledMinutes / disabledSeconds`(動態根據已選其他欄位)\n * - Visual:對齊 ref/timepicker.png — 多欄並排 + border-r 分隔\n */\n\nimport * as React from 'react'\nimport { ScrollArea } from '@/design-system/components/ScrollArea/scroll-area'\nimport { cn } from '@/lib/utils'\n\n// ── Types ───────────────────────────────────────────────────────────────\n\nexport interface TimeParts {\n hours: number\n minutes: number\n seconds: number\n}\n\nexport type TimeStep = 1 | 5 | 10 | 15 | 30\n\nexport interface TimeColumnsDisabled {\n hours?: number[]\n minutes?: number[]\n seconds?: number[]\n}\n\n// ── ISO time parsing ────────────────────────────────────────────────────\n\n/** Parse \"HH:MM:SS\" / \"HH:MM\" / full ISO datetime — returns time parts only */\nexport function isoToTimeParts(iso: string | null | undefined): TimeParts | undefined {\n if (!iso) return undefined\n const timeMatch = iso.match(/(\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?/)\n if (!timeMatch) return undefined\n const h = Number(timeMatch[1])\n const m = Number(timeMatch[2])\n const s = timeMatch[3] !== undefined ? Number(timeMatch[3]) : 0\n if (\n Number.isNaN(h) || h < 0 || h > 23 ||\n Number.isNaN(m) || m < 0 || m > 59 ||\n Number.isNaN(s) || s < 0 || s > 59\n ) return undefined\n return { hours: h, minutes: m, seconds: s }\n}\n\n/** Format time parts → \"HH:MM\" or \"HH:MM:SS\" depending on showSeconds */\nexport function timePartsToString(parts: TimeParts, showSeconds = false): string {\n const hh = String(parts.hours).padStart(2, '0')\n const mm = String(parts.minutes).padStart(2, '0')\n if (!showSeconds) return `${hh}:${mm}`\n const ss = String(parts.seconds).padStart(2, '0')\n return `${hh}:${mm}:${ss}`\n}\n\n// ── Range builder ───────────────────────────────────────────────────────\n\nfunction buildRange(max: number, step: number): number[] {\n const arr: number[] = []\n for (let v = 0; v < max; v += step) arr.push(v)\n return arr\n}\n\n// ── Single column ───────────────────────────────────────────────────────\n\ninterface TimeColumnProps {\n values: number[]\n selected: number\n /** disabled value set(動態根據其他欄位推) */\n disabledSet?: Set<number>\n label: string\n onSelect: (value: number) => void\n /** 右側分隔線(對齊 ref 多欄樣式) */\n withDivider?: boolean\n}\n\n// code-quality-allow: long-function — column 含 scroll-into-view useEffect / WAI-ARIA listbox 鍵盤 handler / 視覺 state 計算,拆 sub-fn 會切散 listbox accessibility 邏輯\nfunction TimeColumn({ values, selected, disabledSet, label, onSelect, withDivider }: TimeColumnProps) {\n const listRef = React.useRef<HTMLDivElement>(null)\n\n // 開啟時跳到 selected 位置(置中);後續變更走 smooth(對齊 iOS / Material / Ant timepicker idiom)。\n // 用 scrollIntoView({ block: 'center' }) 自動找最近的 scrollable ancestor —\n // 比 manual scrollTop + parentElement 強健(Radix ScrollArea 結構為 Viewport > inner-div > content,\n // listRef.parentElement 不是真正 scrollable 元素)。\n // mount 用 'auto' 避免開啟瞬間出現飄移,後續 user 操作走 'smooth'(同 Tabs/Chip/FileViewer canonical)。\n const isFirstRunRef = React.useRef(true)\n React.useEffect(() => {\n const list = listRef.current\n if (!list) return\n const idx = values.indexOf(selected)\n if (idx < 0) return\n const item = list.children[idx] as HTMLElement | undefined\n if (!item) return\n item.scrollIntoView({ block: 'center', behavior: isFirstRunRef.current ? 'auto' : 'smooth' })\n isFirstRunRef.current = false\n }, [values, selected])\n\n // a11y:穩定 id 前綴,讓 listbox `aria-activedescendant` 指向目前 active(= selected)option。\n // single-tabstop + 方向鍵 listbox 應透過 activedescendant 告知 AT 哪個 option active,\n // 否則 SR 方向鍵移動時讀不出目前 option(WAI-ARIA listbox virtual-focus 慣例)。\n const baseId = React.useId()\n\n // WAI-ARIA listbox keyboard pattern:ArrowUp/Down 切 option / Home / End 跳邊界。\n // 對標 Ant TimePicker / Material TimePicker。Tab 跳離 listbox(走預設行為,不 stopPropagation)。\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const idx = values.indexOf(selected)\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = values.find((_, i) => i > idx && !disabledSet?.has(values[i])) ?? values[idx]\n onSelect(next)\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n // 反向找第一個 enabled\n let i = idx - 1\n while (i >= 0 && disabledSet?.has(values[i])) i--\n if (i >= 0) onSelect(values[i])\n } else if (e.key === 'Home') {\n e.preventDefault()\n const first = values.find((v) => !disabledSet?.has(v))\n if (first !== undefined) onSelect(first)\n } else if (e.key === 'End') {\n e.preventDefault()\n for (let i = values.length - 1; i >= 0; i--) {\n if (!disabledSet?.has(values[i])) {\n onSelect(values[i])\n break\n }\n }\n }\n }\n\n // WAI-ARIA listbox pattern:role=listbox 直接包 role=option(button),不另用 li 包\n // (li role=option + 內含 button 會被 axe 抓 nested-interactive)\n // 高度策略:本 primitive 不鎖死 height(對齊 ScrollArea / Combobox 同 idiom)。\n // ScrollArea 用 h-full,parent flex container 控高 — 讓 consumer:\n // - TimePicker:wrap in h-[216px] container(預設 ~7 items)\n // - DatePicker showTime / Range:flex-row items-stretch + calendar 一起決定高度(自動同高)\n return (\n <ScrollArea className={cn('flex-1 h-full', withDivider && 'border-r border-divider')}>\n <div\n ref={listRef}\n role=\"listbox\"\n aria-label={label}\n aria-activedescendant={selected != null ? `${baseId}-opt-${selected}` : undefined}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n className=\"flex flex-col py-2 focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-[-2px]\"\n >\n {values.map((v) => {\n const isSelected = v === selected\n const isDisabled = disabledSet?.has(v) ?? false\n return (\n <button\n key={v}\n id={`${baseId}-opt-${v}`}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={isDisabled}\n // tabIndex=-1:listbox 自身 tabbable + 用 ArrowUp/Down 切 option(WAI-ARIA roving),\n // 不讓每個 option 都進 Tab order(會 Tab 84 次過完 hours+minutes)\n tabIndex={-1}\n onClick={() => onSelect(v)}\n className={cn(\n 'w-full h-field-sm text-body tabular-nums',\n 'flex items-center justify-center',\n 'cursor-pointer transition-colors',\n 'hover:bg-neutral-hover',\n isSelected && 'bg-neutral-selected text-foreground hover:bg-neutral-selected',\n isDisabled && 'text-fg-disabled cursor-not-allowed hover:bg-transparent',\n )}\n >\n {String(v).padStart(2, '0')}\n </button>\n )\n })}\n </div>\n </ScrollArea>\n )\n}\n\n// ── Composite — H/M/S columns ──────────────────────────────────────────\n\nexport interface TimeColumnsProps {\n value?: TimeParts\n onChange: (next: TimeParts) => void\n showSeconds?: boolean\n minuteStep?: TimeStep\n secondStep?: TimeStep\n /** 動態 disabled 各欄位 value 子集 */\n disabled?: TimeColumnsDisabled\n className?: string\n /** 是否在最左側加 border-l(配 DatePicker showTime / Range date+time 拼接時用) */\n leadingDivider?: boolean\n}\n\nexport function TimeColumns({\n value,\n onChange,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n disabled,\n className,\n leadingDivider = false,\n}: TimeColumnsProps) {\n const safeValue: TimeParts = value ?? { hours: 0, minutes: 0, seconds: 0 }\n const hourValues = React.useMemo(() => buildRange(24, 1), [])\n const minuteValues = React.useMemo(() => buildRange(60, minuteStep), [minuteStep])\n const secondValues = React.useMemo(() => buildRange(60, secondStep), [secondStep])\n\n const disabledSets = React.useMemo(() => ({\n hours: disabled?.hours ? new Set(disabled.hours) : undefined,\n minutes: disabled?.minutes ? new Set(disabled.minutes) : undefined,\n seconds: disabled?.seconds ? new Set(disabled.seconds) : undefined,\n }), [disabled?.hours, disabled?.minutes, disabled?.seconds])\n\n const widthClass = showSeconds ? 'w-60' : 'w-40'\n\n return (\n <div\n className={cn(\n 'flex flex-row',\n widthClass,\n leadingDivider && 'border-l border-divider',\n className,\n )}\n >\n <TimeColumn\n values={hourValues}\n selected={safeValue.hours}\n disabledSet={disabledSets.hours}\n label=\"hours\"\n onSelect={(h) => onChange({ ...safeValue, hours: h })}\n withDivider\n />\n <TimeColumn\n values={minuteValues}\n selected={safeValue.minutes}\n disabledSet={disabledSets.minutes}\n label=\"minutes\"\n onSelect={(m) => onChange({ ...safeValue, minutes: m })}\n withDivider={showSeconds}\n />\n {showSeconds && (\n <TimeColumn\n values={secondValues}\n selected={safeValue.seconds}\n disabledSet={disabledSets.seconds}\n label=\"seconds\"\n onSelect={(s) => onChange({ ...safeValue, seconds: s })}\n />\n )}\n </div>\n )\n}\n"],"names":[],"mappings":";;;;AA0CO,SAAS,eAAe,KAAuD;AACpF,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,YAAY,IAAI,MAAM,oCAAoC;AAChE,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,IAAI,OAAO,UAAU,CAAC,CAAC;AAC7B,QAAM,IAAI,OAAO,UAAU,CAAC,CAAC;AAC7B,QAAM,IAAI,UAAU,CAAC,MAAM,SAAY,OAAO,UAAU,CAAC,CAAC,IAAI;AAC9D,MACE,OAAO,MAAM,CAAC,KAAK,IAAI,KAAK,IAAI,MAChC,OAAO,MAAM,CAAC,KAAK,IAAI,KAAK,IAAI,MAChC,OAAO,MAAM,CAAC,KAAK,IAAI,KAAK,IAAI,GAChC,QAAO;AACT,SAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,EAAA;AAC1C;AAGO,SAAS,kBAAkB,OAAkB,cAAc,OAAe;AAC/E,QAAM,KAAK,OAAO,MAAM,KAAK,EAAE,SAAS,GAAG,GAAG;AAC9C,QAAM,KAAK,OAAO,MAAM,OAAO,EAAE,SAAS,GAAG,GAAG;AAChD,MAAI,CAAC,YAAa,QAAO,GAAG,EAAE,IAAI,EAAE;AACpC,QAAM,KAAK,OAAO,MAAM,OAAO,EAAE,SAAS,GAAG,GAAG;AAChD,SAAO,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE;AAC1B;AAIA,SAAS,WAAW,KAAa,MAAwB;AACvD,QAAM,MAAgB,CAAA;AACtB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK,KAAM,KAAI,KAAK,CAAC;AAC9C,SAAO;AACT;AAgBA,SAAS,WAAW,EAAE,QAAQ,UAAU,aAAa,OAAO,UAAU,eAAgC;AACpG,QAAM,UAAU,MAAM,OAAuB,IAAI;AAOjD,QAAM,gBAAgB,MAAM,OAAO,IAAI;AACvC,QAAM,UAAU,MAAM;AACpB,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM;AACX,UAAM,MAAM,OAAO,QAAQ,QAAQ;AACnC,QAAI,MAAM,EAAG;AACb,UAAM,OAAO,KAAK,SAAS,GAAG;AAC9B,QAAI,CAAC,KAAM;AACX,SAAK,eAAe,EAAE,OAAO,UAAU,UAAU,cAAc,UAAU,SAAS,UAAU;AAC5F,kBAAc,UAAU;AAAA,EAC1B,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAKrB,QAAM,SAAS,MAAM,MAAA;AAIrB,QAAM,gBAAgB,CAAC,MAA2C;AAChE,UAAM,MAAM,OAAO,QAAQ,QAAQ;AACnC,QAAI,EAAE,QAAQ,aAAa;AACzB,QAAE,eAAA;AACF,YAAM,OAAO,OAAO,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,EAAC,2CAAa,IAAI,OAAO,CAAC,GAAE,KAAK,OAAO,GAAG;AACzF,eAAS,IAAI;AAAA,IACf,WAAW,EAAE,QAAQ,WAAW;AAC9B,QAAE,eAAA;AAEF,UAAI,IAAI,MAAM;AACd,aAAO,KAAK,MAAK,2CAAa,IAAI,OAAO,CAAC,IAAI;AAC9C,UAAI,KAAK,EAAG,UAAS,OAAO,CAAC,CAAC;AAAA,IAChC,WAAW,EAAE,QAAQ,QAAQ;AAC3B,QAAE,eAAA;AACF,YAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAC,2CAAa,IAAI,GAAE;AACrD,UAAI,UAAU,OAAW,UAAS,KAAK;AAAA,IACzC,WAAW,EAAE,QAAQ,OAAO;AAC1B,QAAE,eAAA;AACF,eAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,YAAI,EAAC,2CAAa,IAAI,OAAO,CAAC,KAAI;AAChC,mBAAS,OAAO,CAAC,CAAC;AAClB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAQA,6BACG,YAAA,EAAW,WAAW,GAAG,iBAAiB,eAAe,yBAAyB,GACjF,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACL,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,yBAAuB,YAAY,OAAO,GAAG,MAAM,QAAQ,QAAQ,KAAK;AAAA,MACxE,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAU;AAAA,MAET,UAAA,OAAO,IAAI,CAAC,MAAM;AACjB,cAAM,aAAa,MAAM;AACzB,cAAM,cAAa,2CAAa,IAAI,OAAM;AAC1C,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,IAAI,GAAG,MAAM,QAAQ,CAAC;AAAA,YACtB,MAAK;AAAA,YACL,MAAK;AAAA,YACL,iBAAe;AAAA,YACf,UAAU;AAAA,YAGV,UAAU;AAAA,YACV,SAAS,MAAM,SAAS,CAAC;AAAA,YACzB,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,cAAc;AAAA,cACd,cAAc;AAAA,YAAA;AAAA,YAGf,UAAA,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AAAA,UAAA;AAAA,UAnBrB;AAAA,QAAA;AAAA,MAsBX,CAAC;AAAA,IAAA;AAAA,EAAA,GAEL;AAEJ;AAiBO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA,iBAAiB;AACnB,GAAqB;AACnB,QAAM,YAAuB,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,EAAA;AACvE,QAAM,aAAa,MAAM,QAAQ,MAAM,WAAW,IAAI,CAAC,GAAG,EAAE;AAC5D,QAAM,eAAe,MAAM,QAAQ,MAAM,WAAW,IAAI,UAAU,GAAG,CAAC,UAAU,CAAC;AACjF,QAAM,eAAe,MAAM,QAAQ,MAAM,WAAW,IAAI,UAAU,GAAG,CAAC,UAAU,CAAC;AAEjF,QAAM,eAAe,MAAM,QAAQ,OAAO;AAAA,IACxC,QAAS,qCAAU,SAAU,IAAI,IAAI,SAAS,KAAK,IAAM;AAAA,IACzD,UAAS,qCAAU,WAAU,IAAI,IAAI,SAAS,OAAO,IAAI;AAAA,IACzD,UAAS,qCAAU,WAAU,IAAI,IAAI,SAAS,OAAO,IAAI;AAAA,EAAA,IACvD,CAAC,qCAAU,OAAO,qCAAU,SAAS,qCAAU,OAAO,CAAC;AAE3D,QAAM,aAAa,cAAc,SAAS;AAE1C,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,kBAAkB;AAAA,QAClB;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,QAAQ;AAAA,YACR,UAAU,UAAU;AAAA,YACpB,aAAa,aAAa;AAAA,YAC1B,OAAM;AAAA,YACN,UAAU,CAAC,MAAM,SAAS,EAAE,GAAG,WAAW,OAAO,GAAG;AAAA,YACpD,aAAW;AAAA,UAAA;AAAA,QAAA;AAAA,QAEb;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,QAAQ;AAAA,YACR,UAAU,UAAU;AAAA,YACpB,aAAa,aAAa;AAAA,YAC1B,OAAM;AAAA,YACN,UAAU,CAAC,MAAM,SAAS,EAAE,GAAG,WAAW,SAAS,GAAG;AAAA,YACtD,aAAa;AAAA,UAAA;AAAA,QAAA;AAAA,QAEd,eACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,QAAQ;AAAA,YACR,UAAU,UAAU;AAAA,YACpB,aAAa,aAAa;AAAA,YAC1B,OAAM;AAAA,YACN,UAAU,CAAC,MAAM,SAAS,EAAE,GAAG,WAAW,SAAS,EAAA,CAAG;AAAA,UAAA;AAAA,QAAA;AAAA,MACxD;AAAA,IAAA;AAAA,EAAA;AAIR;"}
1
+ {"version":3,"file":"time-columns.js","sources":["../../../src/components/TimePicker/time-columns.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\n/**\n * TimeColumns — H/M/S scroll selector primitive(M17 Rule-of-3 SSOT)。\n *\n * 共用消費者:\n * - `TimePicker`(本元件家)\n * - `DatePicker showTime`(date + time)\n * - `DatePickerRange showTime`(range with time)\n *\n * 抽出原因:三個 picker 共用 H/M/S column scroll-pick 行為,公式重覆 = M17 違反。\n * 抽到 TimePicker/(time scroll selector 的 canonical 家)。\n *\n * ── 設計 ──\n * - Value:`TimeParts { hours, minutes, seconds }`(對齊 date-fns / Date getHours()...)\n * - Step:每欄獨立 `minuteStep` / `secondStep`(會議常用 15)\n * - Disabled:`disabledHours / disabledMinutes / disabledSeconds`(動態根據已選其他欄位)\n * - Visual:對齊 ref/timepicker.png — 多欄並排 + border-r 分隔\n */\n\nimport * as React from 'react'\nimport { ScrollArea } from '@/design-system/components/ScrollArea/scroll-area'\nimport { cn } from '@/lib/utils'\n\n// ── Types ───────────────────────────────────────────────────────────────\n\nexport interface TimeParts {\n hours: number\n minutes: number\n seconds: number\n}\n\nexport type TimeStep = 1 | 5 | 10 | 15 | 30\n\nexport interface TimeColumnsDisabled {\n hours?: number[]\n minutes?: number[]\n seconds?: number[]\n}\n\n// ── ISO time parsing ────────────────────────────────────────────────────\n\n/** Parse \"HH:MM:SS\" / \"HH:MM\" / full ISO datetime — returns time parts only */\nexport function isoToTimeParts(iso: string | null | undefined): TimeParts | undefined {\n if (!iso) return undefined\n const timeMatch = iso.match(/(\\d{1,2}):(\\d{1,2})(?::(\\d{1,2}))?/)\n if (!timeMatch) return undefined\n const h = Number(timeMatch[1])\n const m = Number(timeMatch[2])\n const s = timeMatch[3] !== undefined ? Number(timeMatch[3]) : 0\n if (\n Number.isNaN(h) || h < 0 || h > 23 ||\n Number.isNaN(m) || m < 0 || m > 59 ||\n Number.isNaN(s) || s < 0 || s > 59\n ) return undefined\n return { hours: h, minutes: m, seconds: s }\n}\n\n/** Format time parts → \"HH:MM\" or \"HH:MM:SS\" depending on showSeconds */\nexport function timePartsToString(parts: TimeParts, showSeconds = false): string {\n const hh = String(parts.hours).padStart(2, '0')\n const mm = String(parts.minutes).padStart(2, '0')\n if (!showSeconds) return `${hh}:${mm}`\n const ss = String(parts.seconds).padStart(2, '0')\n return `${hh}:${mm}:${ss}`\n}\n\n// ── Range builder ───────────────────────────────────────────────────────\n\nfunction buildRange(max: number, step: number): number[] {\n const arr: number[] = []\n for (let v = 0; v < max; v += step) arr.push(v)\n return arr\n}\n\n// ── Single column ───────────────────────────────────────────────────────\n\ninterface TimeColumnProps {\n values: number[]\n selected: number\n /** disabled value set(動態根據其他欄位推) */\n disabledSet?: Set<number>\n label: string\n onSelect: (value: number) => void\n /** 右側分隔線(對齊 ref 多欄樣式) */\n withDivider?: boolean\n}\n\n// code-quality-allow: long-function — column 含 scroll-into-view useEffect / WAI-ARIA listbox 鍵盤 handler / 視覺 state 計算,拆 sub-fn 會切散 listbox accessibility 邏輯\nfunction TimeColumn({ values, selected, disabledSet, label, onSelect, withDivider }: TimeColumnProps) {\n const listRef = React.useRef<HTMLDivElement>(null)\n\n // 開啟時跳到 selected 位置(置中);後續變更走 smooth(對齊 iOS / Material / Ant timepicker idiom)。\n // 用 scrollIntoView({ block: 'center' }) 自動找最近的 scrollable ancestor —\n // 比 manual scrollTop + parentElement 強健(Radix ScrollArea 結構為 Viewport > inner-div > content,\n // listRef.parentElement 不是真正 scrollable 元素)。\n // mount 用 'auto' 避免開啟瞬間出現飄移,後續 user 操作走 'smooth'(同 Tabs/Chip/FileViewer canonical)。\n const isFirstRunRef = React.useRef(true)\n React.useEffect(() => {\n const list = listRef.current\n if (!list) return\n const idx = values.indexOf(selected)\n if (idx < 0) return\n const item = list.children[idx] as HTMLElement | undefined\n if (!item) return\n item.scrollIntoView({ block: 'center', behavior: isFirstRunRef.current ? 'auto' : 'smooth' })\n isFirstRunRef.current = false\n }, [values, selected])\n\n // a11y:穩定 id 前綴,讓 listbox `aria-activedescendant` 指向目前 active(= selected)option。\n // single-tabstop + 方向鍵 listbox 應透過 activedescendant 告知 AT 哪個 option active,\n // 否則 SR 方向鍵移動時讀不出目前 option(WAI-ARIA listbox virtual-focus 慣例)。\n const baseId = React.useId()\n\n // WAI-ARIA listbox keyboard pattern:ArrowUp/Down 切 option / Home / End 跳邊界。\n // 對標 Ant TimePicker / Material TimePicker。Tab 跳離 listbox(走預設行為,不 stopPropagation)。\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const idx = values.indexOf(selected)\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n const next = values.find((_, i) => i > idx && !disabledSet?.has(values[i])) ?? values[idx]\n onSelect(next)\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n // 反向找第一個 enabled\n let i = idx - 1\n while (i >= 0 && disabledSet?.has(values[i])) i--\n if (i >= 0) onSelect(values[i])\n } else if (e.key === 'Home') {\n e.preventDefault()\n const first = values.find((v) => !disabledSet?.has(v))\n if (first !== undefined) onSelect(first)\n } else if (e.key === 'End') {\n e.preventDefault()\n for (let i = values.length - 1; i >= 0; i--) {\n if (!disabledSet?.has(values[i])) {\n onSelect(values[i])\n break\n }\n }\n }\n }\n\n // WAI-ARIA listbox pattern:role=listbox 直接包 role=option(button),不另用 li 包\n // (li role=option + 內含 button 會被 axe 抓 nested-interactive)\n // 高度策略:本 primitive 不鎖死 height(對齊 ScrollArea / Combobox 同 idiom)。\n // ScrollArea 用 h-full,parent flex container 控高 — 讓 consumer:\n // - TimePicker:wrap in h-[216px] container(預設 ~7 items)\n // - DatePicker showTime / Range:flex-row items-stretch + calendar 一起決定高度(自動同高)\n return (\n // viewportTabIndex=-1:焦點直接落 listbox(有 keydown),消除「Viewport+listbox 雙 tab stop\n // + 焦點落 Viewport 時 ↑/↓ 觸發原生捲動非選值」a11y bug(axe 由 listbox tabIndex=0 滿足)\n <ScrollArea viewportTabIndex={-1} className={cn('flex-1 h-full', withDivider && 'border-r border-divider')}>\n <div\n ref={listRef}\n role=\"listbox\"\n aria-label={label}\n // guard values.includes:off-step value(如 minuteStep=15 但分=07)的 option 未渲染,\n // 指向未渲染 id = 懸空 ARIA 參照(AT 讀空);僅真實渲染的 option 才掛\n aria-activedescendant={selected != null && values.includes(selected) ? `${baseId}-opt-${selected}` : undefined}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n className=\"flex flex-col py-2 focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-[-2px]\"\n >\n {values.map((v) => {\n const isSelected = v === selected\n const isDisabled = disabledSet?.has(v) ?? false\n return (\n <button\n key={v}\n id={`${baseId}-opt-${v}`}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={isDisabled}\n // tabIndex=-1:listbox 自身 tabbable + 用 ArrowUp/Down 切 option(WAI-ARIA roving),\n // 不讓每個 option 都進 Tab order(會 Tab 84 次過完 hours+minutes)\n tabIndex={-1}\n onClick={() => onSelect(v)}\n className={cn(\n 'w-full h-field-sm text-body tabular-nums',\n 'flex items-center justify-center',\n 'cursor-pointer transition-colors',\n 'hover:bg-neutral-hover',\n isSelected && 'bg-neutral-selected text-foreground hover:bg-neutral-selected',\n isDisabled && 'text-fg-disabled cursor-not-allowed hover:bg-transparent',\n )}\n >\n {String(v).padStart(2, '0')}\n </button>\n )\n })}\n </div>\n </ScrollArea>\n )\n}\n\n// ── Composite — H/M/S columns ──────────────────────────────────────────\n\nexport interface TimeColumnsProps {\n value?: TimeParts\n onChange: (next: TimeParts) => void\n showSeconds?: boolean\n minuteStep?: TimeStep\n secondStep?: TimeStep\n /** 動態 disabled 各欄位 value 子集 */\n disabled?: TimeColumnsDisabled\n className?: string\n /** 是否在最左側加 border-l(配 DatePicker showTime / Range date+time 拼接時用) */\n leadingDivider?: boolean\n}\n\nexport function TimeColumns({\n value,\n onChange,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n disabled,\n className,\n leadingDivider = false,\n}: TimeColumnsProps) {\n const safeValue: TimeParts = value ?? { hours: 0, minutes: 0, seconds: 0 }\n const hourValues = React.useMemo(() => buildRange(24, 1), [])\n const minuteValues = React.useMemo(() => buildRange(60, minuteStep), [minuteStep])\n const secondValues = React.useMemo(() => buildRange(60, secondStep), [secondStep])\n\n const disabledSets = React.useMemo(() => ({\n hours: disabled?.hours ? new Set(disabled.hours) : undefined,\n minutes: disabled?.minutes ? new Set(disabled.minutes) : undefined,\n seconds: disabled?.seconds ? new Set(disabled.seconds) : undefined,\n }), [disabled?.hours, disabled?.minutes, disabled?.seconds])\n\n const widthClass = showSeconds ? 'w-60' : 'w-40'\n\n return (\n <div\n className={cn(\n 'flex flex-row',\n widthClass,\n leadingDivider && 'border-l border-divider',\n className,\n )}\n >\n <TimeColumn\n values={hourValues}\n selected={safeValue.hours}\n disabledSet={disabledSets.hours}\n label=\"hours\"\n onSelect={(h) => onChange({ ...safeValue, hours: h })}\n withDivider\n />\n <TimeColumn\n values={minuteValues}\n selected={safeValue.minutes}\n disabledSet={disabledSets.minutes}\n label=\"minutes\"\n onSelect={(m) => onChange({ ...safeValue, minutes: m })}\n withDivider={showSeconds}\n />\n {showSeconds && (\n <TimeColumn\n values={secondValues}\n selected={safeValue.seconds}\n disabledSet={disabledSets.seconds}\n label=\"seconds\"\n onSelect={(s) => onChange({ ...safeValue, seconds: s })}\n />\n )}\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.d.ts","sourceRoot":"","sources":["../../../src/components/TimePicker/time-picker.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,8CAA8C,CAAA;AAY3F,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,QAAQ,EAEd,MAAM,oDAAoD,CAAA;AAG3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AASH,MAAM,WAAW,iBAAiB;IAChC,4FAA4F;IAC5F,aAAa,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAA;IAC1C,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAqCD,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAID,MAAM,WAAW,eACf,SAAQ,iBAAiB,EACvB,IAAI,CACF,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EACpC,UAAU,GAAG,aAAa,CAC3B;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,yBAAyB;IACzB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,qCAAqC;IACrC,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,qCAAqC;IACrC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,kBAAkB,CAAA;IACvD;;;OAGG;IACH,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;IAC3B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,mFAAmF;IACnF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAGD,QAAA,MAAM,UAAU,wFAiRf,CAAA;AAKD,eAAO,MAAM,cAAc;;;;;;;;;;;CAkBjB,CAAA;AAEV,OAAO,EAAE,UAAU,EAAE,CAAA"}
1
+ {"version":3,"file":"time-picker.d.ts","sourceRoot":"","sources":["../../../src/components/TimePicker/time-picker.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAwB,MAAM,8CAA8C,CAAA;AAWjH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,QAAQ,EAEd,MAAM,oDAAoD,CAAA;AAG3D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AASH,MAAM,WAAW,iBAAiB;IAChC,4FAA4F;IAC5F,aAAa,CAAC,EAAE,IAAI,CAAC,qBAAqB,CAAA;IAC1C,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAqCD,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAID,MAAM,WAAW,eACf,SAAQ,iBAAiB,EACvB,IAAI,CACF,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EACpC,UAAU,GAAG,aAAa,CAC3B;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,uFAAuF;IACvF,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,yBAAyB;IACzB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,qCAAqC;IACrC,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,qCAAqC;IACrC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,kBAAkB,CAAA;IACvD;;;OAGG;IACH,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;IAC3B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,mFAAmF;IACnF,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAGD,QAAA,MAAM,UAAU,wFAwRf,CAAA;AAKD,eAAO,MAAM,cAAc;;;;;;;;;;;CAkBjB,CAAA;AAEV,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -2,10 +2,10 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { Clock, X } from "lucide-react";
4
4
  import { cn } from "../../lib/utils.js";
5
- import { EMPTY_DISPLAY, fieldDisplayTextClass, bareInputStyles, fieldWrapperStyles } from "../Field/field-wrapper.js";
5
+ import { fieldDisplayTextClass, bareInputStyles, fieldWrapperStyles } from "../Field/field-wrapper.js";
6
6
  import { ICON_SIZE, ItemSuffix, ItemInlineAction } from "../../patterns/element-anatomy/item-anatomy.js";
7
7
  import { Popover, PopoverTrigger, PopoverContent } from "../Popover/popover.js";
8
- import { useFieldContext, useResolvedFieldSize, useResolvedFieldInvalid, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant } from "../Field/field-context.js";
8
+ import { useFieldContext, useResolvedFieldSize, useResolvedFieldInvalid, useResolvedFieldDisabled, useResolvedFieldMode, useFieldEmptyDisplay, useResolvedFieldVariant, fieldEmptyColorClass } from "../Field/field-context.js";
9
9
  import { Button } from "../Button/button.js";
10
10
  import { SurfaceFooter } from "../../patterns/overlay-surface/overlay-surface.js";
11
11
  import { isoToTimeParts, TimeColumns, timePartsToString } from "./time-columns.js";
@@ -62,6 +62,7 @@ const TimePicker = React.forwardRef(
62
62
  const error = useResolvedFieldInvalid(errorProp);
63
63
  const disabled = useResolvedFieldDisabled(disabledProp);
64
64
  const resolvedMode = useResolvedFieldMode({ mode, disabled });
65
+ const emptyDisplay = useFieldEmptyDisplay();
65
66
  const variant = useResolvedFieldVariant(variantProp);
66
67
  const isEditable = resolvedMode === "edit";
67
68
  const iconSize = ICON_SIZE[size];
@@ -112,7 +113,7 @@ const TimePicker = React.forwardRef(
112
113
  };
113
114
  if (resolvedMode === "display") {
114
115
  if (!showDisplayEndIcon) {
115
- if (!value) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(size), "text-fg-muted", className), children: EMPTY_DISPLAY });
116
+ if (!value) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className), children: emptyDisplay });
116
117
  return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(size), "truncate", className), children: formatTime(value, { formatOptions, locale }) });
117
118
  }
118
119
  return /* @__PURE__ */ jsxs(
@@ -121,7 +122,7 @@ const TimePicker = React.forwardRef(
121
122
  className: cn(fieldWrapperStyles({ mode: "display", variant, size }), className),
122
123
  "data-field-mode": "display",
123
124
  children: [
124
- /* @__PURE__ */ jsx("span", { className: cn(bareInputStyles, "flex-1 min-w-0 truncate", !value && "text-fg-muted"), children: value ? formatTime(value, { formatOptions, locale }) : EMPTY_DISPLAY }),
125
+ /* @__PURE__ */ jsx("span", { className: cn(bareInputStyles, "flex-1 min-w-0 truncate", !value && fieldEmptyColorClass(resolvedMode)), children: value ? formatTime(value, { formatOptions, locale }) : emptyDisplay }),
125
126
  EndIconCmp && /* @__PURE__ */ jsx(ItemSuffix, { className: "pointer-events-none", children: /* @__PURE__ */ jsx(EndIconCmp, { size: iconSize, className: "text-fg-muted", "aria-hidden": true }) })
126
127
  ]
127
128
  }
@@ -143,7 +144,7 @@ const TimePicker = React.forwardRef(
143
144
  "flex-1 min-w-0",
144
145
  resolvedMode === "disabled" && "text-fg-disabled"
145
146
  ),
146
- children: value ? formatTime(value, { formatOptions, locale }) : /* @__PURE__ */ jsx("span", { className: "text-fg-muted", children: EMPTY_DISPLAY })
147
+ children: value ? formatTime(value, { formatOptions, locale }) : /* @__PURE__ */ jsx("span", { className: fieldEmptyColorClass(resolvedMode), children: emptyDisplay })
147
148
  }
148
149
  ),
149
150
  EndIconCmp && (variant === "naked" ? showDisplayEndIcon : resolvedMode === "disabled") && /* @__PURE__ */ jsx(ItemSuffix, { className: "pointer-events-none", children: /* @__PURE__ */ jsx(
@@ -177,6 +178,7 @@ const TimePicker = React.forwardRef(
177
178
  "aria-expanded": open,
178
179
  onKeyDown: (e) => {
179
180
  if (disabled) return;
181
+ if (e.target !== e.currentTarget) return;
180
182
  if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
181
183
  e.preventDefault();
182
184
  setOpen(true);
@@ -213,7 +215,7 @@ const TimePicker = React.forwardRef(
213
215
  ]
214
216
  }
215
217
  ) }),
216
- /* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-[216px]", children: [
218
+ /* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-0", align: "start", "aria-label": "選擇時間", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-[216px]", children: [
217
219
  /* @__PURE__ */ jsx(
218
220
  TimeColumns,
219
221
  {
@@ -1 +1 @@
1
- {"version":3,"file":"time-picker.js","sources":["../../../src/components/TimePicker/time-picker.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from 'react'\nimport { X, Clock } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'\nimport {\n fieldWrapperStyles,\n bareInputStyles,\n EMPTY_DISPLAY,\n fieldDisplayTextClass,\n} from '@/design-system/components/Field/field-wrapper'\nimport { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { Popover, PopoverTrigger, PopoverContent } from '@/design-system/components/Popover/popover'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'\nimport { Button } from '@/design-system/components/Button/button'\nimport { SurfaceFooter } from '@/design-system/patterns/overlay-surface/overlay-surface'\nimport {\n TimeColumns,\n isoToTimeParts,\n timePartsToString,\n type TimeParts,\n type TimeStep,\n type TimeColumnsDisabled,\n} from '@/design-system/components/TimePicker/time-columns'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * TimePicker — 單一時間(時/分/秒)輸入與顯示元件\n *\n * ── 定位(同 DatePicker 家族)──\n * Value 以 ISO time string 儲存(\"HH:mm\" 或 \"HH:mm:ss\"),local-time 語義(不帶時區)。\n * Edit 用本 DS 自建 time column panel + Popover 呈現,視覺與 DatePicker 一致。\n * Display 用 Intl.DateTimeFormat 格式化(跨 locale / 12h-24h 統一經過此 API)。\n *\n * ── Layout Family ──\n * CLAUDE.md 4-Family Model Family 4(Field control layout)消費者。結構繼承\n * `fieldWrapperStyles + [<editable>] [endIcon=Clock]`,視覺對齊 DatePicker(同\n * 「點擊觸發浮層」role:indicator 在 suffix slot,對齊 Material `endAdornment` /\n * Ant DatePicker / Polaris Picker 共識)。\n *\n * ── 實作基礎 ──\n * Trigger:`<div role=\"combobox\">` + `fieldWrapperStyles`(視覺仍是 Input wrapper,改為可點擊觸發浮層;\n * 2026-04-25 由 `<button>` 改 div 避 nested-interactive,對齊 Select / Combobox,鍵盤靠顯式 onKeyDown)\n * Popup:`Popover`(消費 overlay-surface pattern)\n * Panel 主體:自建 column picker(三欄 scrollable list),不引入第三方 time library\n *\n * ── 共用規則 ──\n * Mode / size / disabled / error 等詳見 `../Field/field-controls.spec.md`。\n */\n\n// ── Time ISO <-> parts conversion ───────────────────────────────────────────\n// Value 用 ISO time string(HH:mm 或 HH:mm:ss),local-time 語義(不帶時區/日期)。\n// 跟 DatePicker 的 ISO date string 策略一致。\n// `isoToTimeParts` / `timePartsToString` 改 import from time-columns(M17 SSOT)。\n\n// ── Display formatting ──────────────────────────────────────────────────────\n\nexport interface TimeFormatOptions {\n /** Intl.DateTimeFormat options(預設 { hour: '2-digit', minute: '2-digit', hour12: false }) */\n formatOptions?: Intl.DateTimeFormatOptions\n /** locale(預設 'en-US') */\n locale?: string\n}\n\n// 2026-07-06 D3 perf:Intl.DateTimeFormat 是標準庫最貴 constructor 之一(locale data\n// resolution)。formatTime 無 cheap path,display/readonly/edit 每 render 必建;DataTable time 欄\n// 轉發 config 時每 cell × 每次表格 re-render 重建。module-level cache 重用實例(DateTimeFormat\n// 無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。\nconst dtfCache = new Map<string, Intl.DateTimeFormat>()\nfunction getDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + '|' + JSON.stringify(options)\n let fmt = dtfCache.get(key)\n if (!fmt) {\n fmt = new Intl.DateTimeFormat(locale, options)\n dtfCache.set(key, fmt)\n }\n return fmt\n}\n\nfunction formatTime(\n iso: string,\n options: TimeFormatOptions = {},\n): string {\n const parts = isoToTimeParts(iso)\n if (!parts) return iso\n const {\n formatOptions = { hour: '2-digit', minute: '2-digit', hour12: false },\n locale = 'en-US',\n } = options\n // 借用 Date 讓 Intl.DateTimeFormat 處理 locale / 12h-24h\n const d = new Date()\n d.setHours(parts.hours, parts.minutes, parts.seconds, 0)\n return getDateTimeFormatter(locale, formatOptions).format(d)\n}\n\n// ── Disabled time callback ──────────────────────────────────────────────────\n// `Step` / `buildRange` / `TimeColumn`(內部欄位實作)拔掉,改 import `TimeColumns` primitive。\n\n// code-quality-allow: dead-export — public API surface — consumer-exposed for future use\nexport interface DisabledTimeResult {\n disabledHours?: number[]\n disabledMinutes?: number[]\n disabledSeconds?: number[]\n}\n\n// ── Component props ─────────────────────────────────────────────────────────\n\nexport interface TimePickerProps\n extends TimeFormatOptions,\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'placeholder'\n > {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** ISO time string(\"HH:mm\" 或 \"HH:mm:ss\") */\n value?: string | null\n onChange?: (value: string) => void\n placeholder?: string\n className?: string\n disabled?: boolean\n /** 允許清空已選值 */\n clearable?: boolean\n /**\n * 是否顯示秒欄(三欄 picker)。預設 false(兩欄:時/分)。\n * format 自動對應:false → \"HH:mm\",true → \"HH:mm:ss\"。\n */\n showSeconds?: boolean\n /** 分鐘步進(會議常用 15)。預設 1 */\n minuteStep?: TimeStep\n /** 秒步進。預設 1。僅 showSeconds=true 有效 */\n secondStep?: TimeStep\n /** 動態 disabled 某些時/分/秒(基於已選其他欄位)。 */\n disabledTime?: (parts: TimeParts) => DisabledTimeResult\n /**\n * Suffix indicator(2026-05-05 v9 canonical fix):「點擊觸發浮層」indicator 一律 suffix\n * (對齊 DatePicker calendar / Material endAdornment)。預設 Clock,傳 null 可關閉。\n */\n endIcon?: LucideIcon | null\n /**\n * Display 是否渲 endIcon + Field naked wrapper(D-path opt-in,2026-05-08)\n * — DataTable cell display↔edit 像素級對齊用。預設 false(裸 span,backward compat)。\n * 設 true 時 display 也走 fieldWrapperStyles(naked variant)+ ItemSuffix Clock,\n * 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。\n */\n showDisplayEndIcon?: boolean\n /** Initial open state(uncontrolled)— DataTable cell-as-input 1-step open canonical */\n defaultOpen?: boolean\n /** open state 變更 callback。DataTable cell-as-input 用:open=false → cell exit edit */\n onOpenChange?: (open: boolean) => void\n}\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(\n (\n {\n mode,\n variant: variantProp,\n error: errorProp = false,\n size: sizeProp,\n value,\n onChange,\n placeholder,\n className,\n disabled: disabledProp,\n clearable = false,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n disabledTime,\n endIcon,\n showDisplayEndIcon = false,\n formatOptions,\n locale,\n defaultOpen = false,\n onOpenChange,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref,\n ) => {\n const fieldCtx = useFieldContext()\n const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode=\"display\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const variant: FieldVariant = useResolvedFieldVariant(variantProp)\n const isEditable = resolvedMode === 'edit'\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const EndIconCmp: LucideIcon | null =\n endIcon === null ? null : (endIcon ?? Clock)\n const defaultPlaceholder = showSeconds ? 'HH:MM:SS' : 'HH:MM'\n const resolvedPlaceholder = placeholder ?? defaultPlaceholder\n const showClear = clearable && !!value && isEditable\n const [open, setOpenState] = React.useState(defaultOpen)\n const setOpen = React.useCallback((next: boolean) => { setOpenState(next); onOpenChange?.(next) }, [onOpenChange])\n\n const currentParts = React.useMemo(() => isoToTimeParts(value), [value])\n // draft 僅在 panel 開啟時用來處理 commit(OK button)的暫存\n const [draft, setDraft] = React.useState<TimeParts>(\n () => currentParts ?? { hours: 0, minutes: 0, seconds: 0 },\n )\n\n // 每次 popover 開啟時以當前 value 初始化 draft\n React.useEffect(() => {\n if (open) {\n setDraft(currentParts ?? { hours: 0, minutes: 0, seconds: 0 })\n }\n }, [open, currentParts])\n\n const disabledForColumns: TimeColumnsDisabled | undefined = React.useMemo(() => {\n if (!disabledTime) return undefined\n const res = disabledTime(draft)\n return {\n hours: res.disabledHours,\n minutes: res.disabledMinutes,\n seconds: res.disabledSeconds,\n }\n }, [disabledTime, draft])\n\n const commitDraft = (next: TimeParts) => {\n setDraft(next)\n onChange?.(timePartsToString(next, showSeconds))\n }\n\n const handleNow = () => {\n const now = new Date()\n // 按照 minuteStep / secondStep 對齊。\n // 2026-07-05 邊界修正(deep-audit A.1b):Math.round 進位到 60 時,舊 `Math.min(round, 59)`\n // 會產生 59 這種「不對齊 step、也不在 column values 內」的值(如 53 分 @ step=15 → 59)\n // → panel 該欄失去 selected 高亮。改 clamp 到 ≤59 的最大 step 對齊值(= buildRange 產出的\n // 最後一格,如 45),不跨小時進位 — 保持「此刻 = commit 當前時間(step 對齊)」語意\n // (time-picker.spec.md「每次欄位選取當下即 commit」段)。\n const alignToStep = (raw: number, step: number) => {\n const rounded = Math.round(raw / step) * step\n return rounded > 59 ? Math.floor(59 / step) * step : rounded\n }\n const next: TimeParts = {\n hours: now.getHours(),\n minutes: alignToStep(now.getMinutes(), minuteStep),\n seconds: showSeconds ? alignToStep(now.getSeconds(), secondStep) : 0,\n }\n commitDraft(next)\n setOpen(false)\n }\n\n // mode='display'(Phase B2 2026-05-05):純內容輸出 — 對齊原 TimePickerDisplay sub-component(retired)。\n // Default(showDisplayEndIcon=false):無 Field wrapper / 無 Clock icon — backward compat 裸 span。\n // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix Clock,\n // 與 edit 同結構消除 cell display↔edit 像素偏移(Layer-B padding mismatch)。\n if (resolvedMode === 'display') {\n if (!showDisplayEndIcon) {\n // 2026-05-14 I2 fix(spec contract (e) display typography canonical):bare span 套\n // `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)— 對齊 Field family 統一。\n if (!value) return <span className={cn(fieldDisplayTextClass(size), 'text-fg-muted', className)}>{EMPTY_DISPLAY}</span>\n return <span className={cn(fieldDisplayTextClass(size), 'truncate', className)}>{formatTime(value, { formatOptions, locale })}</span>\n }\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: 'display', variant, size }), className)}\n data-field-mode=\"display\"\n >\n <span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value && 'text-fg-muted')}>\n {value ? formatTime(value, { formatOptions, locale }) : EMPTY_DISPLAY}\n </span>\n {EndIconCmp && (\n <ItemSuffix className=\"pointer-events-none\">\n <EndIconCmp size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n // readonly / disabled\n if (!isEditable) {\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, size }), className)}\n data-field-mode={resolvedMode}\n aria-disabled={resolvedMode === 'disabled' ? true : undefined}\n {...(props as React.HTMLAttributes<HTMLDivElement>)}\n >\n <span\n className={cn(\n 'flex-1 min-w-0',\n resolvedMode === 'disabled' && 'text-fg-disabled',\n )}\n >\n {value\n ? formatTime(value, { formatOptions, locale })\n : <span className=\"text-fg-muted\">{EMPTY_DISPLAY}</span>\n }\n </span>\n {/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示 / disabled 保留(fg-disabled);naked cell 依 showDisplayEndIcon=isEditable(修 disabled cell 漏顯 bug)*/}\n {EndIconCmp && (variant === 'naked' ? showDisplayEndIcon : resolvedMode === 'disabled') && (\n <ItemSuffix className=\"pointer-events-none\">\n <EndIconCmp\n size={iconSize}\n className={resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'}\n aria-hidden\n />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n const displayText = value\n ? formatTime(value, { formatOptions, locale })\n : <span className=\"text-fg-muted\">{resolvedPlaceholder}</span>\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n {/* a11y(2026-04-25 nested-interactive fix):trigger 改 <div role='combobox'>\n (對齊 Select / Combobox 同 pattern),原 <button> 會與內層 ItemInlineAction\n 清除 button 構成 nested-interactive。\n 2026-06-01 鍵盤開啟修正:Radix PopoverTrigger 只 compose onClick\n (@radix-ui/react-popover index.js:145),不 inject 任何 onKeyDown。原生 <button>\n 靠瀏覽器在 Enter/Space 自動派發 click 才能開;但本 trigger 是 <div role=combobox>,\n div 不會自動派發 click → 鍵盤使用者打不開 panel。故顯式加 onKeyDown\n (對齊 select.tsx:609-619 既有 canonical + WAI-ARIA APG combobox required keys)。 */}\n <PopoverTrigger asChild>\n <div\n ref={ref}\n id={idProp ?? fieldCtx?.id}\n role=\"combobox\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n aria-labelledby={fieldCtx?.labelId}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onKeyDown={(e) => {\n if (disabled) return\n // Enter / Space / ArrowDown / Alt+ArrowDown → 開 panel(APG combobox required + Select canonical)\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault()\n setOpen(true)\n }\n // Escape → 關(Radix Content 已自帶,trigger 補位對齊 select.tsx:597)\n if (e.key === 'Escape') setOpen(false)\n }}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }),\n 'text-left cursor-pointer',\n 'focus-visible:outline-none',\n className,\n )}\n {...props}\n >\n <span className={cn(bareInputStyles, 'truncate', !value && 'text-fg-muted')}>\n {displayText}\n </span>\n {showClear && (\n <ItemInlineAction\n size={size ?? 'md'}\n action={{\n icon: X,\n label: '清除時間', // i18n-allow: DS default inline-action label\n onClick: (e) => {\n e?.stopPropagation()\n onChange?.('')\n },\n }}\n />\n )}\n {EndIconCmp && (\n <ItemSuffix className=\"pointer-events-none\">\n <EndIconCmp size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n )}\n </div>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n {/* Panel 對齊 ref/timepicker.png:2-3 個 SelectMenu 式欄位並排,分隔線分開。\n Width 依欄數由 TimeColumns 決定:2 欄 w-40 / 3 欄 w-60。\n Height 由 wrapper 控:216px 預設(扣 footer 後 list 約可見 5-6 items)。\n TimeColumns 本身 h-full,parent 控 height — 讓 DatePicker showTime / Range 可\n 用 flex-row items-stretch 自動同 calendar 高。 */}\n <div className=\"flex flex-col h-[216px]\">\n <TimeColumns\n value={draft}\n onChange={commitDraft}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n disabled={disabledForColumns}\n // 2026-05-06 v9.1 M25 chain fix:TimeColumns 自然高 = 24 buttons × ~28.7px = 688px\n // 會撐破 parent h-[216px]。flex-1 + min-h-0 讓 TimeColumns 取 parent 剩餘空間\n // (216 − footer ≈53px ≈ 163px)→ ScrollArea h-full 才能正確收斂 →\n // listbox scrollIntoView 找對 nearest scrollable ancestor(內部 viewport),\n // 不會走到 document body 把 popover 內容推出畫面(user 報「hours 欄空白」根因)。\n className=\"flex-1 min-h-0\"\n />\n {/* Footer:消費 SurfaceFooter SSOT(border-t + py-tight + gap-2 + shrink-0 + justify-end)。\n px override 回 layout-space-tight,因 TimePicker 滿欄 column 面板無 chrome-padded body\n 內縮邊可對齊(footer px = body 內縮 原則;column-selector 數字置中、零內距,\n 見 time-picker.spec.md)。此刻 mr-auto 把確定推右,視覺等同原 justify-between\n —— 純結構消費 overlay-surface canonical(overlay-surface.spec.md:17「不自寫 padding token」),零視覺變化。 */}\n <SurfaceFooter className=\"px-[var(--layout-space-tight)]\">\n <Button variant=\"text\" size=\"sm\" onClick={handleNow} className=\"mr-auto\">\n 此刻\n </Button>\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={() => setOpen(false)}\n >\n 確定\n </Button>\n </SurfaceFooter>\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nTimePicker.displayName = 'TimePicker'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const timePickerMeta = {\n component: 'TimePicker',\n family: 4,\n variants: {\n\n },\n sizes: {\n\n },\n // states 對齊真實視覺態:trigger 與 panel item 皆無 'active'(按下)專屬視覺態(2026-07-04 audit 對齊);\n // 'selected' = panel item 持續選中(bg-neutral-selected,time-columns.tsx isSelected;2026-07-07 詞彙統一補列)。\n states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n // panel selected 走 bg-neutral-selected(time-columns.tsx isSelected;spec「欄內 item 狀態」),非 bg-primary。\n bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-transparent'],\n fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],\n ring: [],\n },\n} as const\n\nexport { TimePicker }\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"time-picker.js","sources":["../../../src/components/TimePicker/time-picker.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from 'react'\nimport { X, Clock } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'\nimport {\n fieldWrapperStyles,\n bareInputStyles,\n fieldDisplayTextClass,\n} from '@/design-system/components/Field/field-wrapper'\nimport { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { Popover, PopoverTrigger, PopoverContent } from '@/design-system/components/Popover/popover'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\nimport { Button } from '@/design-system/components/Button/button'\nimport { SurfaceFooter } from '@/design-system/patterns/overlay-surface/overlay-surface'\nimport {\n TimeColumns,\n isoToTimeParts,\n timePartsToString,\n type TimeParts,\n type TimeStep,\n type TimeColumnsDisabled,\n} from '@/design-system/components/TimePicker/time-columns'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * TimePicker — 單一時間(時/分/秒)輸入與顯示元件\n *\n * ── 定位(同 DatePicker 家族)──\n * Value 以 ISO time string 儲存(\"HH:mm\" 或 \"HH:mm:ss\"),local-time 語義(不帶時區)。\n * Edit 用本 DS 自建 time column panel + Popover 呈現,視覺與 DatePicker 一致。\n * Display 用 Intl.DateTimeFormat 格式化(跨 locale / 12h-24h 統一經過此 API)。\n *\n * ── Layout Family ──\n * CLAUDE.md 4-Family Model Family 4(Field control layout)消費者。結構繼承\n * `fieldWrapperStyles + [<editable>] [endIcon=Clock]`,視覺對齊 DatePicker(同\n * 「點擊觸發浮層」role:indicator 在 suffix slot,對齊 Material `endAdornment` /\n * Ant DatePicker / Polaris Picker 共識)。\n *\n * ── 實作基礎 ──\n * Trigger:`<div role=\"combobox\">` + `fieldWrapperStyles`(視覺仍是 Input wrapper,改為可點擊觸發浮層;\n * 2026-04-25 由 `<button>` 改 div 避 nested-interactive,對齊 Select / Combobox,鍵盤靠顯式 onKeyDown)\n * Popup:`Popover`(消費 overlay-surface pattern)\n * Panel 主體:自建 column picker(三欄 scrollable list),不引入第三方 time library\n *\n * ── 共用規則 ──\n * Mode / size / disabled / error 等詳見 `../Field/field-controls.spec.md`。\n */\n\n// ── Time ISO <-> parts conversion ───────────────────────────────────────────\n// Value 用 ISO time string(HH:mm 或 HH:mm:ss),local-time 語義(不帶時區/日期)。\n// 跟 DatePicker 的 ISO date string 策略一致。\n// `isoToTimeParts` / `timePartsToString` 改 import from time-columns(M17 SSOT)。\n\n// ── Display formatting ──────────────────────────────────────────────────────\n\nexport interface TimeFormatOptions {\n /** Intl.DateTimeFormat options(預設 { hour: '2-digit', minute: '2-digit', hour12: false }) */\n formatOptions?: Intl.DateTimeFormatOptions\n /** locale(預設 'en-US') */\n locale?: string\n}\n\n// 2026-07-06 D3 perf:Intl.DateTimeFormat 是標準庫最貴 constructor 之一(locale data\n// resolution)。formatTime 無 cheap path,display/readonly/edit 每 render 必建;DataTable time 欄\n// 轉發 config 時每 cell × 每次表格 re-render 重建。module-level cache 重用實例(DateTimeFormat\n// 無狀態可安全重用),key = locale|JSON.stringify(options)。行為 Δ=0。\nconst dtfCache = new Map<string, Intl.DateTimeFormat>()\nfunction getDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n const key = locale + '|' + JSON.stringify(options)\n let fmt = dtfCache.get(key)\n if (!fmt) {\n fmt = new Intl.DateTimeFormat(locale, options)\n dtfCache.set(key, fmt)\n }\n return fmt\n}\n\nfunction formatTime(\n iso: string,\n options: TimeFormatOptions = {},\n): string {\n const parts = isoToTimeParts(iso)\n if (!parts) return iso\n const {\n formatOptions = { hour: '2-digit', minute: '2-digit', hour12: false },\n locale = 'en-US',\n } = options\n // 借用 Date 讓 Intl.DateTimeFormat 處理 locale / 12h-24h\n const d = new Date()\n d.setHours(parts.hours, parts.minutes, parts.seconds, 0)\n return getDateTimeFormatter(locale, formatOptions).format(d)\n}\n\n// ── Disabled time callback ──────────────────────────────────────────────────\n// `Step` / `buildRange` / `TimeColumn`(內部欄位實作)拔掉,改 import `TimeColumns` primitive。\n\n// code-quality-allow: dead-export — public API surface — consumer-exposed for future use\nexport interface DisabledTimeResult {\n disabledHours?: number[]\n disabledMinutes?: number[]\n disabledSeconds?: number[]\n}\n\n// ── Component props ─────────────────────────────────────────────────────────\n\nexport interface TimePickerProps\n extends TimeFormatOptions,\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange' | 'placeholder'\n > {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** ISO time string(\"HH:mm\" 或 \"HH:mm:ss\") */\n value?: string | null\n onChange?: (value: string) => void\n placeholder?: string\n className?: string\n disabled?: boolean\n /** 允許清空已選值 */\n clearable?: boolean\n /**\n * 是否顯示秒欄(三欄 picker)。預設 false(兩欄:時/分)。\n * format 自動對應:false → \"HH:mm\",true → \"HH:mm:ss\"。\n */\n showSeconds?: boolean\n /** 分鐘步進(會議常用 15)。預設 1 */\n minuteStep?: TimeStep\n /** 秒步進。預設 1。僅 showSeconds=true 有效 */\n secondStep?: TimeStep\n /** 動態 disabled 某些時/分/秒(基於已選其他欄位)。 */\n disabledTime?: (parts: TimeParts) => DisabledTimeResult\n /**\n * Suffix indicator(2026-05-05 v9 canonical fix):「點擊觸發浮層」indicator 一律 suffix\n * (對齊 DatePicker calendar / Material endAdornment)。預設 Clock,傳 null 可關閉。\n */\n endIcon?: LucideIcon | null\n /**\n * Display 是否渲 endIcon + Field naked wrapper(D-path opt-in,2026-05-08)\n * — DataTable cell display↔edit 像素級對齊用。預設 false(裸 span,backward compat)。\n * 設 true 時 display 也走 fieldWrapperStyles(naked variant)+ ItemSuffix Clock,\n * 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。\n */\n showDisplayEndIcon?: boolean\n /** Initial open state(uncontrolled)— DataTable cell-as-input 1-step open canonical */\n defaultOpen?: boolean\n /** open state 變更 callback。DataTable cell-as-input 用:open=false → cell exit edit */\n onOpenChange?: (open: boolean) => void\n}\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(\n (\n {\n mode,\n variant: variantProp,\n error: errorProp = false,\n size: sizeProp,\n value,\n onChange,\n placeholder,\n className,\n disabled: disabledProp,\n clearable = false,\n showSeconds = false,\n minuteStep = 1,\n secondStep = 1,\n disabledTime,\n endIcon,\n showDisplayEndIcon = false,\n formatOptions,\n locale,\n defaultOpen = false,\n onOpenChange,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref,\n ) => {\n const fieldCtx = useFieldContext()\n const size = useResolvedFieldSize(sizeProp) // B 組 cascade fix\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode=\"display\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const emptyDisplay = useFieldEmptyDisplay()\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const isEditable = resolvedMode === 'edit'\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const EndIconCmp: LucideIcon | null =\n endIcon === null ? null : (endIcon ?? Clock)\n const defaultPlaceholder = showSeconds ? 'HH:MM:SS' : 'HH:MM'\n const resolvedPlaceholder = placeholder ?? defaultPlaceholder\n const showClear = clearable && !!value && isEditable\n const [open, setOpenState] = React.useState(defaultOpen)\n const setOpen = React.useCallback((next: boolean) => { setOpenState(next); onOpenChange?.(next) }, [onOpenChange])\n\n const currentParts = React.useMemo(() => isoToTimeParts(value), [value])\n // draft = panel 開啟期間的暫存:供 eager commit(每次欄選 commitDraft 即 commit)+ disabledTime 重算;OK 按鈕只 setOpen(false) 關 Panel,不做 commit\n const [draft, setDraft] = React.useState<TimeParts>(\n () => currentParts ?? { hours: 0, minutes: 0, seconds: 0 },\n )\n\n // 每次 popover 開啟時以當前 value 初始化 draft\n React.useEffect(() => {\n if (open) {\n setDraft(currentParts ?? { hours: 0, minutes: 0, seconds: 0 })\n }\n }, [open, currentParts])\n\n const disabledForColumns: TimeColumnsDisabled | undefined = React.useMemo(() => {\n if (!disabledTime) return undefined\n const res = disabledTime(draft)\n return {\n hours: res.disabledHours,\n minutes: res.disabledMinutes,\n seconds: res.disabledSeconds,\n }\n }, [disabledTime, draft])\n\n const commitDraft = (next: TimeParts) => {\n setDraft(next)\n onChange?.(timePartsToString(next, showSeconds))\n }\n\n const handleNow = () => {\n const now = new Date()\n // 按照 minuteStep / secondStep 對齊。\n // 2026-07-05 邊界修正(deep-audit A.1b):Math.round 進位到 60 時,舊 `Math.min(round, 59)`\n // 會產生 59 這種「不對齊 step、也不在 column values 內」的值(如 53 分 @ step=15 → 59)\n // → panel 該欄失去 selected 高亮。改 clamp 到 ≤59 的最大 step 對齊值(= buildRange 產出的\n // 最後一格,如 45),不跨小時進位 — 保持「此刻 = commit 當前時間(step 對齊)」語意\n // (time-picker.spec.md「每次欄位選取當下即 commit」段)。\n const alignToStep = (raw: number, step: number) => {\n const rounded = Math.round(raw / step) * step\n return rounded > 59 ? Math.floor(59 / step) * step : rounded\n }\n const next: TimeParts = {\n hours: now.getHours(),\n minutes: alignToStep(now.getMinutes(), minuteStep),\n seconds: showSeconds ? alignToStep(now.getSeconds(), secondStep) : 0,\n }\n commitDraft(next)\n setOpen(false)\n }\n\n // mode='display'(Phase B2 2026-05-05):純內容輸出 — 對齊原 TimePickerDisplay sub-component(retired)。\n // Default(showDisplayEndIcon=false):無 Field wrapper / 無 Clock icon — backward compat 裸 span。\n // Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix Clock,\n // 與 edit 同結構消除 cell display↔edit 像素偏移(Layer-B padding mismatch)。\n if (resolvedMode === 'display') {\n if (!showDisplayEndIcon) {\n // 2026-05-14 I2 fix(spec contract (e) display typography canonical):bare span 套\n // `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)— 對齊 Field family 統一。\n if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>\n return <span className={cn(fieldDisplayTextClass(size), 'truncate', className)}>{formatTime(value, { formatOptions, locale })}</span>\n }\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: 'display', variant, size }), className)}\n data-field-mode=\"display\"\n >\n <span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value && fieldEmptyColorClass(resolvedMode))}>\n {value ? formatTime(value, { formatOptions, locale }) : emptyDisplay}\n </span>\n {EndIconCmp && (\n <ItemSuffix className=\"pointer-events-none\">\n <EndIconCmp size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n // readonly / disabled\n if (!isEditable) {\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: resolvedMode, variant: variant, size }), className)}\n data-field-mode={resolvedMode}\n aria-disabled={resolvedMode === 'disabled' ? true : undefined}\n {...(props as React.HTMLAttributes<HTMLDivElement>)}\n >\n <span\n className={cn(\n 'flex-1 min-w-0',\n resolvedMode === 'disabled' && 'text-fg-disabled',\n )}\n >\n {value\n ? formatTime(value, { formatOptions, locale })\n : <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>\n }\n </span>\n {/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示 / disabled 保留(fg-disabled);naked cell 依 showDisplayEndIcon=isEditable(修 disabled cell 漏顯 bug)*/}\n {EndIconCmp && (variant === 'naked' ? showDisplayEndIcon : resolvedMode === 'disabled') && (\n <ItemSuffix className=\"pointer-events-none\">\n <EndIconCmp\n size={iconSize}\n className={resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'}\n aria-hidden\n />\n </ItemSuffix>\n )}\n </div>\n )\n }\n\n const displayText = value\n ? formatTime(value, { formatOptions, locale })\n : <span className=\"text-fg-muted\">{resolvedPlaceholder}</span>\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n {/* a11y(2026-04-25 nested-interactive fix):trigger 改 <div role='combobox'>\n (對齊 Select / Combobox 同 pattern),原 <button> 會與內層 ItemInlineAction\n 清除 button 構成 nested-interactive。\n 2026-06-01 鍵盤開啟修正:Radix PopoverTrigger 只 compose onClick\n (@radix-ui/react-popover index.js:145),不 inject 任何 onKeyDown。原生 <button>\n 靠瀏覽器在 Enter/Space 自動派發 click 才能開;但本 trigger 是 <div role=combobox>,\n div 不會自動派發 click → 鍵盤使用者打不開 panel。故顯式加 onKeyDown\n (對齊 select.tsx:609-619 既有 canonical + WAI-ARIA APG combobox required keys)。 */}\n <PopoverTrigger asChild>\n <div\n ref={ref}\n id={idProp ?? fieldCtx?.id}\n role=\"combobox\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n aria-labelledby={fieldCtx?.labelId}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onKeyDown={(e) => {\n if (disabled) return\n // 2026-07-14 dim-10 修:內層「清除時間」ItemInlineAction button 的 Enter/Space\n // 會 bubble 到本 handler 被 preventDefault 吞掉(按鈕 activation 被取消、panel 反而開)。\n // trigger 鍵盤邏輯只在事件源自 trigger div 本身時執行。\n if (e.target !== e.currentTarget) return\n // Enter / Space / ArrowDown / Alt+ArrowDown → 開 panel(APG combobox required + Select canonical)\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault()\n setOpen(true)\n }\n // Escape → 關(Radix Content 已自帶,trigger 補位對齊 select.tsx:597)\n if (e.key === 'Escape') setOpen(false)\n }}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }),\n 'text-left cursor-pointer',\n 'focus-visible:outline-none',\n className,\n )}\n {...props}\n >\n <span className={cn(bareInputStyles, 'truncate', !value && 'text-fg-muted')}>\n {displayText}\n </span>\n {showClear && (\n <ItemInlineAction\n size={size ?? 'md'}\n action={{\n icon: X,\n label: '清除時間', // i18n-allow: DS default inline-action label\n onClick: (e) => {\n e?.stopPropagation()\n onChange?.('')\n },\n }}\n />\n )}\n {EndIconCmp && (\n <ItemSuffix className=\"pointer-events-none\">\n <EndIconCmp size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n )}\n </div>\n </PopoverTrigger>\n {/* a11y(2026-07-14 dim-10):Radix PopoverContent 輸出 role=\"dialog\",無 title 需\n accessible name(WAI-ARIA dialog required);對齊 date-picker.tsx:1040「日期區間選擇」同款 DS default。 */}\n <PopoverContent className=\"w-auto p-0\" align=\"start\" aria-label=\"選擇時間\" /* i18n-allow: DS default dialog label */>\n {/* Panel 對齊 ref/timepicker.png:2-3 個 SelectMenu 式欄位並排,分隔線分開。\n Width 依欄數由 TimeColumns 決定:2 欄 w-40 / 3 欄 w-60。\n Height 由 wrapper 控:216px 預設(扣 footer 後 list 約可見 5-6 items)。\n TimeColumns 本身 h-full,parent 控 height — 讓 DatePicker showTime / Range 可\n 用 flex-row items-stretch 自動同 calendar 高。 */}\n <div className=\"flex flex-col h-[216px]\">\n <TimeColumns\n value={draft}\n onChange={commitDraft}\n showSeconds={showSeconds}\n minuteStep={minuteStep}\n secondStep={secondStep}\n disabled={disabledForColumns}\n // 2026-05-06 v9.1 M25 chain fix:TimeColumns 自然高 = 24 buttons × ~28.7px = 688px\n // 會撐破 parent h-[216px]。flex-1 + min-h-0 讓 TimeColumns 取 parent 剩餘空間\n // (216 − footer ≈53px ≈ 163px)→ ScrollArea h-full 才能正確收斂 →\n // listbox scrollIntoView 找對 nearest scrollable ancestor(內部 viewport),\n // 不會走到 document body 把 popover 內容推出畫面(user 報「hours 欄空白」根因)。\n className=\"flex-1 min-h-0\"\n />\n {/* Footer:消費 SurfaceFooter SSOT(border-t + py-tight + gap-2 + shrink-0 + justify-end)。\n px override 回 layout-space-tight,因 TimePicker 滿欄 column 面板無 chrome-padded body\n 內縮邊可對齊(footer px = body 內縮 原則;column-selector 數字置中、零內距,\n 見 time-picker.spec.md)。此刻 mr-auto 把確定推右,視覺等同原 justify-between\n —— 純結構消費 overlay-surface canonical(overlay-surface.spec.md:17「不自寫 padding token」),零視覺變化。 */}\n <SurfaceFooter className=\"px-[var(--layout-space-tight)]\">\n <Button variant=\"text\" size=\"sm\" onClick={handleNow} className=\"mr-auto\">\n 此刻\n </Button>\n <Button\n variant=\"primary\"\n size=\"sm\"\n onClick={() => setOpen(false)}\n >\n 確定\n </Button>\n </SurfaceFooter>\n </div>\n </PopoverContent>\n </Popover>\n )\n },\n)\nTimePicker.displayName = 'TimePicker'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const timePickerMeta = {\n component: 'TimePicker',\n family: 4,\n variants: {\n\n },\n sizes: {\n\n },\n // states 對齊真實視覺態:trigger 與 panel item 皆無 'active'(按下)專屬視覺態(2026-07-04 audit 對齊);\n // 'selected' = panel item 持續選中(bg-neutral-selected,time-columns.tsx isSelected;2026-07-07 詞彙統一補列)。\n states: ['default', 'hover', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n // panel selected 走 bg-neutral-selected(time-columns.tsx isSelected;spec「欄內 item 狀態」),非 bg-primary。\n bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-transparent'],\n fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],\n ring: [],\n },\n} as const\n\nexport { TimePicker }\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/Tooltip/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAA;AAU3D,QAAA,MAAM,eAAe,GAAI,6BAAoD,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,4CAElI,CAAA;AAED,QAAA,MAAM,OAAO,GAAI,6BAAoD,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,4CAEtH,CAAA;AAED,QAAA,MAAM,cAAc,gHAA2B,CAAA;AAE/C,QAAA,MAAM,cAAc,gKA2BlB,CAAA;AAKF,eAAO,MAAM,WAAW;;;;;;;;;;;CAed,CAAA;AAEV,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/Tooltip/tooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAA;AAW3D,QAAA,MAAM,eAAe,GAAI,6BAAqD,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,4CAEnI,CAAA;AAED,QAAA,MAAM,OAAO,GAAI,6BAAqD,KAAK,CAAC,cAAc,CAAC,OAAO,gBAAgB,CAAC,IAAI,CAAC,4CAEvH,CAAA;AAED,QAAA,MAAM,cAAc,gHAA2B,CAAA;AAE/C,QAAA,MAAM,cAAc,gKA4BlB,CAAA;AAKF,eAAO,MAAM,WAAW;;;;;;;;;;;CAed,CAAA;AAEV,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,CAAA"}
@@ -3,9 +3,10 @@ import * as React from "react";
3
3
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
4
4
  import { cn } from "../../lib/utils.js";
5
5
  import { OVERLAY_COLLISION_PADDING, OVERLAY_SIDE_OFFSET } from "../../tokens/elevation/overlay-geometry.js";
6
- import { HOVER_DELAY_PLAIN_MS } from "../../tokens/motion/motion.js";
7
- const TooltipProvider = ({ delayDuration = HOVER_DELAY_PLAIN_MS, ...props }) => /* @__PURE__ */ jsx(TooltipPrimitive.Provider, { delayDuration, ...props });
8
- const Tooltip = ({ delayDuration = HOVER_DELAY_PLAIN_MS, ...props }) => /* @__PURE__ */ jsx(TooltipPrimitive.Root, { delayDuration, ...props });
6
+ import { MOTION_DELAY_PLAIN_MS } from "../../tokens/motion/motion.js";
7
+ import { overlayMotion } from "../../tokens/motion/overlay-motion.js";
8
+ const TooltipProvider = ({ delayDuration = MOTION_DELAY_PLAIN_MS, ...props }) => /* @__PURE__ */ jsx(TooltipPrimitive.Provider, { delayDuration, ...props });
9
+ const Tooltip = ({ delayDuration = MOTION_DELAY_PLAIN_MS, ...props }) => /* @__PURE__ */ jsx(TooltipPrimitive.Root, { delayDuration, ...props });
9
10
  const TooltipTrigger = TooltipPrimitive.Trigger;
10
11
  const TooltipContent = React.forwardRef(({ className, sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, style, children, ...props }, ref) => (
11
12
  // collisionPadding default 8px:避免 tooltip 貼 viewport 邊(Radix avoidCollisions 預設 true 但 padding 0 會貼邊)
@@ -17,8 +18,9 @@ const TooltipContent = React.forwardRef(({ className, sideOffset = OVERLAY_SIDE_
17
18
  sideOffset,
18
19
  collisionPadding,
19
20
  className: cn(
20
- "z-50 overflow-hidden rounded-md px-3 py-2 text-body font-normal text-on-emphasis bg-tooltip max-w-[280px]",
21
- "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 motion-reduce:animate-none motion-reduce:zoom-in-100",
21
+ "z-50 overflow-hidden rounded-md px-3 py-2 text-body font-normal text-on-emphasis bg-tooltip max-w-[280px] break-words",
22
+ overlayMotion,
23
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
22
24
  "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
23
25
  "origin-[var(--radix-tooltip-content-transform-origin)]",
24
26
  className
@@ -1 +1 @@
1
- {"version":3,"file":"tooltip.js","sources":["../../../src/components/Tooltip/tooltip.tsx"],"sourcesContent":["import * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } from \"@/lib/utils\"\nimport { OVERLAY_SIDE_OFFSET, OVERLAY_COLLISION_PADDING } from \"@/design-system/tokens/elevation/overlay-geometry\"\nimport { HOVER_DELAY_PLAIN_MS } from \"@/design-system/tokens/motion/motion\"\n\n// 2026-05-18 ship per user 拍板 #3A:Tooltip Provider 預設 delayDuration 對齊 motion token SSOT。\n// Radix 預設 700ms 過保守(被 Material 150-200 / MUI 100 / Atlassian 300 集體驗證),DS 統一用\n// `--hover-delay-plain` (500ms,JS mirror `HOVER_DELAY_PLAIN_MS`)。Consumer 仍可 per-instance override。\n// 2026-05-21 D5 Phase B codex 抓 comment 200 vs token 500 drift → 註解 500ms 對齊 motion.css:27 SSOT。\nconst TooltipProvider = ({ delayDuration = HOVER_DELAY_PLAIN_MS, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) => (\n <TooltipPrimitive.Provider delayDuration={delayDuration} {...props} />\n)\n\nconst Tooltip = ({ delayDuration = HOVER_DELAY_PLAIN_MS, ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) => (\n <TooltipPrimitive.Root delayDuration={delayDuration} {...props} />\n)\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, style, children, ...props }, ref) => (\n // collisionPadding default 8px:避免 tooltip 貼 viewport 邊(Radix avoidCollisions 預設 true 但 padding 0 會貼邊)\n // 消費 OVERLAY_COLLISION_PADDING overlay 家族 canonical(與 Popover 一致;HoverCard 特例 12 補 rounding)避免 viewport edge clipping\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n // Density:繼承 page density(2026-06-15 canonical)。Tooltip padding 寫死 px-3 py-2、內容 text-body,\n // 不消費任何 density / layout-space token → 鎖 density 對它是 inert(原 data-density=\"md\" 是 409b91da\n // a11y 批次「對齊 Popover」順手加,非設計決策)→ 移除,讓全浮層行為一致(全繼承 page)。\n className={cn(\n \"z-50 overflow-hidden rounded-md px-3 py-2 text-body font-normal text-on-emphasis bg-tooltip max-w-[280px]\",\n \"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 motion-reduce:animate-none motion-reduce:zoom-in-100\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n \"origin-[var(--radix-tooltip-content-transform-origin)]\",\n className\n )}\n style={{ boxShadow: 'var(--elevation-200)', ...style }}\n {...props}\n >\n <div data-theme=\"dark\" className=\"contents\">{children}</div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n))\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const tooltipMeta = {\n component: 'Tooltip',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default'], // 2026-06-11 R2:Tooltip 浮層本身無互動 state(anatomy rationale L9-12),\n tokens: {\n bg: [],\n fg: [],\n ring: [],\n },\n} as const\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n"],"names":[],"mappings":";;;;;;AAWA,MAAM,kBAAkB,CAAC,EAAE,gBAAgB,sBAAsB,GAAG,MAAA,MAClE,oBAAC,iBAAiB,UAAjB,EAA0B,eAA+B,GAAG,MAAA,CAAO;AAGtE,MAAM,UAAU,CAAC,EAAE,gBAAgB,sBAAsB,GAAG,MAAA,MAC1D,oBAAC,iBAAiB,MAAjB,EAAsB,eAA+B,GAAG,MAAA,CAAO;AAGlE,MAAM,iBAAiB,iBAAiB;AAExC,MAAM,iBAAiB,MAAM,WAG3B,CAAC,EAAE,WAAW,aAAa,qBAAqB,mBAAmB,2BAA2B,OAAO,UAAU,GAAG,SAAS;AAAA;AAAA;AAAA,EAG3H,oBAAC,iBAAiB,QAAjB,EACC,UAAA;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MAIA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,OAAO,EAAE,WAAW,wBAAwB,GAAG,MAAA;AAAA,MAC9C,GAAG;AAAA,MAEJ,8BAAC,OAAA,EAAI,cAAW,QAAO,WAAU,YAAY,SAAA,CAAS;AAAA,IAAA;AAAA,EAAA,EACxD,CACF;AAAA,CACD;AACD,eAAe,cAAc,iBAAiB,QAAQ;AAI/C,MAAM,cAAc;AAAA,EACzB,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA,EAGP,QAAQ,CAAC,SAAS;AAAA;AAAA,EAClB,QAAQ;AAAA,IACN,IAAI,CAAA;AAAA,IACJ,IAAI,CAAA;AAAA,IACJ,MAAM,CAAA;AAAA,EAAC;AAEX;"}
1
+ {"version":3,"file":"tooltip.js","sources":["../../../src/components/Tooltip/tooltip.tsx"],"sourcesContent":["import * as React from \"react\"\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\"\n\nimport { cn } from \"@/lib/utils\"\nimport { OVERLAY_SIDE_OFFSET, OVERLAY_COLLISION_PADDING } from \"@/design-system/tokens/elevation/overlay-geometry\"\nimport { MOTION_DELAY_PLAIN_MS } from \"@/design-system/tokens/motion/motion\"\nimport { overlayMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n// 2026-05-18 ship per user 拍板 #3A:Tooltip Provider 預設 delayDuration 對齊 motion token SSOT。\n// Radix 預設 700ms 過保守(被 Material 150-200 / MUI 100 / Atlassian 300 集體驗證),DS 統一用\n// `--motion-delay-plain` (500ms,JS mirror `MOTION_DELAY_PLAIN_MS`)。Consumer 仍可 per-instance override。\n// 2026-05-21 D5 Phase B codex 抓 comment 200 vs token 500 drift → 註解 500ms 對齊 motion.css:27 SSOT。\nconst TooltipProvider = ({ delayDuration = MOTION_DELAY_PLAIN_MS, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>) => (\n <TooltipPrimitive.Provider delayDuration={delayDuration} {...props} />\n)\n\nconst Tooltip = ({ delayDuration = MOTION_DELAY_PLAIN_MS, ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) => (\n <TooltipPrimitive.Root delayDuration={delayDuration} {...props} />\n)\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, style, children, ...props }, ref) => (\n // collisionPadding default 8px:避免 tooltip 貼 viewport 邊(Radix avoidCollisions 預設 true 但 padding 0 會貼邊)\n // 消費 OVERLAY_COLLISION_PADDING overlay 家族 canonical(與 Popover 一致;HoverCard 特例 12 補 rounding)避免 viewport edge clipping\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n // Density:繼承 page density(2026-06-15 canonical)。Tooltip padding 寫死 px-3 py-2、內容 text-body,\n // 不消費任何 density / layout-space token → 鎖 density 對它是 inert(原 data-density=\"md\" 是 409b91da\n // a11y 批次「對齊 Popover」順手加,非設計決策)→ 移除,讓全浮層行為一致(全繼承 page)。\n className={cn(\n \"z-50 overflow-hidden rounded-md px-3 py-2 text-body font-normal text-on-emphasis bg-tooltip max-w-[280px] break-words\",\n overlayMotion,\n \"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n \"origin-[var(--radix-tooltip-content-transform-origin)]\",\n className\n )}\n style={{ boxShadow: 'var(--elevation-200)', ...style }}\n {...props}\n >\n <div data-theme=\"dark\" className=\"contents\">{children}</div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n))\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const tooltipMeta = {\n component: 'Tooltip',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default'], // 2026-06-11 R2:Tooltip 浮層本身無互動 state(anatomy rationale L9-12),\n tokens: {\n bg: [],\n fg: [],\n ring: [],\n },\n} as const\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }\n"],"names":[],"mappings":";;;;;;;AAYA,MAAM,kBAAkB,CAAC,EAAE,gBAAgB,uBAAuB,GAAG,MAAA,MACnE,oBAAC,iBAAiB,UAAjB,EAA0B,eAA+B,GAAG,MAAA,CAAO;AAGtE,MAAM,UAAU,CAAC,EAAE,gBAAgB,uBAAuB,GAAG,MAAA,MAC3D,oBAAC,iBAAiB,MAAjB,EAAsB,eAA+B,GAAG,MAAA,CAAO;AAGlE,MAAM,iBAAiB,iBAAiB;AAExC,MAAM,iBAAiB,MAAM,WAG3B,CAAC,EAAE,WAAW,aAAa,qBAAqB,mBAAmB,2BAA2B,OAAO,UAAU,GAAG,SAAS;AAAA;AAAA;AAAA,EAG3H,oBAAC,iBAAiB,QAAjB,EACC,UAAA;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MAIA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,OAAO,EAAE,WAAW,wBAAwB,GAAG,MAAA;AAAA,MAC9C,GAAG;AAAA,MAEJ,8BAAC,OAAA,EAAI,cAAW,QAAO,WAAU,YAAY,SAAA,CAAS;AAAA,IAAA;AAAA,EAAA,EACxD,CACF;AAAA,CACD;AACD,eAAe,cAAc,iBAAiB,QAAQ;AAI/C,MAAM,cAAc;AAAA,EACzB,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA,EAGP,QAAQ,CAAC,SAAS;AAAA;AAAA,EAClB,QAAQ;AAAA,IACN,IAAI,CAAA;AAAA,IACJ,IAAI,CAAA;AAAA,IACJ,MAAM,CAAA;AAAA,EAAC;AAEX;"}