@kopexa/filter 0.0.2
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/LICENSE +201 -0
- package/dist/chunk-3WNAREG6.mjs +29 -0
- package/dist/chunk-63P4ITVP.mjs +263 -0
- package/dist/chunk-7KY2PDNL.mjs +136 -0
- package/dist/chunk-7QP7FRID.mjs +47 -0
- package/dist/chunk-EF4VI36D.mjs +36 -0
- package/dist/chunk-I3Z2T4N2.mjs +14 -0
- package/dist/chunk-PTJ7HZPA.mjs +164 -0
- package/dist/chunk-TBHYZZSX.mjs +139 -0
- package/dist/chunk-URDCG5NI.mjs +111 -0
- package/dist/filter-active.d.mts +13 -0
- package/dist/filter-active.d.ts +13 -0
- package/dist/filter-active.js +538 -0
- package/dist/filter-active.mjs +12 -0
- package/dist/filter-context.d.mts +28 -0
- package/dist/filter-context.d.ts +28 -0
- package/dist/filter-context.js +39 -0
- package/dist/filter-context.mjs +10 -0
- package/dist/filter-i18n.d.mts +20 -0
- package/dist/filter-i18n.d.ts +20 -0
- package/dist/filter-i18n.js +52 -0
- package/dist/filter-i18n.mjs +7 -0
- package/dist/filter-menu.d.mts +65 -0
- package/dist/filter-menu.d.ts +65 -0
- package/dist/filter-menu.js +169 -0
- package/dist/filter-menu.mjs +15 -0
- package/dist/filter-trigger.d.mts +14 -0
- package/dist/filter-trigger.d.ts +14 -0
- package/dist/filter-trigger.js +170 -0
- package/dist/filter-trigger.mjs +10 -0
- package/dist/filter-types.d.mts +60 -0
- package/dist/filter-types.d.ts +60 -0
- package/dist/filter-types.js +72 -0
- package/dist/filter-types.mjs +11 -0
- package/dist/filter-value-editor.d.mts +11 -0
- package/dist/filter-value-editor.d.ts +11 -0
- package/dist/filter-value-editor.js +414 -0
- package/dist/filter-value-editor.mjs +11 -0
- package/dist/filter.d.mts +24 -0
- package/dist/filter.d.ts +24 -0
- package/dist/filter.js +242 -0
- package/dist/filter.mjs +11 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +909 -0
- package/dist/index.mjs +39 -0
- package/dist/messages.d.mts +104 -0
- package/dist/messages.d.ts +104 -0
- package/dist/messages.js +134 -0
- package/dist/messages.mjs +7 -0
- package/package.json +69 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/filter-active.tsx
|
|
23
|
+
var filter_active_exports = {};
|
|
24
|
+
__export(filter_active_exports, {
|
|
25
|
+
FilterActive: () => FilterActive
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(filter_active_exports);
|
|
28
|
+
var import_i18n3 = require("@kopexa/i18n");
|
|
29
|
+
var import_icons = require("@kopexa/icons");
|
|
30
|
+
var import_popover = require("@kopexa/popover");
|
|
31
|
+
var import_shared_utils = require("@kopexa/shared-utils");
|
|
32
|
+
|
|
33
|
+
// src/filter-context.tsx
|
|
34
|
+
var import_react_utils = require("@kopexa/react-utils");
|
|
35
|
+
var [FilterProvider, useFilterContext] = (0, import_react_utils.createContext)({
|
|
36
|
+
name: "FilterContext",
|
|
37
|
+
strict: true,
|
|
38
|
+
errorMessage: "useFilterContext must be used within a Filter component. Make sure to wrap your FilterMenu, FilterTrigger, etc. inside a <Filter> component."
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// src/filter-value-editor.tsx
|
|
42
|
+
var import_checkbox = require("@kopexa/checkbox");
|
|
43
|
+
var import_i18n2 = require("@kopexa/i18n");
|
|
44
|
+
var import_input = require("@kopexa/input");
|
|
45
|
+
var import_select = require("@kopexa/select");
|
|
46
|
+
|
|
47
|
+
// src/filter-types.ts
|
|
48
|
+
var DEFAULT_OPERATORS = {
|
|
49
|
+
text: [
|
|
50
|
+
"equals",
|
|
51
|
+
"not_equals",
|
|
52
|
+
"contains",
|
|
53
|
+
"not_contains",
|
|
54
|
+
"starts_with",
|
|
55
|
+
"ends_with",
|
|
56
|
+
"is_empty",
|
|
57
|
+
"is_not_empty"
|
|
58
|
+
],
|
|
59
|
+
number: ["equals", "not_equals", "gt", "lt", "gte", "lte", "between"],
|
|
60
|
+
select: ["equals", "not_equals", "is_empty", "is_not_empty"],
|
|
61
|
+
multiselect: ["equals", "not_equals", "is_empty", "is_not_empty"],
|
|
62
|
+
date: ["equals", "not_equals", "gt", "lt", "gte", "lte", "between"],
|
|
63
|
+
daterange: ["between"],
|
|
64
|
+
boolean: ["equals"]
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// src/messages.ts
|
|
68
|
+
var import_i18n = require("@kopexa/i18n");
|
|
69
|
+
var messages = (0, import_i18n.defineMessages)({
|
|
70
|
+
add_filter: {
|
|
71
|
+
id: "filter.add_filter",
|
|
72
|
+
defaultMessage: "Add Filter",
|
|
73
|
+
description: "Button text for adding a new filter"
|
|
74
|
+
},
|
|
75
|
+
clear_all: {
|
|
76
|
+
id: "filter.clear_all",
|
|
77
|
+
defaultMessage: "Clear all",
|
|
78
|
+
description: "Button text for clearing all filters"
|
|
79
|
+
},
|
|
80
|
+
no_fields: {
|
|
81
|
+
id: "filter.no_fields",
|
|
82
|
+
defaultMessage: "No filter options available",
|
|
83
|
+
description: "Message when no filter fields are configured"
|
|
84
|
+
},
|
|
85
|
+
apply: {
|
|
86
|
+
id: "filter.apply",
|
|
87
|
+
defaultMessage: "Apply",
|
|
88
|
+
description: "Button text for applying a filter value"
|
|
89
|
+
},
|
|
90
|
+
cancel: {
|
|
91
|
+
id: "filter.cancel",
|
|
92
|
+
defaultMessage: "Cancel",
|
|
93
|
+
description: "Button text for canceling filter editing"
|
|
94
|
+
},
|
|
95
|
+
select_value: {
|
|
96
|
+
id: "filter.select_value",
|
|
97
|
+
defaultMessage: "Select value...",
|
|
98
|
+
description: "Placeholder for value selection"
|
|
99
|
+
},
|
|
100
|
+
enter_value: {
|
|
101
|
+
id: "filter.enter_value",
|
|
102
|
+
defaultMessage: "Enter value...",
|
|
103
|
+
description: "Placeholder for value input"
|
|
104
|
+
},
|
|
105
|
+
// Operators
|
|
106
|
+
op_equals: {
|
|
107
|
+
id: "filter.operator.equals",
|
|
108
|
+
defaultMessage: "is",
|
|
109
|
+
description: "Operator: equals"
|
|
110
|
+
},
|
|
111
|
+
op_not_equals: {
|
|
112
|
+
id: "filter.operator.not_equals",
|
|
113
|
+
defaultMessage: "is not",
|
|
114
|
+
description: "Operator: not equals"
|
|
115
|
+
},
|
|
116
|
+
op_contains: {
|
|
117
|
+
id: "filter.operator.contains",
|
|
118
|
+
defaultMessage: "contains",
|
|
119
|
+
description: "Operator: contains"
|
|
120
|
+
},
|
|
121
|
+
op_not_contains: {
|
|
122
|
+
id: "filter.operator.not_contains",
|
|
123
|
+
defaultMessage: "does not contain",
|
|
124
|
+
description: "Operator: does not contain"
|
|
125
|
+
},
|
|
126
|
+
op_starts_with: {
|
|
127
|
+
id: "filter.operator.starts_with",
|
|
128
|
+
defaultMessage: "starts with",
|
|
129
|
+
description: "Operator: starts with"
|
|
130
|
+
},
|
|
131
|
+
op_ends_with: {
|
|
132
|
+
id: "filter.operator.ends_with",
|
|
133
|
+
defaultMessage: "ends with",
|
|
134
|
+
description: "Operator: ends with"
|
|
135
|
+
},
|
|
136
|
+
op_gt: {
|
|
137
|
+
id: "filter.operator.gt",
|
|
138
|
+
defaultMessage: "greater than",
|
|
139
|
+
description: "Operator: greater than"
|
|
140
|
+
},
|
|
141
|
+
op_lt: {
|
|
142
|
+
id: "filter.operator.lt",
|
|
143
|
+
defaultMessage: "less than",
|
|
144
|
+
description: "Operator: less than"
|
|
145
|
+
},
|
|
146
|
+
op_gte: {
|
|
147
|
+
id: "filter.operator.gte",
|
|
148
|
+
defaultMessage: "greater or equal",
|
|
149
|
+
description: "Operator: greater than or equal"
|
|
150
|
+
},
|
|
151
|
+
op_lte: {
|
|
152
|
+
id: "filter.operator.lte",
|
|
153
|
+
defaultMessage: "less or equal",
|
|
154
|
+
description: "Operator: less than or equal"
|
|
155
|
+
},
|
|
156
|
+
op_between: {
|
|
157
|
+
id: "filter.operator.between",
|
|
158
|
+
defaultMessage: "between",
|
|
159
|
+
description: "Operator: between"
|
|
160
|
+
},
|
|
161
|
+
op_is_empty: {
|
|
162
|
+
id: "filter.operator.is_empty",
|
|
163
|
+
defaultMessage: "is empty",
|
|
164
|
+
description: "Operator: is empty"
|
|
165
|
+
},
|
|
166
|
+
op_is_not_empty: {
|
|
167
|
+
id: "filter.operator.is_not_empty",
|
|
168
|
+
defaultMessage: "is not empty",
|
|
169
|
+
description: "Operator: is not empty"
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// src/filter-value-editor.tsx
|
|
174
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
175
|
+
function FilterValueEditor({ filter, field }) {
|
|
176
|
+
var _a;
|
|
177
|
+
const { styles, updateFilter, setEditingFilterId } = useFilterContext();
|
|
178
|
+
const t = (0, import_i18n2.useSafeIntl)();
|
|
179
|
+
const operators = (_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type];
|
|
180
|
+
const handleOperatorChange = (newOperator) => {
|
|
181
|
+
updateFilter(filter.id, { operator: newOperator });
|
|
182
|
+
if (newOperator === "is_empty" || newOperator === "is_not_empty") {
|
|
183
|
+
setEditingFilterId(null);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const handleValueChange = (newValue) => {
|
|
187
|
+
updateFilter(filter.id, { value: newValue });
|
|
188
|
+
};
|
|
189
|
+
const handleSelectValue = (newValue) => {
|
|
190
|
+
updateFilter(filter.id, { value: newValue });
|
|
191
|
+
setEditingFilterId(null);
|
|
192
|
+
};
|
|
193
|
+
const getOperatorLabel = (op) => {
|
|
194
|
+
const key = `op_${op}`;
|
|
195
|
+
return messages[key] ? t.formatMessage(messages[key]) : op;
|
|
196
|
+
};
|
|
197
|
+
const needsValue = filter.operator !== "is_empty" && filter.operator !== "is_not_empty";
|
|
198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-slot": "filter-value-editor", children: [
|
|
199
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorHeader(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: styles.editorTitle(), children: field.label }) }),
|
|
200
|
+
operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
201
|
+
import_select.Select,
|
|
202
|
+
{
|
|
203
|
+
value: filter.operator,
|
|
204
|
+
onValueChange: (val) => handleOperatorChange(val),
|
|
205
|
+
size: "sm",
|
|
206
|
+
children: [
|
|
207
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.Select.Trigger, { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.Select.Value, {}) }),
|
|
208
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.Select.Content, { children: operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.Select.Item, { value: op, children: getOperatorLabel(op) }, op)) })
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
) }),
|
|
212
|
+
needsValue && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorInput(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
213
|
+
ValueInput,
|
|
214
|
+
{
|
|
215
|
+
field,
|
|
216
|
+
operator: filter.operator,
|
|
217
|
+
value: filter.value,
|
|
218
|
+
onChange: handleValueChange,
|
|
219
|
+
onSelect: handleSelectValue
|
|
220
|
+
}
|
|
221
|
+
) })
|
|
222
|
+
] });
|
|
223
|
+
}
|
|
224
|
+
function ValueInput({
|
|
225
|
+
field,
|
|
226
|
+
operator,
|
|
227
|
+
value,
|
|
228
|
+
onChange,
|
|
229
|
+
onSelect
|
|
230
|
+
}) {
|
|
231
|
+
var _a, _b, _c, _d, _e, _f;
|
|
232
|
+
const t = (0, import_i18n2.useSafeIntl)();
|
|
233
|
+
switch (field.type) {
|
|
234
|
+
case "text":
|
|
235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
236
|
+
import_input.Input,
|
|
237
|
+
{
|
|
238
|
+
value: String(value != null ? value : ""),
|
|
239
|
+
onChange: (e) => onChange(e.target.value),
|
|
240
|
+
placeholder: (_a = field.placeholder) != null ? _a : t.formatMessage(messages.enter_value),
|
|
241
|
+
size: "sm",
|
|
242
|
+
autoFocus: true
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
case "number":
|
|
246
|
+
if (operator === "between") {
|
|
247
|
+
const [min, max] = Array.isArray(value) ? value : [void 0, void 0];
|
|
248
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
249
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
250
|
+
import_input.Input,
|
|
251
|
+
{
|
|
252
|
+
type: "number",
|
|
253
|
+
value: min != null ? min : "",
|
|
254
|
+
onChange: (e) => onChange([
|
|
255
|
+
e.target.value ? Number(e.target.value) : void 0,
|
|
256
|
+
max
|
|
257
|
+
]),
|
|
258
|
+
placeholder: "Min",
|
|
259
|
+
min: field.min,
|
|
260
|
+
max: field.max,
|
|
261
|
+
step: field.step,
|
|
262
|
+
size: "sm",
|
|
263
|
+
autoFocus: true
|
|
264
|
+
}
|
|
265
|
+
),
|
|
266
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground", children: "-" }),
|
|
267
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
268
|
+
import_input.Input,
|
|
269
|
+
{
|
|
270
|
+
type: "number",
|
|
271
|
+
value: max != null ? max : "",
|
|
272
|
+
onChange: (e) => onChange([
|
|
273
|
+
min,
|
|
274
|
+
e.target.value ? Number(e.target.value) : void 0
|
|
275
|
+
]),
|
|
276
|
+
placeholder: "Max",
|
|
277
|
+
min: field.min,
|
|
278
|
+
max: field.max,
|
|
279
|
+
step: field.step,
|
|
280
|
+
size: "sm"
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
] });
|
|
284
|
+
}
|
|
285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
286
|
+
import_input.Input,
|
|
287
|
+
{
|
|
288
|
+
type: "number",
|
|
289
|
+
value: value !== void 0 && value !== null ? String(value) : "",
|
|
290
|
+
onChange: (e) => onChange(e.target.value ? Number(e.target.value) : void 0),
|
|
291
|
+
placeholder: (_b = field.placeholder) != null ? _b : t.formatMessage(messages.enter_value),
|
|
292
|
+
min: field.min,
|
|
293
|
+
max: field.max,
|
|
294
|
+
step: field.step,
|
|
295
|
+
size: "sm",
|
|
296
|
+
autoFocus: true
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
case "select":
|
|
300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_select.Select, { value: String(value != null ? value : ""), onValueChange: onSelect, size: "sm", children: [
|
|
301
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.Select.Trigger, { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
302
|
+
import_select.Select.Value,
|
|
303
|
+
{
|
|
304
|
+
placeholder: (_c = field.placeholder) != null ? _c : t.formatMessage(messages.select_value)
|
|
305
|
+
}
|
|
306
|
+
) }),
|
|
307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.Select.Content, { children: (_d = field.options) == null ? void 0 : _d.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
308
|
+
import_select.Select.Item,
|
|
309
|
+
{
|
|
310
|
+
value: option.value,
|
|
311
|
+
disabled: option.disabled,
|
|
312
|
+
children: [
|
|
313
|
+
option.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "mr-2 size-4", children: option.icon }),
|
|
314
|
+
option.label
|
|
315
|
+
]
|
|
316
|
+
},
|
|
317
|
+
option.value
|
|
318
|
+
)) })
|
|
319
|
+
] });
|
|
320
|
+
case "multiselect": {
|
|
321
|
+
const selectedValues = Array.isArray(value) ? value : [];
|
|
322
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-2 max-h-[200px] overflow-auto", children: (_e = field.options) == null ? void 0 : _e.map((option) => (
|
|
323
|
+
// biome-ignore lint/a11y/noLabelWithoutControl: Checkbox is a custom form control inside the label
|
|
324
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
325
|
+
"label",
|
|
326
|
+
{
|
|
327
|
+
className: "flex items-center gap-2 cursor-pointer",
|
|
328
|
+
children: [
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
330
|
+
import_checkbox.Checkbox,
|
|
331
|
+
{
|
|
332
|
+
checked: selectedValues.includes(option.value),
|
|
333
|
+
onCheckedChange: (checked) => {
|
|
334
|
+
if (checked) {
|
|
335
|
+
onChange([...selectedValues, option.value]);
|
|
336
|
+
} else {
|
|
337
|
+
onChange(selectedValues.filter((v) => v !== option.value));
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
disabled: option.disabled
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
option.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "size-4", children: option.icon }),
|
|
344
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm", children: option.label })
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
option.value
|
|
348
|
+
)
|
|
349
|
+
)) });
|
|
350
|
+
}
|
|
351
|
+
case "boolean":
|
|
352
|
+
return (
|
|
353
|
+
// biome-ignore lint/a11y/noLabelWithoutControl: Checkbox is a custom form control inside the label
|
|
354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer", children: [
|
|
355
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
356
|
+
import_checkbox.Checkbox,
|
|
357
|
+
{
|
|
358
|
+
checked: Boolean(value),
|
|
359
|
+
onCheckedChange: (checked) => onSelect(Boolean(checked))
|
|
360
|
+
}
|
|
361
|
+
),
|
|
362
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-sm", children: field.label })
|
|
363
|
+
] })
|
|
364
|
+
);
|
|
365
|
+
case "date":
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
367
|
+
import_input.Input,
|
|
368
|
+
{
|
|
369
|
+
type: "date",
|
|
370
|
+
value: value instanceof Date ? value.toISOString().split("T")[0] : String(value != null ? value : ""),
|
|
371
|
+
onChange: (e) => onChange(e.target.value ? new Date(e.target.value) : void 0),
|
|
372
|
+
size: "sm"
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
case "daterange": {
|
|
376
|
+
const [start, end] = Array.isArray(value) ? value : [void 0, void 0];
|
|
377
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
378
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
379
|
+
import_input.Input,
|
|
380
|
+
{
|
|
381
|
+
type: "date",
|
|
382
|
+
value: start instanceof Date ? start.toISOString().split("T")[0] : String(start != null ? start : ""),
|
|
383
|
+
onChange: (e) => onChange([
|
|
384
|
+
e.target.value ? new Date(e.target.value) : void 0,
|
|
385
|
+
end
|
|
386
|
+
]),
|
|
387
|
+
size: "sm"
|
|
388
|
+
}
|
|
389
|
+
),
|
|
390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground", children: "-" }),
|
|
391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
392
|
+
import_input.Input,
|
|
393
|
+
{
|
|
394
|
+
type: "date",
|
|
395
|
+
value: end instanceof Date ? end.toISOString().split("T")[0] : String(end != null ? end : ""),
|
|
396
|
+
onChange: (e) => onChange([
|
|
397
|
+
start,
|
|
398
|
+
e.target.value ? new Date(e.target.value) : void 0
|
|
399
|
+
]),
|
|
400
|
+
size: "sm"
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
] });
|
|
404
|
+
}
|
|
405
|
+
default:
|
|
406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
407
|
+
import_input.Input,
|
|
408
|
+
{
|
|
409
|
+
value: String(value != null ? value : ""),
|
|
410
|
+
onChange: (e) => onChange(e.target.value),
|
|
411
|
+
placeholder: (_f = field.placeholder) != null ? _f : t.formatMessage(messages.enter_value),
|
|
412
|
+
size: "sm"
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// src/filter-active.tsx
|
|
419
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
420
|
+
function FilterActive(props) {
|
|
421
|
+
const { showClearAll = true, className, ...rest } = props;
|
|
422
|
+
const { value, styles, clearFilters } = useFilterContext();
|
|
423
|
+
const t = (0, import_i18n3.useSafeIntl)();
|
|
424
|
+
if (value.length === 0) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
428
|
+
"div",
|
|
429
|
+
{
|
|
430
|
+
"data-slot": "filter-active",
|
|
431
|
+
className: (0, import_shared_utils.cn)(styles.active(), className),
|
|
432
|
+
...rest,
|
|
433
|
+
children: [
|
|
434
|
+
value.map((filter) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FilterField, { filter }, filter.id)),
|
|
435
|
+
showClearAll && value.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
436
|
+
"button",
|
|
437
|
+
{
|
|
438
|
+
type: "button",
|
|
439
|
+
className: styles.clearAll(),
|
|
440
|
+
onClick: clearFilters,
|
|
441
|
+
children: t.formatMessage(messages.clear_all)
|
|
442
|
+
}
|
|
443
|
+
)
|
|
444
|
+
]
|
|
445
|
+
}
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
FilterActive.displayName = "FilterActive";
|
|
449
|
+
function FilterField({ filter }) {
|
|
450
|
+
const { fields, styles, removeFilter, editingFilterId, setEditingFilterId } = useFilterContext();
|
|
451
|
+
const t = (0, import_i18n3.useSafeIntl)();
|
|
452
|
+
const field = fields.get(filter.fieldId);
|
|
453
|
+
if (!field) return null;
|
|
454
|
+
const isEditing = editingFilterId === filter.id;
|
|
455
|
+
const operatorKey = `op_${filter.operator}`;
|
|
456
|
+
const operatorLabel = messages[operatorKey] ? t.formatMessage(messages[operatorKey]) : filter.operator;
|
|
457
|
+
const valueDisplay = getValueDisplay(filter, field);
|
|
458
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
459
|
+
import_popover.Popover.Root,
|
|
460
|
+
{
|
|
461
|
+
open: isEditing,
|
|
462
|
+
onOpenChange: (open) => setEditingFilterId(open ? filter.id : null),
|
|
463
|
+
children: [
|
|
464
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_popover.Popover.Trigger, { "data-slot": "filter-field", className: styles.field(), children: [
|
|
465
|
+
field.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.menuItemIcon(), children: field.icon }),
|
|
466
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldLabel(), children: field.label }),
|
|
467
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldOperator(), children: operatorLabel }),
|
|
468
|
+
valueDisplay && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.fieldValue(), children: valueDisplay }),
|
|
469
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
470
|
+
"button",
|
|
471
|
+
{
|
|
472
|
+
type: "button",
|
|
473
|
+
className: styles.fieldRemove(),
|
|
474
|
+
onClick: (e) => {
|
|
475
|
+
e.stopPropagation();
|
|
476
|
+
removeFilter(filter.id);
|
|
477
|
+
},
|
|
478
|
+
"aria-label": "Remove filter",
|
|
479
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_icons.CloseIcon, { className: "size-3" })
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
] }),
|
|
483
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
484
|
+
import_popover.Popover.Content,
|
|
485
|
+
{
|
|
486
|
+
"data-slot": "filter-editor",
|
|
487
|
+
className: styles.editor(),
|
|
488
|
+
align: "start",
|
|
489
|
+
showArrow: false,
|
|
490
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FilterValueEditor, { filter, field })
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
]
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
function getValueDisplay(filter, field) {
|
|
498
|
+
var _a, _b, _c, _d;
|
|
499
|
+
const { value, operator } = filter;
|
|
500
|
+
if (operator === "is_empty" || operator === "is_not_empty") {
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
if (value === void 0 || value === null || value === "") {
|
|
504
|
+
return "...";
|
|
505
|
+
}
|
|
506
|
+
if (field.type === "boolean") {
|
|
507
|
+
return value ? "Yes" : "No";
|
|
508
|
+
}
|
|
509
|
+
if (field.type === "select" && field.options) {
|
|
510
|
+
const option = field.options.find((o) => o.value === value);
|
|
511
|
+
return (_a = option == null ? void 0 : option.label) != null ? _a : String(value);
|
|
512
|
+
}
|
|
513
|
+
if (field.type === "multiselect" && Array.isArray(value)) {
|
|
514
|
+
if (value.length === 0) return "...";
|
|
515
|
+
if (value.length === 1 && field.options) {
|
|
516
|
+
const option = field.options.find((o) => o.value === value[0]);
|
|
517
|
+
return (_b = option == null ? void 0 : option.label) != null ? _b : String(value[0]);
|
|
518
|
+
}
|
|
519
|
+
return `${value.length} selected`;
|
|
520
|
+
}
|
|
521
|
+
if (field.type === "date" && value instanceof Date) {
|
|
522
|
+
return value.toLocaleDateString();
|
|
523
|
+
}
|
|
524
|
+
if (field.type === "daterange" && Array.isArray(value)) {
|
|
525
|
+
const [start, end] = value;
|
|
526
|
+
const startStr = start instanceof Date ? start.toLocaleDateString() : "...";
|
|
527
|
+
const endStr = end instanceof Date ? end.toLocaleDateString() : "...";
|
|
528
|
+
return `${startStr} - ${endStr}`;
|
|
529
|
+
}
|
|
530
|
+
if (operator === "between" && Array.isArray(value)) {
|
|
531
|
+
return `${(_c = value[0]) != null ? _c : "..."} - ${(_d = value[1]) != null ? _d : "..."}`;
|
|
532
|
+
}
|
|
533
|
+
return String(value);
|
|
534
|
+
}
|
|
535
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
536
|
+
0 && (module.exports = {
|
|
537
|
+
FilterActive
|
|
538
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { filter, FilterVariantProps } from '@kopexa/theme';
|
|
3
|
+
import { FilterI18nConfig } from './filter-i18n.mjs';
|
|
4
|
+
import { FilterValue, FilterFieldConfig } from './filter-types.mjs';
|
|
5
|
+
|
|
6
|
+
interface FilterContextValue {
|
|
7
|
+
value: FilterValue[];
|
|
8
|
+
fields: Map<string, FilterFieldConfig>;
|
|
9
|
+
allowMultiple: boolean;
|
|
10
|
+
styles: ReturnType<typeof filter>;
|
|
11
|
+
size: FilterVariantProps["size"];
|
|
12
|
+
variant: FilterVariantProps["variant"];
|
|
13
|
+
i18n: FilterI18nConfig;
|
|
14
|
+
addFilter: (fieldId: string) => void;
|
|
15
|
+
updateFilter: (filterId: string, updates: Partial<FilterValue>) => void;
|
|
16
|
+
removeFilter: (filterId: string) => void;
|
|
17
|
+
clearFilters: () => void;
|
|
18
|
+
registerField: (field: FilterFieldConfig) => void;
|
|
19
|
+
unregisterField: (fieldId: string) => void;
|
|
20
|
+
menuOpen: boolean;
|
|
21
|
+
setMenuOpen: (open: boolean) => void;
|
|
22
|
+
editingFilterId: string | null;
|
|
23
|
+
setEditingFilterId: (id: string | null) => void;
|
|
24
|
+
}
|
|
25
|
+
declare const FilterProvider: React.Provider<FilterContextValue>;
|
|
26
|
+
declare const useFilterContext: () => FilterContextValue;
|
|
27
|
+
|
|
28
|
+
export { type FilterContextValue, FilterProvider, useFilterContext };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { filter, FilterVariantProps } from '@kopexa/theme';
|
|
3
|
+
import { FilterI18nConfig } from './filter-i18n.js';
|
|
4
|
+
import { FilterValue, FilterFieldConfig } from './filter-types.js';
|
|
5
|
+
|
|
6
|
+
interface FilterContextValue {
|
|
7
|
+
value: FilterValue[];
|
|
8
|
+
fields: Map<string, FilterFieldConfig>;
|
|
9
|
+
allowMultiple: boolean;
|
|
10
|
+
styles: ReturnType<typeof filter>;
|
|
11
|
+
size: FilterVariantProps["size"];
|
|
12
|
+
variant: FilterVariantProps["variant"];
|
|
13
|
+
i18n: FilterI18nConfig;
|
|
14
|
+
addFilter: (fieldId: string) => void;
|
|
15
|
+
updateFilter: (filterId: string, updates: Partial<FilterValue>) => void;
|
|
16
|
+
removeFilter: (filterId: string) => void;
|
|
17
|
+
clearFilters: () => void;
|
|
18
|
+
registerField: (field: FilterFieldConfig) => void;
|
|
19
|
+
unregisterField: (fieldId: string) => void;
|
|
20
|
+
menuOpen: boolean;
|
|
21
|
+
setMenuOpen: (open: boolean) => void;
|
|
22
|
+
editingFilterId: string | null;
|
|
23
|
+
setEditingFilterId: (id: string | null) => void;
|
|
24
|
+
}
|
|
25
|
+
declare const FilterProvider: React.Provider<FilterContextValue>;
|
|
26
|
+
declare const useFilterContext: () => FilterContextValue;
|
|
27
|
+
|
|
28
|
+
export { type FilterContextValue, FilterProvider, useFilterContext };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/filter-context.tsx
|
|
23
|
+
var filter_context_exports = {};
|
|
24
|
+
__export(filter_context_exports, {
|
|
25
|
+
FilterProvider: () => FilterProvider,
|
|
26
|
+
useFilterContext: () => useFilterContext
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(filter_context_exports);
|
|
29
|
+
var import_react_utils = require("@kopexa/react-utils");
|
|
30
|
+
var [FilterProvider, useFilterContext] = (0, import_react_utils.createContext)({
|
|
31
|
+
name: "FilterContext",
|
|
32
|
+
strict: true,
|
|
33
|
+
errorMessage: "useFilterContext must be used within a Filter component. Make sure to wrap your FilterMenu, FilterTrigger, etc. inside a <Filter> component."
|
|
34
|
+
});
|
|
35
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
36
|
+
0 && (module.exports = {
|
|
37
|
+
FilterProvider,
|
|
38
|
+
useFilterContext
|
|
39
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FilterOperator } from './filter-types.mjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* i18n configuration for the Filter component
|
|
6
|
+
*/
|
|
7
|
+
interface FilterI18nConfig {
|
|
8
|
+
addFilter: string;
|
|
9
|
+
clearAll: string;
|
|
10
|
+
noFields: string;
|
|
11
|
+
selectValue: string;
|
|
12
|
+
enterValue: string;
|
|
13
|
+
operators: Record<FilterOperator, string>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default English translations
|
|
17
|
+
*/
|
|
18
|
+
declare const DEFAULT_I18N: FilterI18nConfig;
|
|
19
|
+
|
|
20
|
+
export { DEFAULT_I18N, type FilterI18nConfig };
|