@kopexa/filter 0.0.24 → 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
package/dist/index.js CHANGED
@@ -33,12 +33,15 @@ var index_exports = {};
33
33
  __export(index_exports, {
34
34
  Filter: () => Filter,
35
35
  FilterActive: () => FilterActive,
36
+ FilterBar: () => FilterBar,
36
37
  FilterGroup: () => FilterGroup,
37
38
  FilterItem: () => FilterItem,
38
39
  FilterMenu: () => FilterMenu,
39
40
  FilterMenuSeparator: () => FilterMenuSeparator,
40
41
  FilterTrigger: () => FilterTrigger,
41
42
  FilterValueEditor: () => FilterValueEditor,
43
+ SearchFilterBar: () => SearchFilterBar,
44
+ filterBarMessages: () => filterBarMessages,
42
45
  filterMessages: () => messages,
43
46
  useFilterContext: () => useFilterContext
44
47
  });
@@ -381,6 +384,58 @@ var messages = (0, import_i18n.defineMessages)({
381
384
  id: "filter.operator.is_not_empty",
382
385
  defaultMessage: "is not empty",
383
386
  description: "Operator: is not empty"
387
+ },
388
+ // Additional UI strings
389
+ loading: {
390
+ id: "filter.loading",
391
+ defaultMessage: "Loading...",
392
+ description: "Loading state text"
393
+ },
394
+ boolean_true: {
395
+ id: "filter.boolean_true",
396
+ defaultMessage: "Yes",
397
+ description: "Boolean true value display"
398
+ },
399
+ boolean_false: {
400
+ id: "filter.boolean_false",
401
+ defaultMessage: "No",
402
+ description: "Boolean false value display"
403
+ },
404
+ range_min: {
405
+ id: "filter.range_min",
406
+ defaultMessage: "Min",
407
+ description: "Minimum value placeholder for range inputs"
408
+ },
409
+ range_max: {
410
+ id: "filter.range_max",
411
+ defaultMessage: "Max",
412
+ description: "Maximum value placeholder for range inputs"
413
+ },
414
+ // Accessibility labels
415
+ remove_filter: {
416
+ id: "filter.remove_filter",
417
+ defaultMessage: "Remove {field} filter",
418
+ description: "Accessibility label for remove filter button"
419
+ },
420
+ operator_label: {
421
+ id: "filter.operator_label",
422
+ defaultMessage: "{field} operator",
423
+ description: "Accessibility label for operator select"
424
+ },
425
+ value_label: {
426
+ id: "filter.value_label",
427
+ defaultMessage: "{field} value",
428
+ description: "Accessibility label for value input"
429
+ },
430
+ value_min_label: {
431
+ id: "filter.value_min_label",
432
+ defaultMessage: "{field} minimum value",
433
+ description: "Accessibility label for minimum value input"
434
+ },
435
+ value_max_label: {
436
+ id: "filter.value_max_label",
437
+ defaultMessage: "{field} maximum value",
438
+ description: "Accessibility label for maximum value input"
384
439
  }
385
440
  });
386
441
 
@@ -522,27 +577,29 @@ function ValueInput({
522
577
  placeholder: (_c = field.placeholder) != null ? _c : t.formatMessage(messages.select_value)
523
578
  }
524
579
  ) }),
525
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_select.Select.Content, { children: (_d = field.options) == null ? void 0 : _d.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
580
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_select.Select.Content, { children: (_d = field.options) == null ? void 0 : _d.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
526
581
  import_select.Select.Item,
527
582
  {
528
583
  value: option.value,
529
584
  disabled: option.disabled,
530
- children: [
531
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "mr-2 size-4", children: option.icon }),
585
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "flex items-center gap-2", children: [
586
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "size-4 flex-shrink-0", children: option.icon }),
532
587
  option.label
533
- ]
588
+ ] })
534
589
  },
535
590
  option.value
536
591
  )) })
537
592
  ] });
538
593
  case "multiselect": {
539
- const selectedValues = Array.isArray(value) ? value : [];
540
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-2 max-h-[200px] overflow-auto", children: (_e = field.options) == null ? void 0 : _e.map((option) => (
594
+ const selectedValues = Array.isArray(value) ? value.filter(
595
+ (v) => typeof v === "string" && v.length > 1
596
+ ) : [];
597
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-1 max-h-[200px] overflow-auto p-1", children: (_e = field.options) == null ? void 0 : _e.map((option) => (
541
598
  // biome-ignore lint/a11y/noLabelWithoutControl: Checkbox is a custom form control inside the label
542
599
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
543
600
  "label",
544
601
  {
545
- className: "flex items-center gap-2 cursor-pointer",
602
+ className: "flex items-center gap-2 cursor-pointer hover:bg-muted/50 rounded-md px-2 py-1.5",
546
603
  children: [
547
604
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
548
605
  import_checkbox.Checkbox,
@@ -555,11 +612,12 @@ function ValueInput({
555
612
  onChange(selectedValues.filter((v) => v !== option.value));
556
613
  }
557
614
  },
558
- disabled: option.disabled
615
+ disabled: option.disabled,
616
+ className: "flex-shrink-0 border-border bg-background data-[state=checked]:bg-primary data-[state=checked]:border-primary [&_svg]:text-primary-foreground"
559
617
  }
560
618
  ),
561
- option.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "size-4", children: option.icon }),
562
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-sm", children: option.label })
619
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "flex-shrink-0 [&>*]:size-5", children: option.icon }),
620
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-sm truncate", children: option.label })
563
621
  ]
564
622
  },
565
623
  option.value
@@ -671,6 +729,7 @@ function FilterField({ filter }) {
671
729
  const field = fields.get(filter.fieldId);
672
730
  if (!field) return null;
673
731
  const availableOperators = (_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type];
732
+ const hasSingleOperator = availableOperators.length === 1;
674
733
  const getOperatorLabel = (op) => {
675
734
  const opKey = `op_${op}`;
676
735
  return messages[opKey] ? t.formatMessage(messages[opKey]) : op;
@@ -695,20 +754,29 @@ function FilterField({ filter }) {
695
754
  field.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "size-4 flex-shrink-0", children: field.icon }),
696
755
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: field.label })
697
756
  ] }),
698
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: styles.fieldOperator(), children: [
757
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldOperator(), children: hasSingleOperator ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
758
+ "span",
759
+ {
760
+ className: styles.fieldOperatorSelect(),
761
+ style: { width: operatorWidth },
762
+ children: currentOperatorLabel
763
+ }
764
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
699
765
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
700
766
  "select",
701
767
  {
702
768
  className: styles.fieldOperatorSelect(),
703
769
  value: filter.operator,
704
770
  onChange: handleOperatorChange,
705
- title: currentOperatorLabel,
771
+ "aria-label": t.formatMessage(messages.operator_label, {
772
+ field: field.label
773
+ }),
706
774
  style: { width: operatorWidth },
707
775
  children: availableOperators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: op, children: getOperatorLabel(op) }, op))
708
776
  }
709
777
  ),
710
778
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: styles.fieldOperatorIcon() })
711
- ] }),
779
+ ] }) }),
712
780
  useInlineInput && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(InlineValueInput, { filter, field }),
713
781
  useInlineSwitch && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValue(), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
714
782
  import_switch.Switch,
@@ -728,7 +796,9 @@ function FilterField({ filter }) {
728
796
  type: "button",
729
797
  className: styles.fieldRemove(),
730
798
  onClick: () => removeFilter(filter.id),
731
- "aria-label": "Remove filter",
799
+ "aria-label": t.formatMessage(messages.remove_filter, {
800
+ field: field.label
801
+ }),
732
802
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.CloseIcon, { className: "size-3" })
733
803
  }
734
804
  )
@@ -757,6 +827,8 @@ function InlineValueInput({
757
827
  const placeholder = (_a = field.placeholder) != null ? _a : t.formatMessage(messages.enter_value);
758
828
  if (filter.operator === "between") {
759
829
  const [min, max] = Array.isArray(filter.value) ? filter.value : [void 0, void 0];
830
+ const minPlaceholder = t.formatMessage(messages.range_min);
831
+ const maxPlaceholder = t.formatMessage(messages.range_max);
760
832
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: (0, import_shared_utils2.cn)(styles.fieldValue(), "gap-1"), children: [
761
833
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
762
834
  "input",
@@ -768,8 +840,11 @@ function InlineValueInput({
768
840
  const val = e.target.value ? Number(e.target.value) : void 0;
769
841
  updateFilter(filter.id, { value: [val, max] });
770
842
  },
771
- placeholder: "Min",
772
- style: { width: getInputWidth(min, "Min", 3) },
843
+ placeholder: minPlaceholder,
844
+ "aria-label": t.formatMessage(messages.value_min_label, {
845
+ field: field.label
846
+ }),
847
+ style: { width: getInputWidth(min, minPlaceholder, 3) },
773
848
  min: field.min,
774
849
  max: field.max,
775
850
  step: field.step
@@ -786,8 +861,11 @@ function InlineValueInput({
786
861
  const val = e.target.value ? Number(e.target.value) : void 0;
787
862
  updateFilter(filter.id, { value: [min, val] });
788
863
  },
789
- placeholder: "Max",
790
- style: { width: getInputWidth(max, "Max", 3) },
864
+ placeholder: maxPlaceholder,
865
+ "aria-label": t.formatMessage(messages.value_max_label, {
866
+ field: field.label
867
+ }),
868
+ style: { width: getInputWidth(max, maxPlaceholder, 3) },
791
869
  min: field.min,
792
870
  max: field.max,
793
871
  step: field.step
@@ -803,6 +881,9 @@ function InlineValueInput({
803
881
  value: filter.value !== void 0 && filter.value !== null ? String(filter.value) : "",
804
882
  onChange: handleChange,
805
883
  placeholder,
884
+ "aria-label": t.formatMessage(messages.value_label, {
885
+ field: field.label
886
+ }),
806
887
  style: { width: getInputWidth(filter.value, placeholder, 6) },
807
888
  min: field.min,
808
889
  max: field.max,
@@ -815,7 +896,8 @@ function PopoverValueEditor({
815
896
  field
816
897
  }) {
817
898
  const { styles, updateFilter } = useFilterContext();
818
- const valueDisplay = field.renderValue ? field.renderValue({ filter, field }) : getValueDisplay(filter, field);
899
+ const t = (0, import_i18n3.useSafeIntl)();
900
+ const valueDisplay = field.renderValue ? field.renderValue({ filter, field }) : getValueDisplay(filter, field, t);
819
901
  const hasValue = filter.value !== void 0 && filter.value !== null && filter.value !== "" && !(Array.isArray(filter.value) && filter.value.length === 0);
820
902
  const handleCustomChange = (value) => {
821
903
  updateFilter(filter.id, { value });
@@ -828,7 +910,7 @@ function PopoverValueEditor({
828
910
  render: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", {}),
829
911
  nativeButton: false,
830
912
  children: [
831
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValueText(), children: hasValue ? valueDisplay : "Select..." }),
913
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: styles.fieldValueText(), children: hasValue ? valueDisplay : t.formatMessage(messages.select_value) }),
832
914
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_icons.ChevronDownIcon, { className: styles.fieldValueIcon() })
833
915
  ]
834
916
  }
@@ -855,7 +937,7 @@ function PopoverValueEditor({
855
937
  )
856
938
  ] });
857
939
  }
858
- function getValueDisplay(filter, field) {
940
+ function getValueDisplay(filter, field, t) {
859
941
  var _a, _b, _c, _d;
860
942
  const { value, operator } = filter;
861
943
  if (operator === "is_empty" || operator === "is_not_empty") {
@@ -865,19 +947,20 @@ function getValueDisplay(filter, field) {
865
947
  return null;
866
948
  }
867
949
  if (field.type === "boolean") {
868
- return value ? "Yes" : "No";
950
+ return value ? t.formatMessage(messages.boolean_true) : t.formatMessage(messages.boolean_false);
869
951
  }
870
952
  if (field.type === "select" && field.options) {
871
953
  const option = field.options.find((o) => o.value === value);
872
954
  return (_a = option == null ? void 0 : option.label) != null ? _a : String(value);
873
955
  }
874
- if (field.type === "multiselect" && Array.isArray(value)) {
875
- if (value.length === 0) return null;
876
- if (value.length === 1 && field.options) {
877
- const option = field.options.find((o) => o.value === value[0]);
878
- return (_b = option == null ? void 0 : option.label) != null ? _b : String(value[0]);
956
+ if (field.type === "multiselect") {
957
+ const values = Array.isArray(value) ? value.filter((v) => typeof v === "string" && v.length > 1) : [];
958
+ if (values.length === 0) return null;
959
+ if (values.length === 1 && field.options) {
960
+ const option = field.options.find((o) => o.value === values[0]);
961
+ return (_b = option == null ? void 0 : option.label) != null ? _b : String(values[0]);
879
962
  }
880
- return `${value.length} selected`;
963
+ return `${values.length} selected`;
881
964
  }
882
965
  if (field.type === "date" && value instanceof Date) {
883
966
  return value.toLocaleDateString();
@@ -896,6 +979,7 @@ function getValueDisplay(filter, field) {
896
979
 
897
980
  // src/filter-menu.tsx
898
981
  var import_dropdown_menu2 = require("@kopexa/dropdown-menu");
982
+ var import_i18n4 = require("@kopexa/i18n");
899
983
  var import_shared_utils3 = require("@kopexa/shared-utils");
900
984
  var import_react = require("react");
901
985
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -903,6 +987,7 @@ function FilterMenu(props) {
903
987
  var _a;
904
988
  const { children, className } = props;
905
989
  const { fields, styles, addFilter, allowMultiple, value } = useFilterContext();
990
+ const t = (0, import_i18n4.useSafeIntl)();
906
991
  const fieldGroups = /* @__PURE__ */ new Map();
907
992
  for (const [id, field] of fields) {
908
993
  const group = field.group;
@@ -919,7 +1004,7 @@ function FilterMenu(props) {
919
1004
  "data-slot": "filter-menu",
920
1005
  className: (0, import_shared_utils3.cn)("min-w-[220px]", className),
921
1006
  align: "start",
922
- children: fields.size === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "px-2 py-3 text-sm text-muted-foreground text-center", children: "Loading..." }) : Array.from(fieldGroups.entries()).map(([groupLabel, groupFields]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_dropdown_menu2.DropdownMenu.Group, { children: [
1007
+ children: fields.size === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "px-2 py-3 text-sm text-muted-foreground text-center", children: t.formatMessage(messages.loading) }) : Array.from(fieldGroups.entries()).map(([groupLabel, groupFields]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_dropdown_menu2.DropdownMenu.Group, { children: [
923
1008
  groupLabel && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_dropdown_menu2.DropdownMenu.Label, { children: groupLabel }),
924
1009
  Array.from(groupFields.values()).map((field) => {
925
1010
  const hasFilter = value.some((f) => f.fieldId === field.id);
@@ -1026,14 +1111,14 @@ FilterMenuSeparator.displayName = "FilterMenuSeparator";
1026
1111
 
1027
1112
  // src/filter-trigger.tsx
1028
1113
  var import_dropdown_menu3 = require("@kopexa/dropdown-menu");
1029
- var import_i18n4 = require("@kopexa/i18n");
1114
+ var import_i18n5 = require("@kopexa/i18n");
1030
1115
  var import_icons2 = require("@kopexa/icons");
1031
1116
  var import_shared_utils4 = require("@kopexa/shared-utils");
1032
1117
  var import_jsx_runtime5 = require("react/jsx-runtime");
1033
1118
  function FilterTrigger(props) {
1034
1119
  const { className, children, icon, ...rest } = props;
1035
1120
  const { styles } = useFilterContext();
1036
- const t = (0, import_i18n4.useSafeIntl)();
1121
+ const t = (0, import_i18n5.useSafeIntl)();
1037
1122
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1038
1123
  import_dropdown_menu3.DropdownMenu.Trigger,
1039
1124
  {
@@ -1048,16 +1133,1163 @@ function FilterTrigger(props) {
1048
1133
  );
1049
1134
  }
1050
1135
  FilterTrigger.displayName = "FilterTrigger";
1136
+
1137
+ // src/filter-bar.tsx
1138
+ var import_i18n7 = require("@kopexa/i18n");
1139
+ var import_shared_utils6 = require("@kopexa/shared-utils");
1140
+ var import_use_controllable_state = require("@kopexa/use-controllable-state");
1141
+ var import_react3 = require("react");
1142
+
1143
+ // src/filter-bar-internal.tsx
1144
+ var import_button = require("@kopexa/button");
1145
+ var import_checkbox2 = require("@kopexa/checkbox");
1146
+ var import_command = require("@kopexa/command");
1147
+ var import_dropdown_menu4 = require("@kopexa/dropdown-menu");
1148
+ var import_icons3 = require("@kopexa/icons");
1149
+ var import_input2 = require("@kopexa/input");
1150
+ var import_popover2 = require("@kopexa/popover");
1151
+ var import_shared_utils5 = require("@kopexa/shared-utils");
1152
+ var import_react2 = require("react");
1153
+
1154
+ // src/filter-bar-types.ts
1155
+ var DEFAULT_FILTER_BAR_OPERATORS = {
1156
+ select: ["equals", "not_equals", "is_empty", "is_not_empty"],
1157
+ multiselect: ["equals", "not_equals", "is_empty", "is_not_empty"],
1158
+ text: [
1159
+ "equals",
1160
+ "not_equals",
1161
+ "contains",
1162
+ "not_contains",
1163
+ "starts_with",
1164
+ "ends_with",
1165
+ "is_empty",
1166
+ "is_not_empty"
1167
+ ],
1168
+ number: [
1169
+ "equals",
1170
+ "not_equals",
1171
+ "gt",
1172
+ "lt",
1173
+ "gte",
1174
+ "lte",
1175
+ "is_empty",
1176
+ "is_not_empty"
1177
+ ],
1178
+ custom: ["equals", "not_equals"]
1179
+ };
1180
+
1181
+ // src/filter-bar-internal.tsx
1182
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1183
+ function AddFilterDropdown(props) {
1184
+ const { fieldGroups, isFieldAvailable, onAddFilter, i18n, disabled } = props;
1185
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_dropdown_menu4.DropdownMenu.Root, { children: [
1186
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dropdown_menu4.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1187
+ import_button.Button,
1188
+ {
1189
+ variant: "outline",
1190
+ size: "sm",
1191
+ className: "rounded-full border-dashed",
1192
+ disabled,
1193
+ startContent: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.PlusIcon, { className: "size-4" }),
1194
+ children: i18n.addFilter
1195
+ }
1196
+ ) }),
1197
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dropdown_menu4.DropdownMenu.Content, { align: "start", className: "min-w-[200px]", children: Array.from(fieldGroups.entries()).map(([groupLabel, groupFields]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_dropdown_menu4.DropdownMenu.Group, { children: [
1198
+ groupLabel && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dropdown_menu4.DropdownMenu.Label, { children: groupLabel }),
1199
+ groupFields.map((field) => {
1200
+ const available = isFieldAvailable(field.id);
1201
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1202
+ import_dropdown_menu4.DropdownMenu.Item,
1203
+ {
1204
+ disabled: field.disabled || !available,
1205
+ onSelect: () => onAddFilter(field.id),
1206
+ children: [
1207
+ field.icon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "size-4 text-muted-foreground mr-2", children: field.icon }),
1208
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: field.label })
1209
+ ]
1210
+ },
1211
+ field.id
1212
+ );
1213
+ })
1214
+ ] }, groupLabel != null ? groupLabel : "ungrouped")) })
1215
+ ] });
1216
+ }
1217
+ function FilterPill(props) {
1218
+ var _a;
1219
+ const {
1220
+ filter,
1221
+ field,
1222
+ i18n,
1223
+ onUpdate,
1224
+ onRemove,
1225
+ autoOpen,
1226
+ onAutoOpenComplete,
1227
+ disabled
1228
+ } = props;
1229
+ const [open, setOpen] = (0, import_react2.useState)(autoOpen != null ? autoOpen : false);
1230
+ const operators = (_a = field.operators) != null ? _a : DEFAULT_FILTER_BAR_OPERATORS[field.type];
1231
+ const isEmptyOperator = filter.operator === "is_empty" || filter.operator === "is_not_empty";
1232
+ const hasCustomRenderer = !!field.customRenderer;
1233
+ const useInlineInput = !hasCustomRenderer && (field.type === "text" || field.type === "number");
1234
+ const usePopover = !useInlineInput && !isEmptyOperator;
1235
+ const handleOpenChange = (0, import_react2.useCallback)(
1236
+ (isOpen) => {
1237
+ setOpen(isOpen);
1238
+ if (!isOpen && autoOpen) {
1239
+ onAutoOpenComplete == null ? void 0 : onAutoOpenComplete();
1240
+ }
1241
+ },
1242
+ [autoOpen, onAutoOpenComplete]
1243
+ );
1244
+ const valueDisplay = (0, import_react2.useMemo)(() => {
1245
+ var _a2, _b;
1246
+ if (isEmptyOperator) return null;
1247
+ if (filter.value === null || filter.value === "") return null;
1248
+ if (field.type === "select" && field.options) {
1249
+ const option = field.options.find((o) => o.value === filter.value);
1250
+ return (_a2 = option == null ? void 0 : option.label) != null ? _a2 : String(filter.value);
1251
+ }
1252
+ if (field.type === "multiselect") {
1253
+ const values = Array.isArray(filter.value) ? filter.value.filter(
1254
+ (v) => typeof v === "string" && v.length > 1
1255
+ ) : [];
1256
+ if (values.length === 0) return null;
1257
+ if (values.length === 1 && field.options) {
1258
+ const option = field.options.find((o) => o.value === values[0]);
1259
+ return (_b = option == null ? void 0 : option.label) != null ? _b : values[0];
1260
+ }
1261
+ return `${values.length} selected`;
1262
+ }
1263
+ return String(filter.value);
1264
+ }, [filter.value, field, isEmptyOperator]);
1265
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1266
+ "div",
1267
+ {
1268
+ "data-slot": "filter-pill",
1269
+ className: (0, import_shared_utils5.cn)(
1270
+ "group inline-flex items-center rounded-full overflow-hidden",
1271
+ "border border-border bg-background",
1272
+ "transition-all hover:shadow-sm focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-1"
1273
+ ),
1274
+ children: [
1275
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1276
+ "span",
1277
+ {
1278
+ className: (0, import_shared_utils5.cn)(
1279
+ "bg-muted text-foreground font-medium text-xs",
1280
+ "flex items-center gap-1.5 flex-shrink-0",
1281
+ "rounded-l-full px-3 py-1.5"
1282
+ ),
1283
+ children: [
1284
+ field.icon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "size-4 flex-shrink-0", children: field.icon }),
1285
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: field.label })
1286
+ ]
1287
+ }
1288
+ ),
1289
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1290
+ OperatorDropdown,
1291
+ {
1292
+ operator: filter.operator,
1293
+ operators,
1294
+ i18n,
1295
+ onChange: (op) => {
1296
+ const newUpdates = { operator: op };
1297
+ if (op === "is_empty" || op === "is_not_empty") {
1298
+ newUpdates.value = null;
1299
+ }
1300
+ onUpdate(newUpdates);
1301
+ },
1302
+ disabled
1303
+ }
1304
+ ),
1305
+ useInlineInput && !isEmptyOperator && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1306
+ InlineValueInput2,
1307
+ {
1308
+ filter,
1309
+ field,
1310
+ i18n,
1311
+ onUpdate,
1312
+ disabled
1313
+ }
1314
+ ),
1315
+ usePopover && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_popover2.Popover.Root, { open, onOpenChange: handleOpenChange, children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1317
+ import_popover2.Popover.Trigger,
1318
+ {
1319
+ className: (0, import_shared_utils5.cn)(
1320
+ "bg-background text-foreground text-xs",
1321
+ "flex items-center gap-1 px-2.5 py-1.5",
1322
+ "cursor-pointer hover:bg-muted/30"
1323
+ ),
1324
+ nativeButton: false,
1325
+ render: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", {}),
1326
+ children: [
1327
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate max-w-[150px]", children: valueDisplay != null ? valueDisplay : i18n.selectValue }),
1328
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.ChevronDownIcon, { className: "size-3.5 opacity-60 flex-shrink-0" })
1329
+ ]
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1333
+ import_popover2.Popover.Content,
1334
+ {
1335
+ "data-slot": "filter-editor",
1336
+ className: "p-0 w-[280px]",
1337
+ align: "start",
1338
+ showArrow: false,
1339
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1340
+ ValueEditor,
1341
+ {
1342
+ filter,
1343
+ field,
1344
+ i18n,
1345
+ onUpdate,
1346
+ onClose: () => handleOpenChange(false)
1347
+ }
1348
+ )
1349
+ }
1350
+ )
1351
+ ] }),
1352
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1353
+ "button",
1354
+ {
1355
+ type: "button",
1356
+ onClick: onRemove,
1357
+ className: (0, import_shared_utils5.cn)(
1358
+ "size-5 rounded-full flex items-center justify-center flex-shrink-0",
1359
+ "hover:bg-destructive/10 hover:text-destructive",
1360
+ "transition-colors cursor-pointer mr-1"
1361
+ ),
1362
+ disabled,
1363
+ "aria-label": `Remove ${field.label} filter`,
1364
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.CloseIcon, { className: "size-3" })
1365
+ }
1366
+ )
1367
+ ]
1368
+ }
1369
+ );
1370
+ }
1371
+ function OperatorDropdown(props) {
1372
+ const { operator, operators, i18n, onChange, disabled } = props;
1373
+ const hasSingleOperator = operators.length === 1;
1374
+ const currentLabel = i18n.operators[operator];
1375
+ if (hasSingleOperator) {
1376
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1377
+ "span",
1378
+ {
1379
+ className: (0, import_shared_utils5.cn)(
1380
+ "bg-primary text-primary-foreground",
1381
+ "flex items-center px-3 py-0.5",
1382
+ "rounded-full shadow-sm mx-1 my-0.5",
1383
+ "text-xs font-normal"
1384
+ ),
1385
+ children: currentLabel
1386
+ }
1387
+ );
1388
+ }
1389
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_dropdown_menu4.DropdownMenu.Root, { children: [
1390
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dropdown_menu4.DropdownMenu.Trigger, { asChild: true, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1391
+ "button",
1392
+ {
1393
+ type: "button",
1394
+ className: (0, import_shared_utils5.cn)(
1395
+ "bg-primary text-primary-foreground",
1396
+ "flex items-center gap-1 px-3 py-0.5",
1397
+ "rounded-full shadow-sm mx-1 my-0.5",
1398
+ "text-xs font-normal cursor-pointer",
1399
+ "hover:bg-primary/90 transition-colors"
1400
+ ),
1401
+ children: [
1402
+ currentLabel,
1403
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.ChevronDownIcon, { className: "size-3 opacity-70" })
1404
+ ]
1405
+ }
1406
+ ) }),
1407
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dropdown_menu4.DropdownMenu.Content, { align: "start", className: "min-w-[140px]", children: operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1408
+ import_dropdown_menu4.DropdownMenu.Item,
1409
+ {
1410
+ onSelect: () => onChange(op),
1411
+ className: "gap-2",
1412
+ children: [
1413
+ op === operator && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.CheckIcon, { className: "size-4" }),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: op !== operator ? "pl-6" : "", children: i18n.operators[op] })
1415
+ ]
1416
+ },
1417
+ op
1418
+ )) })
1419
+ ] });
1420
+ }
1421
+ function getInputWidth2(value, placeholder, minWidth = 4) {
1422
+ const displayValue = value !== void 0 && value !== null && value !== "" ? String(value) : placeholder;
1423
+ const width = Math.max(displayValue.length + 1, minWidth);
1424
+ return `${width}ch`;
1425
+ }
1426
+ function InlineValueInput2(props) {
1427
+ var _a;
1428
+ const { filter, field, i18n, onUpdate, disabled } = props;
1429
+ const placeholder = (_a = field.placeholder) != null ? _a : i18n.enterValue;
1430
+ const handleChange = (e) => {
1431
+ const val = e.target.value;
1432
+ if (field.type === "number") {
1433
+ onUpdate({ value: val ? Number(val) : null });
1434
+ } else {
1435
+ onUpdate({ value: val });
1436
+ }
1437
+ };
1438
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1439
+ "span",
1440
+ {
1441
+ className: (0, import_shared_utils5.cn)(
1442
+ "bg-background text-foreground text-xs",
1443
+ "flex items-center gap-1 px-2.5 py-1.5"
1444
+ ),
1445
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1446
+ "input",
1447
+ {
1448
+ type: field.type === "number" ? "number" : "text",
1449
+ className: (0, import_shared_utils5.cn)(
1450
+ "bg-transparent border-none outline-none",
1451
+ "placeholder:text-muted-foreground",
1452
+ "text-xs"
1453
+ ),
1454
+ value: filter.value !== void 0 && filter.value !== null ? String(filter.value) : "",
1455
+ onChange: handleChange,
1456
+ placeholder,
1457
+ "aria-label": `${field.label} value`,
1458
+ style: { width: getInputWidth2(filter.value, placeholder, 6) },
1459
+ min: field.min,
1460
+ max: field.max,
1461
+ step: field.step,
1462
+ disabled
1463
+ }
1464
+ )
1465
+ }
1466
+ );
1467
+ }
1468
+ function ValueEditor(props) {
1469
+ var _a, _b, _c, _d;
1470
+ const { filter, field, i18n, onUpdate, onClose } = props;
1471
+ if (field.customRenderer) {
1472
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "p-3", children: field.customRenderer({
1473
+ field,
1474
+ value: filter.value,
1475
+ onChange: (value) => onUpdate({ value })
1476
+ }) });
1477
+ }
1478
+ if (field.type === "select" && field.options) {
1479
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1480
+ SelectEditor,
1481
+ {
1482
+ options: field.options,
1483
+ value: filter.value,
1484
+ onChange: (value) => {
1485
+ onUpdate({ value });
1486
+ onClose();
1487
+ },
1488
+ i18n
1489
+ }
1490
+ );
1491
+ }
1492
+ if (field.type === "multiselect" && field.options) {
1493
+ const currentValue = Array.isArray(filter.value) ? filter.value.filter(
1494
+ (v) => typeof v === "string" && v.length > 1
1495
+ ) : [];
1496
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1497
+ MultiselectEditor,
1498
+ {
1499
+ options: field.options,
1500
+ value: currentValue,
1501
+ onChange: (value) => onUpdate({ value }),
1502
+ i18n
1503
+ }
1504
+ );
1505
+ }
1506
+ if (field.type === "text") {
1507
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1508
+ TextEditor,
1509
+ {
1510
+ value: (_a = filter.value) != null ? _a : "",
1511
+ onChange: (value) => onUpdate({ value }),
1512
+ placeholder: (_b = field.placeholder) != null ? _b : i18n.enterValue,
1513
+ validation: field.validation,
1514
+ onClose
1515
+ }
1516
+ );
1517
+ }
1518
+ if (field.type === "number") {
1519
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1520
+ NumberEditor,
1521
+ {
1522
+ value: (_c = filter.value) != null ? _c : null,
1523
+ onChange: (value) => onUpdate({ value }),
1524
+ placeholder: (_d = field.placeholder) != null ? _d : i18n.enterValue,
1525
+ min: field.min,
1526
+ max: field.max,
1527
+ step: field.step,
1528
+ onClose
1529
+ }
1530
+ );
1531
+ }
1532
+ return null;
1533
+ }
1534
+ function SelectEditor(props) {
1535
+ const { options = [], value, onChange, i18n } = props;
1536
+ const [search, setSearch] = (0, import_react2.useState)("");
1537
+ const filteredOptions = (0, import_react2.useMemo)(() => {
1538
+ if (!search) return options;
1539
+ const lower = search.toLowerCase();
1540
+ return options.filter(
1541
+ (o) => o.label.toLowerCase().includes(lower) || o.value.toLowerCase().includes(lower)
1542
+ );
1543
+ }, [options, search]);
1544
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_command.Command.Root, { shouldFilter: false, children: [
1545
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1546
+ import_command.Command.Input,
1547
+ {
1548
+ placeholder: i18n.searchPlaceholder,
1549
+ value: search,
1550
+ onValueChange: setSearch
1551
+ }
1552
+ ),
1553
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_command.Command.List, { className: "max-h-[300px]", children: [
1554
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_command.Command.Empty, { children: i18n.noResults }),
1555
+ filteredOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1556
+ import_command.Command.Item,
1557
+ {
1558
+ value: option.value,
1559
+ onSelect: () => onChange(option.value),
1560
+ className: "flex items-center gap-2 min-w-0",
1561
+ children: [
1562
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "flex-shrink-0 [&>*]:size-5", children: option.icon }),
1563
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate flex-1 min-w-0", children: option.label }),
1564
+ value === option.value && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_icons3.CheckIcon, { className: "size-4 flex-shrink-0" })
1565
+ ]
1566
+ },
1567
+ option.value
1568
+ ))
1569
+ ] })
1570
+ ] });
1571
+ }
1572
+ function MultiselectEditor(props) {
1573
+ const { options = [], value, onChange, i18n } = props;
1574
+ const [search, setSearch] = (0, import_react2.useState)("");
1575
+ const filteredOptions = (0, import_react2.useMemo)(() => {
1576
+ if (!search) return options;
1577
+ const lower = search.toLowerCase();
1578
+ return options.filter(
1579
+ (o) => o.label.toLowerCase().includes(lower) || o.value.toLowerCase().includes(lower)
1580
+ );
1581
+ }, [options, search]);
1582
+ const toggleOption = (0, import_react2.useCallback)(
1583
+ (optionValue) => {
1584
+ if (value.includes(optionValue)) {
1585
+ onChange(value.filter((v) => v !== optionValue));
1586
+ } else {
1587
+ onChange([...value, optionValue]);
1588
+ }
1589
+ },
1590
+ [value, onChange]
1591
+ );
1592
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_command.Command.Root, { shouldFilter: false, children: [
1593
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1594
+ import_command.Command.Input,
1595
+ {
1596
+ placeholder: i18n.searchPlaceholder,
1597
+ value: search,
1598
+ onValueChange: setSearch
1599
+ }
1600
+ ),
1601
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_command.Command.List, { className: "max-h-[300px]", children: [
1602
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_command.Command.Empty, { children: i18n.noResults }),
1603
+ filteredOptions.map((option) => {
1604
+ const isSelected = value.includes(option.value);
1605
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1606
+ import_command.Command.Item,
1607
+ {
1608
+ value: option.value,
1609
+ onSelect: () => toggleOption(option.value),
1610
+ className: "flex items-center gap-2 min-w-0",
1611
+ children: [
1612
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1613
+ import_checkbox2.Checkbox,
1614
+ {
1615
+ checked: isSelected,
1616
+ className: "flex-shrink-0 border-border bg-background data-[state=checked]:bg-primary data-[state=checked]:border-primary [&_svg]:text-primary-foreground"
1617
+ }
1618
+ ),
1619
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "flex-shrink-0 [&>*]:size-5", children: option.icon }),
1620
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate flex-1 min-w-0", children: option.label })
1621
+ ]
1622
+ },
1623
+ option.value
1624
+ );
1625
+ })
1626
+ ] })
1627
+ ] });
1628
+ }
1629
+ function TextEditor(props) {
1630
+ const {
1631
+ value: initialValue,
1632
+ onChange,
1633
+ placeholder,
1634
+ validation,
1635
+ onClose
1636
+ } = props;
1637
+ const [localValue, setLocalValue] = (0, import_react2.useState)(initialValue);
1638
+ const [error, setError] = (0, import_react2.useState)(null);
1639
+ const inputRef = (0, import_react2.useRef)(null);
1640
+ const validate = (0, import_react2.useCallback)(
1641
+ (val) => {
1642
+ var _a;
1643
+ if (!validation) return true;
1644
+ if (validation.pattern) {
1645
+ const regex = new RegExp(validation.pattern);
1646
+ if (!regex.test(val)) {
1647
+ setError((_a = validation.message) != null ? _a : "Invalid format");
1648
+ return false;
1649
+ }
1650
+ }
1651
+ if (validation.validate) {
1652
+ const result = validation.validate(val);
1653
+ if (result !== true) {
1654
+ setError(typeof result === "string" ? result : "Invalid value");
1655
+ return false;
1656
+ }
1657
+ }
1658
+ setError(null);
1659
+ return true;
1660
+ },
1661
+ [validation]
1662
+ );
1663
+ const handleSubmit = (0, import_react2.useCallback)(() => {
1664
+ if (validate(localValue)) {
1665
+ onChange(localValue);
1666
+ onClose();
1667
+ }
1668
+ }, [localValue, validate, onChange, onClose]);
1669
+ const handleKeyDown = (0, import_react2.useCallback)(
1670
+ (e) => {
1671
+ if (e.key === "Enter") {
1672
+ e.preventDefault();
1673
+ handleSubmit();
1674
+ } else if (e.key === "Escape") {
1675
+ e.preventDefault();
1676
+ onClose();
1677
+ }
1678
+ },
1679
+ [handleSubmit, onClose]
1680
+ );
1681
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-3 space-y-2", children: [
1682
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1683
+ import_input2.Input,
1684
+ {
1685
+ ref: inputRef,
1686
+ type: "text",
1687
+ value: localValue,
1688
+ onChange: (e) => {
1689
+ setLocalValue(e.target.value);
1690
+ setError(null);
1691
+ },
1692
+ onKeyDown: handleKeyDown,
1693
+ placeholder,
1694
+ className: "w-full",
1695
+ autoFocus: true
1696
+ }
1697
+ ),
1698
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-destructive", children: error }),
1699
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-end gap-2", children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_button.Button, { variant: "ghost", size: "sm", onClick: onClose, children: "Cancel" }),
1701
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_button.Button, { size: "sm", onClick: handleSubmit, children: "Apply" })
1702
+ ] })
1703
+ ] });
1704
+ }
1705
+ function NumberEditor(props) {
1706
+ const {
1707
+ value: initialValue,
1708
+ onChange,
1709
+ placeholder,
1710
+ min,
1711
+ max,
1712
+ step,
1713
+ onClose
1714
+ } = props;
1715
+ const [localValue, setLocalValue] = (0, import_react2.useState)(
1716
+ initialValue !== null ? String(initialValue) : ""
1717
+ );
1718
+ const inputRef = (0, import_react2.useRef)(null);
1719
+ const handleSubmit = (0, import_react2.useCallback)(() => {
1720
+ const numValue = localValue === "" ? null : Number(localValue);
1721
+ onChange(numValue);
1722
+ onClose();
1723
+ }, [localValue, onChange, onClose]);
1724
+ const handleKeyDown = (0, import_react2.useCallback)(
1725
+ (e) => {
1726
+ if (e.key === "Enter") {
1727
+ e.preventDefault();
1728
+ handleSubmit();
1729
+ } else if (e.key === "Escape") {
1730
+ e.preventDefault();
1731
+ onClose();
1732
+ }
1733
+ },
1734
+ [handleSubmit, onClose]
1735
+ );
1736
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-3 space-y-2", children: [
1737
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1738
+ import_input2.Input,
1739
+ {
1740
+ ref: inputRef,
1741
+ type: "number",
1742
+ value: localValue,
1743
+ onChange: (e) => setLocalValue(e.target.value),
1744
+ onKeyDown: handleKeyDown,
1745
+ placeholder,
1746
+ min,
1747
+ max,
1748
+ step,
1749
+ className: "w-full",
1750
+ autoFocus: true
1751
+ }
1752
+ ),
1753
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-end gap-2", children: [
1754
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_button.Button, { variant: "ghost", size: "sm", onClick: onClose, children: "Cancel" }),
1755
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_button.Button, { size: "sm", onClick: handleSubmit, children: "Apply" })
1756
+ ] })
1757
+ ] });
1758
+ }
1759
+
1760
+ // src/filter-bar-messages.ts
1761
+ var import_i18n6 = require("@kopexa/i18n");
1762
+ var filterBarMessages = (0, import_i18n6.defineMessages)({
1763
+ add_filter: {
1764
+ id: "filter-bar.add_filter",
1765
+ defaultMessage: "Add Filter",
1766
+ description: "Button text for adding a new filter"
1767
+ },
1768
+ clear_all: {
1769
+ id: "filter-bar.clear_all",
1770
+ defaultMessage: "Clear all",
1771
+ description: "Button text for clearing all filters"
1772
+ },
1773
+ no_results: {
1774
+ id: "filter-bar.no_results",
1775
+ defaultMessage: "No results found",
1776
+ description: "Message when search returns no results"
1777
+ },
1778
+ search_placeholder: {
1779
+ id: "filter-bar.search_placeholder",
1780
+ defaultMessage: "Search...",
1781
+ description: "Placeholder for search input"
1782
+ },
1783
+ select_value: {
1784
+ id: "filter-bar.select_value",
1785
+ defaultMessage: "Select value...",
1786
+ description: "Placeholder for value selection"
1787
+ },
1788
+ enter_value: {
1789
+ id: "filter-bar.enter_value",
1790
+ defaultMessage: "Enter value...",
1791
+ description: "Placeholder for value input"
1792
+ },
1793
+ apply: {
1794
+ id: "filter-bar.apply",
1795
+ defaultMessage: "Apply",
1796
+ description: "Button text for applying a filter value"
1797
+ },
1798
+ cancel: {
1799
+ id: "filter-bar.cancel",
1800
+ defaultMessage: "Cancel",
1801
+ description: "Button text for canceling"
1802
+ },
1803
+ // Operators
1804
+ op_equals: {
1805
+ id: "filter-bar.operator.equals",
1806
+ defaultMessage: "is",
1807
+ description: "Operator: equals"
1808
+ },
1809
+ op_not_equals: {
1810
+ id: "filter-bar.operator.not_equals",
1811
+ defaultMessage: "is not",
1812
+ description: "Operator: not equals"
1813
+ },
1814
+ op_contains: {
1815
+ id: "filter-bar.operator.contains",
1816
+ defaultMessage: "contains",
1817
+ description: "Operator: contains"
1818
+ },
1819
+ op_not_contains: {
1820
+ id: "filter-bar.operator.not_contains",
1821
+ defaultMessage: "does not contain",
1822
+ description: "Operator: does not contain"
1823
+ },
1824
+ op_starts_with: {
1825
+ id: "filter-bar.operator.starts_with",
1826
+ defaultMessage: "starts with",
1827
+ description: "Operator: starts with"
1828
+ },
1829
+ op_ends_with: {
1830
+ id: "filter-bar.operator.ends_with",
1831
+ defaultMessage: "ends with",
1832
+ description: "Operator: ends with"
1833
+ },
1834
+ op_gt: {
1835
+ id: "filter-bar.operator.gt",
1836
+ defaultMessage: "greater than",
1837
+ description: "Operator: greater than"
1838
+ },
1839
+ op_lt: {
1840
+ id: "filter-bar.operator.lt",
1841
+ defaultMessage: "less than",
1842
+ description: "Operator: less than"
1843
+ },
1844
+ op_gte: {
1845
+ id: "filter-bar.operator.gte",
1846
+ defaultMessage: "greater or equal",
1847
+ description: "Operator: greater than or equal"
1848
+ },
1849
+ op_lte: {
1850
+ id: "filter-bar.operator.lte",
1851
+ defaultMessage: "less or equal",
1852
+ description: "Operator: less than or equal"
1853
+ },
1854
+ op_is_empty: {
1855
+ id: "filter-bar.operator.is_empty",
1856
+ defaultMessage: "is empty",
1857
+ description: "Operator: is empty"
1858
+ },
1859
+ op_is_not_empty: {
1860
+ id: "filter-bar.operator.is_not_empty",
1861
+ defaultMessage: "is not empty",
1862
+ description: "Operator: is not empty"
1863
+ },
1864
+ // Accessibility
1865
+ remove_filter: {
1866
+ id: "filter-bar.remove_filter",
1867
+ defaultMessage: "Remove {field} filter",
1868
+ description: "Accessibility label for remove filter button"
1869
+ },
1870
+ operator_label: {
1871
+ id: "filter-bar.operator_label",
1872
+ defaultMessage: "{field} operator",
1873
+ description: "Accessibility label for operator dropdown"
1874
+ },
1875
+ value_label: {
1876
+ id: "filter-bar.value_label",
1877
+ defaultMessage: "{field} value",
1878
+ description: "Accessibility label for value editor"
1879
+ },
1880
+ // Multiselect
1881
+ selected_count: {
1882
+ id: "filter-bar.selected_count",
1883
+ defaultMessage: "{count} selected",
1884
+ description: "Display text for multiselect with multiple selections"
1885
+ }
1886
+ });
1887
+
1888
+ // src/filter-bar.tsx
1889
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1890
+ function FilterBar(props) {
1891
+ const {
1892
+ fields,
1893
+ value: valueProp,
1894
+ defaultValue = [],
1895
+ onChange,
1896
+ i18n: i18nProp,
1897
+ className,
1898
+ allowMultiple = true,
1899
+ disabled = false
1900
+ } = props;
1901
+ const t = (0, import_i18n7.useSafeIntl)();
1902
+ const instanceId = (0, import_react3.useId)();
1903
+ const [value, setValue] = (0, import_use_controllable_state.useControllableState)({
1904
+ value: valueProp,
1905
+ defaultValue,
1906
+ onChange
1907
+ });
1908
+ const [sessionFilterId, setSessionFilterId] = (0, import_react3.useState)(null);
1909
+ const i18n = (0, import_react3.useMemo)(() => {
1910
+ const defaultI18n = {
1911
+ addFilter: t.formatMessage(filterBarMessages.add_filter),
1912
+ clearAll: t.formatMessage(filterBarMessages.clear_all),
1913
+ noResults: t.formatMessage(filterBarMessages.no_results),
1914
+ searchPlaceholder: t.formatMessage(filterBarMessages.search_placeholder),
1915
+ selectValue: t.formatMessage(filterBarMessages.select_value),
1916
+ enterValue: t.formatMessage(filterBarMessages.enter_value),
1917
+ operators: {
1918
+ equals: t.formatMessage(filterBarMessages.op_equals),
1919
+ not_equals: t.formatMessage(filterBarMessages.op_not_equals),
1920
+ contains: t.formatMessage(filterBarMessages.op_contains),
1921
+ not_contains: t.formatMessage(filterBarMessages.op_not_contains),
1922
+ starts_with: t.formatMessage(filterBarMessages.op_starts_with),
1923
+ ends_with: t.formatMessage(filterBarMessages.op_ends_with),
1924
+ gt: t.formatMessage(filterBarMessages.op_gt),
1925
+ lt: t.formatMessage(filterBarMessages.op_lt),
1926
+ gte: t.formatMessage(filterBarMessages.op_gte),
1927
+ lte: t.formatMessage(filterBarMessages.op_lte),
1928
+ is_empty: t.formatMessage(filterBarMessages.op_is_empty),
1929
+ is_not_empty: t.formatMessage(filterBarMessages.op_is_not_empty)
1930
+ }
1931
+ };
1932
+ return {
1933
+ ...defaultI18n,
1934
+ ...i18nProp,
1935
+ operators: {
1936
+ ...defaultI18n.operators,
1937
+ ...i18nProp == null ? void 0 : i18nProp.operators
1938
+ }
1939
+ };
1940
+ }, [t, i18nProp]);
1941
+ const fieldGroups = (0, import_react3.useMemo)(() => {
1942
+ var _a;
1943
+ const groups = /* @__PURE__ */ new Map();
1944
+ for (const field of fields) {
1945
+ const group = field.group;
1946
+ if (!groups.has(group)) {
1947
+ groups.set(group, []);
1948
+ }
1949
+ (_a = groups.get(group)) == null ? void 0 : _a.push(field);
1950
+ }
1951
+ return groups;
1952
+ }, [fields]);
1953
+ const addFilter = (0, import_react3.useCallback)(
1954
+ (fieldId) => {
1955
+ var _a;
1956
+ const field = fields.find((f) => f.id === fieldId);
1957
+ if (!field) return;
1958
+ if (!allowMultiple && value.some((f) => f.fieldId === fieldId)) {
1959
+ return;
1960
+ }
1961
+ const newFilter = {
1962
+ id: `${instanceId}-${fieldId}-${Date.now()}`,
1963
+ fieldId,
1964
+ operator: (_a = field.defaultOperator) != null ? _a : "equals",
1965
+ value: field.type === "multiselect" ? [] : null
1966
+ };
1967
+ setValue([...value, newFilter]);
1968
+ setSessionFilterId(newFilter.id);
1969
+ },
1970
+ [fields, value, setValue, allowMultiple, instanceId]
1971
+ );
1972
+ const updateFilter = (0, import_react3.useCallback)(
1973
+ (filterId, updates) => {
1974
+ setValue(
1975
+ value.map((f) => f.id === filterId ? { ...f, ...updates } : f)
1976
+ );
1977
+ },
1978
+ [value, setValue]
1979
+ );
1980
+ const removeFilter = (0, import_react3.useCallback)(
1981
+ (filterId) => {
1982
+ setValue(value.filter((f) => f.id !== filterId));
1983
+ if (sessionFilterId === filterId) {
1984
+ setSessionFilterId(null);
1985
+ }
1986
+ },
1987
+ [value, setValue, sessionFilterId]
1988
+ );
1989
+ const clearFilters = (0, import_react3.useCallback)(() => {
1990
+ setValue([]);
1991
+ setSessionFilterId(null);
1992
+ }, [setValue]);
1993
+ const isFieldAvailable = (0, import_react3.useCallback)(
1994
+ (fieldId) => {
1995
+ if (allowMultiple) return true;
1996
+ return !value.some((f) => f.fieldId === fieldId);
1997
+ },
1998
+ [value, allowMultiple]
1999
+ );
2000
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2001
+ "div",
2002
+ {
2003
+ "data-slot": "filter-bar",
2004
+ className: (0, import_shared_utils6.cn)("flex flex-wrap items-center gap-2", className),
2005
+ children: [
2006
+ value.map((filter) => {
2007
+ const field = fields.find((f) => f.id === filter.fieldId);
2008
+ if (!field) return null;
2009
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2010
+ FilterPill,
2011
+ {
2012
+ filter,
2013
+ field,
2014
+ i18n,
2015
+ onUpdate: (updates) => updateFilter(filter.id, updates),
2016
+ onRemove: () => removeFilter(filter.id),
2017
+ autoOpen: sessionFilterId === filter.id,
2018
+ onAutoOpenComplete: () => setSessionFilterId(null),
2019
+ disabled
2020
+ },
2021
+ filter.id
2022
+ );
2023
+ }),
2024
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2025
+ AddFilterDropdown,
2026
+ {
2027
+ fieldGroups,
2028
+ isFieldAvailable,
2029
+ onAddFilter: addFilter,
2030
+ i18n,
2031
+ disabled
2032
+ }
2033
+ ),
2034
+ value.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2035
+ "button",
2036
+ {
2037
+ type: "button",
2038
+ onClick: clearFilters,
2039
+ className: "text-xs text-muted-foreground hover:text-foreground cursor-pointer transition-colors underline-offset-2 hover:underline",
2040
+ disabled,
2041
+ children: i18n.clearAll
2042
+ }
2043
+ )
2044
+ ]
2045
+ }
2046
+ );
2047
+ }
2048
+ FilterBar.displayName = "FilterBar";
2049
+
2050
+ // src/search-filter-bar.tsx
2051
+ var import_i18n8 = require("@kopexa/i18n");
2052
+ var import_icons4 = require("@kopexa/icons");
2053
+ var import_shared_utils7 = require("@kopexa/shared-utils");
2054
+ var import_spinner = require("@kopexa/spinner");
2055
+ var import_use_controllable_state2 = require("@kopexa/use-controllable-state");
2056
+ var import_use_debounced_callback = require("@kopexa/use-debounced-callback");
2057
+ var import_react4 = require("react");
2058
+ var import_jsx_runtime8 = require("react/jsx-runtime");
2059
+ function SearchFilterBar(props) {
2060
+ const {
2061
+ fields,
2062
+ filters: filtersProp,
2063
+ defaultFilters = [],
2064
+ onFiltersChange,
2065
+ search: searchProp,
2066
+ defaultSearch = "",
2067
+ onSearchChange,
2068
+ onSearchValueChange,
2069
+ searchDebounce = 300,
2070
+ searchPlaceholder,
2071
+ searchLoading = false,
2072
+ i18n: i18nProp,
2073
+ className,
2074
+ allowMultiple = true,
2075
+ disabled = false,
2076
+ endContent
2077
+ } = props;
2078
+ const t = (0, import_i18n8.useSafeIntl)();
2079
+ const instanceId = (0, import_react4.useId)();
2080
+ const [filters, setFilters] = (0, import_use_controllable_state2.useControllableState)({
2081
+ value: filtersProp,
2082
+ defaultValue: defaultFilters,
2083
+ onChange: onFiltersChange
2084
+ });
2085
+ const [search, setSearch] = (0, import_use_controllable_state2.useControllableState)({
2086
+ value: searchProp,
2087
+ defaultValue: defaultSearch,
2088
+ onChange: onSearchChange
2089
+ });
2090
+ const [sessionFilterId, setSessionFilterId] = (0, import_react4.useState)(null);
2091
+ const i18n = (0, import_react4.useMemo)(() => {
2092
+ const defaultI18n = {
2093
+ addFilter: t.formatMessage(filterBarMessages.add_filter),
2094
+ clearAll: t.formatMessage(filterBarMessages.clear_all),
2095
+ noResults: t.formatMessage(filterBarMessages.no_results),
2096
+ searchPlaceholder: t.formatMessage(filterBarMessages.search_placeholder),
2097
+ selectValue: t.formatMessage(filterBarMessages.select_value),
2098
+ enterValue: t.formatMessage(filterBarMessages.enter_value),
2099
+ operators: {
2100
+ equals: t.formatMessage(filterBarMessages.op_equals),
2101
+ not_equals: t.formatMessage(filterBarMessages.op_not_equals),
2102
+ contains: t.formatMessage(filterBarMessages.op_contains),
2103
+ not_contains: t.formatMessage(filterBarMessages.op_not_contains),
2104
+ starts_with: t.formatMessage(filterBarMessages.op_starts_with),
2105
+ ends_with: t.formatMessage(filterBarMessages.op_ends_with),
2106
+ gt: t.formatMessage(filterBarMessages.op_gt),
2107
+ lt: t.formatMessage(filterBarMessages.op_lt),
2108
+ gte: t.formatMessage(filterBarMessages.op_gte),
2109
+ lte: t.formatMessage(filterBarMessages.op_lte),
2110
+ is_empty: t.formatMessage(filterBarMessages.op_is_empty),
2111
+ is_not_empty: t.formatMessage(filterBarMessages.op_is_not_empty)
2112
+ }
2113
+ };
2114
+ return {
2115
+ ...defaultI18n,
2116
+ ...i18nProp,
2117
+ operators: {
2118
+ ...defaultI18n.operators,
2119
+ ...i18nProp == null ? void 0 : i18nProp.operators
2120
+ }
2121
+ };
2122
+ }, [t, i18nProp]);
2123
+ const fieldGroups = (0, import_react4.useMemo)(() => {
2124
+ var _a;
2125
+ const groups = /* @__PURE__ */ new Map();
2126
+ for (const field of fields) {
2127
+ const group = field.group;
2128
+ if (!groups.has(group)) {
2129
+ groups.set(group, []);
2130
+ }
2131
+ (_a = groups.get(group)) == null ? void 0 : _a.push(field);
2132
+ }
2133
+ return groups;
2134
+ }, [fields]);
2135
+ const addFilter = (0, import_react4.useCallback)(
2136
+ (fieldId) => {
2137
+ var _a;
2138
+ const field = fields.find((f) => f.id === fieldId);
2139
+ if (!field) return;
2140
+ if (!allowMultiple && filters.some((f) => f.fieldId === fieldId)) {
2141
+ return;
2142
+ }
2143
+ const newFilter = {
2144
+ id: `${instanceId}-${fieldId}-${Date.now()}`,
2145
+ fieldId,
2146
+ operator: (_a = field.defaultOperator) != null ? _a : "equals",
2147
+ value: field.type === "multiselect" ? [] : null
2148
+ };
2149
+ setFilters([...filters, newFilter]);
2150
+ setSessionFilterId(newFilter.id);
2151
+ },
2152
+ [fields, filters, setFilters, allowMultiple, instanceId]
2153
+ );
2154
+ const updateFilter = (0, import_react4.useCallback)(
2155
+ (filterId, updates) => {
2156
+ setFilters(
2157
+ filters.map((f) => f.id === filterId ? { ...f, ...updates } : f)
2158
+ );
2159
+ },
2160
+ [filters, setFilters]
2161
+ );
2162
+ const removeFilter = (0, import_react4.useCallback)(
2163
+ (filterId) => {
2164
+ setFilters(filters.filter((f) => f.id !== filterId));
2165
+ if (sessionFilterId === filterId) {
2166
+ setSessionFilterId(null);
2167
+ }
2168
+ },
2169
+ [filters, setFilters, sessionFilterId]
2170
+ );
2171
+ const clearFilters = (0, import_react4.useCallback)(() => {
2172
+ setFilters([]);
2173
+ setSessionFilterId(null);
2174
+ }, [setFilters]);
2175
+ const isFieldAvailable = (0, import_react4.useCallback)(
2176
+ (fieldId) => {
2177
+ if (allowMultiple) return true;
2178
+ return !filters.some((f) => f.fieldId === fieldId);
2179
+ },
2180
+ [filters, allowMultiple]
2181
+ );
2182
+ const debouncedSearchCallback = (0, import_use_debounced_callback.useDebounceCallback)((value) => {
2183
+ onSearchValueChange == null ? void 0 : onSearchValueChange(value);
2184
+ }, searchDebounce);
2185
+ const handleSearchChange = (0, import_react4.useCallback)(
2186
+ (e) => {
2187
+ const value = e.target.value;
2188
+ setSearch(value);
2189
+ if (onSearchValueChange) {
2190
+ debouncedSearchCallback(value);
2191
+ }
2192
+ },
2193
+ [setSearch, onSearchValueChange, debouncedSearchCallback]
2194
+ );
2195
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2196
+ "div",
2197
+ {
2198
+ "data-slot": "search-filter-bar",
2199
+ className: (0, import_shared_utils7.cn)("flex items-center gap-2 w-full", className),
2200
+ children: [
2201
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2202
+ "div",
2203
+ {
2204
+ className: (0, import_shared_utils7.cn)(
2205
+ "flex flex-1 flex-wrap items-center gap-2",
2206
+ "border border-input rounded-lg",
2207
+ "bg-background",
2208
+ "px-3 py-2",
2209
+ "focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-1",
2210
+ "transition-shadow",
2211
+ disabled && "opacity-50 pointer-events-none"
2212
+ ),
2213
+ children: [
2214
+ filters.map((filter) => {
2215
+ const field = fields.find((f) => f.id === filter.fieldId);
2216
+ if (!field) return null;
2217
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2218
+ FilterPill,
2219
+ {
2220
+ filter,
2221
+ field,
2222
+ i18n,
2223
+ onUpdate: (updates) => updateFilter(filter.id, updates),
2224
+ onRemove: () => removeFilter(filter.id),
2225
+ autoOpen: sessionFilterId === filter.id,
2226
+ onAutoOpenComplete: () => setSessionFilterId(null),
2227
+ disabled
2228
+ },
2229
+ filter.id
2230
+ );
2231
+ }),
2232
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2233
+ AddFilterDropdown,
2234
+ {
2235
+ fieldGroups,
2236
+ isFieldAvailable,
2237
+ onAddFilter: addFilter,
2238
+ i18n,
2239
+ disabled
2240
+ }
2241
+ ),
2242
+ filters.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2243
+ "button",
2244
+ {
2245
+ type: "button",
2246
+ onClick: clearFilters,
2247
+ className: "text-xs text-muted-foreground hover:text-foreground cursor-pointer transition-colors underline-offset-2 hover:underline",
2248
+ disabled,
2249
+ children: i18n.clearAll
2250
+ }
2251
+ ),
2252
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-1 items-center gap-2 min-w-[120px]", children: [
2253
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons4.SearchIcon, { className: "size-4 text-muted-foreground flex-shrink-0" }),
2254
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2255
+ "input",
2256
+ {
2257
+ type: "text",
2258
+ value: search,
2259
+ onChange: handleSearchChange,
2260
+ placeholder: searchPlaceholder != null ? searchPlaceholder : i18n.searchPlaceholder,
2261
+ disabled,
2262
+ className: (0, import_shared_utils7.cn)(
2263
+ "flex-1 bg-transparent border-none outline-none",
2264
+ "text-sm placeholder:text-muted-foreground",
2265
+ "min-w-0"
2266
+ )
2267
+ }
2268
+ ),
2269
+ searchLoading && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_spinner.Spinner, { size: "xs", className: "flex-shrink-0" })
2270
+ ] })
2271
+ ]
2272
+ }
2273
+ ),
2274
+ endContent
2275
+ ]
2276
+ }
2277
+ );
2278
+ }
2279
+ SearchFilterBar.displayName = "SearchFilterBar";
1051
2280
  // Annotate the CommonJS export names for ESM import in node:
1052
2281
  0 && (module.exports = {
1053
2282
  Filter,
1054
2283
  FilterActive,
2284
+ FilterBar,
1055
2285
  FilterGroup,
1056
2286
  FilterItem,
1057
2287
  FilterMenu,
1058
2288
  FilterMenuSeparator,
1059
2289
  FilterTrigger,
1060
2290
  FilterValueEditor,
2291
+ SearchFilterBar,
2292
+ filterBarMessages,
1061
2293
  filterMessages,
1062
2294
  useFilterContext
1063
2295
  });