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