@kopexa/filter 0.0.17 → 0.0.19
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/dist/{chunk-WUUOSJHW.mjs → chunk-3ZBNWXRA.mjs} +6 -2
- package/dist/chunk-45QJL74L.mjs +283 -0
- package/dist/{chunk-TBHYZZSX.mjs → chunk-RFCPJLIQ.mjs} +8 -2
- package/dist/filter-active.js +200 -82
- package/dist/filter-active.mjs +2 -2
- package/dist/filter-menu.d.mts +11 -1
- package/dist/filter-menu.d.ts +11 -1
- package/dist/filter-menu.js +8 -2
- package/dist/filter-menu.mjs +1 -1
- package/dist/filter-types.d.mts +27 -1
- package/dist/filter-types.d.ts +27 -1
- package/dist/filter-value-editor.d.mts +3 -1
- package/dist/filter-value-editor.d.ts +3 -1
- package/dist/filter-value-editor.js +6 -2
- package/dist/filter-value-editor.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +202 -78
- package/dist/index.mjs +3 -3
- package/package.json +16 -16
- package/dist/chunk-WCKTCW7Y.mjs +0 -166
|
@@ -15,7 +15,11 @@ import { useSafeIntl } from "@kopexa/i18n";
|
|
|
15
15
|
import { Input } from "@kopexa/input";
|
|
16
16
|
import { Select } from "@kopexa/select";
|
|
17
17
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
|
-
function FilterValueEditor({
|
|
18
|
+
function FilterValueEditor({
|
|
19
|
+
filter,
|
|
20
|
+
field,
|
|
21
|
+
hideOperator
|
|
22
|
+
}) {
|
|
19
23
|
var _a;
|
|
20
24
|
const { styles, updateFilter, setEditingFilterId } = useFilterContext();
|
|
21
25
|
const t = useSafeIntl();
|
|
@@ -40,7 +44,7 @@ function FilterValueEditor({ filter, field }) {
|
|
|
40
44
|
const needsValue = filter.operator !== "is_empty" && filter.operator !== "is_not_empty";
|
|
41
45
|
return /* @__PURE__ */ jsxs("div", { "data-slot": "filter-value-editor", children: [
|
|
42
46
|
/* @__PURE__ */ jsx("div", { className: styles.editorHeader(), children: /* @__PURE__ */ jsx("span", { className: styles.editorTitle(), children: field.label }) }),
|
|
43
|
-
operators.length > 1 && /* @__PURE__ */ jsx("div", { className: styles.editorOperator(), children: /* @__PURE__ */ jsxs(
|
|
47
|
+
!hideOperator && operators.length > 1 && /* @__PURE__ */ jsx("div", { className: styles.editorOperator(), children: /* @__PURE__ */ jsxs(
|
|
44
48
|
Select,
|
|
45
49
|
{
|
|
46
50
|
value: filter.operator,
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
FilterValueEditor
|
|
4
|
+
} from "./chunk-3ZBNWXRA.mjs";
|
|
5
|
+
import {
|
|
6
|
+
messages
|
|
7
|
+
} from "./chunk-URDCG5NI.mjs";
|
|
8
|
+
import {
|
|
9
|
+
useFilterContext
|
|
10
|
+
} from "./chunk-I3Z2T4N2.mjs";
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_OPERATORS
|
|
13
|
+
} from "./chunk-PHESMHTT.mjs";
|
|
14
|
+
|
|
15
|
+
// src/filter-active.tsx
|
|
16
|
+
import { useSafeIntl } from "@kopexa/i18n";
|
|
17
|
+
import { ChevronDownIcon, CloseIcon } from "@kopexa/icons";
|
|
18
|
+
import { Popover } from "@kopexa/popover";
|
|
19
|
+
import { cn } from "@kopexa/shared-utils";
|
|
20
|
+
import { Switch } from "@kopexa/switch";
|
|
21
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
function FilterActive(props) {
|
|
23
|
+
const { showClearAll = true, className, ...rest } = props;
|
|
24
|
+
const { value, styles, clearFilters } = useFilterContext();
|
|
25
|
+
const t = useSafeIntl();
|
|
26
|
+
if (value.length === 0) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
"div",
|
|
31
|
+
{
|
|
32
|
+
"data-slot": "filter-active",
|
|
33
|
+
className: cn(styles.active(), className),
|
|
34
|
+
...rest,
|
|
35
|
+
children: [
|
|
36
|
+
value.map((filter) => /* @__PURE__ */ jsx(FilterField, { filter }, filter.id)),
|
|
37
|
+
showClearAll && value.length > 1 && /* @__PURE__ */ jsx(
|
|
38
|
+
"button",
|
|
39
|
+
{
|
|
40
|
+
type: "button",
|
|
41
|
+
className: styles.clearAll(),
|
|
42
|
+
onClick: clearFilters,
|
|
43
|
+
children: t.formatMessage(messages.clear_all)
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
FilterActive.displayName = "FilterActive";
|
|
51
|
+
function FilterField({ filter }) {
|
|
52
|
+
var _a;
|
|
53
|
+
const { fields, styles, removeFilter, updateFilter } = useFilterContext();
|
|
54
|
+
const t = useSafeIntl();
|
|
55
|
+
const field = fields.get(filter.fieldId);
|
|
56
|
+
if (!field) return null;
|
|
57
|
+
const availableOperators = (_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type];
|
|
58
|
+
const getOperatorLabel = (op) => {
|
|
59
|
+
const opKey = `op_${op}`;
|
|
60
|
+
return messages[opKey] ? t.formatMessage(messages[opKey]) : op;
|
|
61
|
+
};
|
|
62
|
+
const handleOperatorChange = (e) => {
|
|
63
|
+
const newOperator = e.target.value;
|
|
64
|
+
if (newOperator === "is_empty" || newOperator === "is_not_empty") {
|
|
65
|
+
updateFilter(filter.id, { operator: newOperator, value: void 0 });
|
|
66
|
+
} else {
|
|
67
|
+
updateFilter(filter.id, { operator: newOperator });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const isEmptyOperator = filter.operator === "is_empty" || filter.operator === "is_not_empty";
|
|
71
|
+
const hasCustomEditor = !!field.renderEditor;
|
|
72
|
+
const useInlineInput = !hasCustomEditor && (field.type === "text" || field.type === "number");
|
|
73
|
+
const useInlineSwitch = !hasCustomEditor && field.type === "boolean" && !isEmptyOperator;
|
|
74
|
+
const usePopover = !useInlineInput && !useInlineSwitch && !isEmptyOperator;
|
|
75
|
+
const currentOperatorLabel = getOperatorLabel(filter.operator);
|
|
76
|
+
const operatorWidth = `${currentOperatorLabel.length + 2}ch`;
|
|
77
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "filter-field", className: styles.field(), children: [
|
|
78
|
+
/* @__PURE__ */ jsxs("span", { className: styles.fieldLabel(), children: [
|
|
79
|
+
field.icon && /* @__PURE__ */ jsx("span", { className: "size-4 flex-shrink-0", children: field.icon }),
|
|
80
|
+
/* @__PURE__ */ jsx("span", { children: field.label })
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ jsxs("span", { className: styles.fieldOperator(), children: [
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
"select",
|
|
85
|
+
{
|
|
86
|
+
className: styles.fieldOperatorSelect(),
|
|
87
|
+
value: filter.operator,
|
|
88
|
+
onChange: handleOperatorChange,
|
|
89
|
+
title: currentOperatorLabel,
|
|
90
|
+
style: { width: operatorWidth },
|
|
91
|
+
children: availableOperators.map((op) => /* @__PURE__ */ jsx("option", { value: op, children: getOperatorLabel(op) }, op))
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
/* @__PURE__ */ jsx(ChevronDownIcon, { className: styles.fieldOperatorIcon() })
|
|
95
|
+
] }),
|
|
96
|
+
useInlineInput && /* @__PURE__ */ jsx(InlineValueInput, { filter, field }),
|
|
97
|
+
useInlineSwitch && /* @__PURE__ */ jsx("span", { className: styles.fieldValue(), children: /* @__PURE__ */ jsx(
|
|
98
|
+
Switch,
|
|
99
|
+
{
|
|
100
|
+
size: "sm",
|
|
101
|
+
checked: Boolean(filter.value),
|
|
102
|
+
onCheckedChange: (checked) => {
|
|
103
|
+
updateFilter(filter.id, { value: checked });
|
|
104
|
+
},
|
|
105
|
+
"aria-label": field.label
|
|
106
|
+
}
|
|
107
|
+
) }),
|
|
108
|
+
usePopover && /* @__PURE__ */ jsx(PopoverValueEditor, { filter, field }),
|
|
109
|
+
/* @__PURE__ */ jsx(
|
|
110
|
+
"button",
|
|
111
|
+
{
|
|
112
|
+
type: "button",
|
|
113
|
+
className: styles.fieldRemove(),
|
|
114
|
+
onClick: () => removeFilter(filter.id),
|
|
115
|
+
"aria-label": "Remove filter",
|
|
116
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { className: "size-3" })
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
] });
|
|
120
|
+
}
|
|
121
|
+
function getInputWidth(value, placeholder, minWidth = 4) {
|
|
122
|
+
const displayValue = value !== void 0 && value !== null && value !== "" ? String(value) : placeholder;
|
|
123
|
+
const width = Math.max(displayValue.length + 1, minWidth);
|
|
124
|
+
return `${width}ch`;
|
|
125
|
+
}
|
|
126
|
+
function InlineValueInput({
|
|
127
|
+
filter,
|
|
128
|
+
field
|
|
129
|
+
}) {
|
|
130
|
+
var _a;
|
|
131
|
+
const { styles, updateFilter } = useFilterContext();
|
|
132
|
+
const t = useSafeIntl();
|
|
133
|
+
const handleChange = (e) => {
|
|
134
|
+
const val = e.target.value;
|
|
135
|
+
if (field.type === "number") {
|
|
136
|
+
updateFilter(filter.id, { value: val ? Number(val) : void 0 });
|
|
137
|
+
} else {
|
|
138
|
+
updateFilter(filter.id, { value: val });
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const placeholder = (_a = field.placeholder) != null ? _a : t.formatMessage(messages.enter_value);
|
|
142
|
+
if (filter.operator === "between") {
|
|
143
|
+
const [min, max] = Array.isArray(filter.value) ? filter.value : [void 0, void 0];
|
|
144
|
+
return /* @__PURE__ */ jsxs("span", { className: cn(styles.fieldValue(), "gap-1"), children: [
|
|
145
|
+
/* @__PURE__ */ jsx(
|
|
146
|
+
"input",
|
|
147
|
+
{
|
|
148
|
+
type: "number",
|
|
149
|
+
className: styles.fieldValueInput(),
|
|
150
|
+
value: min != null ? min : "",
|
|
151
|
+
onChange: (e) => {
|
|
152
|
+
const val = e.target.value ? Number(e.target.value) : void 0;
|
|
153
|
+
updateFilter(filter.id, { value: [val, max] });
|
|
154
|
+
},
|
|
155
|
+
placeholder: "Min",
|
|
156
|
+
style: { width: getInputWidth(min, "Min", 3) },
|
|
157
|
+
min: field.min,
|
|
158
|
+
max: field.max,
|
|
159
|
+
step: field.step
|
|
160
|
+
}
|
|
161
|
+
),
|
|
162
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
"input",
|
|
165
|
+
{
|
|
166
|
+
type: "number",
|
|
167
|
+
className: styles.fieldValueInput(),
|
|
168
|
+
value: max != null ? max : "",
|
|
169
|
+
onChange: (e) => {
|
|
170
|
+
const val = e.target.value ? Number(e.target.value) : void 0;
|
|
171
|
+
updateFilter(filter.id, { value: [min, val] });
|
|
172
|
+
},
|
|
173
|
+
placeholder: "Max",
|
|
174
|
+
style: { width: getInputWidth(max, "Max", 3) },
|
|
175
|
+
min: field.min,
|
|
176
|
+
max: field.max,
|
|
177
|
+
step: field.step
|
|
178
|
+
}
|
|
179
|
+
)
|
|
180
|
+
] });
|
|
181
|
+
}
|
|
182
|
+
return /* @__PURE__ */ jsx("span", { className: styles.fieldValue(), children: /* @__PURE__ */ jsx(
|
|
183
|
+
"input",
|
|
184
|
+
{
|
|
185
|
+
type: field.type === "number" ? "number" : "text",
|
|
186
|
+
className: styles.fieldValueInput(),
|
|
187
|
+
value: filter.value !== void 0 && filter.value !== null ? String(filter.value) : "",
|
|
188
|
+
onChange: handleChange,
|
|
189
|
+
placeholder,
|
|
190
|
+
style: { width: getInputWidth(filter.value, placeholder, 6) },
|
|
191
|
+
min: field.min,
|
|
192
|
+
max: field.max,
|
|
193
|
+
step: field.step
|
|
194
|
+
}
|
|
195
|
+
) });
|
|
196
|
+
}
|
|
197
|
+
function PopoverValueEditor({
|
|
198
|
+
filter,
|
|
199
|
+
field
|
|
200
|
+
}) {
|
|
201
|
+
const { styles, updateFilter } = useFilterContext();
|
|
202
|
+
const valueDisplay = field.renderValue ? field.renderValue({ filter, field }) : getValueDisplay(filter, field);
|
|
203
|
+
const hasValue = filter.value !== void 0 && filter.value !== null && filter.value !== "" && !(Array.isArray(filter.value) && filter.value.length === 0);
|
|
204
|
+
const handleCustomChange = (value) => {
|
|
205
|
+
updateFilter(filter.id, { value });
|
|
206
|
+
};
|
|
207
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { children: [
|
|
208
|
+
/* @__PURE__ */ jsxs(
|
|
209
|
+
Popover.Trigger,
|
|
210
|
+
{
|
|
211
|
+
className: cn(styles.fieldValue(), "cursor-pointer hover:bg-muted/30"),
|
|
212
|
+
render: /* @__PURE__ */ jsx("div", {}),
|
|
213
|
+
nativeButton: false,
|
|
214
|
+
children: [
|
|
215
|
+
/* @__PURE__ */ jsx("span", { className: styles.fieldValueText(), children: hasValue ? valueDisplay : "Select..." }),
|
|
216
|
+
/* @__PURE__ */ jsx(ChevronDownIcon, { className: styles.fieldValueIcon() })
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
),
|
|
220
|
+
/* @__PURE__ */ jsx(
|
|
221
|
+
Popover.Content,
|
|
222
|
+
{
|
|
223
|
+
"data-slot": "filter-editor",
|
|
224
|
+
className: styles.editor(),
|
|
225
|
+
align: "start",
|
|
226
|
+
showArrow: false,
|
|
227
|
+
children: field.renderEditor ? (
|
|
228
|
+
// Use custom editor if provided
|
|
229
|
+
field.renderEditor({
|
|
230
|
+
filter,
|
|
231
|
+
field,
|
|
232
|
+
onChange: handleCustomChange
|
|
233
|
+
})
|
|
234
|
+
) : (
|
|
235
|
+
// Default editor
|
|
236
|
+
/* @__PURE__ */ jsx(FilterValueEditor, { filter, field, hideOperator: true })
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
] });
|
|
241
|
+
}
|
|
242
|
+
function getValueDisplay(filter, field) {
|
|
243
|
+
var _a, _b, _c, _d;
|
|
244
|
+
const { value, operator } = filter;
|
|
245
|
+
if (operator === "is_empty" || operator === "is_not_empty") {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (value === void 0 || value === null || value === "") {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
if (field.type === "boolean") {
|
|
252
|
+
return value ? "Yes" : "No";
|
|
253
|
+
}
|
|
254
|
+
if (field.type === "select" && field.options) {
|
|
255
|
+
const option = field.options.find((o) => o.value === value);
|
|
256
|
+
return (_a = option == null ? void 0 : option.label) != null ? _a : String(value);
|
|
257
|
+
}
|
|
258
|
+
if (field.type === "multiselect" && Array.isArray(value)) {
|
|
259
|
+
if (value.length === 0) return null;
|
|
260
|
+
if (value.length === 1 && field.options) {
|
|
261
|
+
const option = field.options.find((o) => o.value === value[0]);
|
|
262
|
+
return (_b = option == null ? void 0 : option.label) != null ? _b : String(value[0]);
|
|
263
|
+
}
|
|
264
|
+
return `${value.length} selected`;
|
|
265
|
+
}
|
|
266
|
+
if (field.type === "date" && value instanceof Date) {
|
|
267
|
+
return value.toLocaleDateString();
|
|
268
|
+
}
|
|
269
|
+
if (field.type === "daterange" && Array.isArray(value)) {
|
|
270
|
+
const [start, end] = value;
|
|
271
|
+
const startStr = start instanceof Date ? start.toLocaleDateString() : "...";
|
|
272
|
+
const endStr = end instanceof Date ? end.toLocaleDateString() : "...";
|
|
273
|
+
return `${startStr} \u2013 ${endStr}`;
|
|
274
|
+
}
|
|
275
|
+
if (operator === "between" && Array.isArray(value)) {
|
|
276
|
+
return `${(_c = value[0]) != null ? _c : "..."} \u2013 ${(_d = value[1]) != null ? _d : "..."}`;
|
|
277
|
+
}
|
|
278
|
+
return String(value);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export {
|
|
282
|
+
FilterActive
|
|
283
|
+
};
|
|
@@ -85,7 +85,9 @@ function FilterItem(props) {
|
|
|
85
85
|
max,
|
|
86
86
|
step,
|
|
87
87
|
searchable,
|
|
88
|
-
disabled
|
|
88
|
+
disabled,
|
|
89
|
+
renderEditor,
|
|
90
|
+
renderValue
|
|
89
91
|
} = props;
|
|
90
92
|
const { registerField, unregisterField } = useFilterContext();
|
|
91
93
|
const group = useFilterGroup();
|
|
@@ -103,7 +105,9 @@ function FilterItem(props) {
|
|
|
103
105
|
step,
|
|
104
106
|
searchable,
|
|
105
107
|
group,
|
|
106
|
-
disabled
|
|
108
|
+
disabled,
|
|
109
|
+
renderEditor,
|
|
110
|
+
renderValue
|
|
107
111
|
});
|
|
108
112
|
return () => unregisterField(id);
|
|
109
113
|
}, [
|
|
@@ -120,6 +124,8 @@ function FilterItem(props) {
|
|
|
120
124
|
searchable,
|
|
121
125
|
group,
|
|
122
126
|
disabled,
|
|
127
|
+
renderEditor,
|
|
128
|
+
renderValue,
|
|
123
129
|
registerField,
|
|
124
130
|
unregisterField
|
|
125
131
|
]);
|
package/dist/filter-active.js
CHANGED
|
@@ -39,12 +39,6 @@ var [FilterProvider, useFilterContext] = (0, import_react_utils.createContext)({
|
|
|
39
39
|
errorMessage: "useFilterContext must be used within a Filter component. Make sure to wrap your FilterMenu, FilterTrigger, etc. inside a <Filter> component."
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
// src/filter-value-editor.tsx
|
|
43
|
-
var import_checkbox = require("@kopexa/checkbox");
|
|
44
|
-
var import_i18n2 = require("@kopexa/i18n");
|
|
45
|
-
var import_input = require("@kopexa/input");
|
|
46
|
-
var import_select = require("@kopexa/select");
|
|
47
|
-
|
|
48
42
|
// src/filter-types.ts
|
|
49
43
|
var DEFAULT_OPERATORS = {
|
|
50
44
|
text: [
|
|
@@ -65,6 +59,12 @@ var DEFAULT_OPERATORS = {
|
|
|
65
59
|
boolean: ["equals", "not_equals", "is_empty", "is_not_empty"]
|
|
66
60
|
};
|
|
67
61
|
|
|
62
|
+
// src/filter-value-editor.tsx
|
|
63
|
+
var import_checkbox = require("@kopexa/checkbox");
|
|
64
|
+
var import_i18n2 = require("@kopexa/i18n");
|
|
65
|
+
var import_input = require("@kopexa/input");
|
|
66
|
+
var import_select = require("@kopexa/select");
|
|
67
|
+
|
|
68
68
|
// src/messages.ts
|
|
69
69
|
var import_i18n = require("@kopexa/i18n");
|
|
70
70
|
var messages = (0, import_i18n.defineMessages)({
|
|
@@ -173,7 +173,11 @@ var messages = (0, import_i18n.defineMessages)({
|
|
|
173
173
|
|
|
174
174
|
// src/filter-value-editor.tsx
|
|
175
175
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
176
|
-
function FilterValueEditor({
|
|
176
|
+
function FilterValueEditor({
|
|
177
|
+
filter,
|
|
178
|
+
field,
|
|
179
|
+
hideOperator
|
|
180
|
+
}) {
|
|
177
181
|
var _a;
|
|
178
182
|
const { styles, updateFilter, setEditingFilterId } = useFilterContext();
|
|
179
183
|
const t = (0, import_i18n2.useSafeIntl)();
|
|
@@ -198,7 +202,7 @@ function FilterValueEditor({ filter, field }) {
|
|
|
198
202
|
const needsValue = filter.operator !== "is_empty" && filter.operator !== "is_not_empty";
|
|
199
203
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-slot": "filter-value-editor", children: [
|
|
200
204
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorHeader(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: styles.editorTitle(), children: field.label }) }),
|
|
201
|
-
operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
205
|
+
!hideOperator && operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
202
206
|
import_select.Select,
|
|
203
207
|
{
|
|
204
208
|
value: filter.operator,
|
|
@@ -448,81 +452,195 @@ function FilterActive(props) {
|
|
|
448
452
|
}
|
|
449
453
|
FilterActive.displayName = "FilterActive";
|
|
450
454
|
function FilterField({ filter }) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
styles,
|
|
454
|
-
removeFilter,
|
|
455
|
-
editingFilterId,
|
|
456
|
-
setEditingFilterId,
|
|
457
|
-
updateFilter
|
|
458
|
-
} = useFilterContext();
|
|
455
|
+
var _a;
|
|
456
|
+
const { fields, styles, removeFilter, updateFilter } = useFilterContext();
|
|
459
457
|
const t = (0, import_i18n3.useSafeIntl)();
|
|
460
458
|
const field = fields.get(filter.fieldId);
|
|
461
459
|
if (!field) return null;
|
|
462
|
-
const
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
460
|
+
const availableOperators = (_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type];
|
|
461
|
+
const getOperatorLabel = (op) => {
|
|
462
|
+
const opKey = `op_${op}`;
|
|
463
|
+
return messages[opKey] ? t.formatMessage(messages[opKey]) : op;
|
|
464
|
+
};
|
|
465
|
+
const handleOperatorChange = (e) => {
|
|
466
|
+
const newOperator = e.target.value;
|
|
467
|
+
if (newOperator === "is_empty" || newOperator === "is_not_empty") {
|
|
468
|
+
updateFilter(filter.id, { operator: newOperator, value: void 0 });
|
|
469
|
+
} else {
|
|
470
|
+
updateFilter(filter.id, { operator: newOperator });
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
const isEmptyOperator = filter.operator === "is_empty" || filter.operator === "is_not_empty";
|
|
474
|
+
const hasCustomEditor = !!field.renderEditor;
|
|
475
|
+
const useInlineInput = !hasCustomEditor && (field.type === "text" || field.type === "number");
|
|
476
|
+
const useInlineSwitch = !hasCustomEditor && field.type === "boolean" && !isEmptyOperator;
|
|
477
|
+
const usePopover = !useInlineInput && !useInlineSwitch && !isEmptyOperator;
|
|
478
|
+
const currentOperatorLabel = getOperatorLabel(filter.operator);
|
|
479
|
+
const operatorWidth = `${currentOperatorLabel.length + 2}ch`;
|
|
480
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-slot": "filter-field", className: styles.field(), children: [
|
|
481
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: styles.fieldLabel(), children: [
|
|
482
|
+
field.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "size-4 flex-shrink-0", children: field.icon }),
|
|
483
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: field.label })
|
|
484
|
+
] }),
|
|
485
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: styles.fieldOperator(), children: [
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
487
|
+
"select",
|
|
488
|
+
{
|
|
489
|
+
className: styles.fieldOperatorSelect(),
|
|
490
|
+
value: filter.operator,
|
|
491
|
+
onChange: handleOperatorChange,
|
|
492
|
+
title: currentOperatorLabel,
|
|
493
|
+
style: { width: operatorWidth },
|
|
494
|
+
children: availableOperators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: op, children: getOperatorLabel(op) }, op))
|
|
495
|
+
}
|
|
496
|
+
),
|
|
497
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronDownIcon, { className: styles.fieldOperatorIcon() })
|
|
498
|
+
] }),
|
|
499
|
+
useInlineInput && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(InlineValueInput, { filter, field }),
|
|
500
|
+
useInlineSwitch && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldValue(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
501
|
+
import_switch.Switch,
|
|
502
|
+
{
|
|
503
|
+
size: "sm",
|
|
504
|
+
checked: Boolean(filter.value),
|
|
505
|
+
onCheckedChange: (checked) => {
|
|
506
|
+
updateFilter(filter.id, { value: checked });
|
|
507
|
+
},
|
|
508
|
+
"aria-label": field.label
|
|
509
|
+
}
|
|
510
|
+
) }),
|
|
511
|
+
usePopover && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PopoverValueEditor, { filter, field }),
|
|
512
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
513
|
+
"button",
|
|
514
|
+
{
|
|
515
|
+
type: "button",
|
|
516
|
+
className: styles.fieldRemove(),
|
|
517
|
+
onClick: () => removeFilter(filter.id),
|
|
518
|
+
"aria-label": "Remove filter",
|
|
519
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.CloseIcon, { className: "size-3" })
|
|
520
|
+
}
|
|
521
|
+
)
|
|
522
|
+
] });
|
|
523
|
+
}
|
|
524
|
+
function getInputWidth(value, placeholder, minWidth = 4) {
|
|
525
|
+
const displayValue = value !== void 0 && value !== null && value !== "" ? String(value) : placeholder;
|
|
526
|
+
const width = Math.max(displayValue.length + 1, minWidth);
|
|
527
|
+
return `${width}ch`;
|
|
528
|
+
}
|
|
529
|
+
function InlineValueInput({
|
|
530
|
+
filter,
|
|
531
|
+
field
|
|
532
|
+
}) {
|
|
533
|
+
var _a;
|
|
534
|
+
const { styles, updateFilter } = useFilterContext();
|
|
535
|
+
const t = (0, import_i18n3.useSafeIntl)();
|
|
536
|
+
const handleChange = (e) => {
|
|
537
|
+
const val = e.target.value;
|
|
538
|
+
if (field.type === "number") {
|
|
539
|
+
updateFilter(filter.id, { value: val ? Number(val) : void 0 });
|
|
540
|
+
} else {
|
|
541
|
+
updateFilter(filter.id, { value: val });
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
const placeholder = (_a = field.placeholder) != null ? _a : t.formatMessage(messages.enter_value);
|
|
545
|
+
if (filter.operator === "between") {
|
|
546
|
+
const [min, max] = Array.isArray(filter.value) ? filter.value : [void 0, void 0];
|
|
547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: (0, import_shared_utils.cn)(styles.fieldValue(), "gap-1"), children: [
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
549
|
+
"input",
|
|
550
|
+
{
|
|
551
|
+
type: "number",
|
|
552
|
+
className: styles.fieldValueInput(),
|
|
553
|
+
value: min != null ? min : "",
|
|
554
|
+
onChange: (e) => {
|
|
555
|
+
const val = e.target.value ? Number(e.target.value) : void 0;
|
|
556
|
+
updateFilter(filter.id, { value: [val, max] });
|
|
557
|
+
},
|
|
558
|
+
placeholder: "Min",
|
|
559
|
+
style: { width: getInputWidth(min, "Min", 3) },
|
|
560
|
+
min: field.min,
|
|
561
|
+
max: field.max,
|
|
562
|
+
step: field.step
|
|
563
|
+
}
|
|
564
|
+
),
|
|
565
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-muted-foreground", children: "\u2013" }),
|
|
566
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
567
|
+
"input",
|
|
568
|
+
{
|
|
569
|
+
type: "number",
|
|
570
|
+
className: styles.fieldValueInput(),
|
|
571
|
+
value: max != null ? max : "",
|
|
572
|
+
onChange: (e) => {
|
|
573
|
+
const val = e.target.value ? Number(e.target.value) : void 0;
|
|
574
|
+
updateFilter(filter.id, { value: [min, val] });
|
|
575
|
+
},
|
|
576
|
+
placeholder: "Max",
|
|
577
|
+
style: { width: getInputWidth(max, "Max", 3) },
|
|
578
|
+
min: field.min,
|
|
579
|
+
max: field.max,
|
|
580
|
+
step: field.step
|
|
581
|
+
}
|
|
582
|
+
)
|
|
583
|
+
] });
|
|
584
|
+
}
|
|
585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldValue(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
586
|
+
"input",
|
|
469
587
|
{
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
nativeButton: false,
|
|
480
|
-
children: [
|
|
481
|
-
field.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.menuItemIcon(), children: field.icon }),
|
|
482
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldLabel(), children: field.label }),
|
|
483
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldOperator(), children: operatorLabel }),
|
|
484
|
-
isBooleanWithValue && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
485
|
-
import_switch.Switch,
|
|
486
|
-
{
|
|
487
|
-
size: "sm",
|
|
488
|
-
checked: Boolean(filter.value),
|
|
489
|
-
onCheckedChange: (checked) => {
|
|
490
|
-
updateFilter(filter.id, { value: checked });
|
|
491
|
-
},
|
|
492
|
-
onClick: (e) => e.stopPropagation(),
|
|
493
|
-
"aria-label": field.label
|
|
494
|
-
}
|
|
495
|
-
),
|
|
496
|
-
valueDisplay && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldValue(), children: valueDisplay }),
|
|
497
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
498
|
-
"button",
|
|
499
|
-
{
|
|
500
|
-
type: "button",
|
|
501
|
-
className: styles.fieldRemove(),
|
|
502
|
-
onClick: (e) => {
|
|
503
|
-
e.stopPropagation();
|
|
504
|
-
removeFilter(filter.id);
|
|
505
|
-
},
|
|
506
|
-
"aria-label": "Remove filter",
|
|
507
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.CloseIcon, { className: "size-3" })
|
|
508
|
-
}
|
|
509
|
-
)
|
|
510
|
-
]
|
|
511
|
-
}
|
|
512
|
-
),
|
|
513
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
514
|
-
import_popover.Popover.Content,
|
|
515
|
-
{
|
|
516
|
-
"data-slot": "filter-editor",
|
|
517
|
-
className: styles.editor(),
|
|
518
|
-
align: "start",
|
|
519
|
-
showArrow: false,
|
|
520
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FilterValueEditor, { filter, field })
|
|
521
|
-
}
|
|
522
|
-
)
|
|
523
|
-
]
|
|
588
|
+
type: field.type === "number" ? "number" : "text",
|
|
589
|
+
className: styles.fieldValueInput(),
|
|
590
|
+
value: filter.value !== void 0 && filter.value !== null ? String(filter.value) : "",
|
|
591
|
+
onChange: handleChange,
|
|
592
|
+
placeholder,
|
|
593
|
+
style: { width: getInputWidth(filter.value, placeholder, 6) },
|
|
594
|
+
min: field.min,
|
|
595
|
+
max: field.max,
|
|
596
|
+
step: field.step
|
|
524
597
|
}
|
|
525
|
-
);
|
|
598
|
+
) });
|
|
599
|
+
}
|
|
600
|
+
function PopoverValueEditor({
|
|
601
|
+
filter,
|
|
602
|
+
field
|
|
603
|
+
}) {
|
|
604
|
+
const { styles, updateFilter } = useFilterContext();
|
|
605
|
+
const valueDisplay = field.renderValue ? field.renderValue({ filter, field }) : getValueDisplay(filter, field);
|
|
606
|
+
const hasValue = filter.value !== void 0 && filter.value !== null && filter.value !== "" && !(Array.isArray(filter.value) && filter.value.length === 0);
|
|
607
|
+
const handleCustomChange = (value) => {
|
|
608
|
+
updateFilter(filter.id, { value });
|
|
609
|
+
};
|
|
610
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_popover.Popover.Root, { children: [
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
612
|
+
import_popover.Popover.Trigger,
|
|
613
|
+
{
|
|
614
|
+
className: (0, import_shared_utils.cn)(styles.fieldValue(), "cursor-pointer hover:bg-muted/30"),
|
|
615
|
+
render: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", {}),
|
|
616
|
+
nativeButton: false,
|
|
617
|
+
children: [
|
|
618
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldValueText(), children: hasValue ? valueDisplay : "Select..." }),
|
|
619
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.ChevronDownIcon, { className: styles.fieldValueIcon() })
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
),
|
|
623
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
624
|
+
import_popover.Popover.Content,
|
|
625
|
+
{
|
|
626
|
+
"data-slot": "filter-editor",
|
|
627
|
+
className: styles.editor(),
|
|
628
|
+
align: "start",
|
|
629
|
+
showArrow: false,
|
|
630
|
+
children: field.renderEditor ? (
|
|
631
|
+
// Use custom editor if provided
|
|
632
|
+
field.renderEditor({
|
|
633
|
+
filter,
|
|
634
|
+
field,
|
|
635
|
+
onChange: handleCustomChange
|
|
636
|
+
})
|
|
637
|
+
) : (
|
|
638
|
+
// Default editor
|
|
639
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FilterValueEditor, { filter, field, hideOperator: true })
|
|
640
|
+
)
|
|
641
|
+
}
|
|
642
|
+
)
|
|
643
|
+
] });
|
|
526
644
|
}
|
|
527
645
|
function getValueDisplay(filter, field) {
|
|
528
646
|
var _a, _b, _c, _d;
|
|
@@ -531,7 +649,7 @@ function getValueDisplay(filter, field) {
|
|
|
531
649
|
return null;
|
|
532
650
|
}
|
|
533
651
|
if (value === void 0 || value === null || value === "") {
|
|
534
|
-
return
|
|
652
|
+
return null;
|
|
535
653
|
}
|
|
536
654
|
if (field.type === "boolean") {
|
|
537
655
|
return value ? "Yes" : "No";
|
|
@@ -541,7 +659,7 @@ function getValueDisplay(filter, field) {
|
|
|
541
659
|
return (_a = option == null ? void 0 : option.label) != null ? _a : String(value);
|
|
542
660
|
}
|
|
543
661
|
if (field.type === "multiselect" && Array.isArray(value)) {
|
|
544
|
-
if (value.length === 0) return
|
|
662
|
+
if (value.length === 0) return null;
|
|
545
663
|
if (value.length === 1 && field.options) {
|
|
546
664
|
const option = field.options.find((o) => o.value === value[0]);
|
|
547
665
|
return (_b = option == null ? void 0 : option.label) != null ? _b : String(value[0]);
|
|
@@ -555,10 +673,10 @@ function getValueDisplay(filter, field) {
|
|
|
555
673
|
const [start, end] = value;
|
|
556
674
|
const startStr = start instanceof Date ? start.toLocaleDateString() : "...";
|
|
557
675
|
const endStr = end instanceof Date ? end.toLocaleDateString() : "...";
|
|
558
|
-
return `${startStr}
|
|
676
|
+
return `${startStr} \u2013 ${endStr}`;
|
|
559
677
|
}
|
|
560
678
|
if (operator === "between" && Array.isArray(value)) {
|
|
561
|
-
return `${(_c = value[0]) != null ? _c : "..."}
|
|
679
|
+
return `${(_c = value[0]) != null ? _c : "..."} \u2013 ${(_d = value[1]) != null ? _d : "..."}`;
|
|
562
680
|
}
|
|
563
681
|
return String(value);
|
|
564
682
|
}
|