@kopexa/filter 0.0.25 → 0.0.27
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.
- package/dist/chunk-6TRAIAKS.mjs +178 -0
- package/dist/chunk-I7WCYMXD.mjs +133 -0
- package/dist/{chunk-45QJL74L.mjs → chunk-JSRGUDCG.mjs} +49 -24
- package/dist/chunk-NUDUXOHP.mjs +243 -0
- package/dist/chunk-ON2UFJ3Y.mjs +32 -0
- package/dist/{chunk-RFCPJLIQ.mjs → chunk-SJXRD3RO.mjs} +6 -1
- package/dist/{chunk-EF4VI36D.mjs → chunk-UBTUCPOG.mjs} +1 -1
- package/dist/{chunk-3ZBNWXRA.mjs → chunk-WD7YU6IN.mjs} +17 -14
- package/dist/chunk-XCWKWXBW.mjs +602 -0
- package/dist/{chunk-URDCG5NI.mjs → chunk-YTYOFT33.mjs} +52 -0
- package/dist/filter-active.js +108 -28
- package/dist/filter-active.mjs +4 -4
- package/dist/filter-bar-internal.d.mts +25 -0
- package/dist/filter-bar-internal.d.ts +25 -0
- package/dist/filter-bar-internal.js +648 -0
- package/dist/filter-bar-internal.mjs +11 -0
- package/dist/filter-bar-messages.d.mts +124 -0
- package/dist/filter-bar-messages.d.ts +124 -0
- package/dist/filter-bar-messages.js +156 -0
- package/dist/filter-bar-messages.mjs +7 -0
- package/dist/filter-bar-types.d.mts +112 -0
- package/dist/filter-bar-types.d.ts +112 -0
- package/dist/filter-bar-types.js +56 -0
- package/dist/filter-bar-types.mjs +8 -0
- package/dist/filter-bar.d.mts +29 -0
- package/dist/filter-bar.d.ts +29 -0
- package/dist/filter-bar.js +942 -0
- package/dist/filter-bar.mjs +11 -0
- package/dist/filter-menu.js +161 -1
- package/dist/filter-menu.mjs +2 -1
- package/dist/filter-trigger.js +52 -0
- package/dist/filter-trigger.mjs +2 -2
- package/dist/filter-value-editor.js +65 -10
- package/dist/filter-value-editor.mjs +3 -3
- package/dist/filter.d.mts +6 -0
- package/dist/filter.d.ts +6 -0
- package/dist/filter.mjs +2 -2
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1263 -31
- package/dist/index.mjs +28 -14
- package/dist/messages.d.mts +50 -0
- package/dist/messages.d.ts +50 -0
- package/dist/messages.js +52 -0
- package/dist/messages.mjs +1 -1
- package/dist/search-filter-bar.d.mts +44 -0
- package/dist/search-filter-bar.d.ts +44 -0
- package/dist/search-filter-bar.js +1007 -0
- package/dist/search-filter-bar.mjs +11 -0
- package/package.json +19 -16
- package/dist/{chunk-SH7DBK54.mjs → chunk-LWDVRMCI.mjs} +3 -3
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
AddFilterDropdown,
|
|
4
|
+
FilterPill
|
|
5
|
+
} from "./chunk-XCWKWXBW.mjs";
|
|
6
|
+
import {
|
|
7
|
+
filterBarMessages
|
|
8
|
+
} from "./chunk-I7WCYMXD.mjs";
|
|
9
|
+
|
|
10
|
+
// src/search-filter-bar.tsx
|
|
11
|
+
import { useSafeIntl } from "@kopexa/i18n";
|
|
12
|
+
import { SearchIcon } from "@kopexa/icons";
|
|
13
|
+
import { cn } from "@kopexa/shared-utils";
|
|
14
|
+
import { Spinner } from "@kopexa/spinner";
|
|
15
|
+
import { useControllableState } from "@kopexa/use-controllable-state";
|
|
16
|
+
import { useDebounceCallback } from "@kopexa/use-debounced-callback";
|
|
17
|
+
import { useCallback, useId, useMemo, useState } from "react";
|
|
18
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
function SearchFilterBar(props) {
|
|
20
|
+
const {
|
|
21
|
+
fields,
|
|
22
|
+
filters: filtersProp,
|
|
23
|
+
defaultFilters = [],
|
|
24
|
+
onFiltersChange,
|
|
25
|
+
search: searchProp,
|
|
26
|
+
defaultSearch = "",
|
|
27
|
+
onSearchChange,
|
|
28
|
+
onSearchValueChange,
|
|
29
|
+
searchDebounce = 300,
|
|
30
|
+
searchPlaceholder,
|
|
31
|
+
searchLoading = false,
|
|
32
|
+
i18n: i18nProp,
|
|
33
|
+
className,
|
|
34
|
+
allowMultiple = true,
|
|
35
|
+
disabled = false,
|
|
36
|
+
endContent
|
|
37
|
+
} = props;
|
|
38
|
+
const t = useSafeIntl();
|
|
39
|
+
const instanceId = useId();
|
|
40
|
+
const [filters, setFilters] = useControllableState({
|
|
41
|
+
value: filtersProp,
|
|
42
|
+
defaultValue: defaultFilters,
|
|
43
|
+
onChange: onFiltersChange
|
|
44
|
+
});
|
|
45
|
+
const [search, setSearch] = useControllableState({
|
|
46
|
+
value: searchProp,
|
|
47
|
+
defaultValue: defaultSearch,
|
|
48
|
+
onChange: onSearchChange
|
|
49
|
+
});
|
|
50
|
+
const [sessionFilterId, setSessionFilterId] = useState(null);
|
|
51
|
+
const i18n = useMemo(() => {
|
|
52
|
+
const defaultI18n = {
|
|
53
|
+
addFilter: t.formatMessage(filterBarMessages.add_filter),
|
|
54
|
+
clearAll: t.formatMessage(filterBarMessages.clear_all),
|
|
55
|
+
noResults: t.formatMessage(filterBarMessages.no_results),
|
|
56
|
+
searchPlaceholder: t.formatMessage(filterBarMessages.search_placeholder),
|
|
57
|
+
selectValue: t.formatMessage(filterBarMessages.select_value),
|
|
58
|
+
enterValue: t.formatMessage(filterBarMessages.enter_value),
|
|
59
|
+
operators: {
|
|
60
|
+
equals: t.formatMessage(filterBarMessages.op_equals),
|
|
61
|
+
not_equals: t.formatMessage(filterBarMessages.op_not_equals),
|
|
62
|
+
contains: t.formatMessage(filterBarMessages.op_contains),
|
|
63
|
+
not_contains: t.formatMessage(filterBarMessages.op_not_contains),
|
|
64
|
+
starts_with: t.formatMessage(filterBarMessages.op_starts_with),
|
|
65
|
+
ends_with: t.formatMessage(filterBarMessages.op_ends_with),
|
|
66
|
+
gt: t.formatMessage(filterBarMessages.op_gt),
|
|
67
|
+
lt: t.formatMessage(filterBarMessages.op_lt),
|
|
68
|
+
gte: t.formatMessage(filterBarMessages.op_gte),
|
|
69
|
+
lte: t.formatMessage(filterBarMessages.op_lte),
|
|
70
|
+
is_empty: t.formatMessage(filterBarMessages.op_is_empty),
|
|
71
|
+
is_not_empty: t.formatMessage(filterBarMessages.op_is_not_empty)
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
...defaultI18n,
|
|
76
|
+
...i18nProp,
|
|
77
|
+
operators: {
|
|
78
|
+
...defaultI18n.operators,
|
|
79
|
+
...i18nProp == null ? void 0 : i18nProp.operators
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}, [t, i18nProp]);
|
|
83
|
+
const fieldGroups = useMemo(() => {
|
|
84
|
+
var _a;
|
|
85
|
+
const groups = /* @__PURE__ */ new Map();
|
|
86
|
+
for (const field of fields) {
|
|
87
|
+
const group = field.group;
|
|
88
|
+
if (!groups.has(group)) {
|
|
89
|
+
groups.set(group, []);
|
|
90
|
+
}
|
|
91
|
+
(_a = groups.get(group)) == null ? void 0 : _a.push(field);
|
|
92
|
+
}
|
|
93
|
+
return groups;
|
|
94
|
+
}, [fields]);
|
|
95
|
+
const addFilter = useCallback(
|
|
96
|
+
(fieldId) => {
|
|
97
|
+
var _a;
|
|
98
|
+
const field = fields.find((f) => f.id === fieldId);
|
|
99
|
+
if (!field) return;
|
|
100
|
+
if (!allowMultiple && filters.some((f) => f.fieldId === fieldId)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const newFilter = {
|
|
104
|
+
id: `${instanceId}-${fieldId}-${Date.now()}`,
|
|
105
|
+
fieldId,
|
|
106
|
+
operator: (_a = field.defaultOperator) != null ? _a : "equals",
|
|
107
|
+
value: field.type === "multiselect" ? [] : null
|
|
108
|
+
};
|
|
109
|
+
setFilters([...filters, newFilter]);
|
|
110
|
+
setSessionFilterId(newFilter.id);
|
|
111
|
+
},
|
|
112
|
+
[fields, filters, setFilters, allowMultiple, instanceId]
|
|
113
|
+
);
|
|
114
|
+
const updateFilter = useCallback(
|
|
115
|
+
(filterId, updates) => {
|
|
116
|
+
setFilters(
|
|
117
|
+
filters.map((f) => f.id === filterId ? { ...f, ...updates } : f)
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
[filters, setFilters]
|
|
121
|
+
);
|
|
122
|
+
const removeFilter = useCallback(
|
|
123
|
+
(filterId) => {
|
|
124
|
+
setFilters(filters.filter((f) => f.id !== filterId));
|
|
125
|
+
if (sessionFilterId === filterId) {
|
|
126
|
+
setSessionFilterId(null);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
[filters, setFilters, sessionFilterId]
|
|
130
|
+
);
|
|
131
|
+
const clearFilters = useCallback(() => {
|
|
132
|
+
setFilters([]);
|
|
133
|
+
setSessionFilterId(null);
|
|
134
|
+
}, [setFilters]);
|
|
135
|
+
const isFieldAvailable = useCallback(
|
|
136
|
+
(fieldId) => {
|
|
137
|
+
if (allowMultiple) return true;
|
|
138
|
+
return !filters.some((f) => f.fieldId === fieldId);
|
|
139
|
+
},
|
|
140
|
+
[filters, allowMultiple]
|
|
141
|
+
);
|
|
142
|
+
const debouncedSearchCallback = useDebounceCallback((value) => {
|
|
143
|
+
onSearchValueChange == null ? void 0 : onSearchValueChange(value);
|
|
144
|
+
}, searchDebounce);
|
|
145
|
+
const handleSearchChange = useCallback(
|
|
146
|
+
(e) => {
|
|
147
|
+
const value = e.target.value;
|
|
148
|
+
setSearch(value);
|
|
149
|
+
if (onSearchValueChange) {
|
|
150
|
+
debouncedSearchCallback(value);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
[setSearch, onSearchValueChange, debouncedSearchCallback]
|
|
154
|
+
);
|
|
155
|
+
return /* @__PURE__ */ jsxs(
|
|
156
|
+
"div",
|
|
157
|
+
{
|
|
158
|
+
"data-slot": "search-filter-bar",
|
|
159
|
+
className: cn("flex items-center gap-2 w-full", className),
|
|
160
|
+
children: [
|
|
161
|
+
/* @__PURE__ */ jsxs(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
className: cn(
|
|
165
|
+
"flex flex-1 flex-wrap items-center gap-2",
|
|
166
|
+
"border border-input rounded-lg",
|
|
167
|
+
"bg-background",
|
|
168
|
+
"px-3 py-2",
|
|
169
|
+
"focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-1",
|
|
170
|
+
"transition-shadow",
|
|
171
|
+
disabled && "opacity-50 pointer-events-none"
|
|
172
|
+
),
|
|
173
|
+
children: [
|
|
174
|
+
filters.map((filter) => {
|
|
175
|
+
const field = fields.find((f) => f.id === filter.fieldId);
|
|
176
|
+
if (!field) return null;
|
|
177
|
+
return /* @__PURE__ */ jsx(
|
|
178
|
+
FilterPill,
|
|
179
|
+
{
|
|
180
|
+
filter,
|
|
181
|
+
field,
|
|
182
|
+
i18n,
|
|
183
|
+
onUpdate: (updates) => updateFilter(filter.id, updates),
|
|
184
|
+
onRemove: () => removeFilter(filter.id),
|
|
185
|
+
autoOpen: sessionFilterId === filter.id,
|
|
186
|
+
onAutoOpenComplete: () => setSessionFilterId(null),
|
|
187
|
+
disabled
|
|
188
|
+
},
|
|
189
|
+
filter.id
|
|
190
|
+
);
|
|
191
|
+
}),
|
|
192
|
+
/* @__PURE__ */ jsx(
|
|
193
|
+
AddFilterDropdown,
|
|
194
|
+
{
|
|
195
|
+
fieldGroups,
|
|
196
|
+
isFieldAvailable,
|
|
197
|
+
onAddFilter: addFilter,
|
|
198
|
+
i18n,
|
|
199
|
+
disabled
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
filters.length > 1 && /* @__PURE__ */ jsx(
|
|
203
|
+
"button",
|
|
204
|
+
{
|
|
205
|
+
type: "button",
|
|
206
|
+
onClick: clearFilters,
|
|
207
|
+
className: "text-xs text-muted-foreground hover:text-foreground cursor-pointer transition-colors underline-offset-2 hover:underline",
|
|
208
|
+
disabled,
|
|
209
|
+
children: i18n.clearAll
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2 min-w-[120px]", children: [
|
|
213
|
+
/* @__PURE__ */ jsx(SearchIcon, { className: "size-4 text-muted-foreground flex-shrink-0" }),
|
|
214
|
+
/* @__PURE__ */ jsx(
|
|
215
|
+
"input",
|
|
216
|
+
{
|
|
217
|
+
type: "text",
|
|
218
|
+
value: search,
|
|
219
|
+
onChange: handleSearchChange,
|
|
220
|
+
placeholder: searchPlaceholder != null ? searchPlaceholder : i18n.searchPlaceholder,
|
|
221
|
+
disabled,
|
|
222
|
+
className: cn(
|
|
223
|
+
"flex-1 bg-transparent border-none outline-none",
|
|
224
|
+
"text-sm placeholder:text-muted-foreground",
|
|
225
|
+
"min-w-0"
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
),
|
|
229
|
+
searchLoading && /* @__PURE__ */ jsx(Spinner, { size: "xs", className: "flex-shrink-0" })
|
|
230
|
+
] })
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
),
|
|
234
|
+
endContent
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
SearchFilterBar.displayName = "SearchFilterBar";
|
|
240
|
+
|
|
241
|
+
export {
|
|
242
|
+
SearchFilterBar
|
|
243
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/filter-bar-types.ts
|
|
4
|
+
var DEFAULT_FILTER_BAR_OPERATORS = {
|
|
5
|
+
select: ["equals", "not_equals", "is_empty", "is_not_empty"],
|
|
6
|
+
multiselect: ["equals", "not_equals", "is_empty", "is_not_empty"],
|
|
7
|
+
text: [
|
|
8
|
+
"equals",
|
|
9
|
+
"not_equals",
|
|
10
|
+
"contains",
|
|
11
|
+
"not_contains",
|
|
12
|
+
"starts_with",
|
|
13
|
+
"ends_with",
|
|
14
|
+
"is_empty",
|
|
15
|
+
"is_not_empty"
|
|
16
|
+
],
|
|
17
|
+
number: [
|
|
18
|
+
"equals",
|
|
19
|
+
"not_equals",
|
|
20
|
+
"gt",
|
|
21
|
+
"lt",
|
|
22
|
+
"gte",
|
|
23
|
+
"lte",
|
|
24
|
+
"is_empty",
|
|
25
|
+
"is_not_empty"
|
|
26
|
+
],
|
|
27
|
+
custom: ["equals", "not_equals"]
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
DEFAULT_FILTER_BAR_OPERATORS
|
|
32
|
+
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
messages
|
|
4
|
+
} from "./chunk-YTYOFT33.mjs";
|
|
2
5
|
import {
|
|
3
6
|
useFilterContext
|
|
4
7
|
} from "./chunk-I3Z2T4N2.mjs";
|
|
5
8
|
|
|
6
9
|
// src/filter-menu.tsx
|
|
7
10
|
import { DropdownMenu } from "@kopexa/dropdown-menu";
|
|
11
|
+
import { useSafeIntl } from "@kopexa/i18n";
|
|
8
12
|
import { cn } from "@kopexa/shared-utils";
|
|
9
13
|
import {
|
|
10
14
|
createContext,
|
|
@@ -16,6 +20,7 @@ function FilterMenu(props) {
|
|
|
16
20
|
var _a;
|
|
17
21
|
const { children, className } = props;
|
|
18
22
|
const { fields, styles, addFilter, allowMultiple, value } = useFilterContext();
|
|
23
|
+
const t = useSafeIntl();
|
|
19
24
|
const fieldGroups = /* @__PURE__ */ new Map();
|
|
20
25
|
for (const [id, field] of fields) {
|
|
21
26
|
const group = field.group;
|
|
@@ -32,7 +37,7 @@ function FilterMenu(props) {
|
|
|
32
37
|
"data-slot": "filter-menu",
|
|
33
38
|
className: cn("min-w-[220px]", className),
|
|
34
39
|
align: "start",
|
|
35
|
-
children: fields.size === 0 ? /* @__PURE__ */ jsx("div", { className: "px-2 py-3 text-sm text-muted-foreground text-center", children:
|
|
40
|
+
children: fields.size === 0 ? /* @__PURE__ */ 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__ */ jsxs(DropdownMenu.Group, { children: [
|
|
36
41
|
groupLabel && /* @__PURE__ */ jsx(DropdownMenu.Label, { children: groupLabel }),
|
|
37
42
|
Array.from(groupFields.values()).map((field) => {
|
|
38
43
|
const hasFilter = value.some((f) => f.fieldId === field.id);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_OPERATORS
|
|
4
|
+
} from "./chunk-PHESMHTT.mjs";
|
|
2
5
|
import {
|
|
3
6
|
messages
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YTYOFT33.mjs";
|
|
5
8
|
import {
|
|
6
9
|
useFilterContext
|
|
7
10
|
} from "./chunk-I3Z2T4N2.mjs";
|
|
8
|
-
import {
|
|
9
|
-
DEFAULT_OPERATORS
|
|
10
|
-
} from "./chunk-PHESMHTT.mjs";
|
|
11
11
|
|
|
12
12
|
// src/filter-value-editor.tsx
|
|
13
13
|
import { Checkbox } from "@kopexa/checkbox";
|
|
@@ -151,27 +151,29 @@ function ValueInput({
|
|
|
151
151
|
placeholder: (_c = field.placeholder) != null ? _c : t.formatMessage(messages.select_value)
|
|
152
152
|
}
|
|
153
153
|
) }),
|
|
154
|
-
/* @__PURE__ */ jsx(Select.Content, { children: (_d = field.options) == null ? void 0 : _d.map((option) => /* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ jsx(Select.Content, { children: (_d = field.options) == null ? void 0 : _d.map((option) => /* @__PURE__ */ jsx(
|
|
155
155
|
Select.Item,
|
|
156
156
|
{
|
|
157
157
|
value: option.value,
|
|
158
158
|
disabled: option.disabled,
|
|
159
|
-
children: [
|
|
160
|
-
option.icon && /* @__PURE__ */ jsx("span", { className: "
|
|
159
|
+
children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
160
|
+
option.icon && /* @__PURE__ */ jsx("span", { className: "size-4 flex-shrink-0", children: option.icon }),
|
|
161
161
|
option.label
|
|
162
|
-
]
|
|
162
|
+
] })
|
|
163
163
|
},
|
|
164
164
|
option.value
|
|
165
165
|
)) })
|
|
166
166
|
] });
|
|
167
167
|
case "multiselect": {
|
|
168
|
-
const selectedValues = Array.isArray(value) ? value
|
|
169
|
-
|
|
168
|
+
const selectedValues = Array.isArray(value) ? value.filter(
|
|
169
|
+
(v) => typeof v === "string" && v.length > 1
|
|
170
|
+
) : [];
|
|
171
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-1 max-h-[200px] overflow-auto p-1", children: (_e = field.options) == null ? void 0 : _e.map((option) => (
|
|
170
172
|
// biome-ignore lint/a11y/noLabelWithoutControl: Checkbox is a custom form control inside the label
|
|
171
173
|
/* @__PURE__ */ jsxs(
|
|
172
174
|
"label",
|
|
173
175
|
{
|
|
174
|
-
className: "flex items-center gap-2 cursor-pointer",
|
|
176
|
+
className: "flex items-center gap-2 cursor-pointer hover:bg-muted/50 rounded-md px-2 py-1.5",
|
|
175
177
|
children: [
|
|
176
178
|
/* @__PURE__ */ jsx(
|
|
177
179
|
Checkbox,
|
|
@@ -184,11 +186,12 @@ function ValueInput({
|
|
|
184
186
|
onChange(selectedValues.filter((v) => v !== option.value));
|
|
185
187
|
}
|
|
186
188
|
},
|
|
187
|
-
disabled: option.disabled
|
|
189
|
+
disabled: option.disabled,
|
|
190
|
+
className: "flex-shrink-0 border-border bg-background data-[state=checked]:bg-primary data-[state=checked]:border-primary [&_svg]:text-primary-foreground"
|
|
188
191
|
}
|
|
189
192
|
),
|
|
190
|
-
option.icon && /* @__PURE__ */ jsx("span", { className: "size-
|
|
191
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm", children: option.label })
|
|
193
|
+
option.icon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 [&>*]:size-5", children: option.icon }),
|
|
194
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm truncate", children: option.label })
|
|
192
195
|
]
|
|
193
196
|
},
|
|
194
197
|
option.value
|