@medusajs/ui 4.0.22-snapshot-20250829152522 → 4.0.22-snapshot-20250901132949

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 (113) hide show
  1. package/dist/cjs/blocks/data-table/components/data-table-column-visibility-menu.d.ts +9 -0
  2. package/dist/cjs/blocks/data-table/components/data-table-column-visibility-menu.d.ts.map +1 -0
  3. package/dist/cjs/blocks/data-table/components/data-table-column-visibility-menu.js +58 -0
  4. package/dist/cjs/blocks/data-table/components/data-table-column-visibility-menu.js.map +1 -0
  5. package/dist/cjs/blocks/data-table/components/data-table-filter-bar.d.ts +5 -1
  6. package/dist/cjs/blocks/data-table/components/data-table-filter-bar.d.ts.map +1 -1
  7. package/dist/cjs/blocks/data-table/components/data-table-filter-bar.js +71 -9
  8. package/dist/cjs/blocks/data-table/components/data-table-filter-bar.js.map +1 -1
  9. package/dist/cjs/blocks/data-table/components/data-table-filter-menu.d.ts +5 -1
  10. package/dist/cjs/blocks/data-table/components/data-table-filter-menu.d.ts.map +1 -1
  11. package/dist/cjs/blocks/data-table/components/data-table-filter-menu.js +35 -7
  12. package/dist/cjs/blocks/data-table/components/data-table-filter-menu.js.map +1 -1
  13. package/dist/cjs/blocks/data-table/components/data-table-filter.d.ts +4 -1
  14. package/dist/cjs/blocks/data-table/components/data-table-filter.d.ts.map +1 -1
  15. package/dist/cjs/blocks/data-table/components/data-table-filter.js +488 -73
  16. package/dist/cjs/blocks/data-table/components/data-table-filter.js.map +1 -1
  17. package/dist/cjs/blocks/data-table/components/data-table-non-sortable-header-cell.d.ts +9 -0
  18. package/dist/cjs/blocks/data-table/components/data-table-non-sortable-header-cell.d.ts.map +1 -0
  19. package/dist/cjs/blocks/data-table/components/data-table-non-sortable-header-cell.js +42 -0
  20. package/dist/cjs/blocks/data-table/components/data-table-non-sortable-header-cell.js.map +1 -0
  21. package/dist/cjs/blocks/data-table/components/data-table-sortable-header-cell.d.ts +9 -0
  22. package/dist/cjs/blocks/data-table/components/data-table-sortable-header-cell.d.ts.map +1 -0
  23. package/dist/cjs/blocks/data-table/components/data-table-sortable-header-cell.js +44 -0
  24. package/dist/cjs/blocks/data-table/components/data-table-sortable-header-cell.js.map +1 -0
  25. package/dist/cjs/blocks/data-table/components/data-table-table.d.ts.map +1 -1
  26. package/dist/cjs/blocks/data-table/components/data-table-table.js +132 -4
  27. package/dist/cjs/blocks/data-table/components/data-table-table.js.map +1 -1
  28. package/dist/cjs/blocks/data-table/components/data-table-toolbar.d.ts +12 -0
  29. package/dist/cjs/blocks/data-table/components/data-table-toolbar.d.ts.map +1 -1
  30. package/dist/cjs/blocks/data-table/components/data-table-toolbar.js +5 -2
  31. package/dist/cjs/blocks/data-table/components/data-table-toolbar.js.map +1 -1
  32. package/dist/cjs/blocks/data-table/context/data-table-context-provider.d.ts.map +1 -1
  33. package/dist/cjs/blocks/data-table/context/data-table-context-provider.js +5 -1
  34. package/dist/cjs/blocks/data-table/context/data-table-context-provider.js.map +1 -1
  35. package/dist/cjs/blocks/data-table/context/data-table-context.d.ts +2 -0
  36. package/dist/cjs/blocks/data-table/context/data-table-context.d.ts.map +1 -1
  37. package/dist/cjs/blocks/data-table/context/data-table-context.js.map +1 -1
  38. package/dist/cjs/blocks/data-table/data-table.d.ts +6 -1
  39. package/dist/cjs/blocks/data-table/data-table.d.ts.map +1 -1
  40. package/dist/cjs/blocks/data-table/data-table.js +4 -0
  41. package/dist/cjs/blocks/data-table/data-table.js.map +1 -1
  42. package/dist/cjs/blocks/data-table/index.d.ts +2 -1
  43. package/dist/cjs/blocks/data-table/index.d.ts.map +1 -1
  44. package/dist/cjs/blocks/data-table/index.js.map +1 -1
  45. package/dist/cjs/blocks/data-table/types.d.ts +75 -3
  46. package/dist/cjs/blocks/data-table/types.d.ts.map +1 -1
  47. package/dist/cjs/blocks/data-table/types.js.map +1 -1
  48. package/dist/cjs/blocks/data-table/use-data-table.d.ts +21 -3
  49. package/dist/cjs/blocks/data-table/use-data-table.d.ts.map +1 -1
  50. package/dist/cjs/blocks/data-table/use-data-table.js +47 -7
  51. package/dist/cjs/blocks/data-table/use-data-table.js.map +1 -1
  52. package/dist/cjs/blocks/data-table/utils/create-data-table-column-helper.d.ts.map +1 -1
  53. package/dist/cjs/blocks/data-table/utils/create-data-table-column-helper.js +2 -1
  54. package/dist/cjs/blocks/data-table/utils/create-data-table-column-helper.js.map +1 -1
  55. package/dist/cjs/blocks/data-table/utils/create-data-table-filter-helper.d.ts +27 -0
  56. package/dist/cjs/blocks/data-table/utils/create-data-table-filter-helper.d.ts.map +1 -1
  57. package/dist/esm/blocks/data-table/components/data-table-column-visibility-menu.d.ts +9 -0
  58. package/dist/esm/blocks/data-table/components/data-table-column-visibility-menu.d.ts.map +1 -0
  59. package/dist/esm/blocks/data-table/components/data-table-column-visibility-menu.js +54 -0
  60. package/dist/esm/blocks/data-table/components/data-table-column-visibility-menu.js.map +1 -0
  61. package/dist/esm/blocks/data-table/components/data-table-filter-bar.d.ts +5 -1
  62. package/dist/esm/blocks/data-table/components/data-table-filter-bar.d.ts.map +1 -1
  63. package/dist/esm/blocks/data-table/components/data-table-filter-bar.js +71 -9
  64. package/dist/esm/blocks/data-table/components/data-table-filter-bar.js.map +1 -1
  65. package/dist/esm/blocks/data-table/components/data-table-filter-menu.d.ts +5 -1
  66. package/dist/esm/blocks/data-table/components/data-table-filter-menu.d.ts.map +1 -1
  67. package/dist/esm/blocks/data-table/components/data-table-filter-menu.js +35 -7
  68. package/dist/esm/blocks/data-table/components/data-table-filter-menu.js.map +1 -1
  69. package/dist/esm/blocks/data-table/components/data-table-filter.d.ts +4 -1
  70. package/dist/esm/blocks/data-table/components/data-table-filter.d.ts.map +1 -1
  71. package/dist/esm/blocks/data-table/components/data-table-filter.js +489 -74
  72. package/dist/esm/blocks/data-table/components/data-table-filter.js.map +1 -1
  73. package/dist/esm/blocks/data-table/components/data-table-non-sortable-header-cell.d.ts +9 -0
  74. package/dist/esm/blocks/data-table/components/data-table-non-sortable-header-cell.d.ts.map +1 -0
  75. package/dist/esm/blocks/data-table/components/data-table-non-sortable-header-cell.js +38 -0
  76. package/dist/esm/blocks/data-table/components/data-table-non-sortable-header-cell.js.map +1 -0
  77. package/dist/esm/blocks/data-table/components/data-table-sortable-header-cell.d.ts +9 -0
  78. package/dist/esm/blocks/data-table/components/data-table-sortable-header-cell.d.ts.map +1 -0
  79. package/dist/esm/blocks/data-table/components/data-table-sortable-header-cell.js +40 -0
  80. package/dist/esm/blocks/data-table/components/data-table-sortable-header-cell.js.map +1 -0
  81. package/dist/esm/blocks/data-table/components/data-table-table.d.ts.map +1 -1
  82. package/dist/esm/blocks/data-table/components/data-table-table.js +132 -4
  83. package/dist/esm/blocks/data-table/components/data-table-table.js.map +1 -1
  84. package/dist/esm/blocks/data-table/components/data-table-toolbar.d.ts +12 -0
  85. package/dist/esm/blocks/data-table/components/data-table-toolbar.d.ts.map +1 -1
  86. package/dist/esm/blocks/data-table/components/data-table-toolbar.js +5 -2
  87. package/dist/esm/blocks/data-table/components/data-table-toolbar.js.map +1 -1
  88. package/dist/esm/blocks/data-table/context/data-table-context-provider.d.ts.map +1 -1
  89. package/dist/esm/blocks/data-table/context/data-table-context-provider.js +5 -1
  90. package/dist/esm/blocks/data-table/context/data-table-context-provider.js.map +1 -1
  91. package/dist/esm/blocks/data-table/context/data-table-context.d.ts +2 -0
  92. package/dist/esm/blocks/data-table/context/data-table-context.d.ts.map +1 -1
  93. package/dist/esm/blocks/data-table/context/data-table-context.js.map +1 -1
  94. package/dist/esm/blocks/data-table/data-table.d.ts +6 -1
  95. package/dist/esm/blocks/data-table/data-table.d.ts.map +1 -1
  96. package/dist/esm/blocks/data-table/data-table.js +4 -0
  97. package/dist/esm/blocks/data-table/data-table.js.map +1 -1
  98. package/dist/esm/blocks/data-table/index.d.ts +2 -1
  99. package/dist/esm/blocks/data-table/index.d.ts.map +1 -1
  100. package/dist/esm/blocks/data-table/index.js.map +1 -1
  101. package/dist/esm/blocks/data-table/types.d.ts +75 -3
  102. package/dist/esm/blocks/data-table/types.d.ts.map +1 -1
  103. package/dist/esm/blocks/data-table/types.js.map +1 -1
  104. package/dist/esm/blocks/data-table/use-data-table.d.ts +21 -3
  105. package/dist/esm/blocks/data-table/use-data-table.d.ts.map +1 -1
  106. package/dist/esm/blocks/data-table/use-data-table.js +47 -7
  107. package/dist/esm/blocks/data-table/use-data-table.js.map +1 -1
  108. package/dist/esm/blocks/data-table/utils/create-data-table-column-helper.d.ts.map +1 -1
  109. package/dist/esm/blocks/data-table/utils/create-data-table-column-helper.js +2 -1
  110. package/dist/esm/blocks/data-table/utils/create-data-table-column-helper.js.map +1 -1
  111. package/dist/esm/blocks/data-table/utils/create-data-table-filter-helper.d.ts +27 -0
  112. package/dist/esm/blocks/data-table/utils/create-data-table-filter-helper.d.ts.map +1 -1
  113. package/package.json +6 -3
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- "use client";
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.DataTableFilter = void 0;
5
4
  const tslib_1 = require("tslib");
@@ -10,6 +9,9 @@ const is_date_comparison_operator_1 = require("../../data-table/utils/is-date-co
10
9
  const date_picker_1 = require("../../../components/date-picker");
11
10
  const label_1 = require("../../../components/label");
12
11
  const popover_1 = require("../../../components/popover");
12
+ const input_1 = require("../../../components/input");
13
+ const select_1 = require("../../../components/select");
14
+ const checkbox_1 = require("../../../components/checkbox");
13
15
  const clx_1 = require("../../../utils/clx");
14
16
  const DEFAULT_FORMAT_DATE_VALUE = (d) => d.toLocaleDateString(undefined, {
15
17
  year: "numeric",
@@ -19,28 +21,81 @@ const DEFAULT_FORMAT_DATE_VALUE = (d) => d.toLocaleDateString(undefined, {
19
21
  const DEFAULT_RANGE_OPTION_LABEL = "Custom";
20
22
  const DEFAULT_RANGE_OPTION_START_LABEL = "Starting";
21
23
  const DEFAULT_RANGE_OPTION_END_LABEL = "Ending";
22
- const DataTableFilter = ({ id, filter }) => {
24
+ const DataTableFilter = ({ id, filter, isNew = false, onUpdate, onRemove }) => {
23
25
  const { instance } = (0, use_data_table_context_1.useDataTableContext)();
24
- const [open, setOpen] = React.useState(filter === undefined);
25
- const [isCustom, setIsCustom] = React.useState(false);
26
- const onOpenChange = React.useCallback((open) => {
27
- if (!open &&
28
- (!filter || (Array.isArray(filter) && filter.length === 0))) {
29
- instance.removeFilter(id);
26
+ // Initialize open state based on isNew prop
27
+ const [open, setOpen] = React.useState(isNew);
28
+ const [hasInteracted, setHasInteracted] = React.useState(false);
29
+ const meta = instance.getFilterMeta(id);
30
+ if (!meta) {
31
+ return null;
32
+ }
33
+ const { type, label, ...rest } = meta;
34
+ const options = meta.options;
35
+ // Helper to check if filter has a meaningful value
36
+ const hasValue = React.useMemo(() => {
37
+ if (filter === null || filter === undefined)
38
+ return false;
39
+ if (typeof filter === "string" && filter === "")
40
+ return false;
41
+ if (Array.isArray(filter) && filter.length === 0)
42
+ return false;
43
+ if (typeof filter === "number")
44
+ return true;
45
+ if ((0, is_date_comparison_operator_1.isDateComparisonOperator)(filter)) {
46
+ return !!(filter.$gte || filter.$lte || filter.$gt || filter.$lt);
47
+ }
48
+ if (typeof filter === "object" && filter !== null) {
49
+ // For number comparison operators
50
+ const keys = Object.keys(filter);
51
+ return keys.length > 0 && filter[keys[0]] !== null && filter[keys[0]] !== undefined;
30
52
  }
31
- setOpen(open);
32
- }, [instance, id, filter]);
53
+ return true;
54
+ }, [filter]);
55
+ const onOpenChange = React.useCallback((newOpen) => {
56
+ setOpen(newOpen);
57
+ // Mark as interacted when user closes
58
+ if (!newOpen && open) {
59
+ setHasInteracted(true);
60
+ }
61
+ // If closing without a value, remove filter
62
+ // For new filters that haven't been interacted with, remove immediately
63
+ if (!newOpen && !hasValue) {
64
+ // Only remove if it's a new filter being closed without interaction,
65
+ // or if it's an existing filter with no value
66
+ if ((isNew && !hasInteracted) || !isNew) {
67
+ if (onRemove) {
68
+ onRemove();
69
+ }
70
+ else {
71
+ instance.removeFilter(id);
72
+ }
73
+ }
74
+ }
75
+ }, [instance, id, open, hasInteracted, isNew, hasValue, onRemove]);
33
76
  const removeFilter = React.useCallback(() => {
34
- instance.removeFilter(id);
35
- }, [instance, id]);
36
- const meta = instance.getFilterMeta(id);
37
- const { type, options, label, ...rest } = meta !== null && meta !== void 0 ? meta : {};
77
+ if (onRemove) {
78
+ onRemove();
79
+ }
80
+ else {
81
+ instance.removeFilter(id);
82
+ }
83
+ }, [instance, id, onRemove]);
38
84
  const { displayValue, isCustomRange } = React.useMemo(() => {
39
85
  var _a, _b, _c, _d, _e;
40
86
  let displayValue = null;
41
87
  let isCustomRange = false;
42
88
  if (typeof filter === "string") {
43
- displayValue = (_b = (_a = options === null || options === void 0 ? void 0 : options.find((o) => o.value === filter)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : null;
89
+ // For string filters without options, just show the value
90
+ if (!options || options.length === 0) {
91
+ displayValue = filter;
92
+ }
93
+ else {
94
+ displayValue = (_b = (_a = options === null || options === void 0 ? void 0 : options.find((o) => o.value === filter)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : null;
95
+ }
96
+ }
97
+ if (typeof filter === "number") {
98
+ displayValue = String(filter);
44
99
  }
45
100
  if (Array.isArray(filter)) {
46
101
  displayValue =
@@ -49,65 +104,108 @@ const DataTableFilter = ({ id, filter }) => {
49
104
  .join(", ")) !== null && _c !== void 0 ? _c : null;
50
105
  }
51
106
  if ((0, is_date_comparison_operator_1.isDateComparisonOperator)(filter)) {
107
+ // First check if it matches a predefined option
52
108
  displayValue =
53
109
  (_e = (_d = options === null || options === void 0 ? void 0 : options.find((o) => {
54
110
  if (!(0, is_date_comparison_operator_1.isDateComparisonOperator)(o.value)) {
55
111
  return false;
56
112
  }
57
- return (!isCustom &&
58
- (filter.$gte === o.value.$gte || (!filter.$gte && !o.value.$gte)) &&
113
+ return ((filter.$gte === o.value.$gte || (!filter.$gte && !o.value.$gte)) &&
59
114
  (filter.$lte === o.value.$lte || (!filter.$lte && !o.value.$lte)) &&
60
115
  (filter.$gt === o.value.$gt || (!filter.$gt && !o.value.$gt)) &&
61
116
  (filter.$lt === o.value.$lt || (!filter.$lt && !o.value.$lt)));
62
117
  })) === null || _d === void 0 ? void 0 : _d.label) !== null && _e !== void 0 ? _e : null;
118
+ // If no match found, it's a custom range
63
119
  if (!displayValue && isDateFilterProps(meta)) {
120
+ isCustomRange = true;
64
121
  const formatDateValue = meta.formatDateValue
65
122
  ? meta.formatDateValue
66
123
  : DEFAULT_FORMAT_DATE_VALUE;
67
124
  if (filter.$gte && !filter.$lte) {
68
- isCustomRange = true;
69
125
  displayValue = `${meta.rangeOptionStartLabel || DEFAULT_RANGE_OPTION_START_LABEL} ${formatDateValue(new Date(filter.$gte))}`;
70
126
  }
71
127
  if (filter.$lte && !filter.$gte) {
72
- isCustomRange = true;
73
128
  displayValue = `${meta.rangeOptionEndLabel || DEFAULT_RANGE_OPTION_END_LABEL} ${formatDateValue(new Date(filter.$lte))}`;
74
129
  }
75
130
  if (filter.$gte && filter.$lte) {
76
- isCustomRange = true;
77
131
  displayValue = `${formatDateValue(new Date(filter.$gte))} - ${formatDateValue(new Date(filter.$lte))}`;
78
132
  }
79
133
  }
80
134
  }
81
- return { displayValue, isCustomRange };
82
- }, [filter, options]);
83
- React.useEffect(() => {
84
- if (isCustomRange && !isCustom) {
85
- setIsCustom(true);
135
+ // Handle number comparison operators
136
+ if (typeof filter === "object" && filter !== null && !Array.isArray(filter) && !(0, is_date_comparison_operator_1.isDateComparisonOperator)(filter)) {
137
+ const operators = {
138
+ $eq: "=",
139
+ $gt: ">",
140
+ $gte: "≥",
141
+ $lt: "<",
142
+ $lte: "≤",
143
+ };
144
+ const op = Object.keys(filter)[0];
145
+ const opLabel = operators[op] || op;
146
+ const value = filter[op];
147
+ if (typeof value === "number") {
148
+ displayValue = `${opLabel} ${value}`;
149
+ }
86
150
  }
87
- }, [isCustomRange, isCustom]);
88
- if (!meta) {
89
- return null;
90
- }
151
+ return { displayValue, isCustomRange };
152
+ }, [filter, options, meta]);
91
153
  return (React.createElement(popover_1.Popover, { open: open, onOpenChange: onOpenChange, modal: true },
92
- React.createElement(popover_1.Popover.Anchor, { asChild: true },
93
- React.createElement("div", { className: (0, clx_1.clx)("bg-ui-bg-component flex flex-shrink-0 items-center overflow-hidden rounded-md", "[&>*]:txt-compact-small-plus [&>*]:flex [&>*]:items-center [&>*]:justify-center", {
94
- "shadow-borders-base divide-x": displayValue,
95
- "border border-dashed": !displayValue,
96
- }) },
97
- displayValue && (React.createElement("div", { className: "text-ui-fg-muted whitespace-nowrap px-2 py-1" }, label || id)),
98
- React.createElement(popover_1.Popover.Trigger, { className: (0, clx_1.clx)("text-ui-fg-subtle hover:bg-ui-bg-base-hover active:bg-ui-bg-base-pressed transition-fg whitespace-nowrap px-2 py-1 outline-none", {
99
- "text-ui-fg-muted": !displayValue,
100
- }) }, displayValue || label || id),
101
- displayValue && (React.createElement("button", { type: "button", className: "text-ui-fg-muted hover:bg-ui-bg-base-hover active:bg-ui-bg-base-pressed transition-fg size-7 outline-none", onClick: removeFilter },
154
+ React.createElement("div", { className: (0, clx_1.clx)("bg-ui-bg-field flex flex-shrink-0 items-stretch overflow-hidden rounded-md", "txt-compact-small-plus shadow-borders-base") },
155
+ !hasValue && isNew && React.createElement(popover_1.Popover.Anchor, null),
156
+ React.createElement("div", { className: (0, clx_1.clx)("flex items-center px-2 py-1 text-ui-fg-muted", {
157
+ "border-r": hasValue
158
+ }) }, label || id),
159
+ hasValue && (React.createElement(React.Fragment, null,
160
+ (type === "select" || type === "multiselect" || type === "radio") && (React.createElement("div", { className: "flex items-center border-r px-2 py-1 text-ui-fg-muted" }, "is")),
161
+ React.createElement(popover_1.Popover.Trigger, { asChild: true },
162
+ React.createElement("button", { className: (0, clx_1.clx)("flex flex-1 items-center px-2 py-1 outline-none", "hover:bg-ui-bg-base-hover active:bg-ui-bg-base-pressed transition-fg", {
163
+ "text-ui-fg-subtle": displayValue,
164
+ "text-ui-fg-muted": !displayValue,
165
+ "min-w-[80px] justify-center": !displayValue,
166
+ "border-r": true
167
+ }) }, displayValue || "\u00A0")),
168
+ React.createElement("button", { type: "button", className: "flex size-7 items-center justify-center text-ui-fg-muted outline-none hover:bg-ui-bg-base-hover active:bg-ui-bg-base-pressed transition-fg", onClick: removeFilter },
102
169
  React.createElement(icons_1.XMark, null))))),
103
- React.createElement(popover_1.Popover.Content, { align: "start", className: "bg-ui-bg-component p-0 outline-none" }, (() => {
170
+ React.createElement(popover_1.Popover.Content, { align: "start", sideOffset: 8, collisionPadding: 16, hideWhenDetached: true, className: "bg-ui-bg-component p-0 outline-none", onOpenAutoFocus: (e) => {
171
+ if (isNew) {
172
+ // For new filters, ensure the first input gets focus
173
+ const target = e.currentTarget;
174
+ if (target) {
175
+ const firstInput = target.querySelector('input:not([type="hidden"]), [role="list"][tabindex="0"]');
176
+ firstInput === null || firstInput === void 0 ? void 0 : firstInput.focus();
177
+ }
178
+ }
179
+ }, onCloseAutoFocus: (e) => {
180
+ // Prevent focus from going to the trigger when closing
181
+ e.preventDefault();
182
+ }, onInteractOutside: (e) => {
183
+ // Check if the click is on a filter menu item
184
+ const target = e.target;
185
+ if (target.closest('[role="menuitem"]')) {
186
+ e.preventDefault();
187
+ }
188
+ } }, (() => {
104
189
  switch (type) {
105
190
  case "select":
106
- return (React.createElement(DataTableFilterSelectContent, { id: id, filter: filter, options: options }));
191
+ return (React.createElement(DataTableFilterSelectContent, { id: id, filter: filter, options: options, isNew: isNew, onUpdate: onUpdate }));
107
192
  case "radio":
108
- return (React.createElement(DataTableFilterRadioContent, { id: id, filter: filter, options: options }));
193
+ return (React.createElement(DataTableFilterRadioContent, { id: id, filter: filter, options: options, onUpdate: onUpdate }));
109
194
  case "date":
110
- return (React.createElement(DataTableFilterDateContent, { id: id, filter: filter, options: options, isCustom: isCustom, setIsCustom: setIsCustom, ...rest }));
195
+ const dateRest = rest;
196
+ return (React.createElement(DataTableFilterDateContent, { id: id, filter: filter, options: options, isCustomRange: isCustomRange, format: dateRest.format, rangeOptionLabel: dateRest.rangeOptionLabel, disableRangeOption: dateRest.disableRangeOption, rangeOptionStartLabel: dateRest.rangeOptionStartLabel, rangeOptionEndLabel: dateRest.rangeOptionEndLabel, onUpdate: onUpdate }));
197
+ case "multiselect":
198
+ const multiselectRest = rest;
199
+ return (React.createElement(DataTableFilterMultiselectContent, { id: id, filter: filter, options: options, searchable: multiselectRest.searchable, onUpdate: onUpdate }));
200
+ case "string":
201
+ const stringRest = rest;
202
+ return (React.createElement(DataTableFilterStringContent, { id: id, filter: filter, placeholder: stringRest.placeholder, onUpdate: onUpdate }));
203
+ case "number":
204
+ const numberRest = rest;
205
+ return (React.createElement(DataTableFilterNumberContent, { id: id, filter: filter, placeholder: numberRest.placeholder, includeOperators: numberRest.includeOperators, onUpdate: onUpdate }));
206
+ case "custom":
207
+ const customRest = rest;
208
+ return (React.createElement(DataTableFilterCustomContent, { id: id, filter: filter, onRemove: removeFilter, render: customRest.render, onUpdate: onUpdate }));
111
209
  default:
112
210
  return null;
113
211
  }
@@ -115,9 +213,14 @@ const DataTableFilter = ({ id, filter }) => {
115
213
  };
116
214
  exports.DataTableFilter = DataTableFilter;
117
215
  DataTableFilter.displayName = "DataTable.Filter";
118
- const DataTableFilterDateContent = ({ id, filter, options, format = "date", rangeOptionLabel = DEFAULT_RANGE_OPTION_LABEL, rangeOptionStartLabel = DEFAULT_RANGE_OPTION_START_LABEL, rangeOptionEndLabel = DEFAULT_RANGE_OPTION_END_LABEL, disableRangeOption = false, isCustom, setIsCustom, }) => {
216
+ const DataTableFilterDateContent = ({ id, filter, options, format = "date", rangeOptionLabel = DEFAULT_RANGE_OPTION_LABEL, rangeOptionStartLabel = DEFAULT_RANGE_OPTION_START_LABEL, rangeOptionEndLabel = DEFAULT_RANGE_OPTION_END_LABEL, disableRangeOption = false, isCustomRange, onUpdate, }) => {
119
217
  const currentValue = filter;
120
218
  const { instance } = (0, use_data_table_context_1.useDataTableContext)();
219
+ const [isCustom, setIsCustom] = React.useState(isCustomRange);
220
+ // Sync isCustom state when isCustomRange changes
221
+ React.useEffect(() => {
222
+ setIsCustom(isCustomRange);
223
+ }, [isCustomRange]);
121
224
  const selectedValue = React.useMemo(() => {
122
225
  if (!currentValue || isCustom) {
123
226
  return undefined;
@@ -127,17 +230,27 @@ const DataTableFilterDateContent = ({ id, filter, options, format = "date", rang
127
230
  const onValueChange = React.useCallback((valueStr) => {
128
231
  setIsCustom(false);
129
232
  const value = JSON.parse(valueStr);
130
- instance.updateFilter({ id, value });
131
- }, [instance, id]);
233
+ if (onUpdate) {
234
+ onUpdate(value);
235
+ }
236
+ else {
237
+ instance.updateFilter({ id, value });
238
+ }
239
+ }, [instance, id, onUpdate]);
132
240
  const onSelectCustom = React.useCallback(() => {
133
241
  setIsCustom(true);
134
- instance.updateFilter({ id, value: undefined });
135
- }, [instance, id]);
242
+ // Don't clear the value when selecting custom - keep the current value
243
+ }, []);
136
244
  const onCustomValueChange = React.useCallback((input, value) => {
137
245
  const newCurrentValue = { ...currentValue };
138
246
  newCurrentValue[input] = value ? value.toISOString() : undefined;
139
- instance.updateFilter({ id, value: newCurrentValue });
140
- }, [instance, id]);
247
+ if (onUpdate) {
248
+ onUpdate(newCurrentValue);
249
+ }
250
+ else {
251
+ instance.updateFilter({ id, value: newCurrentValue });
252
+ }
253
+ }, [instance, id, currentValue, onUpdate]);
141
254
  const { focusedIndex, setFocusedIndex } = useKeyboardNavigation(options, (index) => {
142
255
  if (index === options.length && !disableRangeOption) {
143
256
  onSelectCustom();
@@ -186,39 +299,67 @@ const DataTableFilterDateContent = ({ id, filter, options, format = "date", rang
186
299
  React.createElement(label_1.Label, { id: "custom-end-date-label", size: "xsmall", weight: "plus" }, rangeOptionEndLabel),
187
300
  React.createElement(date_picker_1.DatePicker, { "aria-labelledby": "custom-end-date-label", granularity: granularity, minValue: minDate, value: (currentValue === null || currentValue === void 0 ? void 0 : currentValue.$lte) ? new Date(currentValue.$lte) : null, onChange: (value) => onCustomValueChange("$lte", value) })))))));
188
301
  };
189
- const DataTableFilterSelectContent = ({ id, filter = [], options, }) => {
302
+ const DataTableFilterSelectContent = ({ id, filter = [], options, isNew = false, onUpdate, }) => {
190
303
  const { instance } = (0, use_data_table_context_1.useDataTableContext)();
304
+ const [search, setSearch] = React.useState("");
305
+ const filteredOptions = React.useMemo(() => {
306
+ if (!search)
307
+ return options;
308
+ const searchLower = search.toLowerCase();
309
+ return options.filter(opt => opt.label.toLowerCase().includes(searchLower));
310
+ }, [options, search]);
191
311
  const onValueChange = React.useCallback((value) => {
192
312
  if (filter === null || filter === void 0 ? void 0 : filter.includes(value)) {
193
313
  const newValues = filter === null || filter === void 0 ? void 0 : filter.filter((v) => v !== value);
194
- instance.updateFilter({
195
- id,
196
- value: newValues,
197
- });
314
+ const newValue = newValues.length > 0 ? newValues : undefined;
315
+ if (onUpdate) {
316
+ onUpdate(newValue);
317
+ }
318
+ else {
319
+ instance.updateFilter({
320
+ id,
321
+ value: newValue,
322
+ });
323
+ }
198
324
  }
199
325
  else {
200
- instance.updateFilter({
201
- id,
202
- value: [...(filter !== null && filter !== void 0 ? filter : []), value],
203
- });
204
- }
205
- }, [instance, id, filter]);
206
- const { focusedIndex, setFocusedIndex } = useKeyboardNavigation(options, (index) => onValueChange(options[index].value));
207
- const onListFocus = React.useCallback(() => {
208
- if (focusedIndex === -1) {
209
- setFocusedIndex(0);
326
+ const newValue = [...(filter !== null && filter !== void 0 ? filter : []), value];
327
+ if (onUpdate) {
328
+ onUpdate(newValue);
329
+ }
330
+ else {
331
+ instance.updateFilter({
332
+ id,
333
+ value: newValue,
334
+ });
335
+ }
210
336
  }
211
- }, [focusedIndex]);
212
- return (React.createElement("div", { className: "flex flex-col p-1 outline-none", role: "list", tabIndex: 0, onFocus: onListFocus, autoFocus: true }, options.map((option, idx) => {
213
- const isSelected = !!(filter === null || filter === void 0 ? void 0 : filter.includes(option.value));
214
- return (React.createElement(OptionButton, { key: idx, index: idx, option: option, isSelected: isSelected, isFocused: focusedIndex === idx, onClick: () => onValueChange(option.value), onMouseEvent: setFocusedIndex, icon: icons_1.CheckMini }));
215
- })));
337
+ }, [instance, id, filter, onUpdate]);
338
+ return (React.createElement("div", { className: "w-[250px]" },
339
+ React.createElement("div", { className: "flex items-center gap-x-2 border-b px-3 py-1.5" },
340
+ React.createElement(icons_1.MagnifyingGlass, { className: "h-4 w-4 text-ui-fg-muted" }),
341
+ React.createElement("input", { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search...", className: "h-8 flex-1 bg-transparent text-sm outline-none placeholder:text-ui-fg-muted", autoFocus: true }),
342
+ search && (React.createElement("button", { onClick: () => setSearch(""), className: "text-ui-fg-muted hover:text-ui-fg-subtle" },
343
+ React.createElement(icons_1.XMarkMini, { className: "h-4 w-4" })))),
344
+ React.createElement("div", { className: "max-h-[300px] overflow-auto p-1" },
345
+ filteredOptions.length === 0 && (React.createElement("div", { className: "py-6 text-center text-sm text-ui-fg-muted" }, "No results found")),
346
+ filteredOptions.map(option => {
347
+ const isSelected = filter === null || filter === void 0 ? void 0 : filter.includes(option.value);
348
+ return (React.createElement("button", { key: String(option.value), onClick: () => onValueChange(option.value), className: (0, clx_1.clx)("flex w-full cursor-pointer items-center gap-x-2 rounded-md px-2 py-1.5 text-sm text-left", "hover:bg-ui-bg-base-hover") },
349
+ React.createElement("div", { className: "flex size-[15px] items-center justify-center" }, isSelected && React.createElement(icons_1.CheckMini, null)),
350
+ React.createElement("span", null, option.label)));
351
+ }))));
216
352
  };
217
- const DataTableFilterRadioContent = ({ id, filter, options, }) => {
353
+ const DataTableFilterRadioContent = ({ id, filter, options, onUpdate, }) => {
218
354
  const { instance } = (0, use_data_table_context_1.useDataTableContext)();
219
355
  const onValueChange = React.useCallback((value) => {
220
- instance.updateFilter({ id, value });
221
- }, [instance, id]);
356
+ if (onUpdate) {
357
+ onUpdate(value);
358
+ }
359
+ else {
360
+ instance.updateFilter({ id, value });
361
+ }
362
+ }, [instance, id, onUpdate]);
222
363
  const { focusedIndex, setFocusedIndex } = useKeyboardNavigation(options, (index) => onValueChange(options[index].value));
223
364
  const onListFocus = React.useCallback(() => {
224
365
  if (focusedIndex === -1) {
@@ -236,6 +377,30 @@ function isDateFilterProps(props) {
236
377
  }
237
378
  return props.type === "date";
238
379
  }
380
+ function isMultiselectFilterProps(props) {
381
+ if (!props) {
382
+ return false;
383
+ }
384
+ return props.type === "multiselect";
385
+ }
386
+ function isStringFilterProps(props) {
387
+ if (!props) {
388
+ return false;
389
+ }
390
+ return props.type === "string";
391
+ }
392
+ function isNumberFilterProps(props) {
393
+ if (!props) {
394
+ return false;
395
+ }
396
+ return props.type === "number";
397
+ }
398
+ function isCustomFilterProps(props) {
399
+ if (!props) {
400
+ return false;
401
+ }
402
+ return props.type === "custom";
403
+ }
239
404
  const OptionButton = React.memo(({ index, option, isSelected, isFocused, onClick, onMouseEvent, icon: Icon, }) => (React.createElement("button", { type: "button", role: "listitem", className: (0, clx_1.clx)("bg-ui-bg-component txt-compact-small transition-fg flex items-center gap-2 rounded px-2 py-1 outline-none", { "bg-ui-bg-component-hover": isFocused }), onClick: onClick, onMouseEnter: () => onMouseEvent(index), onMouseLeave: () => onMouseEvent(-1), tabIndex: -1 },
240
405
  React.createElement("div", { className: "flex size-[15px] items-center justify-center" }, isSelected && React.createElement(Icon, null)),
241
406
  React.createElement("span", null, option.label))));
@@ -272,4 +437,254 @@ function useKeyboardNavigation(options, onSelect, extraItems = 0) {
272
437
  }, [onKeyDown]);
273
438
  return { focusedIndex, setFocusedIndex };
274
439
  }
440
+ const DataTableFilterMultiselectContent = ({ id, filter = [], options, searchable = true, onUpdate, }) => {
441
+ const { instance } = (0, use_data_table_context_1.useDataTableContext)();
442
+ const [search, setSearch] = React.useState("");
443
+ const filteredOptions = React.useMemo(() => {
444
+ if (!searchable || !search)
445
+ return options;
446
+ const searchLower = search.toLowerCase();
447
+ return options.filter(opt => opt.label.toLowerCase().includes(searchLower));
448
+ }, [options, search, searchable]);
449
+ const onValueChange = React.useCallback((value) => {
450
+ if (filter === null || filter === void 0 ? void 0 : filter.includes(value)) {
451
+ const newValues = filter === null || filter === void 0 ? void 0 : filter.filter((v) => v !== value);
452
+ const newValue = newValues.length > 0 ? newValues : undefined;
453
+ if (onUpdate) {
454
+ onUpdate(newValue);
455
+ }
456
+ else {
457
+ instance.updateFilter({
458
+ id,
459
+ value: newValue,
460
+ });
461
+ }
462
+ }
463
+ else {
464
+ const newValue = [...(filter !== null && filter !== void 0 ? filter : []), value];
465
+ if (onUpdate) {
466
+ onUpdate(newValue);
467
+ }
468
+ else {
469
+ instance.updateFilter({
470
+ id,
471
+ value: newValue,
472
+ });
473
+ }
474
+ }
475
+ }, [instance, id, filter, onUpdate]);
476
+ if (!searchable) {
477
+ return (React.createElement("div", { className: "w-[250px]" },
478
+ React.createElement("div", { className: "max-h-[300px] overflow-auto p-1" }, options.map(option => {
479
+ const isSelected = filter === null || filter === void 0 ? void 0 : filter.includes(option.value);
480
+ return (React.createElement("button", { key: String(option.value), onClick: () => onValueChange(option.value), className: (0, clx_1.clx)("flex w-full items-center gap-x-2 rounded-md px-2 py-1.5 text-sm", "hover:bg-ui-bg-base-hover cursor-pointer text-left") },
481
+ React.createElement(checkbox_1.Checkbox, { checked: isSelected, className: "pointer-events-none" }),
482
+ React.createElement("span", null, option.label)));
483
+ }))));
484
+ }
485
+ return (React.createElement("div", { className: "w-[250px]" },
486
+ React.createElement("div", { className: "flex items-center gap-x-2 border-b px-3 py-1.5" },
487
+ React.createElement(icons_1.MagnifyingGlass, { className: "h-4 w-4 text-ui-fg-muted" }),
488
+ React.createElement("input", { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search...", className: "h-8 flex-1 bg-transparent text-sm outline-none placeholder:text-ui-fg-muted", autoFocus: true }),
489
+ search && (React.createElement("button", { onClick: () => setSearch(""), className: "text-ui-fg-muted hover:text-ui-fg-subtle" },
490
+ React.createElement(icons_1.XMarkMini, { className: "h-4 w-4" })))),
491
+ React.createElement("div", { className: "max-h-[300px] overflow-auto p-1" },
492
+ filteredOptions.length === 0 && (React.createElement("div", { className: "py-6 text-center text-sm text-ui-fg-muted" }, "No results found")),
493
+ filteredOptions.map(option => {
494
+ const isSelected = filter === null || filter === void 0 ? void 0 : filter.includes(option.value);
495
+ return (React.createElement("button", { key: String(option.value), onClick: () => onValueChange(option.value), className: (0, clx_1.clx)("flex w-full cursor-pointer items-center gap-x-2 rounded-md px-2 py-1.5 text-sm text-left", "hover:bg-ui-bg-base-hover") },
496
+ React.createElement(checkbox_1.Checkbox, { checked: isSelected, className: "pointer-events-none" }),
497
+ React.createElement("span", null, option.label)));
498
+ }))));
499
+ };
500
+ const DataTableFilterStringContent = ({ id, filter, placeholder = "Enter value...", onUpdate, }) => {
501
+ const { instance } = (0, use_data_table_context_1.useDataTableContext)();
502
+ const [value, setValue] = React.useState(filter || "");
503
+ const timeoutRef = React.useRef(null);
504
+ const handleChange = React.useCallback((newValue) => {
505
+ setValue(newValue);
506
+ // Clear existing timeout
507
+ if (timeoutRef.current) {
508
+ clearTimeout(timeoutRef.current);
509
+ }
510
+ // Debounce the update
511
+ timeoutRef.current = setTimeout(() => {
512
+ const updateValue = newValue.trim() || undefined;
513
+ if (onUpdate) {
514
+ onUpdate(updateValue);
515
+ }
516
+ else {
517
+ instance.updateFilter({
518
+ id,
519
+ value: updateValue,
520
+ });
521
+ }
522
+ }, 500);
523
+ }, [instance, id, onUpdate]);
524
+ // Cleanup timeout on unmount
525
+ React.useEffect(() => {
526
+ return () => {
527
+ if (timeoutRef.current) {
528
+ clearTimeout(timeoutRef.current);
529
+ }
530
+ };
531
+ }, []);
532
+ const handleKeyDown = React.useCallback((e) => {
533
+ if (e.key === "Enter") {
534
+ // Clear timeout and apply immediately
535
+ if (timeoutRef.current) {
536
+ clearTimeout(timeoutRef.current);
537
+ }
538
+ const updateValue = value.trim() || undefined;
539
+ if (onUpdate) {
540
+ onUpdate(updateValue);
541
+ }
542
+ else {
543
+ instance.updateFilter({
544
+ id,
545
+ value: updateValue,
546
+ });
547
+ }
548
+ }
549
+ }, [instance, id, value, onUpdate]);
550
+ return (React.createElement("div", { className: "p-3 w-[250px]" },
551
+ React.createElement(input_1.Input, { placeholder: placeholder, value: value, onChange: (e) => handleChange(e.target.value), onKeyDown: handleKeyDown, autoFocus: true })));
552
+ };
553
+ const DataTableFilterNumberContent = ({ id, filter, placeholder = "Enter number...", includeOperators = true, onUpdate, }) => {
554
+ const { instance } = (0, use_data_table_context_1.useDataTableContext)();
555
+ const [operator, setOperator] = React.useState("eq");
556
+ const [value, setValue] = React.useState("");
557
+ const timeoutRef = React.useRef(null);
558
+ React.useEffect(() => {
559
+ if (filter) {
560
+ if (typeof filter === "number") {
561
+ setOperator("eq");
562
+ setValue(String(filter));
563
+ }
564
+ else if (typeof filter === "object") {
565
+ const op = Object.keys(filter)[0];
566
+ setOperator(op.replace("$", ""));
567
+ setValue(String(filter[op]));
568
+ }
569
+ }
570
+ }, [filter]);
571
+ const handleValueChange = React.useCallback((newValue) => {
572
+ setValue(newValue);
573
+ // Clear existing timeout
574
+ if (timeoutRef.current) {
575
+ clearTimeout(timeoutRef.current);
576
+ }
577
+ // Debounce the update
578
+ timeoutRef.current = setTimeout(() => {
579
+ const num = parseFloat(newValue);
580
+ if (!isNaN(num)) {
581
+ const filterValue = includeOperators && operator !== "eq"
582
+ ? { [`$${operator}`]: num }
583
+ : num;
584
+ if (onUpdate) {
585
+ onUpdate(filterValue);
586
+ }
587
+ else {
588
+ instance.updateFilter({
589
+ id,
590
+ value: filterValue,
591
+ });
592
+ }
593
+ }
594
+ else if (newValue === "") {
595
+ if (onUpdate) {
596
+ onUpdate(undefined);
597
+ }
598
+ else {
599
+ instance.updateFilter({
600
+ id,
601
+ value: undefined,
602
+ });
603
+ }
604
+ }
605
+ }, 500);
606
+ }, [instance, id, operator, includeOperators, onUpdate]);
607
+ const handleOperatorChange = React.useCallback((newOperator) => {
608
+ setOperator(newOperator);
609
+ // If we have a value, update immediately with new operator
610
+ const num = parseFloat(value);
611
+ if (!isNaN(num)) {
612
+ const filterValue = includeOperators && newOperator !== "eq"
613
+ ? { [`$${newOperator}`]: num }
614
+ : num;
615
+ if (onUpdate) {
616
+ onUpdate(filterValue);
617
+ }
618
+ else {
619
+ instance.updateFilter({
620
+ id,
621
+ value: filterValue,
622
+ });
623
+ }
624
+ }
625
+ }, [instance, id, value, includeOperators, onUpdate]);
626
+ // Cleanup timeout on unmount
627
+ React.useEffect(() => {
628
+ return () => {
629
+ if (timeoutRef.current) {
630
+ clearTimeout(timeoutRef.current);
631
+ }
632
+ };
633
+ }, []);
634
+ const handleKeyDown = React.useCallback((e) => {
635
+ if (e.key === "Enter") {
636
+ // Clear timeout and apply immediately
637
+ if (timeoutRef.current) {
638
+ clearTimeout(timeoutRef.current);
639
+ }
640
+ const num = parseFloat(value);
641
+ if (!isNaN(num)) {
642
+ const filterValue = includeOperators && operator !== "eq"
643
+ ? { [`$${operator}`]: num }
644
+ : num;
645
+ if (onUpdate) {
646
+ onUpdate(filterValue);
647
+ }
648
+ else {
649
+ instance.updateFilter({
650
+ id,
651
+ value: filterValue,
652
+ });
653
+ }
654
+ }
655
+ }
656
+ }, [instance, id, value, operator, includeOperators, onUpdate]);
657
+ const operators = [
658
+ { value: "eq", label: "Equals" },
659
+ { value: "gt", label: "Greater than" },
660
+ { value: "gte", label: "Greater than or equal" },
661
+ { value: "lt", label: "Less than" },
662
+ { value: "lte", label: "Less than or equal" },
663
+ ];
664
+ return (React.createElement("div", { className: "p-3 space-y-3 w-[250px]" },
665
+ includeOperators && (React.createElement(select_1.Select, { value: operator, onValueChange: handleOperatorChange },
666
+ React.createElement(select_1.Select.Trigger, null,
667
+ React.createElement(select_1.Select.Value, null)),
668
+ React.createElement(select_1.Select.Content, null, operators.map(op => (React.createElement(select_1.Select.Item, { key: op.value, value: op.value }, op.label)))))),
669
+ React.createElement(input_1.Input, { type: "number", placeholder: placeholder, value: value, onChange: (e) => handleValueChange(e.target.value), onKeyDown: handleKeyDown, autoFocus: !includeOperators })));
670
+ };
671
+ const DataTableFilterCustomContent = ({ id, filter, onRemove, render, onUpdate, }) => {
672
+ const { instance } = (0, use_data_table_context_1.useDataTableContext)();
673
+ const handleChange = React.useCallback((value) => {
674
+ if (onUpdate) {
675
+ onUpdate(value);
676
+ }
677
+ else {
678
+ instance.updateFilter({
679
+ id,
680
+ value,
681
+ });
682
+ }
683
+ }, [instance, id, onUpdate]);
684
+ return (React.createElement(React.Fragment, null, render({
685
+ value: filter,
686
+ onChange: handleChange,
687
+ onRemove,
688
+ })));
689
+ };
275
690
  //# sourceMappingURL=data-table-filter.js.map