@qijenchen/design-system 0.1.0-beta.96 → 0.1.0-beta.99

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 (294) hide show
  1. package/README.md +1 -0
  2. package/dist/components/AccountMenu/account-menu.d.ts +86 -0
  3. package/dist/components/AccountMenu/account-menu.d.ts.map +1 -0
  4. package/dist/components/AccountMenu/account-menu.js +64 -0
  5. package/dist/components/AccountMenu/account-menu.js.map +1 -0
  6. package/dist/components/AccountMenu/index.d.ts +2 -0
  7. package/dist/components/AccountMenu/index.d.ts.map +1 -0
  8. package/dist/components/AccountMenu/index.js +6 -0
  9. package/dist/components/AccountMenu/index.js.map +1 -0
  10. package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
  11. package/dist/components/AppShell/app-shell.d.ts.map +1 -1
  12. package/dist/components/AppShell/app-shell.js +2 -2
  13. package/dist/components/AppShell/app-shell.js.map +1 -1
  14. package/dist/components/Badge/badge.d.ts +1 -1
  15. package/dist/components/Button/button.d.ts +3 -3
  16. package/dist/components/Button/button.d.ts.map +1 -1
  17. package/dist/components/Button/button.js +3 -2
  18. package/dist/components/Button/button.js.map +1 -1
  19. package/dist/components/Calendar/calendar.d.ts +10 -21
  20. package/dist/components/Calendar/calendar.d.ts.map +1 -1
  21. package/dist/components/Calendar/calendar.js +5 -36
  22. package/dist/components/Calendar/calendar.js.map +1 -1
  23. package/dist/components/Combobox/combobox.d.ts.map +1 -1
  24. package/dist/components/Combobox/combobox.js +1 -0
  25. package/dist/components/Combobox/combobox.js.map +1 -1
  26. package/dist/components/Command/command.d.ts.map +1 -1
  27. package/dist/components/Command/command.js +3 -1
  28. package/dist/components/Command/command.js.map +1 -1
  29. package/dist/components/DataTable/data-table-filter-group.d.ts +6 -2
  30. package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -1
  31. package/dist/components/DataTable/data-table-filter-group.js +32 -27
  32. package/dist/components/DataTable/data-table-filter-group.js.map +1 -1
  33. package/dist/components/DataTable/data-table-filter-labels.d.ts +44 -0
  34. package/dist/components/DataTable/data-table-filter-labels.d.ts.map +1 -0
  35. package/dist/components/DataTable/data-table-filter-labels.js +55 -0
  36. package/dist/components/DataTable/data-table-filter-labels.js.map +1 -0
  37. package/dist/components/DataTable/data-table-filter-panel.d.ts +11 -0
  38. package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
  39. package/dist/components/DataTable/data-table-filter-panel.js +33 -10
  40. package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
  41. package/dist/components/DataTable/data-table-filter-value-picker.d.ts +4 -1
  42. package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -1
  43. package/dist/components/DataTable/data-table-filter-value-picker.js +22 -8
  44. package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -1
  45. package/dist/components/DataTable/data-table.js +2 -2
  46. package/dist/components/DataTable/data-table.js.map +1 -1
  47. package/dist/components/DataTable/index.js +2 -0
  48. package/dist/components/DataTable/index.js.map +1 -1
  49. package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
  50. package/dist/components/DatePicker/date-picker.js +5 -1
  51. package/dist/components/DatePicker/date-picker.js.map +1 -1
  52. package/dist/components/Dialog/dialog.d.ts +1 -1
  53. package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -1
  54. package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
  55. package/dist/components/DropdownMenu/dropdown-menu.js +46 -14
  56. package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
  57. package/dist/components/Field/field.d.ts.map +1 -1
  58. package/dist/components/Field/field.js.map +1 -1
  59. package/dist/components/FieldControlGroup/field-control-group.d.ts +2 -5
  60. package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
  61. package/dist/components/FieldControlGroup/field-control-group.js +2 -2
  62. package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
  63. package/dist/components/FileUpload/file-upload.js +1 -1
  64. package/dist/components/FileUpload/file-upload.js.map +1 -1
  65. package/dist/components/FileViewer/file-viewer.d.ts +1 -1
  66. package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
  67. package/dist/components/FileViewer/file-viewer.js +20 -3
  68. package/dist/components/FileViewer/file-viewer.js.map +1 -1
  69. package/dist/components/Menu/menu-item.d.ts.map +1 -1
  70. package/dist/components/Menu/menu-item.js +4 -2
  71. package/dist/components/Menu/menu-item.js.map +1 -1
  72. package/dist/components/PeoplePicker/people-picker.d.ts +2 -0
  73. package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
  74. package/dist/components/PeoplePicker/people-picker.js +4 -0
  75. package/dist/components/PeoplePicker/people-picker.js.map +1 -1
  76. package/dist/components/ScrollArea/scroll-area.d.ts +4 -3
  77. package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
  78. package/dist/components/ScrollArea/scroll-area.js +1 -1
  79. package/dist/components/ScrollArea/scroll-area.js.map +1 -1
  80. package/dist/components/SelectMenu/select-menu.d.ts +2 -0
  81. package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
  82. package/dist/components/SelectMenu/select-menu.js +3 -1
  83. package/dist/components/SelectMenu/select-menu.js.map +1 -1
  84. package/dist/components/Sheet/sheet.d.ts +1 -1
  85. package/dist/components/Sidebar/sidebar.js +2 -2
  86. package/dist/components/Sidebar/sidebar.js.map +1 -1
  87. package/dist/components/Tabs/tabs.d.ts.map +1 -1
  88. package/dist/components/Tabs/tabs.js +3 -0
  89. package/dist/components/Tabs/tabs.js.map +1 -1
  90. package/dist/components/Tag/tag.d.ts +1 -1
  91. package/dist/components/Tag/tag.d.ts.map +1 -1
  92. package/dist/components/Tag/tag.js +4 -3
  93. package/dist/components/Tag/tag.js.map +1 -1
  94. package/dist/components/Tooltip/tooltip.d.ts +1 -1
  95. package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
  96. package/dist/components/Tooltip/tooltip.js +26 -1
  97. package/dist/components/Tooltip/tooltip.js.map +1 -1
  98. package/dist/components/TreeView/tree-view.d.ts +2 -0
  99. package/dist/components/TreeView/tree-view.d.ts.map +1 -1
  100. package/dist/components/TreeView/tree-view.js +3 -2
  101. package/dist/components/TreeView/tree-view.js.map +1 -1
  102. package/dist/index.d.ts +5 -3
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +4 -0
  105. package/dist/index.js.map +1 -1
  106. package/dist/patterns/element-anatomy/item-anatomy.d.ts +1 -1
  107. package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
  108. package/dist/patterns/element-anatomy/item-anatomy.js +6 -2
  109. package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
  110. package/dist/patterns/header-canonical/chrome-header.d.ts +11 -4
  111. package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
  112. package/dist/patterns/header-canonical/chrome-header.js +1 -1
  113. package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
  114. package/dist/patterns/overlay-surface/overlay-surface.d.ts +4 -3
  115. package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
  116. package/dist/patterns/overlay-surface/overlay-surface.js +1 -0
  117. package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
  118. package/dist/patterns/resize-handle/resize-handle.d.ts +9 -11
  119. package/dist/patterns/resize-handle/resize-handle.d.ts.map +1 -1
  120. package/dist/patterns/resize-handle/resize-handle.js +3 -6
  121. package/dist/patterns/resize-handle/resize-handle.js.map +1 -1
  122. package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +1 -1
  123. package/ds-canonical/adapters/claude-root-instructions.md +1 -1
  124. package/ds-canonical/adapters/claude-settings-base.json +2 -5
  125. package/ds-canonical/fork/AGENTS.md +21 -10
  126. package/ds-canonical/fork/codex/hooks.json +1 -1
  127. package/ds-canonical/fork/common/instruction.md +21 -10
  128. package/ds-canonical/fork/consumer/governance-check.mjs +101 -13
  129. package/ds-canonical/fork/consumer/lock.json +14 -15
  130. package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +26 -3
  131. package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +61 -5
  132. package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +3 -10
  133. package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +3 -2
  134. package/ds-canonical/fork/governance.lock +17 -33
  135. package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +33 -0
  136. package/ds-canonical/fork/hooks/check_pattern_invariants.sh +39 -0
  137. package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
  138. package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +5 -11
  139. package/ds-canonical/fork/launchers/settings-hooks.json +2 -2
  140. package/ds-canonical/fork/manifest.json +11 -13
  141. package/ds-canonical/fork/providers/claude/hook-config.json +1 -7
  142. package/ds-canonical/fork/providers/codex/hook-config.json +1 -1
  143. package/ds-canonical/fork/references/story-baseline-registry.json +7 -1
  144. package/ds-canonical/hooks/check_consumer_code_quality.sh +33 -0
  145. package/ds-canonical/hooks/check_pattern_invariants.sh +39 -0
  146. package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
  147. package/ds-canonical/hooks/session_start_governance_check.sh +5 -6
  148. package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +23 -1
  149. package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +36 -2
  150. package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
  151. package/ds-canonical/references/failure-class-registry.json +151 -144
  152. package/ds-canonical/references/preflight-gate-baseline.json +16 -12
  153. package/ds-canonical/references/story-baseline-registry.json +7 -1
  154. package/ds-canonical/rules/meta-patterns.md +3 -3
  155. package/ds-canonical/skills/design-system-audit/SKILL.md +1 -1
  156. package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1 -1
  157. package/ds-canonical/templates/dashboard-app.tsx +31 -8
  158. package/llms-full.txt +6 -1
  159. package/llms.txt +2 -1
  160. package/package.json +1 -1
  161. package/src/components/AccountMenu/account-menu.anatomy.stories.tsx +115 -0
  162. package/src/components/AccountMenu/account-menu.principles.stories.tsx +99 -0
  163. package/src/components/AccountMenu/account-menu.spec.md +101 -0
  164. package/src/components/AccountMenu/account-menu.stories.tsx +61 -0
  165. package/src/components/AccountMenu/account-menu.tsx +170 -0
  166. package/src/components/AccountMenu/index.ts +5 -0
  167. package/src/components/AppShell/_demo-helpers.tsx +53 -53
  168. package/src/components/AppShell/app-shell.spec.md +5 -1
  169. package/src/components/AppShell/app-shell.tsx +14 -12
  170. package/src/components/Avatar/avatar.principles.stories.tsx +4 -4
  171. package/src/components/Avatar/avatar.stories.tsx +20 -5
  172. package/src/components/Breadcrumb/breadcrumb.spec.md +1 -1
  173. package/src/components/Button/button.spec.md +1 -1
  174. package/src/components/Button/button.stories.tsx +21 -9
  175. package/src/components/Button/button.tsx +5 -4
  176. package/src/components/Calendar/calendar.anatomy.stories.tsx +19 -17
  177. package/src/components/Calendar/calendar.principles.stories.tsx +5 -6
  178. package/src/components/Calendar/calendar.spec.md +24 -28
  179. package/src/components/Calendar/calendar.stories.tsx +5 -7
  180. package/src/components/Calendar/calendar.tsx +16 -62
  181. package/src/components/Carousel/carousel.principles.stories.tsx +5 -1
  182. package/src/components/Chart/chart.spec.md +1 -1
  183. package/src/components/Checkbox/checkbox.anatomy.stories.tsx +14 -5
  184. package/src/components/Chip/chip.spec.md +1 -1
  185. package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +7 -6
  186. package/src/components/CircularProgress/circular-progress.principles.stories.tsx +8 -6
  187. package/src/components/CircularProgress/circular-progress.stories.tsx +3 -3
  188. package/src/components/Combobox/combobox.tsx +1 -0
  189. package/src/components/Command/command.spec.md +1 -0
  190. package/src/components/Command/command.tsx +4 -1
  191. package/src/components/DataTable/data-table-filter-group.tsx +44 -27
  192. package/src/components/DataTable/data-table-filter-labels.ts +100 -0
  193. package/src/components/DataTable/data-table-filter-panel.tsx +48 -8
  194. package/src/components/DataTable/data-table-filter-value-picker.tsx +25 -8
  195. package/src/components/DataTable/data-table.spec.md +17 -1
  196. package/src/components/DataTable/data-table.stories.tsx +346 -33
  197. package/src/components/DataTable/data-table.tsx +2 -2
  198. package/src/components/DataTable/filter-operators.spec.md +2 -2
  199. package/src/components/DateGrid/date-grid.anatomy.stories.tsx +24 -20
  200. package/src/components/DateGrid/date-grid.principles.stories.tsx +27 -21
  201. package/src/components/DateGrid/date-grid.stories.tsx +22 -13
  202. package/src/components/DatePicker/date-picker.anatomy.stories.tsx +3 -3
  203. package/src/components/DatePicker/date-picker.spec.md +3 -3
  204. package/src/components/DatePicker/date-picker.stories.tsx +33 -9
  205. package/src/components/DatePicker/date-picker.tsx +8 -1
  206. package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
  207. package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -0
  208. package/src/components/DropdownMenu/dropdown-menu.stories.tsx +3 -17
  209. package/src/components/DropdownMenu/dropdown-menu.tsx +58 -16
  210. package/src/components/Field/field.spec.md +1 -1
  211. package/src/components/Field/field.tsx +1 -0
  212. package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +3 -3
  213. package/src/components/FieldControlGroup/field-control-group.spec.md +2 -3
  214. package/src/components/FieldControlGroup/field-control-group.stories.tsx +2 -1
  215. package/src/components/FieldControlGroup/field-control-group.tsx +6 -9
  216. package/src/components/FileItem/file-item.spec.md +1 -1
  217. package/src/components/FileItem/file-item.stories.tsx +1 -1
  218. package/src/components/FileUpload/file-upload.stories.tsx +32 -0
  219. package/src/components/FileUpload/file-upload.tsx +1 -1
  220. package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
  221. package/src/components/FileViewer/file-viewer.spec.md +1 -1
  222. package/src/components/FileViewer/file-viewer.stories.tsx +7 -0
  223. package/src/components/FileViewer/file-viewer.tsx +23 -5
  224. package/src/components/InlineEdit/inline-edit.stories.tsx +2 -2
  225. package/src/components/Input/input.anatomy.stories.tsx +28 -12
  226. package/src/components/Input/input.principles.stories.tsx +23 -18
  227. package/src/components/Input/input.stories.tsx +9 -1
  228. package/src/components/LinkInput/link-input.principles.stories.tsx +7 -7
  229. package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
  230. package/src/components/Menu/menu-item.tsx +9 -2
  231. package/src/components/NumberInput/number-input.anatomy.stories.tsx +44 -10
  232. package/src/components/NumberInput/number-input.principles.stories.tsx +20 -14
  233. package/src/components/NumberInput/number-input.spec.md +1 -1
  234. package/src/components/NumberInput/number-input.stories.tsx +12 -5
  235. package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +7 -0
  236. package/src/components/PeoplePicker/people-picker.spec.md +3 -1
  237. package/src/components/PeoplePicker/people-picker.tsx +5 -0
  238. package/src/components/ProgressBar/progress-bar.principles.stories.tsx +14 -12
  239. package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +5 -1
  240. package/src/components/RadioGroup/radio-group.spec.md +1 -1
  241. package/src/components/Rating/rating.principles.stories.tsx +2 -2
  242. package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
  243. package/src/components/ScrollArea/scroll-area.spec.md +3 -3
  244. package/src/components/ScrollArea/scroll-area.tsx +5 -4
  245. package/src/components/SelectMenu/select-menu.spec.md +1 -1
  246. package/src/components/SelectMenu/select-menu.tsx +4 -1
  247. package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +19 -8
  248. package/src/components/SelectionControl/selection-item.stories.tsx +7 -7
  249. package/src/components/Sheet/sheet.spec.md +1 -1
  250. package/src/components/Sidebar/sidebar.spec.md +3 -2
  251. package/src/components/Sidebar/sidebar.stories.tsx +67 -37
  252. package/src/components/Sidebar/sidebar.tsx +3 -3
  253. package/src/components/Slider/slider.principles.stories.tsx +9 -9
  254. package/src/components/Steps/steps.spec.md +1 -0
  255. package/src/components/Steps/steps.stories.tsx +18 -1
  256. package/src/components/Switch/switch.anatomy.stories.tsx +11 -11
  257. package/src/components/Switch/switch.principles.stories.tsx +11 -8
  258. package/src/components/Tabs/tabs.principles.stories.tsx +24 -0
  259. package/src/components/Tabs/tabs.spec.md +6 -4
  260. package/src/components/Tabs/tabs.tsx +11 -0
  261. package/src/components/Tag/tag.anatomy.stories.tsx +5 -2
  262. package/src/components/Tag/tag.spec.md +4 -3
  263. package/src/components/Tag/tag.stories.tsx +19 -0
  264. package/src/components/Tag/tag.tsx +14 -5
  265. package/src/components/Textarea/textarea.anatomy.stories.tsx +15 -9
  266. package/src/components/Textarea/textarea.principles.stories.tsx +21 -5
  267. package/src/components/Tooltip/tooltip.spec.md +4 -0
  268. package/src/components/Tooltip/tooltip.tsx +34 -3
  269. package/src/components/TreeView/tree-view.spec.md +3 -3
  270. package/src/components/TreeView/tree-view.stories.tsx +68 -9
  271. package/src/components/TreeView/tree-view.tsx +7 -2
  272. package/src/index.ts +9 -1
  273. package/src/patterns/element-anatomy/inline-action.spec.md +12 -8
  274. package/src/patterns/element-anatomy/item-anatomy.stories.tsx +24 -5
  275. package/src/patterns/element-anatomy/item-anatomy.tsx +9 -5
  276. package/src/patterns/header-canonical/chrome-header.tsx +14 -5
  277. package/src/patterns/header-canonical/header-canonical.spec.md +10 -7
  278. package/src/patterns/header-canonical/header-canonical.stories.tsx +77 -0
  279. package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +2 -0
  280. package/src/patterns/overlay-surface/overlay-surface.spec.md +1 -0
  281. package/src/patterns/overlay-surface/overlay-surface.stories.tsx +17 -1
  282. package/src/patterns/overlay-surface/overlay-surface.tsx +8 -4
  283. package/src/patterns/resize-handle/resize-handle.spec.md +11 -13
  284. package/src/patterns/resize-handle/resize-handle.stories.tsx +1 -3
  285. package/src/patterns/resize-handle/resize-handle.tsx +13 -18
  286. package/src/tokens/color/semantic.css +3 -1
  287. package/src/tokens/layoutSpace/layoutSpace.stories.tsx +1 -1
  288. package/src/tokens/opacity/opacity.stories.tsx +1 -1
  289. package/src/tokens/radius/radius.stories.tsx +3 -3
  290. package/ds-canonical/commands/gov-status.md +0 -9
  291. package/ds-canonical/fork/consumer/managed-files/77e06f62670dfee01e9b5e8b0f8dad085ce711d75538c1fb0679d9818acaca82.blob +0 -424
  292. package/ds-canonical/fork/preamble.md +0 -500
  293. package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +0 -8
  294. package/ds-canonical/hooks/tests/test_check_post_main_ssot_propagate.sh +0 -37
@@ -5,39 +5,39 @@ import { Select } from "../Select/select.js";
5
5
  import { FieldControlGroup } from "../FieldControlGroup/field-control-group.js";
6
6
  import { DEFAULT_OPERATOR, getOperatorSpec, getValueShape, OPERATOR_REGISTRY } from "./filter-operators.js";
7
7
  import { FilterValuePicker } from "./data-table-filter-value-picker.js";
8
- function getOperatorOptions(type) {
8
+ function getOperatorOptions(type, operatorLabels) {
9
9
  const registry = type && OPERATOR_REGISTRY[type] ? OPERATOR_REGISTRY[type] : OPERATOR_REGISTRY.string;
10
- return registry.map((op) => ({ value: op.op, label: op.label }));
10
+ return registry.map((op) => ({ value: op.op, label: operatorLabels[op.op] ?? op.label }));
11
11
  }
12
12
  function getDefaultOperator(type) {
13
13
  return type && DEFAULT_OPERATOR[type] || DEFAULT_OPERATOR.string;
14
14
  }
15
15
  const datePrecisionOf = (col) => (col == null ? void 0 : col.type) === "date" ? col.includeTime ? "ms" : "day" : void 0;
16
- const CONJ_OPTIONS = [
17
- { value: "and", label: "And" },
18
- { value: "or", label: "Or" }
19
- ];
20
16
  function ConjunctionLabel({
21
17
  index,
22
18
  conjunction,
23
- onChange
19
+ onChange,
20
+ labels
24
21
  }) {
25
22
  if (index === 0) {
26
- return /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0 text-body text-fg-muted px-[var(--field-px)] self-center", children: "Where" });
23
+ return /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0 text-body text-fg-secondary px-[var(--field-px)] self-center", children: labels.where });
27
24
  }
28
25
  if (index >= 2) {
29
- const label = conjunction === "and" ? "And" : "Or";
30
- return /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0 text-body text-fg-muted px-[var(--field-px)] self-center", children: label });
26
+ const label = conjunction === "and" ? labels.and : labels.or;
27
+ return /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0 text-body text-fg-secondary px-[var(--field-px)] self-center", children: label });
31
28
  }
32
29
  return /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsx(
33
30
  Select,
34
31
  {
35
32
  size: "sm",
36
- options: CONJ_OPTIONS,
33
+ options: [
34
+ { value: "and", label: labels.and },
35
+ { value: "or", label: labels.or }
36
+ ],
37
37
  value: conjunction,
38
38
  onChange: (v) => onChange(v),
39
39
  minRows: 2,
40
- "aria-label": "連接詞 — 同 group 共用"
40
+ "aria-label": labels.conjunctionAriaLabel
41
41
  }
42
42
  ) });
43
43
  }
@@ -51,27 +51,28 @@ function FilterRow({
51
51
  onChangeField,
52
52
  onChangeOp,
53
53
  onChangeValue,
54
- onRemove
54
+ onRemove,
55
+ labels
55
56
  }) {
56
57
  const colInfo = filterableColumns.find((c) => c.id === condition.field);
57
- const operatorOptions = getOperatorOptions(colInfo == null ? void 0 : colInfo.type);
58
+ const operatorOptions = getOperatorOptions(colInfo == null ? void 0 : colInfo.type, labels.operatorLabels);
58
59
  const hasField = !!condition.field;
59
60
  const opSpec = colInfo ? getOperatorSpec(colInfo.type, condition.op) : null;
60
61
  const valueShape = colInfo && opSpec ? getValueShape(opSpec, colInfo.type, colInfo.includeTime) : null;
61
62
  const hasValueCell = valueShape !== "none";
62
63
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
63
- /* @__PURE__ */ jsx(ConjunctionLabel, { index, conjunction, onChange: onChangeConjunction }),
64
+ /* @__PURE__ */ jsx(ConjunctionLabel, { index, conjunction, onChange: onChangeConjunction, labels }),
64
65
  /* @__PURE__ */ jsxs(FieldControlGroup, { block: true, className: "flex-1 min-w-0", children: [
65
66
  /* @__PURE__ */ jsx(
66
67
  Select,
67
68
  {
68
- className: "!w-[var(--data-table-filter-field-width)] flex-shrink-0",
69
+ className: "!w-[var(--data-table-filter-field-width)] flex-shrink-0 [&_.text-fg-muted]:!text-fg-secondary",
69
70
  size: "sm",
70
71
  options: fieldOptions,
71
72
  value: condition.field,
72
73
  onChange: onChangeField,
73
- placeholder: "選擇欄位",
74
- "aria-label": "篩選欄位"
74
+ placeholder: labels.fieldPlaceholder,
75
+ "aria-label": labels.fieldAriaLabel
75
76
  }
76
77
  ),
77
78
  /* @__PURE__ */ jsx(
@@ -83,8 +84,8 @@ function FilterRow({
83
84
  value: condition.op,
84
85
  onChange: onChangeOp,
85
86
  disabled: !hasField,
86
- placeholder: "運算子",
87
- "aria-label": "篩選運算子"
87
+ placeholder: labels.operatorPlaceholder,
88
+ "aria-label": labels.operatorAriaLabel
88
89
  }
89
90
  ),
90
91
  hasValueCell && /* @__PURE__ */ jsx(
@@ -95,12 +96,13 @@ function FilterRow({
95
96
  onChange: onChangeValue,
96
97
  colInfo,
97
98
  disabled: !hasField,
98
- ariaLabel: colInfo ? `${colInfo.label} 篩選值` : "篩選值",
99
+ ariaLabel: labels.filterValueAriaLabel(colInfo == null ? void 0 : colInfo.label),
100
+ labels,
99
101
  className: "!flex-1 !min-w-0"
100
102
  }
101
103
  )
102
104
  ] }),
103
- /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", iconOnly: true, startIcon: Trash2, "aria-label": "刪除", onClick: onRemove })
105
+ /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", iconOnly: true, startIcon: Trash2, "aria-label": labels.deleteCondition, onClick: onRemove })
104
106
  ] });
105
107
  }
106
108
  function GroupBlock({
@@ -114,10 +116,12 @@ function GroupBlock({
114
116
  onChangeCondition,
115
117
  onRemoveCondition,
116
118
  onAddCondition,
117
- onRemoveGroup
119
+ onRemoveGroup,
120
+ canAddCondition,
121
+ labels
118
122
  }) {
119
123
  return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
120
- /* @__PURE__ */ jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsx(ConjunctionLabel, { index, conjunction: rootConjunction, onChange: onChangeRootConjunction }) }),
124
+ /* @__PURE__ */ jsx("div", { className: "pt-2", children: /* @__PURE__ */ jsx(ConjunctionLabel, { index, conjunction: rootConjunction, onChange: onChangeRootConjunction, labels }) }),
121
125
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 rounded-md bg-muted p-2 flex flex-col gap-2", children: [
122
126
  group.children.map((cond, cIdx) => /* @__PURE__ */ jsx(
123
127
  FilterRow,
@@ -134,13 +138,14 @@ function GroupBlock({
134
138
  },
135
139
  onChangeOp: (v) => onChangeCondition(cond.id, { op: v, value: "" }),
136
140
  onChangeValue: (v) => onChangeCondition(cond.id, { value: v }),
137
- onRemove: () => onRemoveCondition(cond.id)
141
+ onRemove: () => onRemoveCondition(cond.id),
142
+ labels
138
143
  },
139
144
  cond.id
140
145
  )),
141
146
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
142
- /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", startIcon: Plus, onClick: onAddCondition, children: "加入巢狀篩選" }),
143
- group.children.length === 0 && /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", startIcon: Trash2, danger: true, onClick: onRemoveGroup, children: "移除空群組" })
147
+ /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", startIcon: Plus, onClick: onAddCondition, disabled: !canAddCondition, children: labels.addNestedFilter }),
148
+ group.children.length === 0 && /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", startIcon: Trash2, danger: true, onClick: onRemoveGroup, children: labels.removeEmptyGroup })
144
149
  ] })
145
150
  ] })
146
151
  ] });
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-filter-group.js","sources":["../../../src/components/DataTable/data-table-filter-group.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved(2026-07-14 自 data-table-filter-panel.tsx 拆檔搬移,原檔級 escape 隨段落帶入)。\n// @internal — DS-internal 單元(filter row / nested-group renderer + 共用 column/operator\n// helpers;GroupBlock 為 nested-group renderer,FilterRow / ConjunctionLabel 為其 row 構件、\n// flat mode 共用。只被 data-table-filter-panel.tsx 消費);不隨 index.ts re-export 進 npm public surface。\n// ────────────────────────────── 消費的 SSOT ──────────────────────────────\n// - FieldControlGroup:field + op + value border-collapse 接合(field-control-group.spec.md;\n// FilterValuePicker 為 direct child,className forward 不開 wrapper div — 2026-05-04 #2 fix)\n// - Button(variant=\"text\" iconOnly)row meta trash / Select(size=\"sm\")conjunction・field・op\n// (data-table.spec.md「四、UI canonical」:trash = form-control row text Button;A6 conjunction)\n// - Width tokens:--data-table-filter-field-width / --data-table-filter-op-width(uiSize.css SSOT,\n// 2026-05-23 Phase A.4 Decision 2)\n// - Operator SSOT:./filter-operators.ts OPERATOR_REGISTRY(禁 hardcode op 字串)\n// - Group container 灰底 bg-muted:color.spec.md L651-654 靜態低重要 surface semantic(2026-05-09 Q3 A)\n// 設計規則:./filter-operators.spec.md + ./data-table.spec.md「進階篩選」段\n\nimport { Plus, Trash2 } from 'lucide-react'\nimport { Button } from '@/design-system/components/Button/button'\nimport { Select, type SelectOption } from '@/design-system/components/Select/select'\nimport { FieldControlGroup } from '@/design-system/components/FieldControlGroup/field-control-group'\nimport type { ColumnType } from './column-types'\nimport {\n OPERATOR_REGISTRY,\n DEFAULT_OPERATOR,\n getOperatorSpec,\n getValueShape,\n type ValueShape,\n} from './filter-operators'\nimport type { Conjunction, FilterCondition, FilterGroup } from './filter-tree'\nimport { FilterValuePicker } from './data-table-filter-value-picker'\n\n// ── Shared internal types + operator helpers(panel orchestration 也消費)────\n\nexport interface FilterColumn {\n id: string\n label: string\n type: ColumnType\n options?: Array<{ value: string; label: string }>\n /** People pool for person/multiPerson filter picker(對齊 cell-registry meta.people SSOT)*/\n people?: Array<{ name: string; avatarUrl?: string; description?: string }>\n includeTime?: boolean\n}\n\nfunction getOperatorOptions(type?: ColumnType): SelectOption[] {\n const registry = type && OPERATOR_REGISTRY[type] ? OPERATOR_REGISTRY[type] : OPERATOR_REGISTRY.string\n return registry.map((op) => ({ value: op.op, label: op.label }))\n}\n\nexport function getDefaultOperator(type?: ColumnType): string {\n return (type && DEFAULT_OPERATOR[type]) || DEFAULT_OPERATOR.string\n}\n\n// Q6(2026-07-04):date 欄位把比對精度寫進 condition(includeTime=true → ms,否則 day)。\n// 'time' 欄暫不寫(值非完整日期,truncate 語意不成立;見 filter-operators.spec v3 time 註)。\nexport const datePrecisionOf = (col: FilterColumn | undefined): 'day' | 'ms' | undefined =>\n col?.type === 'date' ? (col.includeTime ? 'ms' : 'day') : undefined\n\n// ── ConjunctionLabel ───────────────────────────────────────────────────\n\nconst CONJ_OPTIONS: SelectOption[] = [\n { value: 'and', label: 'And' },\n { value: 'or', label: 'Or' },\n]\n\nfunction ConjunctionLabel({\n index, conjunction, onChange,\n}: { index: number; conjunction: Conjunction; onChange: (c: Conjunction) => void }) {\n // index === 0:首 row 顯示靜態「Where」label\n // index === 1:**唯一可改**的 AND/OR Select(連動整 group conjunction)\n // index ≥ 2:被連動的 row,read-only 顯示當前 conjunction 文字(同 Where 視覺,A6 canonical)\n // 對齊 Airtable / Notion / Linear 共識 @benchmark-unverified(non-OSS)\n // px-[var(--field-px)] 對齊 Field 內部 padding 12px(Q13)\n if (index === 0) {\n return <div className=\"w-20 shrink-0 text-body text-fg-muted px-[var(--field-px)] self-center\">Where</div>\n }\n if (index >= 2) {\n const label = conjunction === 'and' ? 'And' : 'Or'\n return <div className=\"w-20 shrink-0 text-body text-fg-muted px-[var(--field-px)] self-center\">{label}</div>\n }\n // index === 1:可切換的 AND/OR Select\n // minRows={2} — And/Or 2 選項,顯式縮 menu 高度避免 reserve 3 row 空白(Q5)\n return (\n <div className=\"w-20 shrink-0\">\n <Select\n size=\"sm\"\n options={CONJ_OPTIONS}\n value={conjunction}\n onChange={(v) => onChange(v as Conjunction)}\n minRows={2}\n aria-label=\"連接詞 — 同 group 共用\"\n />\n </div>\n )\n}\n\n// ── FilterRow(flat 用 + group 內共用) ──────────────────────────────\n\nexport function FilterRow({\n index, condition, conjunction, filterableColumns, fieldOptions,\n onChangeConjunction, onChangeField, onChangeOp, onChangeValue, onRemove,\n}: {\n index: number\n condition: FilterCondition\n conjunction: Conjunction\n filterableColumns: FilterColumn[]\n fieldOptions: SelectOption[]\n onChangeConjunction: (c: Conjunction) => void\n onChangeField: (v: string) => void\n onChangeOp: (v: string) => void\n onChangeValue: (v: unknown) => void\n onRemove: () => void\n}) {\n const colInfo = filterableColumns.find((c) => c.id === condition.field)\n const operatorOptions = getOperatorOptions(colInfo?.type)\n const hasField = !!condition.field\n const opSpec = colInfo ? getOperatorSpec(colInfo.type, condition.op) : null\n const valueShape: ValueShape | null = colInfo && opSpec\n ? getValueShape(opSpec, colInfo.type, colInfo.includeTime)\n : null\n // op 'is_set' / 'is_not_set' 等 shape='none' → 無 value cell,op 自動 expand 填剩餘寬\n // 對齊 Notion / Airtable / Linear filter row 行為\n // 注意:valueShape=null(初始無 field 選)時仍 render value cell(disabled placeholder)— 只 'none' 才 fold\n const hasValueCell = valueShape !== 'none'\n\n // FieldControlGroup 接合 field + op + value 視覺(2026-05-04 E refactor + 多輪 fix):\n // - border collapse 取代 3 顆獨立 Select 並排,對齊 Airtable / Linear / Notion filter row idiom\n // - ConjunctionLabel + Trash 在 group 外層(meta actions,不屬 control 一體)\n // - **#5 fix**:row 內水平 gap = `gap-2` (8px),layoutSpace 規則 5 緊密相關\n // - **#9 fix**:cell 用 `min-w-[]`(field 160 / op 120),value flex-1 min-w-0,讓 long label 可撐寬\n // - **#2 fix**:FilterValuePicker 直接是 FieldControlGroup direct child(無 wrapper div),CSS variants 命中正確\n return (\n <div className=\"flex items-center gap-2\">\n <ConjunctionLabel index={index} conjunction={conjunction} onChange={onChangeConjunction} />\n {/* **#9 fix(2026-05-04 v4)**:Field controls trigger `w-full` override 外 className,改用 Tailwind `!`\n important 強制 override(`!w-[160px]` / `!w-[120px]`),value 用 `!flex-1 !min-w-0`。\n Select 元件本身沒 destructure `style` prop 所以 inline style flex-basis 行不通,只能用 className。 */}\n <FieldControlGroup block className=\"flex-1 min-w-0\">\n {/* 2026-05-23 Phase A.4 Decision 2:`!w-[160px]` / `!w-[120px]` → tokens\n `--data-table-filter-field-width` / `--data-table-filter-op-width`(SSOT in uiSize.css)\n Behavior preserved 完好如初:flat + nested 同 width(token 是 design constant) */}\n <Select\n className=\"!w-[var(--data-table-filter-field-width)] flex-shrink-0\"\n size=\"sm\"\n options={fieldOptions}\n value={condition.field}\n onChange={onChangeField}\n placeholder=\"選擇欄位\"\n aria-label=\"篩選欄位\"\n />\n <Select\n className={hasValueCell ? '!w-[var(--data-table-filter-op-width)] flex-shrink-0' : '!flex-1 !min-w-0'}\n size=\"sm\"\n options={operatorOptions}\n value={condition.op}\n onChange={onChangeOp}\n disabled={!hasField}\n placeholder=\"運算子\"\n aria-label=\"篩選運算子\"\n />\n {hasValueCell && (\n <FilterValuePicker\n shape={valueShape}\n value={condition.value}\n onChange={onChangeValue}\n colInfo={colInfo}\n disabled={!hasField}\n ariaLabel={colInfo ? `${colInfo.label} 篩選值` : '篩選值'}\n className=\"!flex-1 !min-w-0\"\n />\n )}\n </FieldControlGroup>\n {/* Trash 用 text Button — filter row 是 form-control row,Field 同高對齊(28 md) */}\n <Button variant=\"text\" size=\"sm\" iconOnly startIcon={Trash2} aria-label=\"刪除\" onClick={onRemove} />\n </div>\n )\n}\n\n// ── GroupBlock(nested 用) ────────────────────────────────────────────\n\nexport function GroupBlock({\n index, group, rootConjunction, filterableColumns, fieldOptions,\n onChangeRootConjunction, onChangeGroupConjunction,\n onChangeCondition, onRemoveCondition, onAddCondition, onRemoveGroup,\n}: {\n index: number\n group: FilterGroup\n rootConjunction: Conjunction\n filterableColumns: FilterColumn[]\n fieldOptions: SelectOption[]\n onChangeRootConjunction: (c: Conjunction) => void\n onChangeGroupConjunction: (c: Conjunction) => void\n onChangeCondition: (condId: string, patch: Partial<FilterCondition>) => void\n onRemoveCondition: (condId: string) => void\n onAddCondition: () => void\n onRemoveGroup: () => void\n}) {\n return (\n <div className=\"flex items-start gap-2\">\n <div className=\"pt-2\">\n <ConjunctionLabel index={index} conjunction={rootConjunction} onChange={onChangeRootConjunction} />\n </div>\n {/* Group container 灰底 — `bg-muted`(`--muted` neutral-2,user 2026-05-09 拍板 Q3 A)。對齊 color.spec.md L651-654 「table header / tab / code block / skeleton」靜態低重要 surface semantic */}\n <div className=\"flex-1 min-w-0 rounded-md bg-muted p-2 flex flex-col gap-2\">\n {group.children.map((cond, cIdx) => (\n <FilterRow\n key={cond.id}\n index={cIdx}\n condition={cond}\n conjunction={group.conjunction}\n filterableColumns={filterableColumns}\n fieldOptions={fieldOptions}\n onChangeConjunction={onChangeGroupConjunction}\n onChangeField={(v) => {\n const newCol = filterableColumns.find((c) => c.id === v)\n onChangeCondition(cond.id, { field: v, op: getDefaultOperator(newCol?.type), value: '', datePrecision: datePrecisionOf(newCol) })\n }}\n onChangeOp={(v) => onChangeCondition(cond.id, { op: v, value: '' })}\n onChangeValue={(v) => onChangeCondition(cond.id, { value: v })}\n onRemove={() => onRemoveCondition(cond.id)}\n />\n ))}\n {/* Q9 — text variant 對齊 inline 派 + 視覺輕量 */}\n <div className=\"flex items-center justify-between\">\n <Button variant=\"text\" size=\"sm\" startIcon={Plus} onClick={onAddCondition}>加入巢狀篩選</Button>\n {group.children.length === 0 && (\n <Button variant=\"text\" size=\"sm\" startIcon={Trash2} danger onClick={onRemoveGroup}>移除空群組</Button>\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-table-filter-group.js","sources":["../../../src/components/DataTable/data-table-filter-group.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved(2026-07-14 自 data-table-filter-panel.tsx 拆檔搬移,原檔級 escape 隨段落帶入)。\n// @internal — DS-internal 單元(filter row / nested-group renderer + 共用 column/operator\n// helpers;GroupBlock 為 nested-group renderer,FilterRow / ConjunctionLabel 為其 row 構件、\n// flat mode 共用。只被 data-table-filter-panel.tsx 消費);不隨 index.ts re-export 進 npm public surface。\n// ────────────────────────────── 消費的 SSOT ──────────────────────────────\n// - FieldControlGroup:field + op + value border-collapse 接合(field-control-group.spec.md;\n// FilterValuePicker 為 direct child,className forward 不開 wrapper div — 2026-05-04 #2 fix)\n// - Button(variant=\"text\" iconOnly)row meta trash / Select(size=\"sm\")conjunction・field・op\n// (data-table.spec.md「四、UI canonical」:trash = form-control row text Button;A6 conjunction)\n// - Width tokens:--data-table-filter-field-width / --data-table-filter-op-width(uiSize.css SSOT,\n// 2026-05-23 Phase A.4 Decision 2)\n// - Operator SSOT:./filter-operators.ts OPERATOR_REGISTRY(禁 hardcode op 字串)\n// - Group container 灰底 bg-muted:color.spec.md L651-654 靜態低重要 surface semantic(2026-05-09 Q3 A)\n// 設計規則:./filter-operators.spec.md + ./data-table.spec.md「進階篩選」段\n\nimport { Plus, Trash2 } from 'lucide-react'\nimport { Button } from '@/design-system/components/Button/button'\nimport { Select, type SelectOption } from '@/design-system/components/Select/select'\nimport { FieldControlGroup } from '@/design-system/components/FieldControlGroup/field-control-group'\nimport type { ColumnType } from './column-types'\nimport {\n OPERATOR_REGISTRY,\n DEFAULT_OPERATOR,\n getOperatorSpec,\n getValueShape,\n type ValueShape,\n} from './filter-operators'\nimport type { Conjunction, FilterCondition, FilterGroup } from './filter-tree'\nimport { FilterValuePicker } from './data-table-filter-value-picker'\nimport type { DataTableFilterPanelLabels } from './data-table-filter-labels'\n\n// ── Shared internal types + operator helpers(panel orchestration 也消費)────\n\nexport interface FilterColumn {\n id: string\n label: string\n type: ColumnType\n options?: Array<{ value: string; label: string }>\n /** People pool for person/multiPerson filter picker(對齊 cell-registry meta.people SSOT)*/\n people?: Array<{ name: string; avatarUrl?: string; description?: string }>\n includeTime?: boolean\n}\n\nfunction getOperatorOptions(\n type: ColumnType | undefined,\n operatorLabels: DataTableFilterPanelLabels['operatorLabels'],\n): SelectOption[] {\n const registry = type && OPERATOR_REGISTRY[type] ? OPERATOR_REGISTRY[type] : OPERATOR_REGISTRY.string\n return registry.map((op) => ({ value: op.op, label: operatorLabels[op.op] ?? op.label }))\n}\n\nexport function getDefaultOperator(type?: ColumnType): string {\n return (type && DEFAULT_OPERATOR[type]) || DEFAULT_OPERATOR.string\n}\n\n// Q6(2026-07-04):date 欄位把比對精度寫進 condition(includeTime=true → ms,否則 day)。\n// 'time' 欄暫不寫(值非完整日期,truncate 語意不成立;見 filter-operators.spec v3 time 註)。\nexport const datePrecisionOf = (col: FilterColumn | undefined): 'day' | 'ms' | undefined =>\n col?.type === 'date' ? (col.includeTime ? 'ms' : 'day') : undefined\n\n// ── ConjunctionLabel ───────────────────────────────────────────────────\n\nfunction ConjunctionLabel({\n index, conjunction, onChange, labels,\n}: {\n index: number\n conjunction: Conjunction\n onChange: (c: Conjunction) => void\n labels: DataTableFilterPanelLabels\n}) {\n // index === 0:首 row 顯示靜態「Where」label\n // index === 1:**唯一可改**的 AND/OR Select(連動整 group conjunction)\n // index ≥ 2:被連動的 row,read-only 顯示當前 conjunction 文字(同 Where 視覺,A6 canonical)\n // 對齊 Airtable / Notion / Linear 共識 @benchmark-unverified(non-OSS)\n // px-[var(--field-px)] 對齊 Field 內部 padding 12px(Q13)\n if (index === 0) {\n return <div className=\"w-20 shrink-0 text-body text-fg-secondary px-[var(--field-px)] self-center\">{labels.where}</div>\n }\n if (index >= 2) {\n const label = conjunction === 'and' ? labels.and : labels.or\n return <div className=\"w-20 shrink-0 text-body text-fg-secondary px-[var(--field-px)] self-center\">{label}</div>\n }\n // index === 1:可切換的 AND/OR Select\n // minRows={2} — And/Or 2 選項,顯式縮 menu 高度避免 reserve 3 row 空白(Q5)\n return (\n <div className=\"w-20 shrink-0\">\n <Select\n size=\"sm\"\n options={[\n { value: 'and', label: labels.and },\n { value: 'or', label: labels.or },\n ]}\n value={conjunction}\n onChange={(v) => onChange(v as Conjunction)}\n minRows={2}\n aria-label={labels.conjunctionAriaLabel}\n />\n </div>\n )\n}\n\n// ── FilterRow(flat 用 + group 內共用) ──────────────────────────────\n\nexport function FilterRow({\n index, condition, conjunction, filterableColumns, fieldOptions,\n onChangeConjunction, onChangeField, onChangeOp, onChangeValue, onRemove, labels,\n}: {\n index: number\n condition: FilterCondition\n conjunction: Conjunction\n filterableColumns: FilterColumn[]\n fieldOptions: SelectOption[]\n onChangeConjunction: (c: Conjunction) => void\n onChangeField: (v: string) => void\n onChangeOp: (v: string) => void\n onChangeValue: (v: unknown) => void\n onRemove: () => void\n labels: DataTableFilterPanelLabels\n}) {\n const colInfo = filterableColumns.find((c) => c.id === condition.field)\n const operatorOptions = getOperatorOptions(colInfo?.type, labels.operatorLabels)\n const hasField = !!condition.field\n const opSpec = colInfo ? getOperatorSpec(colInfo.type, condition.op) : null\n const valueShape: ValueShape | null = colInfo && opSpec\n ? getValueShape(opSpec, colInfo.type, colInfo.includeTime)\n : null\n // op 'is_set' / 'is_not_set' 等 shape='none' → 無 value cell,op 自動 expand 填剩餘寬\n // 對齊 Notion / Airtable / Linear filter row 行為\n // 注意:valueShape=null(初始無 field 選)時仍 render value cell(disabled placeholder)— 只 'none' 才 fold\n const hasValueCell = valueShape !== 'none'\n\n // FieldControlGroup 接合 field + op + value 視覺(2026-05-04 E refactor + 多輪 fix):\n // - border collapse 取代 3 顆獨立 Select 並排,對齊 Airtable / Linear / Notion filter row idiom\n // - ConjunctionLabel + Trash 在 group 外層(meta actions,不屬 control 一體)\n // - **#5 fix**:row 內水平 gap = `gap-2` (8px),layoutSpace 規則 5 緊密相關\n // - **#9 fix**:cell 用 `min-w-[]`(field 160 / op 120),value flex-1 min-w-0,讓 long label 可撐寬\n // - **#2 fix**:FilterValuePicker 直接是 FieldControlGroup direct child(無 wrapper div),CSS variants 命中正確\n return (\n <div className=\"flex items-center gap-2\">\n <ConjunctionLabel index={index} conjunction={conjunction} onChange={onChangeConjunction} labels={labels} />\n {/* **#9 fix(2026-05-04 v4)**:Field controls trigger `w-full` override 外 className,改用 Tailwind `!`\n important 強制 override(`!w-[160px]` / `!w-[120px]`),value 用 `!flex-1 !min-w-0`。\n Select 元件本身沒 destructure `style` prop 所以 inline style flex-basis 行不通,只能用 className。 */}\n <FieldControlGroup block className=\"flex-1 min-w-0\">\n {/* 2026-05-23 Phase A.4 Decision 2:`!w-[160px]` / `!w-[120px]` → tokens\n `--data-table-filter-field-width` / `--data-table-filter-op-width`(SSOT in uiSize.css)\n Behavior preserved 完好如初:flat + nested 同 width(token 是 design constant) */}\n <Select\n className=\"!w-[var(--data-table-filter-field-width)] flex-shrink-0 [&_.text-fg-muted]:!text-fg-secondary\"\n size=\"sm\"\n options={fieldOptions}\n value={condition.field}\n onChange={onChangeField}\n placeholder={labels.fieldPlaceholder}\n aria-label={labels.fieldAriaLabel}\n />\n <Select\n className={hasValueCell ? '!w-[var(--data-table-filter-op-width)] flex-shrink-0' : '!flex-1 !min-w-0'}\n size=\"sm\"\n options={operatorOptions}\n value={condition.op}\n onChange={onChangeOp}\n disabled={!hasField}\n placeholder={labels.operatorPlaceholder}\n aria-label={labels.operatorAriaLabel}\n />\n {hasValueCell && (\n <FilterValuePicker\n shape={valueShape}\n value={condition.value}\n onChange={onChangeValue}\n colInfo={colInfo}\n disabled={!hasField}\n ariaLabel={labels.filterValueAriaLabel(colInfo?.label)}\n labels={labels}\n className=\"!flex-1 !min-w-0\"\n />\n )}\n </FieldControlGroup>\n {/* Trash 用 text Button — filter row 是 form-control row,Field 同高對齊(28 md) */}\n <Button variant=\"text\" size=\"sm\" iconOnly startIcon={Trash2} aria-label={labels.deleteCondition} onClick={onRemove} />\n </div>\n )\n}\n\n// ── GroupBlock(nested 用) ────────────────────────────────────────────\n\nexport function GroupBlock({\n index, group, rootConjunction, filterableColumns, fieldOptions,\n onChangeRootConjunction, onChangeGroupConjunction,\n onChangeCondition, onRemoveCondition, onAddCondition, onRemoveGroup,\n canAddCondition, labels,\n}: {\n index: number\n group: FilterGroup\n rootConjunction: Conjunction\n filterableColumns: FilterColumn[]\n fieldOptions: SelectOption[]\n onChangeRootConjunction: (c: Conjunction) => void\n onChangeGroupConjunction: (c: Conjunction) => void\n onChangeCondition: (condId: string, patch: Partial<FilterCondition>) => void\n onRemoveCondition: (condId: string) => void\n onAddCondition: () => void\n onRemoveGroup: () => void\n canAddCondition: boolean\n labels: DataTableFilterPanelLabels\n}) {\n return (\n <div className=\"flex items-start gap-2\">\n <div className=\"pt-2\">\n <ConjunctionLabel index={index} conjunction={rootConjunction} onChange={onChangeRootConjunction} labels={labels} />\n </div>\n {/* Group container 灰底 — `bg-muted`(`--muted` neutral-2,user 2026-05-09 拍板 Q3 A)。對齊 color.spec.md L651-654 「table header / tab / code block / skeleton」靜態低重要 surface semantic */}\n <div className=\"flex-1 min-w-0 rounded-md bg-muted p-2 flex flex-col gap-2\">\n {group.children.map((cond, cIdx) => (\n <FilterRow\n key={cond.id}\n index={cIdx}\n condition={cond}\n conjunction={group.conjunction}\n filterableColumns={filterableColumns}\n fieldOptions={fieldOptions}\n onChangeConjunction={onChangeGroupConjunction}\n onChangeField={(v) => {\n const newCol = filterableColumns.find((c) => c.id === v)\n onChangeCondition(cond.id, { field: v, op: getDefaultOperator(newCol?.type), value: '', datePrecision: datePrecisionOf(newCol) })\n }}\n onChangeOp={(v) => onChangeCondition(cond.id, { op: v, value: '' })}\n onChangeValue={(v) => onChangeCondition(cond.id, { value: v })}\n onRemove={() => onRemoveCondition(cond.id)}\n labels={labels}\n />\n ))}\n {/* Q9 — text variant 對齊 inline 派 + 視覺輕量 */}\n <div className=\"flex items-center justify-between\">\n <Button variant=\"text\" size=\"sm\" startIcon={Plus} onClick={onAddCondition} disabled={!canAddCondition}>\n {labels.addNestedFilter}\n </Button>\n {group.children.length === 0 && (\n <Button variant=\"text\" size=\"sm\" startIcon={Trash2} danger onClick={onRemoveGroup}>\n {labels.removeEmptyGroup}\n </Button>\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { DATE_RELATIVE_GROUPS, DATE_RELATIVE_OPTIONS } from './filter-operators';
2
+ type RelativeDateGroupKey = (typeof DATE_RELATIVE_GROUPS)[number]['key'];
3
+ type RelativeDateValue = (typeof DATE_RELATIVE_OPTIONS)[number]['value'];
4
+ export interface DataTableFilterPanelLabels {
5
+ title: string;
6
+ resetToDefault: string;
7
+ close: string;
8
+ where: string;
9
+ and: string;
10
+ or: string;
11
+ conjunctionAriaLabel: string;
12
+ fieldPlaceholder: string;
13
+ fieldAriaLabel: string;
14
+ operatorPlaceholder: string;
15
+ operatorAriaLabel: string;
16
+ valuePlaceholder: string;
17
+ numberPlaceholder: string;
18
+ relativeDatePlaceholder: string;
19
+ singleSelectPlaceholder: string;
20
+ multiSelectPlaceholder: string;
21
+ multiSelectEmptyText: string;
22
+ peoplePlaceholder: string;
23
+ peopleSearchPlaceholder: string;
24
+ peopleSearchAriaLabel: string;
25
+ peopleEmptyText: string;
26
+ filterValueAriaLabel: (columnLabel?: string) => string;
27
+ deleteCondition: string;
28
+ addFilter: string;
29
+ addGroup: string;
30
+ addNestedFilter: string;
31
+ removeEmptyGroup: string;
32
+ /**
33
+ * Operator key → visible label override. Missing keys fall back to
34
+ * `OPERATOR_REGISTRY` labels, preserving the registry as the default SSOT.
35
+ */
36
+ operatorLabels: Partial<Record<string, string>>;
37
+ /** Relative-date group/value overrides; missing keys retain registry defaults. */
38
+ relativeDateGroupLabels: Partial<Record<RelativeDateGroupKey, string>>;
39
+ relativeDateOptionLabels: Partial<Record<RelativeDateValue, string>>;
40
+ }
41
+ export declare const DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS: DataTableFilterPanelLabels;
42
+ export declare function resolveDataTableFilterPanelLabels(overrides?: Partial<DataTableFilterPanelLabels>): DataTableFilterPanelLabels;
43
+ export {};
44
+ //# sourceMappingURL=data-table-filter-labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-filter-labels.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/data-table-filter-labels.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAErF,KAAK,oBAAoB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAA;AACxE,KAAK,iBAAiB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAA;AAExE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,EAAE,EAAE,MAAM,CAAA;IACV,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,EAAE,MAAM,CAAA;IACtB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uBAAuB,EAAE,MAAM,CAAA;IAC/B,sBAAsB,EAAE,MAAM,CAAA;IAC9B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,iBAAiB,EAAE,MAAM,CAAA;IACzB,uBAAuB,EAAE,MAAM,CAAA;IAC/B,qBAAqB,EAAE,MAAM,CAAA;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,oBAAoB,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACtD,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/C,kFAAkF;IAClF,uBAAuB,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,CAAA;IACtE,wBAAwB,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAA;CACrE;AAGD,eAAO,MAAM,sCAAsC,EAAE,0BA+BpD,CAAA;AAED,wBAAgB,iCAAiC,CAC/C,SAAS,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,GAC9C,0BAA0B,CAiB5B"}
@@ -0,0 +1,55 @@
1
+ const DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS = {
2
+ title: "篩選",
3
+ resetToDefault: "恢復預設",
4
+ close: "關閉",
5
+ where: "Where",
6
+ and: "And",
7
+ or: "Or",
8
+ conjunctionAriaLabel: "連接詞 — 同 group 共用",
9
+ fieldPlaceholder: "選擇欄位",
10
+ fieldAriaLabel: "篩選欄位",
11
+ operatorPlaceholder: "運算子",
12
+ operatorAriaLabel: "篩選運算子",
13
+ valuePlaceholder: "輸入值…",
14
+ numberPlaceholder: "輸入數字…",
15
+ relativeDatePlaceholder: "選擇相對日期",
16
+ singleSelectPlaceholder: "選擇值",
17
+ multiSelectPlaceholder: "選擇值…",
18
+ multiSelectEmptyText: "沒有符合的值",
19
+ peoplePlaceholder: "選擇人員",
20
+ peopleSearchPlaceholder: "搜尋人員…",
21
+ peopleSearchAriaLabel: "搜尋篩選人員",
22
+ peopleEmptyText: "沒有符合的人員",
23
+ filterValueAriaLabel: (columnLabel) => columnLabel ? `${columnLabel} 篩選值` : "篩選值",
24
+ deleteCondition: "刪除",
25
+ addFilter: "加篩選",
26
+ addGroup: "加入篩選器",
27
+ addNestedFilter: "加入巢狀篩選",
28
+ removeEmptyGroup: "移除空群組",
29
+ operatorLabels: {},
30
+ relativeDateGroupLabels: {},
31
+ relativeDateOptionLabels: {}
32
+ };
33
+ function resolveDataTableFilterPanelLabels(overrides) {
34
+ return {
35
+ ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS,
36
+ ...overrides,
37
+ operatorLabels: {
38
+ ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS.operatorLabels,
39
+ ...overrides == null ? void 0 : overrides.operatorLabels
40
+ },
41
+ relativeDateGroupLabels: {
42
+ ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS.relativeDateGroupLabels,
43
+ ...overrides == null ? void 0 : overrides.relativeDateGroupLabels
44
+ },
45
+ relativeDateOptionLabels: {
46
+ ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS.relativeDateOptionLabels,
47
+ ...overrides == null ? void 0 : overrides.relativeDateOptionLabels
48
+ }
49
+ };
50
+ }
51
+ export {
52
+ DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS,
53
+ resolveDataTableFilterPanelLabels
54
+ };
55
+ //# sourceMappingURL=data-table-filter-labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-filter-labels.js","sources":["../../../src/components/DataTable/data-table-filter-labels.ts"],"sourcesContent":["// @internal — DataTableFilterPanel chrome/operator/value-picker labels shared by\n// panel orchestration, row/group rendering, and value-picker dispatch.\n\nimport type { DATE_RELATIVE_GROUPS, DATE_RELATIVE_OPTIONS } from './filter-operators'\n\ntype RelativeDateGroupKey = (typeof DATE_RELATIVE_GROUPS)[number]['key']\ntype RelativeDateValue = (typeof DATE_RELATIVE_OPTIONS)[number]['value']\n\nexport interface DataTableFilterPanelLabels {\n title: string\n resetToDefault: string\n close: string\n where: string\n and: string\n or: string\n conjunctionAriaLabel: string\n fieldPlaceholder: string\n fieldAriaLabel: string\n operatorPlaceholder: string\n operatorAriaLabel: string\n valuePlaceholder: string\n numberPlaceholder: string\n relativeDatePlaceholder: string\n singleSelectPlaceholder: string\n multiSelectPlaceholder: string\n multiSelectEmptyText: string\n peoplePlaceholder: string\n peopleSearchPlaceholder: string\n peopleSearchAriaLabel: string\n peopleEmptyText: string\n filterValueAriaLabel: (columnLabel?: string) => string\n deleteCondition: string\n addFilter: string\n addGroup: string\n addNestedFilter: string\n removeEmptyGroup: string\n /**\n * Operator key → visible label override. Missing keys fall back to\n * `OPERATOR_REGISTRY` labels, preserving the registry as the default SSOT.\n */\n operatorLabels: Partial<Record<string, string>>\n /** Relative-date group/value overrides; missing keys retain registry defaults. */\n relativeDateGroupLabels: Partial<Record<RelativeDateGroupKey, string>>\n relativeDateOptionLabels: Partial<Record<RelativeDateValue, string>>\n}\n\n// code-quality-allow: dead-export — public i18n defaults, re-exported by panel.\nexport const DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS: DataTableFilterPanelLabels = {\n title: '篩選',\n resetToDefault: '恢復預設',\n close: '關閉',\n where: 'Where',\n and: 'And',\n or: 'Or',\n conjunctionAriaLabel: '連接詞 — 同 group 共用',\n fieldPlaceholder: '選擇欄位',\n fieldAriaLabel: '篩選欄位',\n operatorPlaceholder: '運算子',\n operatorAriaLabel: '篩選運算子',\n valuePlaceholder: '輸入值…',\n numberPlaceholder: '輸入數字…',\n relativeDatePlaceholder: '選擇相對日期',\n singleSelectPlaceholder: '選擇值',\n multiSelectPlaceholder: '選擇值…',\n multiSelectEmptyText: '沒有符合的值',\n peoplePlaceholder: '選擇人員',\n peopleSearchPlaceholder: '搜尋人員…',\n peopleSearchAriaLabel: '搜尋篩選人員',\n peopleEmptyText: '沒有符合的人員',\n filterValueAriaLabel: (columnLabel) => columnLabel ? `${columnLabel} 篩選值` : '篩選值',\n deleteCondition: '刪除',\n addFilter: '加篩選',\n addGroup: '加入篩選器',\n addNestedFilter: '加入巢狀篩選',\n removeEmptyGroup: '移除空群組',\n operatorLabels: {},\n relativeDateGroupLabels: {},\n relativeDateOptionLabels: {},\n}\n\nexport function resolveDataTableFilterPanelLabels(\n overrides?: Partial<DataTableFilterPanelLabels>,\n): DataTableFilterPanelLabels {\n return {\n ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS,\n ...overrides,\n operatorLabels: {\n ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS.operatorLabels,\n ...overrides?.operatorLabels,\n },\n relativeDateGroupLabels: {\n ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS.relativeDateGroupLabels,\n ...overrides?.relativeDateGroupLabels,\n },\n relativeDateOptionLabels: {\n ...DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS.relativeDateOptionLabels,\n ...overrides?.relativeDateOptionLabels,\n },\n }\n}\n"],"names":[],"mappings":"AA+CO,MAAM,yCAAqE;AAAA,EAChF,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,sBAAsB,CAAC,gBAAgB,cAAc,GAAG,WAAW,SAAS;AAAA,EAC5E,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,gBAAgB,CAAA;AAAA,EAChB,yBAAyB,CAAA;AAAA,EACzB,0BAA0B,CAAA;AAC5B;AAEO,SAAS,kCACd,WAC4B;AAC5B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,gBAAgB;AAAA,MACd,GAAG,uCAAuC;AAAA,MAC1C,GAAG,uCAAW;AAAA,IAAA;AAAA,IAEhB,yBAAyB;AAAA,MACvB,GAAG,uCAAuC;AAAA,MAC1C,GAAG,uCAAW;AAAA,IAAA;AAAA,IAEhB,0BAA0B;AAAA,MACxB,GAAG,uCAAuC;AAAA,MAC1C,GAAG,uCAAW;AAAA,IAAA;AAAA,EAChB;AAEJ;"}
@@ -1,8 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import type { ColumnDef } from '@tanstack/react-table';
3
3
  import { createEmptyFilterTree, isFilterTreeActive, evaluateTree, dataTableFilterMatch, type Conjunction, type FilterCondition, type FilterGroup, type FilterTree, type FilterTreeFlat, type FilterTreeNested } from './filter-tree';
4
+ import { DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS, type DataTableFilterPanelLabels } from './data-table-filter-labels';
4
5
  export { createEmptyFilterTree, isFilterTreeActive, evaluateTree, dataTableFilterMatch, };
5
6
  export type { Conjunction, FilterCondition, FilterGroup, FilterTree, FilterTreeFlat, FilterTreeNested };
7
+ export { DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS };
8
+ export type { DataTableFilterPanelLabels };
6
9
  export interface DataTableFilterPanelProps<TData> {
7
10
  /** 可被 filter 的 columns */
8
11
  columns: ColumnDef<TData, any>[];
@@ -19,6 +22,14 @@ export interface DataTableFilterPanelProps<TData> {
19
22
  /** Cell ⌄ menu「Filter by this」帶入的 column id(自動 add 一條 condition) */
20
23
  prefilledColumnId?: string;
21
24
  onPrefillConsumed?: () => void;
25
+ /**
26
+ * Condition leaf 上限(flat children 或 nested 所有 group.children 的總和)。
27
+ * 預設不限。到上限後所有 add 路徑與 prefill 都 fail closed;controlled value
28
+ * 已超過上限時保留原資料、不裁切,只停用後續新增。
29
+ */
30
+ maxConditions?: number;
31
+ /** Panel chrome/operator/value-picker labels. Nested maps are deep-merged with defaults. */
32
+ labels?: Partial<DataTableFilterPanelLabels>;
22
33
  onClose?: () => void;
23
34
  className?: string;
24
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"data-table-filter-panel.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/data-table-filter-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAgBtD,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAElB,YAAY,EACZ,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAA;AAGtB,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,GACrB,CAAA;AACD,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AAsFvG,MAAM,WAAW,yBAAyB,CAAC,KAAK;IAI9C,0BAA0B;IAC1B,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAA;IAChC,gCAAgC;IAChC,KAAK,EAAE,UAAU,CAAA;IACjB,wBAAwB;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAA;IACpC;;;;OAIG;IACH,YAAY,CAAC,EAAE,UAAU,CAAA;IACzB,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA6PD,eAAO,MAAM,oBAAoB,EAAkD,CAAC,KAAK,EACvF,KAAK,EAAE,yBAAyB,CAAC,KAAK,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;CAAE,KACnF,KAAK,CAAC,YAAY,CACtB"}
1
+ {"version":3,"file":"data-table-filter-panel.d.ts","sourceRoot":"","sources":["../../../src/components/DataTable/data-table-filter-panel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAgBtD,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAElB,YAAY,EACZ,oBAAoB,EACpB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,sCAAsC,EAEtC,KAAK,0BAA0B,EAChC,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,GACrB,CAAA;AACD,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AACvG,OAAO,EAAE,sCAAsC,EAAE,CAAA;AACjD,YAAY,EAAE,0BAA0B,EAAE,CAAA;AAiG1C,MAAM,WAAW,yBAAyB,CAAC,KAAK;IAI9C,0BAA0B;IAC1B,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAA;IAChC,gCAAgC;IAChC,KAAK,EAAE,UAAU,CAAA;IACjB,wBAAwB;IACxB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAA;IACpC;;;;OAIG;IACH,YAAY,CAAC,EAAE,UAAU,CAAA;IACzB,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAA;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA2QD,eAAO,MAAM,oBAAoB,EAAkD,CAAC,KAAK,EACvF,KAAK,EAAE,yBAAyB,CAAC,KAAK,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,cAAc,CAAC,CAAA;CAAE,KACnF,KAAK,CAAC,YAAY,CACtB"}
@@ -10,6 +10,8 @@ import { getColumnId, getColumnMeta, getColumnLabel } from "./lib/column-meta.js
10
10
  import { datePrecisionOf, getDefaultOperator, FilterRow, GroupBlock } from "./data-table-filter-group.js";
11
11
  import { isFilterTreeEqual } from "./filter-tree.js";
12
12
  import { createEmptyFilterTree, dataTableFilterMatch, evaluateTree, isFilterTreeActive } from "./filter-tree.js";
13
+ import { resolveDataTableFilterPanelLabels } from "./data-table-filter-labels.js";
14
+ import { DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS } from "./data-table-filter-labels.js";
13
15
  let _idSeed = 0;
14
16
  const newId = () => `f${++_idSeed}-${Date.now().toString(36)}`;
15
17
  function extractColumns(columns) {
@@ -60,6 +62,12 @@ const newEmptyGroup = () => ({
60
62
  conjunction: "or",
61
63
  children: [newEmptyCondition()]
62
64
  });
65
+ const countFilterConditions = (tree) => tree.mode === "flat" ? tree.children.length : tree.children.reduce((total, group) => total + group.children.length, 0);
66
+ const normalizeMaxConditions = (maxConditions) => {
67
+ if (maxConditions === void 0 || maxConditions === Number.POSITIVE_INFINITY) return Number.POSITIVE_INFINITY;
68
+ if (!Number.isFinite(maxConditions)) return 0;
69
+ return Math.max(0, Math.floor(maxConditions));
70
+ };
63
71
  function DataTableFilterPanelInner({
64
72
  columns,
65
73
  value,
@@ -67,6 +75,8 @@ function DataTableFilterPanelInner({
67
75
  defaultValue,
68
76
  prefilledColumnId,
69
77
  onPrefillConsumed,
78
+ maxConditions,
79
+ labels: labelsOverride,
70
80
  onClose,
71
81
  className
72
82
  }, ref) {
@@ -75,12 +85,20 @@ function DataTableFilterPanelInner({
75
85
  () => filterableColumns.map((c) => ({ value: c.id, label: c.label })),
76
86
  [filterableColumns]
77
87
  );
88
+ const labels = React.useMemo(
89
+ () => resolveDataTableFilterPanelLabels(labelsOverride),
90
+ [labelsOverride]
91
+ );
92
+ const conditionCount = countFilterConditions(value);
93
+ const normalizedMaxConditions = normalizeMaxConditions(maxConditions);
94
+ const canAddCondition = conditionCount < normalizedMaxConditions;
78
95
  const initialMountDoneRef = React.useRef(false);
79
96
  React.useEffect(() => {
80
97
  if (initialMountDoneRef.current) return;
81
98
  initialMountDoneRef.current = true;
82
99
  if (filterableColumns.length === 0) return;
83
100
  if (value.children.length > 0) return;
101
+ if (!canAddCondition) return;
84
102
  if (value.mode === "flat") {
85
103
  onChange({ ...value, children: [newEmptyCondition()] });
86
104
  } else {
@@ -90,7 +108,7 @@ function DataTableFilterPanelInner({
90
108
  React.useEffect(() => {
91
109
  if (!prefilledColumnId) return;
92
110
  const colInfo = filterableColumns.find((c) => c.id === prefilledColumnId);
93
- if (colInfo) {
111
+ if (colInfo && canAddCondition) {
94
112
  const cond = {
95
113
  kind: "cond",
96
114
  id: newId(),
@@ -129,7 +147,7 @@ function DataTableFilterPanelInner({
129
147
  onChange({ ...flatTree, children: flatTree.children.filter((c) => c.id !== id) });
130
148
  };
131
149
  const addFlatCondition = () => {
132
- if (!flatTree) return;
150
+ if (!flatTree || !canAddCondition) return;
133
151
  onChange({ ...flatTree, children: [...flatTree.children, newEmptyCondition()] });
134
152
  };
135
153
  const setFlatConjunction = (conj) => {
@@ -163,7 +181,7 @@ function DataTableFilterPanelInner({
163
181
  });
164
182
  };
165
183
  const addConditionToGroup = (groupId) => {
166
- if (!nestedTree) return;
184
+ if (!nestedTree || !canAddCondition) return;
167
185
  onChange({
168
186
  ...nestedTree,
169
187
  children: nestedTree.children.map(
@@ -176,7 +194,7 @@ function DataTableFilterPanelInner({
176
194
  onChange({ ...nestedTree, children: nestedTree.children.filter((g) => g.id !== groupId) });
177
195
  };
178
196
  const addGroup = () => {
179
- if (!nestedTree) return;
197
+ if (!nestedTree || !canAddCondition) return;
180
198
  onChange({ ...nestedTree, children: [...nestedTree.children, newEmptyGroup()] });
181
199
  };
182
200
  const setRootConjunction = (conj) => {
@@ -204,7 +222,7 @@ function DataTableFilterPanelInner({
204
222
  className
205
223
  ), children: [
206
224
  /* @__PURE__ */ jsxs(SurfaceHeader, { className: COMPACT_HEADER_SLOT, children: [
207
- /* @__PURE__ */ jsx(PopoverTitle, { className: "flex-1", children: "篩選" }),
225
+ /* @__PURE__ */ jsx(PopoverTitle, { className: "flex-1", children: labels.title }),
208
226
  defaultValue && !isFilterTreeEqual(value, defaultValue) && /* @__PURE__ */ jsxs(Fragment, { children: [
209
227
  /* @__PURE__ */ jsx(
210
228
  Button,
@@ -213,13 +231,13 @@ function DataTableFilterPanelInner({
213
231
  size: "sm",
214
232
  iconOnly: true,
215
233
  startIcon: RotateCcw,
216
- "aria-label": "恢復預設",
234
+ "aria-label": labels.resetToDefault,
217
235
  onClick: () => onChange(defaultValue)
218
236
  }
219
237
  ),
220
238
  onClose && /* @__PURE__ */ jsx(ButtonDivider, {})
221
239
  ] }),
222
- onClose && /* @__PURE__ */ jsx(PopoverClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { "data-dismiss": true, iconOnly: true, dismiss: true, size: "sm", startIcon: X, "aria-label": "關閉", onClick: onClose }) })
240
+ onClose && /* @__PURE__ */ jsx(PopoverClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { "data-dismiss": true, iconOnly: true, dismiss: true, size: "sm", startIcon: X, "aria-label": labels.close, onClick: onClose }) })
223
241
  ] }),
224
242
  /* @__PURE__ */ jsxs(SurfaceBody, { className: "flex flex-col gap-[var(--layout-space-tight)]", children: [
225
243
  flatTree && flatTree.children.map((cond, idx) => /* @__PURE__ */ jsx(
@@ -237,7 +255,8 @@ function DataTableFilterPanelInner({
237
255
  },
238
256
  onChangeOp: (v) => updateFlatCondition(cond.id, { op: v, value: "" }),
239
257
  onChangeValue: (v) => updateFlatCondition(cond.id, { value: v }),
240
- onRemove: () => removeFlatCondition(cond.id)
258
+ onRemove: () => removeFlatCondition(cond.id),
259
+ labels
241
260
  },
242
261
  cond.id
243
262
  )),
@@ -254,7 +273,9 @@ function DataTableFilterPanelInner({
254
273
  onChangeCondition: (condId, patch) => updateGroupCondition(group.id, condId, patch),
255
274
  onRemoveCondition: (condId) => removeGroupCondition(group.id, condId),
256
275
  onAddCondition: () => addConditionToGroup(group.id),
257
- onRemoveGroup: () => removeGroup(group.id)
276
+ onRemoveGroup: () => removeGroup(group.id),
277
+ canAddCondition,
278
+ labels
258
279
  },
259
280
  group.id
260
281
  )),
@@ -265,7 +286,8 @@ function DataTableFilterPanelInner({
265
286
  size: "sm",
266
287
  startIcon: Plus,
267
288
  onClick: value.mode === "flat" ? addFlatCondition : addGroup,
268
- children: value.mode === "nested" ? "加入篩選器" : "加篩選"
289
+ disabled: !canAddCondition,
290
+ children: value.mode === "nested" ? labels.addGroup : labels.addFilter
269
291
  }
270
292
  ) })
271
293
  ] })
@@ -275,6 +297,7 @@ function DataTableFilterPanelInner({
275
297
  const DataTableFilterPanel = React.forwardRef(DataTableFilterPanelInner);
276
298
  DataTableFilterPanel.displayName = "DataTableFilterPanel";
277
299
  export {
300
+ DATA_TABLE_FILTER_PANEL_DEFAULT_LABELS,
278
301
  DataTableFilterPanel,
279
302
  createEmptyFilterTree,
280
303
  dataTableFilterMatch,