@kopexa/filter 0.0.25 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/chunk-6TRAIAKS.mjs +178 -0
  2. package/dist/chunk-I7WCYMXD.mjs +133 -0
  3. package/dist/{chunk-45QJL74L.mjs → chunk-JSRGUDCG.mjs} +49 -24
  4. package/dist/chunk-NUDUXOHP.mjs +243 -0
  5. package/dist/chunk-ON2UFJ3Y.mjs +32 -0
  6. package/dist/{chunk-RFCPJLIQ.mjs → chunk-SJXRD3RO.mjs} +6 -1
  7. package/dist/{chunk-EF4VI36D.mjs → chunk-UBTUCPOG.mjs} +1 -1
  8. package/dist/{chunk-3ZBNWXRA.mjs → chunk-WD7YU6IN.mjs} +17 -14
  9. package/dist/chunk-XCWKWXBW.mjs +602 -0
  10. package/dist/{chunk-URDCG5NI.mjs → chunk-YTYOFT33.mjs} +52 -0
  11. package/dist/filter-active.js +108 -28
  12. package/dist/filter-active.mjs +4 -4
  13. package/dist/filter-bar-internal.d.mts +25 -0
  14. package/dist/filter-bar-internal.d.ts +25 -0
  15. package/dist/filter-bar-internal.js +648 -0
  16. package/dist/filter-bar-internal.mjs +11 -0
  17. package/dist/filter-bar-messages.d.mts +124 -0
  18. package/dist/filter-bar-messages.d.ts +124 -0
  19. package/dist/filter-bar-messages.js +156 -0
  20. package/dist/filter-bar-messages.mjs +7 -0
  21. package/dist/filter-bar-types.d.mts +112 -0
  22. package/dist/filter-bar-types.d.ts +112 -0
  23. package/dist/filter-bar-types.js +56 -0
  24. package/dist/filter-bar-types.mjs +8 -0
  25. package/dist/filter-bar.d.mts +29 -0
  26. package/dist/filter-bar.d.ts +29 -0
  27. package/dist/filter-bar.js +942 -0
  28. package/dist/filter-bar.mjs +11 -0
  29. package/dist/filter-menu.js +161 -1
  30. package/dist/filter-menu.mjs +2 -1
  31. package/dist/filter-trigger.js +52 -0
  32. package/dist/filter-trigger.mjs +2 -2
  33. package/dist/filter-value-editor.js +65 -10
  34. package/dist/filter-value-editor.mjs +3 -3
  35. package/dist/filter.d.mts +6 -0
  36. package/dist/filter.d.ts +6 -0
  37. package/dist/filter.mjs +2 -2
  38. package/dist/index.d.mts +4 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +1263 -31
  41. package/dist/index.mjs +28 -14
  42. package/dist/messages.d.mts +50 -0
  43. package/dist/messages.d.ts +50 -0
  44. package/dist/messages.js +52 -0
  45. package/dist/messages.mjs +1 -1
  46. package/dist/search-filter-bar.d.mts +44 -0
  47. package/dist/search-filter-bar.d.ts +44 -0
  48. package/dist/search-filter-bar.js +1007 -0
  49. package/dist/search-filter-bar.mjs +11 -0
  50. package/package.json +19 -16
  51. package/dist/{chunk-SH7DBK54.mjs → chunk-LWDVRMCI.mjs} +3 -3
@@ -0,0 +1,648 @@
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-bar-internal.tsx
23
+ var filter_bar_internal_exports = {};
24
+ __export(filter_bar_internal_exports, {
25
+ AddFilterDropdown: () => AddFilterDropdown,
26
+ FilterPill: () => FilterPill
27
+ });
28
+ module.exports = __toCommonJS(filter_bar_internal_exports);
29
+ var import_button = require("@kopexa/button");
30
+ var import_checkbox = require("@kopexa/checkbox");
31
+ var import_command = require("@kopexa/command");
32
+ var import_dropdown_menu = require("@kopexa/dropdown-menu");
33
+ var import_icons = require("@kopexa/icons");
34
+ var import_input = require("@kopexa/input");
35
+ var import_popover = require("@kopexa/popover");
36
+ var import_shared_utils = require("@kopexa/shared-utils");
37
+ var import_react = require("react");
38
+
39
+ // src/filter-bar-types.ts
40
+ var DEFAULT_FILTER_BAR_OPERATORS = {
41
+ select: ["equals", "not_equals", "is_empty", "is_not_empty"],
42
+ multiselect: ["equals", "not_equals", "is_empty", "is_not_empty"],
43
+ text: [
44
+ "equals",
45
+ "not_equals",
46
+ "contains",
47
+ "not_contains",
48
+ "starts_with",
49
+ "ends_with",
50
+ "is_empty",
51
+ "is_not_empty"
52
+ ],
53
+ number: [
54
+ "equals",
55
+ "not_equals",
56
+ "gt",
57
+ "lt",
58
+ "gte",
59
+ "lte",
60
+ "is_empty",
61
+ "is_not_empty"
62
+ ],
63
+ custom: ["equals", "not_equals"]
64
+ };
65
+
66
+ // src/filter-bar-internal.tsx
67
+ var import_jsx_runtime = require("react/jsx-runtime");
68
+ function AddFilterDropdown(props) {
69
+ const { fieldGroups, isFieldAvailable, onAddFilter, i18n, disabled } = props;
70
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dropdown_menu.DropdownMenu.Root, { children: [
71
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropdown_menu.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
72
+ import_button.Button,
73
+ {
74
+ variant: "outline",
75
+ size: "sm",
76
+ className: "rounded-full border-dashed",
77
+ disabled,
78
+ startContent: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.PlusIcon, { className: "size-4" }),
79
+ children: i18n.addFilter
80
+ }
81
+ ) }),
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropdown_menu.DropdownMenu.Content, { align: "start", className: "min-w-[200px]", children: Array.from(fieldGroups.entries()).map(([groupLabel, groupFields]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dropdown_menu.DropdownMenu.Group, { children: [
83
+ groupLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropdown_menu.DropdownMenu.Label, { children: groupLabel }),
84
+ groupFields.map((field) => {
85
+ const available = isFieldAvailable(field.id);
86
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
87
+ import_dropdown_menu.DropdownMenu.Item,
88
+ {
89
+ disabled: field.disabled || !available,
90
+ onSelect: () => onAddFilter(field.id),
91
+ children: [
92
+ field.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "size-4 text-muted-foreground mr-2", children: field.icon }),
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: field.label })
94
+ ]
95
+ },
96
+ field.id
97
+ );
98
+ })
99
+ ] }, groupLabel != null ? groupLabel : "ungrouped")) })
100
+ ] });
101
+ }
102
+ function FilterPill(props) {
103
+ var _a;
104
+ const {
105
+ filter,
106
+ field,
107
+ i18n,
108
+ onUpdate,
109
+ onRemove,
110
+ autoOpen,
111
+ onAutoOpenComplete,
112
+ disabled
113
+ } = props;
114
+ const [open, setOpen] = (0, import_react.useState)(autoOpen != null ? autoOpen : false);
115
+ const operators = (_a = field.operators) != null ? _a : DEFAULT_FILTER_BAR_OPERATORS[field.type];
116
+ const isEmptyOperator = filter.operator === "is_empty" || filter.operator === "is_not_empty";
117
+ const hasCustomRenderer = !!field.customRenderer;
118
+ const useInlineInput = !hasCustomRenderer && (field.type === "text" || field.type === "number");
119
+ const usePopover = !useInlineInput && !isEmptyOperator;
120
+ const handleOpenChange = (0, import_react.useCallback)(
121
+ (isOpen) => {
122
+ setOpen(isOpen);
123
+ if (!isOpen && autoOpen) {
124
+ onAutoOpenComplete == null ? void 0 : onAutoOpenComplete();
125
+ }
126
+ },
127
+ [autoOpen, onAutoOpenComplete]
128
+ );
129
+ const valueDisplay = (0, import_react.useMemo)(() => {
130
+ var _a2, _b;
131
+ if (isEmptyOperator) return null;
132
+ if (filter.value === null || filter.value === "") return null;
133
+ if (field.type === "select" && field.options) {
134
+ const option = field.options.find((o) => o.value === filter.value);
135
+ return (_a2 = option == null ? void 0 : option.label) != null ? _a2 : String(filter.value);
136
+ }
137
+ if (field.type === "multiselect") {
138
+ const values = Array.isArray(filter.value) ? filter.value.filter(
139
+ (v) => typeof v === "string" && v.length > 1
140
+ ) : [];
141
+ if (values.length === 0) return null;
142
+ if (values.length === 1 && field.options) {
143
+ const option = field.options.find((o) => o.value === values[0]);
144
+ return (_b = option == null ? void 0 : option.label) != null ? _b : values[0];
145
+ }
146
+ return `${values.length} selected`;
147
+ }
148
+ return String(filter.value);
149
+ }, [filter.value, field, isEmptyOperator]);
150
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
151
+ "div",
152
+ {
153
+ "data-slot": "filter-pill",
154
+ className: (0, import_shared_utils.cn)(
155
+ "group inline-flex items-center rounded-full overflow-hidden",
156
+ "border border-border bg-background",
157
+ "transition-all hover:shadow-sm focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-1"
158
+ ),
159
+ children: [
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
161
+ "span",
162
+ {
163
+ className: (0, import_shared_utils.cn)(
164
+ "bg-muted text-foreground font-medium text-xs",
165
+ "flex items-center gap-1.5 flex-shrink-0",
166
+ "rounded-l-full px-3 py-1.5"
167
+ ),
168
+ children: [
169
+ field.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "size-4 flex-shrink-0", children: field.icon }),
170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: field.label })
171
+ ]
172
+ }
173
+ ),
174
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
175
+ OperatorDropdown,
176
+ {
177
+ operator: filter.operator,
178
+ operators,
179
+ i18n,
180
+ onChange: (op) => {
181
+ const newUpdates = { operator: op };
182
+ if (op === "is_empty" || op === "is_not_empty") {
183
+ newUpdates.value = null;
184
+ }
185
+ onUpdate(newUpdates);
186
+ },
187
+ disabled
188
+ }
189
+ ),
190
+ useInlineInput && !isEmptyOperator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
191
+ InlineValueInput,
192
+ {
193
+ filter,
194
+ field,
195
+ i18n,
196
+ onUpdate,
197
+ disabled
198
+ }
199
+ ),
200
+ usePopover && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_popover.Popover.Root, { open, onOpenChange: handleOpenChange, children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
202
+ import_popover.Popover.Trigger,
203
+ {
204
+ className: (0, import_shared_utils.cn)(
205
+ "bg-background text-foreground text-xs",
206
+ "flex items-center gap-1 px-2.5 py-1.5",
207
+ "cursor-pointer hover:bg-muted/30"
208
+ ),
209
+ nativeButton: false,
210
+ render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {}),
211
+ children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate max-w-[150px]", children: valueDisplay != null ? valueDisplay : i18n.selectValue }),
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ChevronDownIcon, { className: "size-3.5 opacity-60 flex-shrink-0" })
214
+ ]
215
+ }
216
+ ),
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
218
+ import_popover.Popover.Content,
219
+ {
220
+ "data-slot": "filter-editor",
221
+ className: "p-0 w-[280px]",
222
+ align: "start",
223
+ showArrow: false,
224
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
225
+ ValueEditor,
226
+ {
227
+ filter,
228
+ field,
229
+ i18n,
230
+ onUpdate,
231
+ onClose: () => handleOpenChange(false)
232
+ }
233
+ )
234
+ }
235
+ )
236
+ ] }),
237
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
238
+ "button",
239
+ {
240
+ type: "button",
241
+ onClick: onRemove,
242
+ className: (0, import_shared_utils.cn)(
243
+ "size-5 rounded-full flex items-center justify-center flex-shrink-0",
244
+ "hover:bg-destructive/10 hover:text-destructive",
245
+ "transition-colors cursor-pointer mr-1"
246
+ ),
247
+ disabled,
248
+ "aria-label": `Remove ${field.label} filter`,
249
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.CloseIcon, { className: "size-3" })
250
+ }
251
+ )
252
+ ]
253
+ }
254
+ );
255
+ }
256
+ function OperatorDropdown(props) {
257
+ const { operator, operators, i18n, onChange, disabled } = props;
258
+ const hasSingleOperator = operators.length === 1;
259
+ const currentLabel = i18n.operators[operator];
260
+ if (hasSingleOperator) {
261
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
262
+ "span",
263
+ {
264
+ className: (0, import_shared_utils.cn)(
265
+ "bg-primary text-primary-foreground",
266
+ "flex items-center px-3 py-0.5",
267
+ "rounded-full shadow-sm mx-1 my-0.5",
268
+ "text-xs font-normal"
269
+ ),
270
+ children: currentLabel
271
+ }
272
+ );
273
+ }
274
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dropdown_menu.DropdownMenu.Root, { children: [
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropdown_menu.DropdownMenu.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
276
+ "button",
277
+ {
278
+ type: "button",
279
+ className: (0, import_shared_utils.cn)(
280
+ "bg-primary text-primary-foreground",
281
+ "flex items-center gap-1 px-3 py-0.5",
282
+ "rounded-full shadow-sm mx-1 my-0.5",
283
+ "text-xs font-normal cursor-pointer",
284
+ "hover:bg-primary/90 transition-colors"
285
+ ),
286
+ children: [
287
+ currentLabel,
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.ChevronDownIcon, { className: "size-3 opacity-70" })
289
+ ]
290
+ }
291
+ ) }),
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropdown_menu.DropdownMenu.Content, { align: "start", className: "min-w-[140px]", children: operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
293
+ import_dropdown_menu.DropdownMenu.Item,
294
+ {
295
+ onSelect: () => onChange(op),
296
+ className: "gap-2",
297
+ children: [
298
+ op === operator && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.CheckIcon, { className: "size-4" }),
299
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: op !== operator ? "pl-6" : "", children: i18n.operators[op] })
300
+ ]
301
+ },
302
+ op
303
+ )) })
304
+ ] });
305
+ }
306
+ function getInputWidth(value, placeholder, minWidth = 4) {
307
+ const displayValue = value !== void 0 && value !== null && value !== "" ? String(value) : placeholder;
308
+ const width = Math.max(displayValue.length + 1, minWidth);
309
+ return `${width}ch`;
310
+ }
311
+ function InlineValueInput(props) {
312
+ var _a;
313
+ const { filter, field, i18n, onUpdate, disabled } = props;
314
+ const placeholder = (_a = field.placeholder) != null ? _a : i18n.enterValue;
315
+ const handleChange = (e) => {
316
+ const val = e.target.value;
317
+ if (field.type === "number") {
318
+ onUpdate({ value: val ? Number(val) : null });
319
+ } else {
320
+ onUpdate({ value: val });
321
+ }
322
+ };
323
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
324
+ "span",
325
+ {
326
+ className: (0, import_shared_utils.cn)(
327
+ "bg-background text-foreground text-xs",
328
+ "flex items-center gap-1 px-2.5 py-1.5"
329
+ ),
330
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
331
+ "input",
332
+ {
333
+ type: field.type === "number" ? "number" : "text",
334
+ className: (0, import_shared_utils.cn)(
335
+ "bg-transparent border-none outline-none",
336
+ "placeholder:text-muted-foreground",
337
+ "text-xs"
338
+ ),
339
+ value: filter.value !== void 0 && filter.value !== null ? String(filter.value) : "",
340
+ onChange: handleChange,
341
+ placeholder,
342
+ "aria-label": `${field.label} value`,
343
+ style: { width: getInputWidth(filter.value, placeholder, 6) },
344
+ min: field.min,
345
+ max: field.max,
346
+ step: field.step,
347
+ disabled
348
+ }
349
+ )
350
+ }
351
+ );
352
+ }
353
+ function ValueEditor(props) {
354
+ var _a, _b, _c, _d;
355
+ const { filter, field, i18n, onUpdate, onClose } = props;
356
+ if (field.customRenderer) {
357
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "p-3", children: field.customRenderer({
358
+ field,
359
+ value: filter.value,
360
+ onChange: (value) => onUpdate({ value })
361
+ }) });
362
+ }
363
+ if (field.type === "select" && field.options) {
364
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
365
+ SelectEditor,
366
+ {
367
+ options: field.options,
368
+ value: filter.value,
369
+ onChange: (value) => {
370
+ onUpdate({ value });
371
+ onClose();
372
+ },
373
+ i18n
374
+ }
375
+ );
376
+ }
377
+ if (field.type === "multiselect" && field.options) {
378
+ const currentValue = Array.isArray(filter.value) ? filter.value.filter(
379
+ (v) => typeof v === "string" && v.length > 1
380
+ ) : [];
381
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
382
+ MultiselectEditor,
383
+ {
384
+ options: field.options,
385
+ value: currentValue,
386
+ onChange: (value) => onUpdate({ value }),
387
+ i18n
388
+ }
389
+ );
390
+ }
391
+ if (field.type === "text") {
392
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
393
+ TextEditor,
394
+ {
395
+ value: (_a = filter.value) != null ? _a : "",
396
+ onChange: (value) => onUpdate({ value }),
397
+ placeholder: (_b = field.placeholder) != null ? _b : i18n.enterValue,
398
+ validation: field.validation,
399
+ onClose
400
+ }
401
+ );
402
+ }
403
+ if (field.type === "number") {
404
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
405
+ NumberEditor,
406
+ {
407
+ value: (_c = filter.value) != null ? _c : null,
408
+ onChange: (value) => onUpdate({ value }),
409
+ placeholder: (_d = field.placeholder) != null ? _d : i18n.enterValue,
410
+ min: field.min,
411
+ max: field.max,
412
+ step: field.step,
413
+ onClose
414
+ }
415
+ );
416
+ }
417
+ return null;
418
+ }
419
+ function SelectEditor(props) {
420
+ const { options = [], value, onChange, i18n } = props;
421
+ const [search, setSearch] = (0, import_react.useState)("");
422
+ const filteredOptions = (0, import_react.useMemo)(() => {
423
+ if (!search) return options;
424
+ const lower = search.toLowerCase();
425
+ return options.filter(
426
+ (o) => o.label.toLowerCase().includes(lower) || o.value.toLowerCase().includes(lower)
427
+ );
428
+ }, [options, search]);
429
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_command.Command.Root, { shouldFilter: false, children: [
430
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
431
+ import_command.Command.Input,
432
+ {
433
+ placeholder: i18n.searchPlaceholder,
434
+ value: search,
435
+ onValueChange: setSearch
436
+ }
437
+ ),
438
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_command.Command.List, { className: "max-h-[300px]", children: [
439
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.Command.Empty, { children: i18n.noResults }),
440
+ filteredOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
441
+ import_command.Command.Item,
442
+ {
443
+ value: option.value,
444
+ onSelect: () => onChange(option.value),
445
+ className: "flex items-center gap-2 min-w-0",
446
+ children: [
447
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex-shrink-0 [&>*]:size-5", children: option.icon }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate flex-1 min-w-0", children: option.label }),
449
+ value === option.value && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.CheckIcon, { className: "size-4 flex-shrink-0" })
450
+ ]
451
+ },
452
+ option.value
453
+ ))
454
+ ] })
455
+ ] });
456
+ }
457
+ function MultiselectEditor(props) {
458
+ const { options = [], value, onChange, i18n } = props;
459
+ const [search, setSearch] = (0, import_react.useState)("");
460
+ const filteredOptions = (0, import_react.useMemo)(() => {
461
+ if (!search) return options;
462
+ const lower = search.toLowerCase();
463
+ return options.filter(
464
+ (o) => o.label.toLowerCase().includes(lower) || o.value.toLowerCase().includes(lower)
465
+ );
466
+ }, [options, search]);
467
+ const toggleOption = (0, import_react.useCallback)(
468
+ (optionValue) => {
469
+ if (value.includes(optionValue)) {
470
+ onChange(value.filter((v) => v !== optionValue));
471
+ } else {
472
+ onChange([...value, optionValue]);
473
+ }
474
+ },
475
+ [value, onChange]
476
+ );
477
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_command.Command.Root, { shouldFilter: false, children: [
478
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
479
+ import_command.Command.Input,
480
+ {
481
+ placeholder: i18n.searchPlaceholder,
482
+ value: search,
483
+ onValueChange: setSearch
484
+ }
485
+ ),
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_command.Command.List, { className: "max-h-[300px]", children: [
487
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_command.Command.Empty, { children: i18n.noResults }),
488
+ filteredOptions.map((option) => {
489
+ const isSelected = value.includes(option.value);
490
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
491
+ import_command.Command.Item,
492
+ {
493
+ value: option.value,
494
+ onSelect: () => toggleOption(option.value),
495
+ className: "flex items-center gap-2 min-w-0",
496
+ children: [
497
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
498
+ import_checkbox.Checkbox,
499
+ {
500
+ checked: isSelected,
501
+ className: "flex-shrink-0 border-border bg-background data-[state=checked]:bg-primary data-[state=checked]:border-primary [&_svg]:text-primary-foreground"
502
+ }
503
+ ),
504
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex-shrink-0 [&>*]:size-5", children: option.icon }),
505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "truncate flex-1 min-w-0", children: option.label })
506
+ ]
507
+ },
508
+ option.value
509
+ );
510
+ })
511
+ ] })
512
+ ] });
513
+ }
514
+ function TextEditor(props) {
515
+ const {
516
+ value: initialValue,
517
+ onChange,
518
+ placeholder,
519
+ validation,
520
+ onClose
521
+ } = props;
522
+ const [localValue, setLocalValue] = (0, import_react.useState)(initialValue);
523
+ const [error, setError] = (0, import_react.useState)(null);
524
+ const inputRef = (0, import_react.useRef)(null);
525
+ const validate = (0, import_react.useCallback)(
526
+ (val) => {
527
+ var _a;
528
+ if (!validation) return true;
529
+ if (validation.pattern) {
530
+ const regex = new RegExp(validation.pattern);
531
+ if (!regex.test(val)) {
532
+ setError((_a = validation.message) != null ? _a : "Invalid format");
533
+ return false;
534
+ }
535
+ }
536
+ if (validation.validate) {
537
+ const result = validation.validate(val);
538
+ if (result !== true) {
539
+ setError(typeof result === "string" ? result : "Invalid value");
540
+ return false;
541
+ }
542
+ }
543
+ setError(null);
544
+ return true;
545
+ },
546
+ [validation]
547
+ );
548
+ const handleSubmit = (0, import_react.useCallback)(() => {
549
+ if (validate(localValue)) {
550
+ onChange(localValue);
551
+ onClose();
552
+ }
553
+ }, [localValue, validate, onChange, onClose]);
554
+ const handleKeyDown = (0, import_react.useCallback)(
555
+ (e) => {
556
+ if (e.key === "Enter") {
557
+ e.preventDefault();
558
+ handleSubmit();
559
+ } else if (e.key === "Escape") {
560
+ e.preventDefault();
561
+ onClose();
562
+ }
563
+ },
564
+ [handleSubmit, onClose]
565
+ );
566
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-3 space-y-2", children: [
567
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
568
+ import_input.Input,
569
+ {
570
+ ref: inputRef,
571
+ type: "text",
572
+ value: localValue,
573
+ onChange: (e) => {
574
+ setLocalValue(e.target.value);
575
+ setError(null);
576
+ },
577
+ onKeyDown: handleKeyDown,
578
+ placeholder,
579
+ className: "w-full",
580
+ autoFocus: true
581
+ }
582
+ ),
583
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-destructive", children: error }),
584
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-end gap-2", children: [
585
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { variant: "ghost", size: "sm", onClick: onClose, children: "Cancel" }),
586
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { size: "sm", onClick: handleSubmit, children: "Apply" })
587
+ ] })
588
+ ] });
589
+ }
590
+ function NumberEditor(props) {
591
+ const {
592
+ value: initialValue,
593
+ onChange,
594
+ placeholder,
595
+ min,
596
+ max,
597
+ step,
598
+ onClose
599
+ } = props;
600
+ const [localValue, setLocalValue] = (0, import_react.useState)(
601
+ initialValue !== null ? String(initialValue) : ""
602
+ );
603
+ const inputRef = (0, import_react.useRef)(null);
604
+ const handleSubmit = (0, import_react.useCallback)(() => {
605
+ const numValue = localValue === "" ? null : Number(localValue);
606
+ onChange(numValue);
607
+ onClose();
608
+ }, [localValue, onChange, onClose]);
609
+ const handleKeyDown = (0, import_react.useCallback)(
610
+ (e) => {
611
+ if (e.key === "Enter") {
612
+ e.preventDefault();
613
+ handleSubmit();
614
+ } else if (e.key === "Escape") {
615
+ e.preventDefault();
616
+ onClose();
617
+ }
618
+ },
619
+ [handleSubmit, onClose]
620
+ );
621
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "p-3 space-y-2", children: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
623
+ import_input.Input,
624
+ {
625
+ ref: inputRef,
626
+ type: "number",
627
+ value: localValue,
628
+ onChange: (e) => setLocalValue(e.target.value),
629
+ onKeyDown: handleKeyDown,
630
+ placeholder,
631
+ min,
632
+ max,
633
+ step,
634
+ className: "w-full",
635
+ autoFocus: true
636
+ }
637
+ ),
638
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-end gap-2", children: [
639
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { variant: "ghost", size: "sm", onClick: onClose, children: "Cancel" }),
640
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { size: "sm", onClick: handleSubmit, children: "Apply" })
641
+ ] })
642
+ ] });
643
+ }
644
+ // Annotate the CommonJS export names for ESM import in node:
645
+ 0 && (module.exports = {
646
+ AddFilterDropdown,
647
+ FilterPill
648
+ });
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ "use client";
3
+ import {
4
+ AddFilterDropdown,
5
+ FilterPill
6
+ } from "./chunk-XCWKWXBW.mjs";
7
+ import "./chunk-ON2UFJ3Y.mjs";
8
+ export {
9
+ AddFilterDropdown,
10
+ FilterPill
11
+ };