@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.
- package/README.md +1 -0
- package/dist/components/AccountMenu/account-menu.d.ts +86 -0
- package/dist/components/AccountMenu/account-menu.d.ts.map +1 -0
- package/dist/components/AccountMenu/account-menu.js +64 -0
- package/dist/components/AccountMenu/account-menu.js.map +1 -0
- package/dist/components/AccountMenu/index.d.ts +2 -0
- package/dist/components/AccountMenu/index.d.ts.map +1 -0
- package/dist/components/AccountMenu/index.js +6 -0
- package/dist/components/AccountMenu/index.js.map +1 -0
- package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +2 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Badge/badge.d.ts +1 -1
- package/dist/components/Button/button.d.ts +3 -3
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +3 -2
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +10 -21
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +5 -36
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +1 -0
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js +3 -1
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-group.d.ts +6 -2
- package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-group.js +32 -27
- package/dist/components/DataTable/data-table-filter-group.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-labels.d.ts +44 -0
- package/dist/components/DataTable/data-table-filter-labels.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-labels.js +55 -0
- package/dist/components/DataTable/data-table-filter-labels.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts +11 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +33 -10
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts +4 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.js +22 -8
- package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -1
- package/dist/components/DataTable/data-table.js +2 -2
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DataTable/index.js +2 -0
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +5 -1
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +46 -14
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts +2 -5
- package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js +2 -2
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileUpload/file-upload.js +1 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +20 -3
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js +4 -2
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +2 -0
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +4 -0
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +4 -3
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +2 -0
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +3 -1
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +3 -0
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +1 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +4 -3
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +26 -1
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +2 -0
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +3 -2
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +6 -2
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.d.ts +11 -4
- package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.js +1 -1
- package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts +4 -3
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js +1 -0
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/dist/patterns/resize-handle/resize-handle.d.ts +9 -11
- package/dist/patterns/resize-handle/resize-handle.d.ts.map +1 -1
- package/dist/patterns/resize-handle/resize-handle.js +3 -6
- package/dist/patterns/resize-handle/resize-handle.js.map +1 -1
- package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +1 -1
- package/ds-canonical/adapters/claude-root-instructions.md +1 -1
- package/ds-canonical/adapters/claude-settings-base.json +2 -5
- package/ds-canonical/fork/AGENTS.md +21 -10
- package/ds-canonical/fork/codex/hooks.json +1 -1
- package/ds-canonical/fork/common/instruction.md +21 -10
- package/ds-canonical/fork/consumer/governance-check.mjs +101 -13
- package/ds-canonical/fork/consumer/lock.json +14 -15
- package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +26 -3
- package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +61 -5
- package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +3 -10
- package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +3 -2
- package/ds-canonical/fork/governance.lock +17 -33
- package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +33 -0
- package/ds-canonical/fork/hooks/check_pattern_invariants.sh +39 -0
- package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
- package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +5 -11
- package/ds-canonical/fork/launchers/settings-hooks.json +2 -2
- package/ds-canonical/fork/manifest.json +11 -13
- package/ds-canonical/fork/providers/claude/hook-config.json +1 -7
- package/ds-canonical/fork/providers/codex/hook-config.json +1 -1
- package/ds-canonical/fork/references/story-baseline-registry.json +7 -1
- package/ds-canonical/hooks/check_consumer_code_quality.sh +33 -0
- package/ds-canonical/hooks/check_pattern_invariants.sh +39 -0
- package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
- package/ds-canonical/hooks/session_start_governance_check.sh +5 -6
- package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +23 -1
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +36 -2
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
- package/ds-canonical/references/failure-class-registry.json +151 -144
- package/ds-canonical/references/preflight-gate-baseline.json +16 -12
- package/ds-canonical/references/story-baseline-registry.json +7 -1
- package/ds-canonical/rules/meta-patterns.md +3 -3
- package/ds-canonical/skills/design-system-audit/SKILL.md +1 -1
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1 -1
- package/ds-canonical/templates/dashboard-app.tsx +31 -8
- package/llms-full.txt +6 -1
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/src/components/AccountMenu/account-menu.anatomy.stories.tsx +115 -0
- package/src/components/AccountMenu/account-menu.principles.stories.tsx +99 -0
- package/src/components/AccountMenu/account-menu.spec.md +101 -0
- package/src/components/AccountMenu/account-menu.stories.tsx +61 -0
- package/src/components/AccountMenu/account-menu.tsx +170 -0
- package/src/components/AccountMenu/index.ts +5 -0
- package/src/components/AppShell/_demo-helpers.tsx +53 -53
- package/src/components/AppShell/app-shell.spec.md +5 -1
- package/src/components/AppShell/app-shell.tsx +14 -12
- package/src/components/Avatar/avatar.principles.stories.tsx +4 -4
- package/src/components/Avatar/avatar.stories.tsx +20 -5
- package/src/components/Breadcrumb/breadcrumb.spec.md +1 -1
- package/src/components/Button/button.spec.md +1 -1
- package/src/components/Button/button.stories.tsx +21 -9
- package/src/components/Button/button.tsx +5 -4
- package/src/components/Calendar/calendar.anatomy.stories.tsx +19 -17
- package/src/components/Calendar/calendar.principles.stories.tsx +5 -6
- package/src/components/Calendar/calendar.spec.md +24 -28
- package/src/components/Calendar/calendar.stories.tsx +5 -7
- package/src/components/Calendar/calendar.tsx +16 -62
- package/src/components/Carousel/carousel.principles.stories.tsx +5 -1
- package/src/components/Chart/chart.spec.md +1 -1
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +14 -5
- package/src/components/Chip/chip.spec.md +1 -1
- package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +7 -6
- package/src/components/CircularProgress/circular-progress.principles.stories.tsx +8 -6
- package/src/components/CircularProgress/circular-progress.stories.tsx +3 -3
- package/src/components/Combobox/combobox.tsx +1 -0
- package/src/components/Command/command.spec.md +1 -0
- package/src/components/Command/command.tsx +4 -1
- package/src/components/DataTable/data-table-filter-group.tsx +44 -27
- package/src/components/DataTable/data-table-filter-labels.ts +100 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +48 -8
- package/src/components/DataTable/data-table-filter-value-picker.tsx +25 -8
- package/src/components/DataTable/data-table.spec.md +17 -1
- package/src/components/DataTable/data-table.stories.tsx +346 -33
- package/src/components/DataTable/data-table.tsx +2 -2
- package/src/components/DataTable/filter-operators.spec.md +2 -2
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +24 -20
- package/src/components/DateGrid/date-grid.principles.stories.tsx +27 -21
- package/src/components/DateGrid/date-grid.stories.tsx +22 -13
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +3 -3
- package/src/components/DatePicker/date-picker.spec.md +3 -3
- package/src/components/DatePicker/date-picker.stories.tsx +33 -9
- package/src/components/DatePicker/date-picker.tsx +8 -1
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -0
- package/src/components/DropdownMenu/dropdown-menu.stories.tsx +3 -17
- package/src/components/DropdownMenu/dropdown-menu.tsx +58 -16
- package/src/components/Field/field.spec.md +1 -1
- package/src/components/Field/field.tsx +1 -0
- package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +2 -3
- package/src/components/FieldControlGroup/field-control-group.stories.tsx +2 -1
- package/src/components/FieldControlGroup/field-control-group.tsx +6 -9
- package/src/components/FileItem/file-item.spec.md +1 -1
- package/src/components/FileItem/file-item.stories.tsx +1 -1
- package/src/components/FileUpload/file-upload.stories.tsx +32 -0
- package/src/components/FileUpload/file-upload.tsx +1 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +7 -0
- package/src/components/FileViewer/file-viewer.tsx +23 -5
- package/src/components/InlineEdit/inline-edit.stories.tsx +2 -2
- package/src/components/Input/input.anatomy.stories.tsx +28 -12
- package/src/components/Input/input.principles.stories.tsx +23 -18
- package/src/components/Input/input.stories.tsx +9 -1
- package/src/components/LinkInput/link-input.principles.stories.tsx +7 -7
- package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
- package/src/components/Menu/menu-item.tsx +9 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +44 -10
- package/src/components/NumberInput/number-input.principles.stories.tsx +20 -14
- package/src/components/NumberInput/number-input.spec.md +1 -1
- package/src/components/NumberInput/number-input.stories.tsx +12 -5
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +7 -0
- package/src/components/PeoplePicker/people-picker.spec.md +3 -1
- package/src/components/PeoplePicker/people-picker.tsx +5 -0
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +14 -12
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +5 -1
- package/src/components/RadioGroup/radio-group.spec.md +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +2 -2
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +3 -3
- package/src/components/ScrollArea/scroll-area.tsx +5 -4
- package/src/components/SelectMenu/select-menu.spec.md +1 -1
- package/src/components/SelectMenu/select-menu.tsx +4 -1
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +19 -8
- package/src/components/SelectionControl/selection-item.stories.tsx +7 -7
- package/src/components/Sheet/sheet.spec.md +1 -1
- package/src/components/Sidebar/sidebar.spec.md +3 -2
- package/src/components/Sidebar/sidebar.stories.tsx +67 -37
- package/src/components/Sidebar/sidebar.tsx +3 -3
- package/src/components/Slider/slider.principles.stories.tsx +9 -9
- package/src/components/Steps/steps.spec.md +1 -0
- package/src/components/Steps/steps.stories.tsx +18 -1
- package/src/components/Switch/switch.anatomy.stories.tsx +11 -11
- package/src/components/Switch/switch.principles.stories.tsx +11 -8
- package/src/components/Tabs/tabs.principles.stories.tsx +24 -0
- package/src/components/Tabs/tabs.spec.md +6 -4
- package/src/components/Tabs/tabs.tsx +11 -0
- package/src/components/Tag/tag.anatomy.stories.tsx +5 -2
- package/src/components/Tag/tag.spec.md +4 -3
- package/src/components/Tag/tag.stories.tsx +19 -0
- package/src/components/Tag/tag.tsx +14 -5
- package/src/components/Textarea/textarea.anatomy.stories.tsx +15 -9
- package/src/components/Textarea/textarea.principles.stories.tsx +21 -5
- package/src/components/Tooltip/tooltip.spec.md +4 -0
- package/src/components/Tooltip/tooltip.tsx +34 -3
- package/src/components/TreeView/tree-view.spec.md +3 -3
- package/src/components/TreeView/tree-view.stories.tsx +68 -9
- package/src/components/TreeView/tree-view.tsx +7 -2
- package/src/index.ts +9 -1
- package/src/patterns/element-anatomy/inline-action.spec.md +12 -8
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +24 -5
- package/src/patterns/element-anatomy/item-anatomy.tsx +9 -5
- package/src/patterns/header-canonical/chrome-header.tsx +14 -5
- package/src/patterns/header-canonical/header-canonical.spec.md +10 -7
- package/src/patterns/header-canonical/header-canonical.stories.tsx +77 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +2 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +1 -0
- package/src/patterns/overlay-surface/overlay-surface.stories.tsx +17 -1
- package/src/patterns/overlay-surface/overlay-surface.tsx +8 -4
- package/src/patterns/resize-handle/resize-handle.spec.md +11 -13
- package/src/patterns/resize-handle/resize-handle.stories.tsx +1 -3
- package/src/patterns/resize-handle/resize-handle.tsx +13 -18
- package/src/tokens/color/semantic.css +3 -1
- package/src/tokens/layoutSpace/layoutSpace.stories.tsx +1 -1
- package/src/tokens/opacity/opacity.stories.tsx +1 -1
- package/src/tokens/radius/radius.stories.tsx +3 -3
- package/ds-canonical/commands/gov-status.md +0 -9
- package/ds-canonical/fork/consumer/managed-files/77e06f62670dfee01e9b5e8b0f8dad085ce711d75538c1fb0679d9818acaca82.blob +0 -424
- package/ds-canonical/fork/preamble.md +0 -500
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +0 -8
- 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-
|
|
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" ?
|
|
30
|
-
return /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0 text-body text-fg-
|
|
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:
|
|
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":
|
|
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 ?
|
|
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":
|
|
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;
|
|
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":
|
|
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
|
-
|
|
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,
|