@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.
@@ -2,8 +2,8 @@
2
2
  "use client";
3
3
  import {
4
4
  FilterActive
5
- } from "./chunk-WCKTCW7Y.mjs";
6
- import "./chunk-WUUOSJHW.mjs";
5
+ } from "./chunk-45QJL74L.mjs";
6
+ import "./chunk-3ZBNWXRA.mjs";
7
7
  import "./chunk-URDCG5NI.mjs";
8
8
  import "./chunk-I3Z2T4N2.mjs";
9
9
  import "./chunk-PHESMHTT.mjs";
@@ -1,4 +1,4 @@
1
- import { FilterFieldType, FilterOption, FilterOperator } from './filter-types.mjs';
1
+ import { FilterFieldType, FilterOption, FilterOperator, FilterFieldConfig } from './filter-types.mjs';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { ReactNode } from 'react';
4
4
 
@@ -52,6 +52,16 @@ interface FilterItemProps {
52
52
  searchable?: boolean;
53
53
  /** Disabled state */
54
54
  disabled?: boolean;
55
+ /**
56
+ * Custom render function for the value editor (e.g., async combobox)
57
+ * When provided, replaces the default editor for this field type
58
+ */
59
+ renderEditor?: FilterFieldConfig["renderEditor"];
60
+ /**
61
+ * Custom render function for displaying the filter value in the pill
62
+ * When provided, replaces the default value display
63
+ */
64
+ renderValue?: FilterFieldConfig["renderValue"];
55
65
  }
56
66
  declare function FilterItem(props: FilterItemProps): null;
57
67
  declare namespace FilterItem {
@@ -1,4 +1,4 @@
1
- import { FilterFieldType, FilterOption, FilterOperator } from './filter-types.js';
1
+ import { FilterFieldType, FilterOption, FilterOperator, FilterFieldConfig } from './filter-types.js';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { ReactNode } from 'react';
4
4
 
@@ -52,6 +52,16 @@ interface FilterItemProps {
52
52
  searchable?: boolean;
53
53
  /** Disabled state */
54
54
  disabled?: boolean;
55
+ /**
56
+ * Custom render function for the value editor (e.g., async combobox)
57
+ * When provided, replaces the default editor for this field type
58
+ */
59
+ renderEditor?: FilterFieldConfig["renderEditor"];
60
+ /**
61
+ * Custom render function for displaying the filter value in the pill
62
+ * When provided, replaces the default value display
63
+ */
64
+ renderValue?: FilterFieldConfig["renderValue"];
55
65
  }
56
66
  declare function FilterItem(props: FilterItemProps): null;
57
67
  declare namespace FilterItem {
@@ -115,7 +115,9 @@ function FilterItem(props) {
115
115
  max,
116
116
  step,
117
117
  searchable,
118
- disabled
118
+ disabled,
119
+ renderEditor,
120
+ renderValue
119
121
  } = props;
120
122
  const { registerField, unregisterField } = useFilterContext();
121
123
  const group = useFilterGroup();
@@ -133,7 +135,9 @@ function FilterItem(props) {
133
135
  step,
134
136
  searchable,
135
137
  group,
136
- disabled
138
+ disabled,
139
+ renderEditor,
140
+ renderValue
137
141
  });
138
142
  return () => unregisterField(id);
139
143
  }, [
@@ -150,6 +154,8 @@ function FilterItem(props) {
150
154
  searchable,
151
155
  group,
152
156
  disabled,
157
+ renderEditor,
158
+ renderValue,
153
159
  registerField,
154
160
  unregisterField
155
161
  ]);
@@ -5,7 +5,7 @@ import {
5
5
  FilterItem,
6
6
  FilterMenu,
7
7
  FilterMenuSeparator
8
- } from "./chunk-TBHYZZSX.mjs";
8
+ } from "./chunk-RFCPJLIQ.mjs";
9
9
  import "./chunk-I3Z2T4N2.mjs";
10
10
  export {
11
11
  FilterGroup,
@@ -1,5 +1,21 @@
1
1
  import { ReactNode } from 'react';
2
2
 
3
+ /**
4
+ * Props passed to custom editor render function
5
+ */
6
+ interface FilterEditorRenderProps {
7
+ filter: FilterValue;
8
+ field: FilterFieldConfig;
9
+ onChange: (value: unknown) => void;
10
+ onClose?: () => void;
11
+ }
12
+ /**
13
+ * Props passed to custom value render function
14
+ */
15
+ interface FilterValueRenderProps {
16
+ filter: FilterValue;
17
+ field: FilterFieldConfig;
18
+ }
3
19
  /**
4
20
  * Filter field types supported by the component
5
21
  */
@@ -43,6 +59,16 @@ interface FilterFieldConfig {
43
59
  searchable?: boolean;
44
60
  group?: string;
45
61
  disabled?: boolean;
62
+ /**
63
+ * Custom render function for the value editor (e.g., async combobox)
64
+ * When provided, replaces the default editor for this field type
65
+ */
66
+ renderEditor?: (props: FilterEditorRenderProps) => ReactNode;
67
+ /**
68
+ * Custom render function for displaying the filter value in the pill
69
+ * When provided, replaces the default value display
70
+ */
71
+ renderValue?: (props: FilterValueRenderProps) => ReactNode;
46
72
  }
47
73
  /**
48
74
  * Default operators per field type
@@ -57,4 +83,4 @@ declare function getDefaultOperator(type: FilterFieldType): FilterOperator;
57
83
  */
58
84
  declare function generateFilterId(): string;
59
85
 
60
- export { DEFAULT_OPERATORS, type FilterFieldConfig, type FilterFieldType, type FilterOperator, type FilterOption, type FilterValue, generateFilterId, getDefaultOperator };
86
+ export { DEFAULT_OPERATORS, type FilterEditorRenderProps, type FilterFieldConfig, type FilterFieldType, type FilterOperator, type FilterOption, type FilterValue, type FilterValueRenderProps, generateFilterId, getDefaultOperator };
@@ -1,5 +1,21 @@
1
1
  import { ReactNode } from 'react';
2
2
 
3
+ /**
4
+ * Props passed to custom editor render function
5
+ */
6
+ interface FilterEditorRenderProps {
7
+ filter: FilterValue;
8
+ field: FilterFieldConfig;
9
+ onChange: (value: unknown) => void;
10
+ onClose?: () => void;
11
+ }
12
+ /**
13
+ * Props passed to custom value render function
14
+ */
15
+ interface FilterValueRenderProps {
16
+ filter: FilterValue;
17
+ field: FilterFieldConfig;
18
+ }
3
19
  /**
4
20
  * Filter field types supported by the component
5
21
  */
@@ -43,6 +59,16 @@ interface FilterFieldConfig {
43
59
  searchable?: boolean;
44
60
  group?: string;
45
61
  disabled?: boolean;
62
+ /**
63
+ * Custom render function for the value editor (e.g., async combobox)
64
+ * When provided, replaces the default editor for this field type
65
+ */
66
+ renderEditor?: (props: FilterEditorRenderProps) => ReactNode;
67
+ /**
68
+ * Custom render function for displaying the filter value in the pill
69
+ * When provided, replaces the default value display
70
+ */
71
+ renderValue?: (props: FilterValueRenderProps) => ReactNode;
46
72
  }
47
73
  /**
48
74
  * Default operators per field type
@@ -57,4 +83,4 @@ declare function getDefaultOperator(type: FilterFieldType): FilterOperator;
57
83
  */
58
84
  declare function generateFilterId(): string;
59
85
 
60
- export { DEFAULT_OPERATORS, type FilterFieldConfig, type FilterFieldType, type FilterOperator, type FilterOption, type FilterValue, generateFilterId, getDefaultOperator };
86
+ export { DEFAULT_OPERATORS, type FilterEditorRenderProps, type FilterFieldConfig, type FilterFieldType, type FilterOperator, type FilterOption, type FilterValue, type FilterValueRenderProps, generateFilterId, getDefaultOperator };
@@ -5,7 +5,9 @@ import 'react';
5
5
  interface FilterValueEditorProps {
6
6
  filter: FilterValue;
7
7
  field: FilterFieldConfig;
8
+ /** Hide the operator select (when operator is shown separately) */
9
+ hideOperator?: boolean;
8
10
  }
9
- declare function FilterValueEditor({ filter, field }: FilterValueEditorProps): react_jsx_runtime.JSX.Element;
11
+ declare function FilterValueEditor({ filter, field, hideOperator, }: FilterValueEditorProps): react_jsx_runtime.JSX.Element;
10
12
 
11
13
  export { FilterValueEditor };
@@ -5,7 +5,9 @@ import 'react';
5
5
  interface FilterValueEditorProps {
6
6
  filter: FilterValue;
7
7
  field: FilterFieldConfig;
8
+ /** Hide the operator select (when operator is shown separately) */
9
+ hideOperator?: boolean;
8
10
  }
9
- declare function FilterValueEditor({ filter, field }: FilterValueEditorProps): react_jsx_runtime.JSX.Element;
11
+ declare function FilterValueEditor({ filter, field, hideOperator, }: FilterValueEditorProps): react_jsx_runtime.JSX.Element;
10
12
 
11
13
  export { FilterValueEditor };
@@ -166,7 +166,11 @@ var messages = (0, import_i18n.defineMessages)({
166
166
 
167
167
  // src/filter-value-editor.tsx
168
168
  var import_jsx_runtime = require("react/jsx-runtime");
169
- function FilterValueEditor({ filter, field }) {
169
+ function FilterValueEditor({
170
+ filter,
171
+ field,
172
+ hideOperator
173
+ }) {
170
174
  var _a;
171
175
  const { styles, updateFilter, setEditingFilterId } = useFilterContext();
172
176
  const t = (0, import_i18n2.useSafeIntl)();
@@ -191,7 +195,7 @@ function FilterValueEditor({ filter, field }) {
191
195
  const needsValue = filter.operator !== "is_empty" && filter.operator !== "is_not_empty";
192
196
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-slot": "filter-value-editor", children: [
193
197
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorHeader(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: styles.editorTitle(), children: field.label }) }),
194
- operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
198
+ !hideOperator && operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
195
199
  import_select.Select,
196
200
  {
197
201
  value: filter.operator,
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
  import {
4
4
  FilterValueEditor
5
- } from "./chunk-WUUOSJHW.mjs";
5
+ } from "./chunk-3ZBNWXRA.mjs";
6
6
  import "./chunk-URDCG5NI.mjs";
7
7
  import "./chunk-I3Z2T4N2.mjs";
8
8
  import "./chunk-PHESMHTT.mjs";
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ export { FilterActive, FilterActiveProps } from './filter-active.mjs';
3
3
  export { FilterContextValue, useFilterContext } from './filter-context.mjs';
4
4
  export { FilterGroup, FilterGroupProps, FilterItem, FilterItemProps, FilterMenu, FilterMenuProps, FilterMenuSeparator } from './filter-menu.mjs';
5
5
  export { FilterTrigger, FilterTriggerProps } from './filter-trigger.mjs';
6
- export { FilterFieldConfig, FilterFieldType, FilterOperator, FilterOption, FilterValue } from './filter-types.mjs';
6
+ export { FilterEditorRenderProps, FilterFieldConfig, FilterFieldType, FilterOperator, FilterOption, FilterValue, FilterValueRenderProps } from './filter-types.mjs';
7
7
  export { FilterValueEditor } from './filter-value-editor.mjs';
8
8
  export { messages as filterMessages } from './messages.mjs';
9
9
  import 'react/jsx-runtime';
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { FilterActive, FilterActiveProps } from './filter-active.js';
3
3
  export { FilterContextValue, useFilterContext } from './filter-context.js';
4
4
  export { FilterGroup, FilterGroupProps, FilterItem, FilterItemProps, FilterMenu, FilterMenuProps, FilterMenuSeparator } from './filter-menu.js';
5
5
  export { FilterTrigger, FilterTriggerProps } from './filter-trigger.js';
6
- export { FilterFieldConfig, FilterFieldType, FilterOperator, FilterOption, FilterValue } from './filter-types.js';
6
+ export { FilterEditorRenderProps, FilterFieldConfig, FilterFieldType, FilterOperator, FilterOption, FilterValue, FilterValueRenderProps } from './filter-types.js';
7
7
  export { FilterValueEditor } from './filter-value-editor.js';
8
8
  export { messages as filterMessages } from './messages.js';
9
9
  import 'react/jsx-runtime';
package/dist/index.js CHANGED
@@ -386,7 +386,11 @@ var messages = (0, import_i18n.defineMessages)({
386
386
 
387
387
  // src/filter-value-editor.tsx
388
388
  var import_jsx_runtime2 = require("react/jsx-runtime");
389
- function FilterValueEditor({ filter, field }) {
389
+ function FilterValueEditor({
390
+ filter,
391
+ field,
392
+ hideOperator
393
+ }) {
390
394
  var _a;
391
395
  const { styles, updateFilter, setEditingFilterId } = useFilterContext();
392
396
  const t = (0, import_i18n2.useSafeIntl)();
@@ -411,7 +415,7 @@ function FilterValueEditor({ filter, field }) {
411
415
  const needsValue = filter.operator !== "is_empty" && filter.operator !== "is_not_empty";
412
416
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-slot": "filter-value-editor", children: [
413
417
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.editorHeader(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: styles.editorTitle(), children: field.label }) }),
414
- operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
418
+ !hideOperator && operators.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: styles.editorOperator(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
415
419
  import_select.Select,
416
420
  {
417
421
  value: filter.operator,
@@ -661,81 +665,195 @@ function FilterActive(props) {
661
665
  }
662
666
  FilterActive.displayName = "FilterActive";
663
667
  function FilterField({ filter }) {
664
- const {
665
- fields,
666
- styles,
667
- removeFilter,
668
- editingFilterId,
669
- setEditingFilterId,
670
- updateFilter
671
- } = useFilterContext();
668
+ var _a;
669
+ const { fields, styles, removeFilter, updateFilter } = useFilterContext();
672
670
  const t = (0, import_i18n3.useSafeIntl)();
673
671
  const field = fields.get(filter.fieldId);
674
672
  if (!field) return null;
675
- const isEditing = editingFilterId === filter.id;
676
- const operatorKey = `op_${filter.operator}`;
677
- const operatorLabel = messages[operatorKey] ? t.formatMessage(messages[operatorKey]) : filter.operator;
678
- const isBooleanWithValue = field.type === "boolean" && filter.operator !== "is_empty" && filter.operator !== "is_not_empty";
679
- const valueDisplay = isBooleanWithValue ? null : getValueDisplay(filter, field);
680
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
681
- import_popover.Popover.Root,
673
+ const availableOperators = (_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type];
674
+ const getOperatorLabel = (op) => {
675
+ const opKey = `op_${op}`;
676
+ return messages[opKey] ? t.formatMessage(messages[opKey]) : op;
677
+ };
678
+ const handleOperatorChange = (e) => {
679
+ const newOperator = e.target.value;
680
+ if (newOperator === "is_empty" || newOperator === "is_not_empty") {
681
+ updateFilter(filter.id, { operator: newOperator, value: void 0 });
682
+ } else {
683
+ updateFilter(filter.id, { operator: newOperator });
684
+ }
685
+ };
686
+ const isEmptyOperator = filter.operator === "is_empty" || filter.operator === "is_not_empty";
687
+ const hasCustomEditor = !!field.renderEditor;
688
+ const useInlineInput = !hasCustomEditor && (field.type === "text" || field.type === "number");
689
+ const useInlineSwitch = !hasCustomEditor && field.type === "boolean" && !isEmptyOperator;
690
+ const usePopover = !useInlineInput && !useInlineSwitch && !isEmptyOperator;
691
+ const currentOperatorLabel = getOperatorLabel(filter.operator);
692
+ const operatorWidth = `${currentOperatorLabel.length + 2}ch`;
693
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-slot": "filter-field", className: styles.field(), children: [
694
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: styles.fieldLabel(), children: [
695
+ field.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "size-4 flex-shrink-0", children: field.icon }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: field.label })
697
+ ] }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: styles.fieldOperator(), children: [
699
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
700
+ "select",
701
+ {
702
+ className: styles.fieldOperatorSelect(),
703
+ value: filter.operator,
704
+ onChange: handleOperatorChange,
705
+ title: currentOperatorLabel,
706
+ style: { width: operatorWidth },
707
+ children: availableOperators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: op, children: getOperatorLabel(op) }, op))
708
+ }
709
+ ),
710
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: styles.fieldOperatorIcon() })
711
+ ] }),
712
+ useInlineInput && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InlineValueInput, { filter, field }),
713
+ useInlineSwitch && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValue(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
714
+ import_switch.Switch,
715
+ {
716
+ size: "sm",
717
+ checked: Boolean(filter.value),
718
+ onCheckedChange: (checked) => {
719
+ updateFilter(filter.id, { value: checked });
720
+ },
721
+ "aria-label": field.label
722
+ }
723
+ ) }),
724
+ usePopover && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PopoverValueEditor, { filter, field }),
725
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
726
+ "button",
727
+ {
728
+ type: "button",
729
+ className: styles.fieldRemove(),
730
+ onClick: () => removeFilter(filter.id),
731
+ "aria-label": "Remove filter",
732
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.CloseIcon, { className: "size-3" })
733
+ }
734
+ )
735
+ ] });
736
+ }
737
+ function getInputWidth(value, placeholder, minWidth = 4) {
738
+ const displayValue = value !== void 0 && value !== null && value !== "" ? String(value) : placeholder;
739
+ const width = Math.max(displayValue.length + 1, minWidth);
740
+ return `${width}ch`;
741
+ }
742
+ function InlineValueInput({
743
+ filter,
744
+ field
745
+ }) {
746
+ var _a;
747
+ const { styles, updateFilter } = useFilterContext();
748
+ const t = (0, import_i18n3.useSafeIntl)();
749
+ const handleChange = (e) => {
750
+ const val = e.target.value;
751
+ if (field.type === "number") {
752
+ updateFilter(filter.id, { value: val ? Number(val) : void 0 });
753
+ } else {
754
+ updateFilter(filter.id, { value: val });
755
+ }
756
+ };
757
+ const placeholder = (_a = field.placeholder) != null ? _a : t.formatMessage(messages.enter_value);
758
+ if (filter.operator === "between") {
759
+ const [min, max] = Array.isArray(filter.value) ? filter.value : [void 0, void 0];
760
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: (0, import_shared_utils2.cn)(styles.fieldValue(), "gap-1"), children: [
761
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
762
+ "input",
763
+ {
764
+ type: "number",
765
+ className: styles.fieldValueInput(),
766
+ value: min != null ? min : "",
767
+ onChange: (e) => {
768
+ const val = e.target.value ? Number(e.target.value) : void 0;
769
+ updateFilter(filter.id, { value: [val, max] });
770
+ },
771
+ placeholder: "Min",
772
+ style: { width: getInputWidth(min, "Min", 3) },
773
+ min: field.min,
774
+ max: field.max,
775
+ step: field.step
776
+ }
777
+ ),
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-muted-foreground", children: "\u2013" }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
780
+ "input",
781
+ {
782
+ type: "number",
783
+ className: styles.fieldValueInput(),
784
+ value: max != null ? max : "",
785
+ onChange: (e) => {
786
+ const val = e.target.value ? Number(e.target.value) : void 0;
787
+ updateFilter(filter.id, { value: [min, val] });
788
+ },
789
+ placeholder: "Max",
790
+ style: { width: getInputWidth(max, "Max", 3) },
791
+ min: field.min,
792
+ max: field.max,
793
+ step: field.step
794
+ }
795
+ )
796
+ ] });
797
+ }
798
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValue(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
799
+ "input",
682
800
  {
683
- open: isEditing,
684
- onOpenChange: (open) => setEditingFilterId(open ? filter.id : null),
685
- children: [
686
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
687
- import_popover.Popover.Trigger,
688
- {
689
- "data-slot": "filter-field",
690
- className: styles.field(),
691
- render: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {}),
692
- nativeButton: false,
693
- children: [
694
- field.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.menuItemIcon(), children: field.icon }),
695
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldLabel(), children: field.label }),
696
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldOperator(), children: operatorLabel }),
697
- isBooleanWithValue && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
698
- import_switch.Switch,
699
- {
700
- size: "sm",
701
- checked: Boolean(filter.value),
702
- onCheckedChange: (checked) => {
703
- updateFilter(filter.id, { value: checked });
704
- },
705
- onClick: (e) => e.stopPropagation(),
706
- "aria-label": field.label
707
- }
708
- ),
709
- valueDisplay && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValue(), children: valueDisplay }),
710
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
711
- "button",
712
- {
713
- type: "button",
714
- className: styles.fieldRemove(),
715
- onClick: (e) => {
716
- e.stopPropagation();
717
- removeFilter(filter.id);
718
- },
719
- "aria-label": "Remove filter",
720
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.CloseIcon, { className: "size-3" })
721
- }
722
- )
723
- ]
724
- }
725
- ),
726
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
727
- import_popover.Popover.Content,
728
- {
729
- "data-slot": "filter-editor",
730
- className: styles.editor(),
731
- align: "start",
732
- showArrow: false,
733
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FilterValueEditor, { filter, field })
734
- }
735
- )
736
- ]
801
+ type: field.type === "number" ? "number" : "text",
802
+ className: styles.fieldValueInput(),
803
+ value: filter.value !== void 0 && filter.value !== null ? String(filter.value) : "",
804
+ onChange: handleChange,
805
+ placeholder,
806
+ style: { width: getInputWidth(filter.value, placeholder, 6) },
807
+ min: field.min,
808
+ max: field.max,
809
+ step: field.step
737
810
  }
738
- );
811
+ ) });
812
+ }
813
+ function PopoverValueEditor({
814
+ filter,
815
+ field
816
+ }) {
817
+ const { styles, updateFilter } = useFilterContext();
818
+ const valueDisplay = field.renderValue ? field.renderValue({ filter, field }) : getValueDisplay(filter, field);
819
+ const hasValue = filter.value !== void 0 && filter.value !== null && filter.value !== "" && !(Array.isArray(filter.value) && filter.value.length === 0);
820
+ const handleCustomChange = (value) => {
821
+ updateFilter(filter.id, { value });
822
+ };
823
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_popover.Popover.Root, { children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
825
+ import_popover.Popover.Trigger,
826
+ {
827
+ className: (0, import_shared_utils2.cn)(styles.fieldValue(), "cursor-pointer hover:bg-muted/30"),
828
+ render: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {}),
829
+ nativeButton: false,
830
+ children: [
831
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValueText(), children: hasValue ? valueDisplay : "Select..." }),
832
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: styles.fieldValueIcon() })
833
+ ]
834
+ }
835
+ ),
836
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
837
+ import_popover.Popover.Content,
838
+ {
839
+ "data-slot": "filter-editor",
840
+ className: styles.editor(),
841
+ align: "start",
842
+ showArrow: false,
843
+ children: field.renderEditor ? (
844
+ // Use custom editor if provided
845
+ field.renderEditor({
846
+ filter,
847
+ field,
848
+ onChange: handleCustomChange
849
+ })
850
+ ) : (
851
+ // Default editor
852
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FilterValueEditor, { filter, field, hideOperator: true })
853
+ )
854
+ }
855
+ )
856
+ ] });
739
857
  }
740
858
  function getValueDisplay(filter, field) {
741
859
  var _a, _b, _c, _d;
@@ -744,7 +862,7 @@ function getValueDisplay(filter, field) {
744
862
  return null;
745
863
  }
746
864
  if (value === void 0 || value === null || value === "") {
747
- return "...";
865
+ return null;
748
866
  }
749
867
  if (field.type === "boolean") {
750
868
  return value ? "Yes" : "No";
@@ -754,7 +872,7 @@ function getValueDisplay(filter, field) {
754
872
  return (_a = option == null ? void 0 : option.label) != null ? _a : String(value);
755
873
  }
756
874
  if (field.type === "multiselect" && Array.isArray(value)) {
757
- if (value.length === 0) return "...";
875
+ if (value.length === 0) return null;
758
876
  if (value.length === 1 && field.options) {
759
877
  const option = field.options.find((o) => o.value === value[0]);
760
878
  return (_b = option == null ? void 0 : option.label) != null ? _b : String(value[0]);
@@ -768,10 +886,10 @@ function getValueDisplay(filter, field) {
768
886
  const [start, end] = value;
769
887
  const startStr = start instanceof Date ? start.toLocaleDateString() : "...";
770
888
  const endStr = end instanceof Date ? end.toLocaleDateString() : "...";
771
- return `${startStr} - ${endStr}`;
889
+ return `${startStr} \u2013 ${endStr}`;
772
890
  }
773
891
  if (operator === "between" && Array.isArray(value)) {
774
- return `${(_c = value[0]) != null ? _c : "..."} - ${(_d = value[1]) != null ? _d : "..."}`;
892
+ return `${(_c = value[0]) != null ? _c : "..."} \u2013 ${(_d = value[1]) != null ? _d : "..."}`;
775
893
  }
776
894
  return String(value);
777
895
  }
@@ -854,7 +972,9 @@ function FilterItem(props) {
854
972
  max,
855
973
  step,
856
974
  searchable,
857
- disabled
975
+ disabled,
976
+ renderEditor,
977
+ renderValue
858
978
  } = props;
859
979
  const { registerField, unregisterField } = useFilterContext();
860
980
  const group = useFilterGroup();
@@ -872,7 +992,9 @@ function FilterItem(props) {
872
992
  step,
873
993
  searchable,
874
994
  group,
875
- disabled
995
+ disabled,
996
+ renderEditor,
997
+ renderValue
876
998
  });
877
999
  return () => unregisterField(id);
878
1000
  }, [
@@ -889,6 +1011,8 @@ function FilterItem(props) {
889
1011
  searchable,
890
1012
  group,
891
1013
  disabled,
1014
+ renderEditor,
1015
+ renderValue,
892
1016
  registerField,
893
1017
  unregisterField
894
1018
  ]);
package/dist/index.mjs CHANGED
@@ -1,19 +1,19 @@
1
1
  "use client";
2
2
  import {
3
3
  FilterActive
4
- } from "./chunk-WCKTCW7Y.mjs";
4
+ } from "./chunk-45QJL74L.mjs";
5
5
  import {
6
6
  FilterGroup,
7
7
  FilterItem,
8
8
  FilterMenu,
9
9
  FilterMenuSeparator
10
- } from "./chunk-TBHYZZSX.mjs";
10
+ } from "./chunk-RFCPJLIQ.mjs";
11
11
  import {
12
12
  FilterTrigger
13
13
  } from "./chunk-EF4VI36D.mjs";
14
14
  import {
15
15
  FilterValueEditor
16
- } from "./chunk-WUUOSJHW.mjs";
16
+ } from "./chunk-3ZBNWXRA.mjs";
17
17
  import {
18
18
  messages
19
19
  } from "./chunk-URDCG5NI.mjs";