@open-mercato/ui 0.6.1-develop.3276.1.1fcd145078 → 0.6.1-develop.3278.1.a4d26475e4
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/.turbo/turbo-build.log +1 -1
- package/dist/backend/DataTable.js +149 -53
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/filters/ActiveFilterChips.js +142 -0
- package/dist/backend/filters/ActiveFilterChips.js.map +7 -0
- package/dist/backend/filters/AdvancedFilterBuilder.js +689 -163
- package/dist/backend/filters/AdvancedFilterBuilder.js.map +2 -2
- package/dist/backend/filters/AdvancedFilterPanel.js +354 -0
- package/dist/backend/filters/AdvancedFilterPanel.js.map +7 -0
- package/dist/backend/filters/FilterEmptyState.js +41 -0
- package/dist/backend/filters/FilterEmptyState.js.map +7 -0
- package/dist/backend/filters/FilterFieldPicker.js +148 -0
- package/dist/backend/filters/FilterFieldPicker.js.map +7 -0
- package/dist/backend/filters/FilteredEmptyResults.js +26 -0
- package/dist/backend/filters/FilteredEmptyResults.js.map +7 -0
- package/dist/backend/filters/QuickFilters.js +41 -0
- package/dist/backend/filters/QuickFilters.js.map +7 -0
- package/dist/backend/filters/filterValidation.js +47 -0
- package/dist/backend/filters/filterValidation.js.map +7 -0
- package/dist/backend/filters/treeReducer.js +159 -0
- package/dist/backend/filters/treeReducer.js.map +7 -0
- package/dist/backend/hooks/useAdvancedFilter.js +82 -1
- package/dist/backend/hooks/useAdvancedFilter.js.map +2 -2
- package/dist/backend/utils/api.js +18 -1
- package/dist/backend/utils/api.js.map +2 -2
- package/dist/backend/utils/useAutoDiscoveredFields.js +1 -0
- package/dist/backend/utils/useAutoDiscoveredFields.js.map +2 -2
- package/dist/backend/utils/useCurrentUserId.js +31 -0
- package/dist/backend/utils/useCurrentUserId.js.map +7 -0
- package/dist/primitives/dialog.js +7 -5
- package/dist/primitives/dialog.js.map +2 -2
- package/dist/primitives/tag.js +4 -2
- package/dist/primitives/tag.js.map +2 -2
- package/package.json +3 -3
- package/src/backend/DataTable.tsx +307 -63
- package/src/backend/__tests__/DataTable.extensions.test.tsx +170 -0
- package/src/backend/filters/ActiveFilterChips.tsx +179 -0
- package/src/backend/filters/AdvancedFilterBuilder.tsx +840 -237
- package/src/backend/filters/AdvancedFilterPanel.tsx +456 -0
- package/src/backend/filters/FilterEmptyState.tsx +51 -0
- package/src/backend/filters/FilterFieldPicker.tsx +139 -0
- package/src/backend/filters/FilteredEmptyResults.tsx +40 -0
- package/src/backend/filters/QuickFilters.tsx +62 -0
- package/src/backend/filters/__tests__/ActiveFilterChips.test.tsx +75 -0
- package/src/backend/filters/__tests__/AdvancedFilterBuilder.test.tsx +210 -0
- package/src/backend/filters/__tests__/AdvancedFilterPanel.test.tsx +212 -0
- package/src/backend/filters/__tests__/FilterEmptyState.test.tsx +31 -0
- package/src/backend/filters/__tests__/FilterFieldPicker.test.tsx +61 -0
- package/src/backend/filters/__tests__/FilteredEmptyResults.test.tsx +35 -0
- package/src/backend/filters/__tests__/QuickFilters.test.tsx +34 -0
- package/src/backend/filters/__tests__/filterValidation.test.ts +69 -0
- package/src/backend/filters/__tests__/treeReducer.test.ts +189 -0
- package/src/backend/filters/filterValidation.ts +61 -0
- package/src/backend/filters/treeReducer.ts +201 -0
- package/src/backend/hooks/__tests__/useAdvancedFilter.test.tsx +213 -0
- package/src/backend/hooks/useAdvancedFilter.ts +130 -0
- package/src/backend/utils/api.ts +27 -2
- package/src/backend/utils/useAutoDiscoveredFields.ts +2 -0
- package/src/backend/utils/useCurrentUserId.ts +40 -0
- package/src/primitives/__tests__/zindex-overlay.test.tsx +22 -0
- package/src/primitives/dialog.tsx +18 -7
- package/src/primitives/tag.tsx +3 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { Tag } from "../../primitives/tag.js";
|
|
6
|
+
import { IconButton } from "../../primitives/icon-button.js";
|
|
7
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
|
+
import { isValuelessOperator } from "@open-mercato/shared/lib/query/advanced-filter";
|
|
9
|
+
import { validateTreeForApply } from "./filterValidation.js";
|
|
10
|
+
const MAX_LABEL = 24;
|
|
11
|
+
function truncate(s) {
|
|
12
|
+
if (s.length <= MAX_LABEL) return s;
|
|
13
|
+
return s.slice(0, MAX_LABEL) + "\u2026";
|
|
14
|
+
}
|
|
15
|
+
function getOption(field, value) {
|
|
16
|
+
if (!field?.options) return void 0;
|
|
17
|
+
return field.options.find((o) => o.value === value);
|
|
18
|
+
}
|
|
19
|
+
function valueText(field, value) {
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
if (value.length === 0) return { text: "" };
|
|
22
|
+
const first = String(value[0]);
|
|
23
|
+
const opt2 = getOption(field, first);
|
|
24
|
+
const display = opt2?.label ?? first;
|
|
25
|
+
const more = value.length - 1;
|
|
26
|
+
return {
|
|
27
|
+
text: more > 0 ? `${truncate(display)} +${more}` : truncate(display),
|
|
28
|
+
tone: opt2?.tone
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (value == null || value === "") return { text: "" };
|
|
32
|
+
const str = String(value);
|
|
33
|
+
const opt = getOption(field, str);
|
|
34
|
+
return { text: truncate(opt?.label ?? str), tone: opt?.tone };
|
|
35
|
+
}
|
|
36
|
+
const VALUELESS_OPERATOR_LABEL = {
|
|
37
|
+
is_empty: "is empty",
|
|
38
|
+
is_not_empty: "is not empty",
|
|
39
|
+
is_true: "is true",
|
|
40
|
+
is_false: "is false"
|
|
41
|
+
};
|
|
42
|
+
function ruleChip(rule, fields, t) {
|
|
43
|
+
const field = fields.find((f) => f.key === rule.field);
|
|
44
|
+
const fieldLabel = field?.label ?? rule.field;
|
|
45
|
+
if (isValuelessOperator(rule.operator)) {
|
|
46
|
+
const fallback = VALUELESS_OPERATOR_LABEL[rule.operator] ?? rule.operator;
|
|
47
|
+
const opLabel = t(`ui.advancedFilter.operator.${rule.operator}`, fallback);
|
|
48
|
+
return { label: `${fieldLabel} ${opLabel}` };
|
|
49
|
+
}
|
|
50
|
+
if (rule.operator === "between" && Array.isArray(rule.value)) {
|
|
51
|
+
const [start, end] = rule.value;
|
|
52
|
+
const startStr = start == null ? "" : String(start);
|
|
53
|
+
const endStr = end == null ? "" : String(end);
|
|
54
|
+
return { label: `${fieldLabel}: ${truncate(startStr)} \u2013 ${truncate(endStr)}` };
|
|
55
|
+
}
|
|
56
|
+
const v = valueText(field, rule.value);
|
|
57
|
+
return { label: `${fieldLabel}: ${v.text}`, tone: v.tone };
|
|
58
|
+
}
|
|
59
|
+
function groupChip(group, fields, invalidRuleIds, t) {
|
|
60
|
+
const ruleChildren = [];
|
|
61
|
+
for (const c of group.children) if (c.type === "rule" && !invalidRuleIds.has(c.id)) ruleChildren.push(c);
|
|
62
|
+
if (!ruleChildren.length) return { label: "", remaining: 0 };
|
|
63
|
+
const firstChip = ruleChip(ruleChildren[0], fields, t);
|
|
64
|
+
const remaining = ruleChildren.length - 1;
|
|
65
|
+
return {
|
|
66
|
+
label: remaining > 0 ? `${firstChip.label} +${remaining}` : firstChip.label,
|
|
67
|
+
tone: firstChip.tone,
|
|
68
|
+
remaining
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function isNodeFullyInvalid(node, invalidRuleIds) {
|
|
72
|
+
if (node.type === "rule") return invalidRuleIds.has(node.id);
|
|
73
|
+
if (!node.children.length) return true;
|
|
74
|
+
return node.children.every((c) => isNodeFullyInvalid(c, invalidRuleIds));
|
|
75
|
+
}
|
|
76
|
+
function ActiveFilterChips({ tree, fields, popoverOpen, onRemoveNode, onOpen }) {
|
|
77
|
+
const t = useT();
|
|
78
|
+
const invalidRuleIds = React.useMemo(() => {
|
|
79
|
+
const result = validateTreeForApply(tree, fields);
|
|
80
|
+
if (result.ok) return /* @__PURE__ */ new Set();
|
|
81
|
+
return new Set(result.errors.map((e) => e.ruleId));
|
|
82
|
+
}, [tree, fields]);
|
|
83
|
+
const visibleChildren = React.useMemo(
|
|
84
|
+
() => tree.root.children.filter((child) => !isNodeFullyInvalid(child, invalidRuleIds)),
|
|
85
|
+
[tree.root.children, invalidRuleIds]
|
|
86
|
+
);
|
|
87
|
+
if (popoverOpen) return null;
|
|
88
|
+
if (!visibleChildren.length) return null;
|
|
89
|
+
return /* @__PURE__ */ jsx(
|
|
90
|
+
"div",
|
|
91
|
+
{
|
|
92
|
+
className: "flex items-center gap-2 px-4 py-2 overflow-x-auto bg-background border-b border-border",
|
|
93
|
+
"data-testid": "active-filter-chips",
|
|
94
|
+
children: visibleChildren.map((child) => {
|
|
95
|
+
if (child.type === "rule") {
|
|
96
|
+
const c2 = ruleChip(child, fields, t);
|
|
97
|
+
return /* @__PURE__ */ jsx(
|
|
98
|
+
ChipWithRemove,
|
|
99
|
+
{
|
|
100
|
+
label: c2.label,
|
|
101
|
+
tone: c2.tone,
|
|
102
|
+
onClick: () => onOpen(child.id),
|
|
103
|
+
onRemove: () => onRemoveNode(child.id),
|
|
104
|
+
removeLabel: t("ui.advancedFilter.chips.remove", "Remove filter")
|
|
105
|
+
},
|
|
106
|
+
child.id
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const c = groupChip(child, fields, invalidRuleIds, t);
|
|
110
|
+
return /* @__PURE__ */ jsx(
|
|
111
|
+
ChipWithRemove,
|
|
112
|
+
{
|
|
113
|
+
label: c.label,
|
|
114
|
+
tone: c.tone,
|
|
115
|
+
onClick: () => onOpen(child.id),
|
|
116
|
+
onRemove: () => onRemoveNode(child.id),
|
|
117
|
+
removeLabel: t("ui.advancedFilter.chips.removeGroup", "Remove filter group")
|
|
118
|
+
},
|
|
119
|
+
child.id
|
|
120
|
+
);
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
function ChipWithRemove({ label, tone, onClick, onRemove, removeLabel }) {
|
|
126
|
+
return /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1", "data-testid": "active-filter-chip", "aria-label": label, children: [
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
"button",
|
|
129
|
+
{
|
|
130
|
+
type: "button",
|
|
131
|
+
onClick,
|
|
132
|
+
className: "inline-flex rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
133
|
+
children: /* @__PURE__ */ jsx(Tag, { variant: tone ?? "default", dot: tone != null, children: label })
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
/* @__PURE__ */ jsx(IconButton, { type: "button", variant: "ghost", size: "xs", "aria-label": removeLabel, onClick: onRemove, children: /* @__PURE__ */ jsx(X, { className: "size-3" }) })
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
ActiveFilterChips
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=ActiveFilterChips.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/backend/filters/ActiveFilterChips.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\nimport * as React from 'react'\nimport { X } from 'lucide-react'\nimport { Tag, type TagVariant } from '../../primitives/tag'\nimport { IconButton } from '../../primitives/icon-button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { AdvancedFilterTree, FilterRule, FilterGroup } from '@open-mercato/shared/lib/query/advanced-filter-tree'\nimport type { FilterFieldDef, FilterOption } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { isValuelessOperator } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { validateTreeForApply } from './filterValidation'\n\nexport type ActiveFilterChipsProps = {\n tree: AdvancedFilterTree\n fields: FilterFieldDef[]\n popoverOpen: boolean\n onRemoveNode: (id: string) => void\n onOpen: (focusNodeId?: string) => void\n}\n\nconst MAX_LABEL = 24\n\nfunction truncate(s: string): string {\n if (s.length <= MAX_LABEL) return s\n return s.slice(0, MAX_LABEL) + '\u2026'\n}\n\nfunction getOption(field: FilterFieldDef | undefined, value: string): FilterOption | undefined {\n if (!field?.options) return undefined\n return field.options.find((o) => o.value === value)\n}\n\nfunction valueText(field: FilterFieldDef | undefined, value: unknown): { text: string; tone?: TagVariant } {\n if (Array.isArray(value)) {\n if (value.length === 0) return { text: '' }\n const first = String(value[0])\n const opt = getOption(field, first)\n const display = opt?.label ?? first\n const more = value.length - 1\n return {\n text: more > 0 ? `${truncate(display)} +${more}` : truncate(display),\n tone: opt?.tone,\n }\n }\n if (value == null || value === '') return { text: '' }\n const str = String(value)\n const opt = getOption(field, str)\n return { text: truncate(opt?.label ?? str), tone: opt?.tone }\n}\n\n// Operator labels in chips read as natural English: \"Email is empty\" beats\n// \"Email: \" (trailing colon, blank value). Keep the map narrow \u2014 only valueless\n// operators need a phrase form here; value-bearing operators show the value.\nconst VALUELESS_OPERATOR_LABEL: Record<string, string> = {\n is_empty: 'is empty',\n is_not_empty: 'is not empty',\n is_true: 'is true',\n is_false: 'is false',\n}\n\ntype Translator = ReturnType<typeof useT>\n\nfunction ruleChip(rule: FilterRule, fields: FilterFieldDef[], t: Translator): { label: string; tone?: TagVariant } {\n const field = fields.find((f) => f.key === rule.field)\n const fieldLabel = field?.label ?? rule.field\n if (isValuelessOperator(rule.operator)) {\n const fallback = VALUELESS_OPERATOR_LABEL[rule.operator] ?? rule.operator\n const opLabel = t(`ui.advancedFilter.operator.${rule.operator}`, fallback)\n return { label: `${fieldLabel} ${opLabel}` }\n }\n // `between` stores `[start, end]` \u2014 render it as a range, not as the\n // multi-select \"first +N\" summary that `valueText` would produce.\n if (rule.operator === 'between' && Array.isArray(rule.value)) {\n const [start, end] = rule.value\n const startStr = start == null ? '' : String(start)\n const endStr = end == null ? '' : String(end)\n return { label: `${fieldLabel}: ${truncate(startStr)} \u2013 ${truncate(endStr)}` }\n }\n const v = valueText(field, rule.value)\n return { label: `${fieldLabel}: ${v.text}`, tone: v.tone }\n}\n\nfunction groupChip(group: FilterGroup, fields: FilterFieldDef[], invalidRuleIds: Set<string>, t: Translator): { label: string; tone?: TagVariant; remaining: number } {\n const ruleChildren: FilterRule[] = []\n for (const c of group.children) if (c.type === 'rule' && !invalidRuleIds.has(c.id)) ruleChildren.push(c)\n if (!ruleChildren.length) return { label: '', remaining: 0 }\n const firstChip = ruleChip(ruleChildren[0], fields, t)\n const remaining = ruleChildren.length - 1\n return {\n label: remaining > 0 ? `${firstChip.label} +${remaining}` : firstChip.label,\n tone: firstChip.tone,\n remaining,\n }\n}\n\nfunction isNodeFullyInvalid(node: FilterRule | FilterGroup, invalidRuleIds: Set<string>): boolean {\n if (node.type === 'rule') return invalidRuleIds.has(node.id)\n if (!node.children.length) return true\n return node.children.every((c) => isNodeFullyInvalid(c, invalidRuleIds))\n}\n\nexport function ActiveFilterChips({ tree, fields, popoverOpen, onRemoveNode, onOpen }: ActiveFilterChipsProps) {\n const t = useT()\n // Suppress chips for rules/groups whose values won't actually filter (matches\n // the same gate the auto-apply hook uses). Stops the \"chip is shown but data\n // appears unfiltered\" mismatch when a substring text rule has < 2 characters\n // or a multi-value rule has no picks yet.\n const invalidRuleIds = React.useMemo(() => {\n const result = validateTreeForApply(tree, fields)\n if (result.ok) return new Set<string>()\n return new Set(result.errors.map((e) => e.ruleId))\n }, [tree, fields])\n const visibleChildren = React.useMemo(\n () => tree.root.children.filter((child) => !isNodeFullyInvalid(child, invalidRuleIds)),\n [tree.root.children, invalidRuleIds],\n )\n if (popoverOpen) return null\n if (!visibleChildren.length) return null\n return (\n <div\n className=\"flex items-center gap-2 px-4 py-2 overflow-x-auto bg-background border-b border-border\"\n data-testid=\"active-filter-chips\"\n >\n {visibleChildren.map((child) => {\n if (child.type === 'rule') {\n const c = ruleChip(child, fields, t)\n return (\n <ChipWithRemove\n key={child.id}\n label={c.label}\n tone={c.tone}\n onClick={() => onOpen(child.id)}\n onRemove={() => onRemoveNode(child.id)}\n removeLabel={t('ui.advancedFilter.chips.remove', 'Remove filter')}\n />\n )\n }\n const c = groupChip(child, fields, invalidRuleIds, t)\n return (\n <ChipWithRemove\n key={child.id}\n label={c.label}\n tone={c.tone}\n onClick={() => onOpen(child.id)}\n onRemove={() => onRemoveNode(child.id)}\n removeLabel={t('ui.advancedFilter.chips.removeGroup', 'Remove filter group')}\n />\n )\n })}\n </div>\n )\n}\n\ntype ChipWithRemoveProps = {\n label: string\n tone?: TagVariant\n onClick: () => void\n onRemove: () => void\n removeLabel: string\n}\n\nfunction ChipWithRemove({ label, tone, onClick, onRemove, removeLabel }: ChipWithRemoveProps) {\n return (\n <div className=\"inline-flex items-center gap-1\" data-testid=\"active-filter-chip\" aria-label={label}>\n {/* Raw <button> required: <Tag> is a presentational pill primitive (not a Button), so wrapping\n it in <Button> would inject Button's own styling around it. The native button keeps Tag's\n visual contract intact while adding click + keyboard semantics; DS focus-visible ring is applied. */}\n <button\n type=\"button\"\n onClick={onClick}\n className=\"inline-flex rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n >\n <Tag variant={tone ?? 'default'} dot={tone != null}>{label}</Tag>\n </button>\n <IconButton type=\"button\" variant=\"ghost\" size=\"xs\" aria-label={removeLabel} onClick={onRemove}>\n <X className=\"size-3\" />\n </IconButton>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["opt", "c"]
|
|
7
|
+
}
|