@undevy-org/tablewright 0.7.2 → 0.7.4
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/es/components/data-table/FilterFormPanel.js +119 -22
- package/dist/es/components/data-table/filter-form-panel-apply.js +114 -0
- package/dist/es/components/data-table/filter-values.js +5 -1
- package/dist/es/components/layout/SidebarAccountMenu.js +45 -31
- package/dist/es/components/ui/dropdown-menu.js +24 -0
- package/dist/es/index.js +3 -1
- package/dist/styles.css +22 -0
- package/dist/tablewright.cjs +288 -51
- package/dist/tablewright.css +22 -0
- package/dist/types/components/data-table/FilterFormPanel.d.ts +6 -0
- package/dist/types/components/data-table/filter-form-panel-apply.d.ts +27 -0
- package/dist/types/components/data-table/filter-types.d.ts +8 -0
- package/dist/types/components/data-table/filter-values.d.ts +2 -0
- package/dist/types/components/ui/dropdown-menu.d.ts +5 -1
- package/package.json +1 -1
|
@@ -6,7 +6,8 @@ import { Button } from "../ui/button.js";
|
|
|
6
6
|
import { ButtonFooter } from "../ui/button-footer.js";
|
|
7
7
|
import { Input } from "../ui/input.js";
|
|
8
8
|
import { Combobox } from "../ui/combobox.js";
|
|
9
|
-
import {
|
|
9
|
+
import { recordFilterFieldTouch, patchFieldState, resolveFieldApplyChange, formatTextMultiSummary, singleStringDraftValue, formatEnumMultiSummary, enumComboboxSelection, mergeCompoundSubDraft } from "./filter-form-panel-apply.js";
|
|
10
|
+
import { isFilterValueEmpty, getDateRange, getNumberRange, getStringArray, getCompoundValue } from "./filter-values.js";
|
|
10
11
|
import { hasActiveManagedFilters } from "./managed-filters.js";
|
|
11
12
|
const DEFAULT_LABELS = {
|
|
12
13
|
closeFiltersAriaLabel: "Close filters",
|
|
@@ -21,7 +22,11 @@ const DEFAULT_LABELS = {
|
|
|
21
22
|
compoundSubRangeFromAriaLabel: (subLabel) => `${subLabel} — from`,
|
|
22
23
|
compoundSubRangeToAriaLabel: (subLabel) => `${subLabel} — to`,
|
|
23
24
|
compoundSubRangeFromPlaceholder: (subLabel) => `${subLabel} from`,
|
|
24
|
-
compoundSubRangeToPlaceholder: (subLabel) => `${subLabel} to
|
|
25
|
+
compoundSubRangeToPlaceholder: (subLabel) => `${subLabel} to`,
|
|
26
|
+
multiValueFormHint: "Multiple values — edit using the column filter chips or popover above the table.",
|
|
27
|
+
valuesCount: (count) => `${count} values`,
|
|
28
|
+
selectedCount: (count) => `${count} selected`,
|
|
29
|
+
clearFieldForSingleEdit: "Clear to edit one value"
|
|
25
30
|
};
|
|
26
31
|
function deriveInitialState(fields, filters) {
|
|
27
32
|
const byId = new Map(filters.map((f) => [f.columnId, f.value]));
|
|
@@ -71,24 +76,38 @@ function FilterFormPanel({
|
|
|
71
76
|
return changed ? next : prev;
|
|
72
77
|
});
|
|
73
78
|
}, [filters, renderableFields]);
|
|
74
|
-
const setField = useCallback(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
const setField = useCallback(
|
|
80
|
+
(id2, value, compoundSubKey) => {
|
|
81
|
+
recordFilterFieldTouch(touchedRef.current, id2, compoundSubKey);
|
|
82
|
+
setState((prev) => patchFieldState(prev, id2, value));
|
|
83
|
+
},
|
|
84
|
+
[]
|
|
85
|
+
);
|
|
78
86
|
const handleApply = useCallback(() => {
|
|
79
87
|
const activeIds = new Set(filters.map((f) => f.columnId));
|
|
88
|
+
const committedById = new Map(filters.map((f) => [f.columnId, f.value]));
|
|
89
|
+
const touched = touchedRef.current;
|
|
80
90
|
const changes = [];
|
|
81
91
|
for (const id2 of renderableFields) {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
const config = filterConfigs[id2];
|
|
93
|
+
const committed = committedById.get(id2);
|
|
94
|
+
const draft = state[id2];
|
|
95
|
+
const resolution = resolveFieldApplyChange(
|
|
96
|
+
config,
|
|
97
|
+
id2,
|
|
98
|
+
committed,
|
|
99
|
+
draft,
|
|
100
|
+
touched,
|
|
101
|
+
hiddenSubSet,
|
|
102
|
+
activeIds.has(id2)
|
|
103
|
+
);
|
|
104
|
+
if (resolution.kind === "skip") continue;
|
|
105
|
+
if (resolution.kind === "remove") changes.push({ columnId: id2, action: "remove" });
|
|
106
|
+
else changes.push({ columnId: id2, action: "set", value: resolution.value });
|
|
88
107
|
}
|
|
89
108
|
onApply(changes);
|
|
90
109
|
touchedRef.current.clear();
|
|
91
|
-
}, [filters, onApply, renderableFields, state]);
|
|
110
|
+
}, [filterConfigs, filters, hiddenSubSet, onApply, renderableFields, state]);
|
|
92
111
|
const handleClear = useCallback(() => {
|
|
93
112
|
setState(deriveInitialState(renderableFields, []));
|
|
94
113
|
onClear(renderableFields);
|
|
@@ -133,7 +152,7 @@ function FilterFormPanel({
|
|
|
133
152
|
{
|
|
134
153
|
config,
|
|
135
154
|
value: state[fieldId],
|
|
136
|
-
onChange: (v) => setField(fieldId, v),
|
|
155
|
+
onChange: (v, compoundSubKey) => setField(fieldId, v, compoundSubKey),
|
|
137
156
|
fieldId,
|
|
138
157
|
hiddenSubSet,
|
|
139
158
|
labels
|
|
@@ -161,7 +180,20 @@ function FilterFormPanel({
|
|
|
161
180
|
}
|
|
162
181
|
function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
163
182
|
if (config.type === "text") {
|
|
164
|
-
const
|
|
183
|
+
const strings = getStringArray(value);
|
|
184
|
+
if (strings.length > 1) {
|
|
185
|
+
return /* @__PURE__ */ jsx(
|
|
186
|
+
MultiValueStringSummary,
|
|
187
|
+
{
|
|
188
|
+
label: config.label,
|
|
189
|
+
summary: formatTextMultiSummary(strings, labels),
|
|
190
|
+
hint: labels.multiValueFormHint,
|
|
191
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
192
|
+
onClearForEdit: () => onChange(void 0)
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const text = singleStringDraftValue(strings);
|
|
165
197
|
return /* @__PURE__ */ jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
|
|
166
198
|
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-[var(--text-secondary)]", children: config.label }),
|
|
167
199
|
/* @__PURE__ */ jsx(
|
|
@@ -179,7 +211,20 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
179
211
|
] });
|
|
180
212
|
}
|
|
181
213
|
if (config.type === "enum") {
|
|
182
|
-
const
|
|
214
|
+
const strings = getStringArray(value);
|
|
215
|
+
if (strings.length > 1) {
|
|
216
|
+
return /* @__PURE__ */ jsx(
|
|
217
|
+
MultiValueStringSummary,
|
|
218
|
+
{
|
|
219
|
+
label: config.label,
|
|
220
|
+
summary: formatEnumMultiSummary(strings, config.options, labels),
|
|
221
|
+
hint: labels.multiValueFormHint,
|
|
222
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
223
|
+
onClearForEdit: () => onChange(void 0)
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
const comboboxValue = enumComboboxSelection(strings);
|
|
183
228
|
return /* @__PURE__ */ jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
|
|
184
229
|
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-[var(--text-secondary)]", children: config.label }),
|
|
185
230
|
/* @__PURE__ */ jsx(
|
|
@@ -267,10 +312,7 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
267
312
|
if (config.type === "compound") {
|
|
268
313
|
const compound = getCompoundValue(value);
|
|
269
314
|
const updateSub = (subKey, next) => {
|
|
270
|
-
|
|
271
|
-
delete rest[subKey];
|
|
272
|
-
const merged = next === void 0 || isFilterValueEmpty(next) ? rest : { ...rest, [subKey]: next };
|
|
273
|
-
onChange(Object.keys(merged).length === 0 ? void 0 : merged);
|
|
315
|
+
onChange(mergeCompoundSubDraft(compound, subKey, next), subKey);
|
|
274
316
|
};
|
|
275
317
|
const visibleSubs = (config.subFilters ?? []).filter(
|
|
276
318
|
(sub) => !hiddenSubSet.has(`${fieldId}:${sub.key}`)
|
|
@@ -287,7 +329,22 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
287
329
|
/* @__PURE__ */ jsx("div", { className: "grid min-w-0 gap-2 grid-cols-[repeat(auto-fit,minmax(180px,1fr))]", children: visibleSubs.map((sub) => {
|
|
288
330
|
const subValue = compound[sub.key];
|
|
289
331
|
if (sub.type === "text") {
|
|
290
|
-
const
|
|
332
|
+
const strings = getStringArray(subValue);
|
|
333
|
+
if (strings.length > 1) {
|
|
334
|
+
return /* @__PURE__ */ jsx(
|
|
335
|
+
MultiValueStringSummary,
|
|
336
|
+
{
|
|
337
|
+
label: sub.label,
|
|
338
|
+
summary: formatTextMultiSummary(strings, labels),
|
|
339
|
+
hint: labels.multiValueFormHint,
|
|
340
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
341
|
+
onClearForEdit: () => updateSub(sub.key, void 0),
|
|
342
|
+
compact: true
|
|
343
|
+
},
|
|
344
|
+
sub.key
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
const text = singleStringDraftValue(strings);
|
|
291
348
|
return /* @__PURE__ */ jsx(
|
|
292
349
|
Input,
|
|
293
350
|
{
|
|
@@ -304,7 +361,22 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
304
361
|
);
|
|
305
362
|
}
|
|
306
363
|
if (sub.type === "enum") {
|
|
307
|
-
const
|
|
364
|
+
const strings = getStringArray(subValue);
|
|
365
|
+
if (strings.length > 1) {
|
|
366
|
+
return /* @__PURE__ */ jsx(
|
|
367
|
+
MultiValueStringSummary,
|
|
368
|
+
{
|
|
369
|
+
label: sub.label,
|
|
370
|
+
summary: formatEnumMultiSummary(strings, sub.options, labels),
|
|
371
|
+
hint: labels.multiValueFormHint,
|
|
372
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
373
|
+
onClearForEdit: () => updateSub(sub.key, void 0),
|
|
374
|
+
compact: true
|
|
375
|
+
},
|
|
376
|
+
sub.key
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
const comboboxValue = enumComboboxSelection(strings);
|
|
308
380
|
return /* @__PURE__ */ jsx(
|
|
309
381
|
Combobox,
|
|
310
382
|
{
|
|
@@ -365,6 +437,31 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
365
437
|
}
|
|
366
438
|
return null;
|
|
367
439
|
}
|
|
440
|
+
function MultiValueStringSummary({
|
|
441
|
+
label,
|
|
442
|
+
summary,
|
|
443
|
+
hint,
|
|
444
|
+
clearLabel,
|
|
445
|
+
onClearForEdit,
|
|
446
|
+
compact
|
|
447
|
+
}) {
|
|
448
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 flex-col gap-1.5", compact && "col-span-full"), children: [
|
|
449
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-[var(--text-secondary)]", children: label }),
|
|
450
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] text-[var(--text-primary)]", children: summary }),
|
|
451
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-[var(--text-secondary)]", children: hint }),
|
|
452
|
+
/* @__PURE__ */ jsx(
|
|
453
|
+
Button,
|
|
454
|
+
{
|
|
455
|
+
type: "button",
|
|
456
|
+
variant: "secondary",
|
|
457
|
+
size: "sm",
|
|
458
|
+
className: "h-9 self-start",
|
|
459
|
+
onClick: onClearForEdit,
|
|
460
|
+
children: clearLabel
|
|
461
|
+
}
|
|
462
|
+
)
|
|
463
|
+
] });
|
|
464
|
+
}
|
|
368
465
|
export {
|
|
369
466
|
FilterFormPanel
|
|
370
467
|
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isFilterValueEmpty, isMultiStringFilterValue, getCompoundValue } from "./filter-values.js";
|
|
2
|
+
function compoundSubTouchKey(fieldId, subKey) {
|
|
3
|
+
return `${fieldId}:${subKey}`;
|
|
4
|
+
}
|
|
5
|
+
function recordFilterFieldTouch(touched, fieldId, compoundSubKey) {
|
|
6
|
+
touched.add(fieldId);
|
|
7
|
+
if (compoundSubKey) {
|
|
8
|
+
touched.add(compoundSubTouchKey(fieldId, compoundSubKey));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function singleStringDraftValue(strings) {
|
|
12
|
+
return strings[0] ?? "";
|
|
13
|
+
}
|
|
14
|
+
function enumComboboxSelection(strings) {
|
|
15
|
+
return strings.length > 0 ? strings[0] : null;
|
|
16
|
+
}
|
|
17
|
+
function patchFieldState(prev, fieldId, value) {
|
|
18
|
+
return { ...prev, [fieldId]: value };
|
|
19
|
+
}
|
|
20
|
+
function mergeCompoundSubDraft(compound, subKey, next) {
|
|
21
|
+
const rest = { ...compound };
|
|
22
|
+
delete rest[subKey];
|
|
23
|
+
const merged = next === void 0 || isFilterValueEmpty(next) ? rest : { ...rest, [subKey]: next };
|
|
24
|
+
return Object.keys(merged).length === 0 ? void 0 : merged;
|
|
25
|
+
}
|
|
26
|
+
function formatTextMultiSummary(values, labels) {
|
|
27
|
+
if (values.length === 2) {
|
|
28
|
+
const joined = values.join(", ");
|
|
29
|
+
return joined.length > 24 ? labels.valuesCount(values.length) : joined;
|
|
30
|
+
}
|
|
31
|
+
return labels.valuesCount(values.length);
|
|
32
|
+
}
|
|
33
|
+
function formatEnumMultiSummary(values, options, labels) {
|
|
34
|
+
if (values.length === 2) {
|
|
35
|
+
const parts = values.map((v) => options?.find((o) => o.value === v)?.label ?? v);
|
|
36
|
+
const joined = parts.join(", ");
|
|
37
|
+
return joined.length > 24 ? labels.selectedCount(values.length) : joined;
|
|
38
|
+
}
|
|
39
|
+
return labels.selectedCount(values.length);
|
|
40
|
+
}
|
|
41
|
+
function shouldPreserveMultiSubOnApply(sub, subCommitted, touchKey, touched) {
|
|
42
|
+
if (sub.type !== "text" && sub.type !== "enum") return false;
|
|
43
|
+
return isMultiStringFilterValue(subCommitted) && !touched.has(touchKey);
|
|
44
|
+
}
|
|
45
|
+
function mergeCompoundValueForApply(config, fieldId, committed, draft, touched, hiddenSubSet) {
|
|
46
|
+
const committedCompound = getCompoundValue(committed);
|
|
47
|
+
const draftCompound = getCompoundValue(draft);
|
|
48
|
+
const merged = {};
|
|
49
|
+
for (const sub of config.subFilters ?? []) {
|
|
50
|
+
if (hiddenSubSet.has(compoundSubTouchKey(fieldId, sub.key))) continue;
|
|
51
|
+
const touchKey = compoundSubTouchKey(fieldId, sub.key);
|
|
52
|
+
const subCommitted = committedCompound[sub.key];
|
|
53
|
+
const subDraft = draftCompound[sub.key];
|
|
54
|
+
if (shouldPreserveMultiSubOnApply(sub, subCommitted, touchKey, touched)) {
|
|
55
|
+
if (subCommitted !== void 0 && !isFilterValueEmpty(subCommitted)) {
|
|
56
|
+
merged[sub.key] = subCommitted;
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (subDraft === void 0 || isFilterValueEmpty(subDraft)) continue;
|
|
61
|
+
merged[sub.key] = subDraft;
|
|
62
|
+
}
|
|
63
|
+
return Object.keys(merged).length === 0 ? void 0 : merged;
|
|
64
|
+
}
|
|
65
|
+
function resolveFieldApplyChange(config, fieldId, committed, draft, touched, hiddenSubSet, isActive) {
|
|
66
|
+
switch (config.type) {
|
|
67
|
+
case "text":
|
|
68
|
+
case "enum": {
|
|
69
|
+
if (isMultiStringFilterValue(committed) && !touched.has(fieldId)) {
|
|
70
|
+
return { kind: "skip" };
|
|
71
|
+
}
|
|
72
|
+
if (draft === void 0 || isFilterValueEmpty(draft)) {
|
|
73
|
+
return isActive ? { kind: "remove" } : { kind: "skip" };
|
|
74
|
+
}
|
|
75
|
+
return { kind: "set", value: draft };
|
|
76
|
+
}
|
|
77
|
+
case "date":
|
|
78
|
+
case "number-range": {
|
|
79
|
+
if (draft === void 0 || isFilterValueEmpty(draft)) {
|
|
80
|
+
return isActive ? { kind: "remove" } : { kind: "skip" };
|
|
81
|
+
}
|
|
82
|
+
return { kind: "set", value: draft };
|
|
83
|
+
}
|
|
84
|
+
case "compound": {
|
|
85
|
+
const merged = mergeCompoundValueForApply(
|
|
86
|
+
config,
|
|
87
|
+
fieldId,
|
|
88
|
+
committed,
|
|
89
|
+
draft,
|
|
90
|
+
touched,
|
|
91
|
+
hiddenSubSet
|
|
92
|
+
);
|
|
93
|
+
if (merged === void 0 || isFilterValueEmpty(merged)) {
|
|
94
|
+
return isActive ? { kind: "remove" } : { kind: "skip" };
|
|
95
|
+
}
|
|
96
|
+
return { kind: "set", value: merged };
|
|
97
|
+
}
|
|
98
|
+
default:
|
|
99
|
+
return { kind: "skip" };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
compoundSubTouchKey,
|
|
104
|
+
enumComboboxSelection,
|
|
105
|
+
formatEnumMultiSummary,
|
|
106
|
+
formatTextMultiSummary,
|
|
107
|
+
mergeCompoundSubDraft,
|
|
108
|
+
mergeCompoundValueForApply,
|
|
109
|
+
patchFieldState,
|
|
110
|
+
recordFilterFieldTouch,
|
|
111
|
+
resolveFieldApplyChange,
|
|
112
|
+
shouldPreserveMultiSubOnApply,
|
|
113
|
+
singleStringDraftValue
|
|
114
|
+
};
|
|
@@ -4,6 +4,9 @@ function isObjectValue(value) {
|
|
|
4
4
|
function getStringArray(value) {
|
|
5
5
|
return Array.isArray(value) ? value : [];
|
|
6
6
|
}
|
|
7
|
+
function isMultiStringFilterValue(value) {
|
|
8
|
+
return getStringArray(value).length > 1;
|
|
9
|
+
}
|
|
7
10
|
function getDateRange(value) {
|
|
8
11
|
const range = {};
|
|
9
12
|
if (!isObjectValue(value)) return range;
|
|
@@ -32,5 +35,6 @@ export {
|
|
|
32
35
|
getDateRange,
|
|
33
36
|
getNumberRange,
|
|
34
37
|
getStringArray,
|
|
35
|
-
isFilterValueEmpty
|
|
38
|
+
isFilterValueEmpty,
|
|
39
|
+
isMultiStringFilterValue
|
|
36
40
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronDown, SunMoon, LogOut } from "lucide-react";
|
|
3
|
-
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "../ui/dropdown-menu.js";
|
|
3
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "../ui/dropdown-menu.js";
|
|
4
4
|
import { ThemeSwitch } from "../ui/theme-switch.js";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
6
|
import { useAppShell } from "./app-shell-context.js";
|
|
@@ -57,37 +57,51 @@ function SidebarAccountMenu({
|
|
|
57
57
|
sideOffset: 6,
|
|
58
58
|
className: "min-w-[220px]",
|
|
59
59
|
children: [
|
|
60
|
-
/* @__PURE__ */ jsxs(
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
/* @__PURE__ */ jsxs(
|
|
61
|
+
DropdownMenuItem,
|
|
62
|
+
{
|
|
63
|
+
disabled: true,
|
|
64
|
+
className: "gap-2.5 px-2 py-2 opacity-100 focus:bg-transparent data-[disabled]:opacity-100",
|
|
65
|
+
children: [
|
|
66
|
+
avatarUrl ? /* @__PURE__ */ jsx(
|
|
67
|
+
"img",
|
|
68
|
+
{
|
|
69
|
+
src: avatarUrl,
|
|
70
|
+
alt: "",
|
|
71
|
+
className: "h-8 w-8 shrink-0 rounded-full object-cover"
|
|
72
|
+
}
|
|
73
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) }),
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
75
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-[13px] font-medium text-[var(--text-primary)]", children: name }),
|
|
76
|
+
role && /* @__PURE__ */ jsx("div", { className: "truncate text-[12px] text-[var(--text-secondary)]", children: role })
|
|
77
|
+
] })
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
82
|
+
showTheme && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
83
|
+
/* @__PURE__ */ jsxs(
|
|
84
|
+
DropdownMenuItem,
|
|
63
85
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
className: "flex items-center justify-between px-3 py-2 focus:bg-transparent",
|
|
87
|
+
onSelect: (event) => event.preventDefault(),
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
90
|
+
/* @__PURE__ */ jsx(SunMoon, { className: "h-4 w-4" }),
|
|
91
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px]", children: "Theme" })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
ThemeSwitch,
|
|
95
|
+
{
|
|
96
|
+
variant: "icon",
|
|
97
|
+
value: theme,
|
|
98
|
+
onValueChange: onThemeChange
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
]
|
|
67
102
|
}
|
|
68
|
-
)
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
/* @__PURE__ */ jsx("div", { className: "truncate text-[13px] font-medium text-[var(--text-primary)]", children: name }),
|
|
71
|
-
role && /* @__PURE__ */ jsx("div", { className: "truncate text-[12px] text-[var(--text-secondary)]", children: role })
|
|
72
|
-
] })
|
|
73
|
-
] }),
|
|
74
|
-
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
|
|
75
|
-
showTheme && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
76
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [
|
|
77
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
78
|
-
/* @__PURE__ */ jsx(SunMoon, { className: "h-4 w-4" }),
|
|
79
|
-
/* @__PURE__ */ jsx("span", { className: "text-[13px]", children: "Theme" })
|
|
80
|
-
] }),
|
|
81
|
-
/* @__PURE__ */ jsx(
|
|
82
|
-
ThemeSwitch,
|
|
83
|
-
{
|
|
84
|
-
variant: "icon",
|
|
85
|
-
value: theme,
|
|
86
|
-
onValueChange: onThemeChange
|
|
87
|
-
}
|
|
88
|
-
)
|
|
89
|
-
] }),
|
|
90
|
-
hasItemsBelow && /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" })
|
|
103
|
+
),
|
|
104
|
+
hasItemsBelow && /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
|
|
91
105
|
] }),
|
|
92
106
|
menuItems && menuItems.map((item) => {
|
|
93
107
|
const Icon = item.icon;
|
|
@@ -107,7 +121,7 @@ function SidebarAccountMenu({
|
|
|
107
121
|
item.key
|
|
108
122
|
);
|
|
109
123
|
}),
|
|
110
|
-
menuItems && menuItems.length > 0 && onSignOut && /* @__PURE__ */ jsx(
|
|
124
|
+
menuItems && menuItems.length > 0 && onSignOut && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
111
125
|
onSignOut && /* @__PURE__ */ jsxs(
|
|
112
126
|
DropdownMenuItem,
|
|
113
127
|
{
|
|
@@ -55,12 +55,36 @@ const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checke
|
|
|
55
55
|
}
|
|
56
56
|
));
|
|
57
57
|
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
58
|
+
const DropdownMenuLabel = React.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
59
|
+
DropdownMenuPrimitive.Label,
|
|
60
|
+
{
|
|
61
|
+
ref,
|
|
62
|
+
className: cn(
|
|
63
|
+
"px-2 py-1.5 text-[13px] font-semibold text-[var(--text-primary)]",
|
|
64
|
+
inset && "pl-8",
|
|
65
|
+
className
|
|
66
|
+
),
|
|
67
|
+
...props
|
|
68
|
+
}
|
|
69
|
+
));
|
|
70
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
71
|
+
const DropdownMenuSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
72
|
+
DropdownMenuPrimitive.Separator,
|
|
73
|
+
{
|
|
74
|
+
ref,
|
|
75
|
+
className: cn("-mx-1 my-1 h-px bg-[var(--border-subtle)]", className),
|
|
76
|
+
...props
|
|
77
|
+
}
|
|
78
|
+
));
|
|
79
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
58
80
|
export {
|
|
59
81
|
DropdownMenu,
|
|
60
82
|
DropdownMenuCheckboxItem,
|
|
61
83
|
DropdownMenuContent,
|
|
62
84
|
DropdownMenuGroup,
|
|
63
85
|
DropdownMenuItem,
|
|
86
|
+
DropdownMenuLabel,
|
|
64
87
|
DropdownMenuPortal,
|
|
88
|
+
DropdownMenuSeparator,
|
|
65
89
|
DropdownMenuTrigger
|
|
66
90
|
};
|
package/dist/es/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Badge, badgeVariants } from "./components/ui/badge.js";
|
|
|
2
2
|
import { Button, buttonVariants } from "./components/ui/button.js";
|
|
3
3
|
import { Checkbox } from "./components/ui/checkbox.js";
|
|
4
4
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./components/ui/dialog.js";
|
|
5
|
-
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuPortal, DropdownMenuTrigger } from "./components/ui/dropdown-menu.js";
|
|
5
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuTrigger } from "./components/ui/dropdown-menu.js";
|
|
6
6
|
import { Input } from "./components/ui/input.js";
|
|
7
7
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "./components/ui/popover.js";
|
|
8
8
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./components/ui/select.js";
|
|
@@ -121,7 +121,9 @@ export {
|
|
|
121
121
|
DropdownMenuContent,
|
|
122
122
|
DropdownMenuGroup,
|
|
123
123
|
DropdownMenuItem,
|
|
124
|
+
DropdownMenuLabel,
|
|
124
125
|
DropdownMenuPortal,
|
|
126
|
+
DropdownMenuSeparator,
|
|
125
127
|
DropdownMenuTrigger,
|
|
126
128
|
DualLineCell,
|
|
127
129
|
EmptyState,
|
package/dist/styles.css
CHANGED
|
@@ -469,6 +469,13 @@
|
|
|
469
469
|
.z-50 {
|
|
470
470
|
z-index: 50
|
|
471
471
|
}
|
|
472
|
+
.col-span-full {
|
|
473
|
+
grid-column: 1 / -1
|
|
474
|
+
}
|
|
475
|
+
.-mx-1 {
|
|
476
|
+
margin-left: -0.25rem;
|
|
477
|
+
margin-right: -0.25rem
|
|
478
|
+
}
|
|
472
479
|
.mx-3 {
|
|
473
480
|
margin-left: 0.75rem;
|
|
474
481
|
margin-right: 0.75rem
|
|
@@ -604,6 +611,9 @@
|
|
|
604
611
|
.h-full {
|
|
605
612
|
height: 100%
|
|
606
613
|
}
|
|
614
|
+
.h-px {
|
|
615
|
+
height: 1px
|
|
616
|
+
}
|
|
607
617
|
.max-h-\[280px\] {
|
|
608
618
|
max-height: 280px
|
|
609
619
|
}
|
|
@@ -889,6 +899,9 @@
|
|
|
889
899
|
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
|
|
890
900
|
margin-bottom: calc(1rem * var(--tw-space-y-reverse))
|
|
891
901
|
}
|
|
902
|
+
.self-start {
|
|
903
|
+
align-self: flex-start
|
|
904
|
+
}
|
|
892
905
|
.self-stretch {
|
|
893
906
|
align-self: stretch
|
|
894
907
|
}
|
|
@@ -1280,6 +1293,9 @@
|
|
|
1280
1293
|
.leading-none {
|
|
1281
1294
|
line-height: 1
|
|
1282
1295
|
}
|
|
1296
|
+
.leading-snug {
|
|
1297
|
+
line-height: 1.375
|
|
1298
|
+
}
|
|
1283
1299
|
.leading-tight {
|
|
1284
1300
|
line-height: 1.25
|
|
1285
1301
|
}
|
|
@@ -1542,6 +1558,9 @@
|
|
|
1542
1558
|
.focus\:border-\[var\(--border-focus\)\]:focus {
|
|
1543
1559
|
border-color: var(--border-focus)
|
|
1544
1560
|
}
|
|
1561
|
+
.focus\:bg-transparent:focus {
|
|
1562
|
+
background-color: transparent
|
|
1563
|
+
}
|
|
1545
1564
|
.focus\:outline-none:focus {
|
|
1546
1565
|
outline: 2px solid transparent;
|
|
1547
1566
|
outline-offset: 2px
|
|
@@ -1661,6 +1680,9 @@
|
|
|
1661
1680
|
.data-\[disabled\=true\]\:opacity-50[data-disabled="true"] {
|
|
1662
1681
|
opacity: 0.5
|
|
1663
1682
|
}
|
|
1683
|
+
.data-\[disabled\]\:opacity-100[data-disabled] {
|
|
1684
|
+
opacity: 1
|
|
1685
|
+
}
|
|
1664
1686
|
.data-\[state\=open\]\:opacity-100[data-state="open"] {
|
|
1665
1687
|
opacity: 1
|
|
1666
1688
|
}
|
package/dist/tablewright.cjs
CHANGED
|
@@ -235,6 +235,28 @@ const DropdownMenuCheckboxItem = React__namespace.forwardRef(({ className, child
|
|
|
235
235
|
}
|
|
236
236
|
));
|
|
237
237
|
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive__namespace.CheckboxItem.displayName;
|
|
238
|
+
const DropdownMenuLabel = React__namespace.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
239
|
+
DropdownMenuPrimitive__namespace.Label,
|
|
240
|
+
{
|
|
241
|
+
ref,
|
|
242
|
+
className: cn(
|
|
243
|
+
"px-2 py-1.5 text-[13px] font-semibold text-[var(--text-primary)]",
|
|
244
|
+
inset && "pl-8",
|
|
245
|
+
className
|
|
246
|
+
),
|
|
247
|
+
...props
|
|
248
|
+
}
|
|
249
|
+
));
|
|
250
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive__namespace.Label.displayName;
|
|
251
|
+
const DropdownMenuSeparator = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
252
|
+
DropdownMenuPrimitive__namespace.Separator,
|
|
253
|
+
{
|
|
254
|
+
ref,
|
|
255
|
+
className: cn("-mx-1 my-1 h-px bg-[var(--border-subtle)]", className),
|
|
256
|
+
...props
|
|
257
|
+
}
|
|
258
|
+
));
|
|
259
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive__namespace.Separator.displayName;
|
|
238
260
|
const Input = React__namespace.forwardRef(
|
|
239
261
|
({ className, type, error, ...props }, ref) => {
|
|
240
262
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2237,37 +2259,51 @@ function SidebarAccountMenu({
|
|
|
2237
2259
|
sideOffset: 6,
|
|
2238
2260
|
className: "min-w-[220px]",
|
|
2239
2261
|
children: [
|
|
2240
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2241
|
-
|
|
2242
|
-
|
|
2262
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2263
|
+
DropdownMenuItem,
|
|
2264
|
+
{
|
|
2265
|
+
disabled: true,
|
|
2266
|
+
className: "gap-2.5 px-2 py-2 opacity-100 focus:bg-transparent data-[disabled]:opacity-100",
|
|
2267
|
+
children: [
|
|
2268
|
+
avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2269
|
+
"img",
|
|
2270
|
+
{
|
|
2271
|
+
src: avatarUrl,
|
|
2272
|
+
alt: "",
|
|
2273
|
+
className: "h-8 w-8 shrink-0 rounded-full object-cover"
|
|
2274
|
+
}
|
|
2275
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[var(--bg-tertiary)] text-[11px] font-semibold text-[var(--text-secondary)]", children: getInitials(name) }),
|
|
2276
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
2277
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[13px] font-medium text-[var(--text-primary)]", children: name }),
|
|
2278
|
+
role && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[12px] text-[var(--text-secondary)]", children: role })
|
|
2279
|
+
] })
|
|
2280
|
+
]
|
|
2281
|
+
}
|
|
2282
|
+
),
|
|
2283
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
|
|
2284
|
+
showTheme && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2285
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2286
|
+
DropdownMenuItem,
|
|
2243
2287
|
{
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2288
|
+
className: "flex items-center justify-between px-3 py-2 focus:bg-transparent",
|
|
2289
|
+
onSelect: (event) => event.preventDefault(),
|
|
2290
|
+
children: [
|
|
2291
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2292
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.SunMoon, { className: "h-4 w-4" }),
|
|
2293
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px]", children: "Theme" })
|
|
2294
|
+
] }),
|
|
2295
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2296
|
+
ThemeSwitch,
|
|
2297
|
+
{
|
|
2298
|
+
variant: "icon",
|
|
2299
|
+
value: theme,
|
|
2300
|
+
onValueChange: onThemeChange
|
|
2301
|
+
}
|
|
2302
|
+
)
|
|
2303
|
+
]
|
|
2247
2304
|
}
|
|
2248
|
-
)
|
|
2249
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2250
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[13px] font-medium text-[var(--text-primary)]", children: name }),
|
|
2251
|
-
role && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-[12px] text-[var(--text-secondary)]", children: role })
|
|
2252
|
-
] })
|
|
2253
|
-
] }),
|
|
2254
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" }),
|
|
2255
|
-
showTheme && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2256
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-3 py-2", children: [
|
|
2257
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2258
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.SunMoon, { className: "h-4 w-4" }),
|
|
2259
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px]", children: "Theme" })
|
|
2260
|
-
] }),
|
|
2261
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2262
|
-
ThemeSwitch,
|
|
2263
|
-
{
|
|
2264
|
-
variant: "icon",
|
|
2265
|
-
value: theme,
|
|
2266
|
-
onValueChange: onThemeChange
|
|
2267
|
-
}
|
|
2268
|
-
)
|
|
2269
|
-
] }),
|
|
2270
|
-
hasItemsBelow && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 border-t border-[var(--border-subtle)]" })
|
|
2305
|
+
),
|
|
2306
|
+
hasItemsBelow && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {})
|
|
2271
2307
|
] }),
|
|
2272
2308
|
menuItems && menuItems.map((item) => {
|
|
2273
2309
|
const Icon = item.icon;
|
|
@@ -2287,7 +2323,7 @@ function SidebarAccountMenu({
|
|
|
2287
2323
|
item.key
|
|
2288
2324
|
);
|
|
2289
2325
|
}),
|
|
2290
|
-
menuItems && menuItems.length > 0 && onSignOut && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2326
|
+
menuItems && menuItems.length > 0 && onSignOut && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
|
|
2291
2327
|
onSignOut && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2292
2328
|
DropdownMenuItem,
|
|
2293
2329
|
{
|
|
@@ -2825,6 +2861,9 @@ function isObjectValue(value) {
|
|
|
2825
2861
|
function getStringArray(value) {
|
|
2826
2862
|
return Array.isArray(value) ? value : [];
|
|
2827
2863
|
}
|
|
2864
|
+
function isMultiStringFilterValue(value) {
|
|
2865
|
+
return getStringArray(value).length > 1;
|
|
2866
|
+
}
|
|
2828
2867
|
function getDateRange(value) {
|
|
2829
2868
|
const range = {};
|
|
2830
2869
|
if (!isObjectValue(value)) return range;
|
|
@@ -3998,6 +4037,106 @@ function AppliedStateChips({
|
|
|
3998
4037
|
)
|
|
3999
4038
|
] });
|
|
4000
4039
|
}
|
|
4040
|
+
function compoundSubTouchKey(fieldId, subKey) {
|
|
4041
|
+
return `${fieldId}:${subKey}`;
|
|
4042
|
+
}
|
|
4043
|
+
function recordFilterFieldTouch(touched, fieldId, compoundSubKey) {
|
|
4044
|
+
touched.add(fieldId);
|
|
4045
|
+
if (compoundSubKey) {
|
|
4046
|
+
touched.add(compoundSubTouchKey(fieldId, compoundSubKey));
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
function singleStringDraftValue(strings) {
|
|
4050
|
+
return strings[0] ?? "";
|
|
4051
|
+
}
|
|
4052
|
+
function enumComboboxSelection(strings) {
|
|
4053
|
+
return strings.length > 0 ? strings[0] : null;
|
|
4054
|
+
}
|
|
4055
|
+
function patchFieldState(prev, fieldId, value) {
|
|
4056
|
+
return { ...prev, [fieldId]: value };
|
|
4057
|
+
}
|
|
4058
|
+
function mergeCompoundSubDraft(compound, subKey, next) {
|
|
4059
|
+
const rest = { ...compound };
|
|
4060
|
+
delete rest[subKey];
|
|
4061
|
+
const merged = next === void 0 || isFilterValueEmpty(next) ? rest : { ...rest, [subKey]: next };
|
|
4062
|
+
return Object.keys(merged).length === 0 ? void 0 : merged;
|
|
4063
|
+
}
|
|
4064
|
+
function formatTextMultiSummary(values, labels) {
|
|
4065
|
+
if (values.length === 2) {
|
|
4066
|
+
const joined = values.join(", ");
|
|
4067
|
+
return joined.length > 24 ? labels.valuesCount(values.length) : joined;
|
|
4068
|
+
}
|
|
4069
|
+
return labels.valuesCount(values.length);
|
|
4070
|
+
}
|
|
4071
|
+
function formatEnumMultiSummary(values, options, labels) {
|
|
4072
|
+
if (values.length === 2) {
|
|
4073
|
+
const parts = values.map((v) => options?.find((o) => o.value === v)?.label ?? v);
|
|
4074
|
+
const joined = parts.join(", ");
|
|
4075
|
+
return joined.length > 24 ? labels.selectedCount(values.length) : joined;
|
|
4076
|
+
}
|
|
4077
|
+
return labels.selectedCount(values.length);
|
|
4078
|
+
}
|
|
4079
|
+
function shouldPreserveMultiSubOnApply(sub, subCommitted, touchKey, touched) {
|
|
4080
|
+
if (sub.type !== "text" && sub.type !== "enum") return false;
|
|
4081
|
+
return isMultiStringFilterValue(subCommitted) && !touched.has(touchKey);
|
|
4082
|
+
}
|
|
4083
|
+
function mergeCompoundValueForApply(config, fieldId, committed, draft, touched, hiddenSubSet) {
|
|
4084
|
+
const committedCompound = getCompoundValue(committed);
|
|
4085
|
+
const draftCompound = getCompoundValue(draft);
|
|
4086
|
+
const merged = {};
|
|
4087
|
+
for (const sub of config.subFilters ?? []) {
|
|
4088
|
+
if (hiddenSubSet.has(compoundSubTouchKey(fieldId, sub.key))) continue;
|
|
4089
|
+
const touchKey = compoundSubTouchKey(fieldId, sub.key);
|
|
4090
|
+
const subCommitted = committedCompound[sub.key];
|
|
4091
|
+
const subDraft = draftCompound[sub.key];
|
|
4092
|
+
if (shouldPreserveMultiSubOnApply(sub, subCommitted, touchKey, touched)) {
|
|
4093
|
+
if (subCommitted !== void 0 && !isFilterValueEmpty(subCommitted)) {
|
|
4094
|
+
merged[sub.key] = subCommitted;
|
|
4095
|
+
}
|
|
4096
|
+
continue;
|
|
4097
|
+
}
|
|
4098
|
+
if (subDraft === void 0 || isFilterValueEmpty(subDraft)) continue;
|
|
4099
|
+
merged[sub.key] = subDraft;
|
|
4100
|
+
}
|
|
4101
|
+
return Object.keys(merged).length === 0 ? void 0 : merged;
|
|
4102
|
+
}
|
|
4103
|
+
function resolveFieldApplyChange(config, fieldId, committed, draft, touched, hiddenSubSet, isActive) {
|
|
4104
|
+
switch (config.type) {
|
|
4105
|
+
case "text":
|
|
4106
|
+
case "enum": {
|
|
4107
|
+
if (isMultiStringFilterValue(committed) && !touched.has(fieldId)) {
|
|
4108
|
+
return { kind: "skip" };
|
|
4109
|
+
}
|
|
4110
|
+
if (draft === void 0 || isFilterValueEmpty(draft)) {
|
|
4111
|
+
return isActive ? { kind: "remove" } : { kind: "skip" };
|
|
4112
|
+
}
|
|
4113
|
+
return { kind: "set", value: draft };
|
|
4114
|
+
}
|
|
4115
|
+
case "date":
|
|
4116
|
+
case "number-range": {
|
|
4117
|
+
if (draft === void 0 || isFilterValueEmpty(draft)) {
|
|
4118
|
+
return isActive ? { kind: "remove" } : { kind: "skip" };
|
|
4119
|
+
}
|
|
4120
|
+
return { kind: "set", value: draft };
|
|
4121
|
+
}
|
|
4122
|
+
case "compound": {
|
|
4123
|
+
const merged = mergeCompoundValueForApply(
|
|
4124
|
+
config,
|
|
4125
|
+
fieldId,
|
|
4126
|
+
committed,
|
|
4127
|
+
draft,
|
|
4128
|
+
touched,
|
|
4129
|
+
hiddenSubSet
|
|
4130
|
+
);
|
|
4131
|
+
if (merged === void 0 || isFilterValueEmpty(merged)) {
|
|
4132
|
+
return isActive ? { kind: "remove" } : { kind: "skip" };
|
|
4133
|
+
}
|
|
4134
|
+
return { kind: "set", value: merged };
|
|
4135
|
+
}
|
|
4136
|
+
default:
|
|
4137
|
+
return { kind: "skip" };
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4001
4140
|
function applyManagedChanges(orch, changes) {
|
|
4002
4141
|
const activeIds = new Set(orch.columnFilters.map((f) => f.columnId));
|
|
4003
4142
|
for (const change of changes) {
|
|
@@ -4031,7 +4170,11 @@ const DEFAULT_LABELS = {
|
|
|
4031
4170
|
compoundSubRangeFromAriaLabel: (subLabel) => `${subLabel} — from`,
|
|
4032
4171
|
compoundSubRangeToAriaLabel: (subLabel) => `${subLabel} — to`,
|
|
4033
4172
|
compoundSubRangeFromPlaceholder: (subLabel) => `${subLabel} from`,
|
|
4034
|
-
compoundSubRangeToPlaceholder: (subLabel) => `${subLabel} to
|
|
4173
|
+
compoundSubRangeToPlaceholder: (subLabel) => `${subLabel} to`,
|
|
4174
|
+
multiValueFormHint: "Multiple values — edit using the column filter chips or popover above the table.",
|
|
4175
|
+
valuesCount: (count) => `${count} values`,
|
|
4176
|
+
selectedCount: (count) => `${count} selected`,
|
|
4177
|
+
clearFieldForSingleEdit: "Clear to edit one value"
|
|
4035
4178
|
};
|
|
4036
4179
|
function deriveInitialState(fields, filters) {
|
|
4037
4180
|
const byId = new Map(filters.map((f) => [f.columnId, f.value]));
|
|
@@ -4081,24 +4224,38 @@ function FilterFormPanel({
|
|
|
4081
4224
|
return changed ? next : prev;
|
|
4082
4225
|
});
|
|
4083
4226
|
}, [filters, renderableFields]);
|
|
4084
|
-
const setField = React.useCallback(
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4227
|
+
const setField = React.useCallback(
|
|
4228
|
+
(id2, value, compoundSubKey) => {
|
|
4229
|
+
recordFilterFieldTouch(touchedRef.current, id2, compoundSubKey);
|
|
4230
|
+
setState((prev) => patchFieldState(prev, id2, value));
|
|
4231
|
+
},
|
|
4232
|
+
[]
|
|
4233
|
+
);
|
|
4088
4234
|
const handleApply = React.useCallback(() => {
|
|
4089
4235
|
const activeIds = new Set(filters.map((f) => f.columnId));
|
|
4236
|
+
const committedById = new Map(filters.map((f) => [f.columnId, f.value]));
|
|
4237
|
+
const touched = touchedRef.current;
|
|
4090
4238
|
const changes = [];
|
|
4091
4239
|
for (const id2 of renderableFields) {
|
|
4092
|
-
const
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4240
|
+
const config = filterConfigs[id2];
|
|
4241
|
+
const committed = committedById.get(id2);
|
|
4242
|
+
const draft = state[id2];
|
|
4243
|
+
const resolution = resolveFieldApplyChange(
|
|
4244
|
+
config,
|
|
4245
|
+
id2,
|
|
4246
|
+
committed,
|
|
4247
|
+
draft,
|
|
4248
|
+
touched,
|
|
4249
|
+
hiddenSubSet,
|
|
4250
|
+
activeIds.has(id2)
|
|
4251
|
+
);
|
|
4252
|
+
if (resolution.kind === "skip") continue;
|
|
4253
|
+
if (resolution.kind === "remove") changes.push({ columnId: id2, action: "remove" });
|
|
4254
|
+
else changes.push({ columnId: id2, action: "set", value: resolution.value });
|
|
4098
4255
|
}
|
|
4099
4256
|
onApply(changes);
|
|
4100
4257
|
touchedRef.current.clear();
|
|
4101
|
-
}, [filters, onApply, renderableFields, state]);
|
|
4258
|
+
}, [filterConfigs, filters, hiddenSubSet, onApply, renderableFields, state]);
|
|
4102
4259
|
const handleClear = React.useCallback(() => {
|
|
4103
4260
|
setState(deriveInitialState(renderableFields, []));
|
|
4104
4261
|
onClear(renderableFields);
|
|
@@ -4143,7 +4300,7 @@ function FilterFormPanel({
|
|
|
4143
4300
|
{
|
|
4144
4301
|
config,
|
|
4145
4302
|
value: state[fieldId],
|
|
4146
|
-
onChange: (v) => setField(fieldId, v),
|
|
4303
|
+
onChange: (v, compoundSubKey) => setField(fieldId, v, compoundSubKey),
|
|
4147
4304
|
fieldId,
|
|
4148
4305
|
hiddenSubSet,
|
|
4149
4306
|
labels
|
|
@@ -4171,7 +4328,20 @@ function FilterFormPanel({
|
|
|
4171
4328
|
}
|
|
4172
4329
|
function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
4173
4330
|
if (config.type === "text") {
|
|
4174
|
-
const
|
|
4331
|
+
const strings = getStringArray(value);
|
|
4332
|
+
if (strings.length > 1) {
|
|
4333
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4334
|
+
MultiValueStringSummary,
|
|
4335
|
+
{
|
|
4336
|
+
label: config.label,
|
|
4337
|
+
summary: formatTextMultiSummary(strings, labels),
|
|
4338
|
+
hint: labels.multiValueFormHint,
|
|
4339
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
4340
|
+
onClearForEdit: () => onChange(void 0)
|
|
4341
|
+
}
|
|
4342
|
+
);
|
|
4343
|
+
}
|
|
4344
|
+
const text = singleStringDraftValue(strings);
|
|
4175
4345
|
return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
|
|
4176
4346
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-[var(--text-secondary)]", children: config.label }),
|
|
4177
4347
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4189,7 +4359,20 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
4189
4359
|
] });
|
|
4190
4360
|
}
|
|
4191
4361
|
if (config.type === "enum") {
|
|
4192
|
-
const
|
|
4362
|
+
const strings = getStringArray(value);
|
|
4363
|
+
if (strings.length > 1) {
|
|
4364
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4365
|
+
MultiValueStringSummary,
|
|
4366
|
+
{
|
|
4367
|
+
label: config.label,
|
|
4368
|
+
summary: formatEnumMultiSummary(strings, config.options, labels),
|
|
4369
|
+
hint: labels.multiValueFormHint,
|
|
4370
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
4371
|
+
onClearForEdit: () => onChange(void 0)
|
|
4372
|
+
}
|
|
4373
|
+
);
|
|
4374
|
+
}
|
|
4375
|
+
const comboboxValue = enumComboboxSelection(strings);
|
|
4193
4376
|
return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1.5", children: [
|
|
4194
4377
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-[var(--text-secondary)]", children: config.label }),
|
|
4195
4378
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4277,10 +4460,7 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
4277
4460
|
if (config.type === "compound") {
|
|
4278
4461
|
const compound = getCompoundValue(value);
|
|
4279
4462
|
const updateSub = (subKey, next) => {
|
|
4280
|
-
|
|
4281
|
-
delete rest[subKey];
|
|
4282
|
-
const merged = next === void 0 || isFilterValueEmpty(next) ? rest : { ...rest, [subKey]: next };
|
|
4283
|
-
onChange(Object.keys(merged).length === 0 ? void 0 : merged);
|
|
4463
|
+
onChange(mergeCompoundSubDraft(compound, subKey, next), subKey);
|
|
4284
4464
|
};
|
|
4285
4465
|
const visibleSubs = (config.subFilters ?? []).filter(
|
|
4286
4466
|
(sub) => !hiddenSubSet.has(`${fieldId}:${sub.key}`)
|
|
@@ -4297,7 +4477,22 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
4297
4477
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid min-w-0 gap-2 grid-cols-[repeat(auto-fit,minmax(180px,1fr))]", children: visibleSubs.map((sub) => {
|
|
4298
4478
|
const subValue = compound[sub.key];
|
|
4299
4479
|
if (sub.type === "text") {
|
|
4300
|
-
const
|
|
4480
|
+
const strings = getStringArray(subValue);
|
|
4481
|
+
if (strings.length > 1) {
|
|
4482
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4483
|
+
MultiValueStringSummary,
|
|
4484
|
+
{
|
|
4485
|
+
label: sub.label,
|
|
4486
|
+
summary: formatTextMultiSummary(strings, labels),
|
|
4487
|
+
hint: labels.multiValueFormHint,
|
|
4488
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
4489
|
+
onClearForEdit: () => updateSub(sub.key, void 0),
|
|
4490
|
+
compact: true
|
|
4491
|
+
},
|
|
4492
|
+
sub.key
|
|
4493
|
+
);
|
|
4494
|
+
}
|
|
4495
|
+
const text = singleStringDraftValue(strings);
|
|
4301
4496
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4302
4497
|
Input,
|
|
4303
4498
|
{
|
|
@@ -4314,7 +4509,22 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
4314
4509
|
);
|
|
4315
4510
|
}
|
|
4316
4511
|
if (sub.type === "enum") {
|
|
4317
|
-
const
|
|
4512
|
+
const strings = getStringArray(subValue);
|
|
4513
|
+
if (strings.length > 1) {
|
|
4514
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4515
|
+
MultiValueStringSummary,
|
|
4516
|
+
{
|
|
4517
|
+
label: sub.label,
|
|
4518
|
+
summary: formatEnumMultiSummary(strings, sub.options, labels),
|
|
4519
|
+
hint: labels.multiValueFormHint,
|
|
4520
|
+
clearLabel: labels.clearFieldForSingleEdit,
|
|
4521
|
+
onClearForEdit: () => updateSub(sub.key, void 0),
|
|
4522
|
+
compact: true
|
|
4523
|
+
},
|
|
4524
|
+
sub.key
|
|
4525
|
+
);
|
|
4526
|
+
}
|
|
4527
|
+
const comboboxValue = enumComboboxSelection(strings);
|
|
4318
4528
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4319
4529
|
Combobox,
|
|
4320
4530
|
{
|
|
@@ -4375,6 +4585,31 @@ function Field({ config, value, onChange, fieldId, hiddenSubSet, labels }) {
|
|
|
4375
4585
|
}
|
|
4376
4586
|
return null;
|
|
4377
4587
|
}
|
|
4588
|
+
function MultiValueStringSummary({
|
|
4589
|
+
label,
|
|
4590
|
+
summary,
|
|
4591
|
+
hint,
|
|
4592
|
+
clearLabel,
|
|
4593
|
+
onClearForEdit,
|
|
4594
|
+
compact
|
|
4595
|
+
}) {
|
|
4596
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex min-w-0 flex-col gap-1.5", compact && "col-span-full"), children: [
|
|
4597
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-[var(--text-secondary)]", children: label }),
|
|
4598
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] text-[var(--text-primary)]", children: summary }),
|
|
4599
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] leading-snug text-[var(--text-secondary)]", children: hint }),
|
|
4600
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4601
|
+
Button,
|
|
4602
|
+
{
|
|
4603
|
+
type: "button",
|
|
4604
|
+
variant: "secondary",
|
|
4605
|
+
size: "sm",
|
|
4606
|
+
className: "h-9 self-start",
|
|
4607
|
+
onClick: onClearForEdit,
|
|
4608
|
+
children: clearLabel
|
|
4609
|
+
}
|
|
4610
|
+
)
|
|
4611
|
+
] });
|
|
4612
|
+
}
|
|
4378
4613
|
const STORAGE_PREFIX = "tablewright.filters.open.";
|
|
4379
4614
|
function readPersisted(persistKey, fallback) {
|
|
4380
4615
|
if (typeof window === "undefined") return fallback;
|
|
@@ -5640,7 +5875,9 @@ exports.DropdownMenuCheckboxItem = DropdownMenuCheckboxItem;
|
|
|
5640
5875
|
exports.DropdownMenuContent = DropdownMenuContent;
|
|
5641
5876
|
exports.DropdownMenuGroup = DropdownMenuGroup;
|
|
5642
5877
|
exports.DropdownMenuItem = DropdownMenuItem;
|
|
5878
|
+
exports.DropdownMenuLabel = DropdownMenuLabel;
|
|
5643
5879
|
exports.DropdownMenuPortal = DropdownMenuPortal;
|
|
5880
|
+
exports.DropdownMenuSeparator = DropdownMenuSeparator;
|
|
5644
5881
|
exports.DropdownMenuTrigger = DropdownMenuTrigger;
|
|
5645
5882
|
exports.DualLineCell = DualLineCell;
|
|
5646
5883
|
exports.EmptyState = EmptyState;
|
package/dist/tablewright.css
CHANGED
|
@@ -317,6 +317,13 @@
|
|
|
317
317
|
.z-50 {
|
|
318
318
|
z-index: 50
|
|
319
319
|
}
|
|
320
|
+
.col-span-full {
|
|
321
|
+
grid-column: 1 / -1
|
|
322
|
+
}
|
|
323
|
+
.-mx-1 {
|
|
324
|
+
margin-left: -0.25rem;
|
|
325
|
+
margin-right: -0.25rem
|
|
326
|
+
}
|
|
320
327
|
.mx-3 {
|
|
321
328
|
margin-left: 0.75rem;
|
|
322
329
|
margin-right: 0.75rem
|
|
@@ -452,6 +459,9 @@
|
|
|
452
459
|
.h-full {
|
|
453
460
|
height: 100%
|
|
454
461
|
}
|
|
462
|
+
.h-px {
|
|
463
|
+
height: 1px
|
|
464
|
+
}
|
|
455
465
|
.max-h-\[280px\] {
|
|
456
466
|
max-height: 280px
|
|
457
467
|
}
|
|
@@ -737,6 +747,9 @@
|
|
|
737
747
|
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
|
|
738
748
|
margin-bottom: calc(1rem * var(--tw-space-y-reverse))
|
|
739
749
|
}
|
|
750
|
+
.self-start {
|
|
751
|
+
align-self: flex-start
|
|
752
|
+
}
|
|
740
753
|
.self-stretch {
|
|
741
754
|
align-self: stretch
|
|
742
755
|
}
|
|
@@ -1128,6 +1141,9 @@
|
|
|
1128
1141
|
.leading-none {
|
|
1129
1142
|
line-height: 1
|
|
1130
1143
|
}
|
|
1144
|
+
.leading-snug {
|
|
1145
|
+
line-height: 1.375
|
|
1146
|
+
}
|
|
1131
1147
|
.leading-tight {
|
|
1132
1148
|
line-height: 1.25
|
|
1133
1149
|
}
|
|
@@ -1390,6 +1406,9 @@
|
|
|
1390
1406
|
.focus\:border-\[var\(--border-focus\)\]:focus {
|
|
1391
1407
|
border-color: var(--border-focus)
|
|
1392
1408
|
}
|
|
1409
|
+
.focus\:bg-transparent:focus {
|
|
1410
|
+
background-color: transparent
|
|
1411
|
+
}
|
|
1393
1412
|
.focus\:outline-none:focus {
|
|
1394
1413
|
outline: 2px solid transparent;
|
|
1395
1414
|
outline-offset: 2px
|
|
@@ -1509,6 +1528,9 @@
|
|
|
1509
1528
|
.data-\[disabled\=true\]\:opacity-50[data-disabled="true"] {
|
|
1510
1529
|
opacity: 0.5
|
|
1511
1530
|
}
|
|
1531
|
+
.data-\[disabled\]\:opacity-100[data-disabled] {
|
|
1532
|
+
opacity: 1
|
|
1533
|
+
}
|
|
1512
1534
|
.data-\[state\=open\]\:opacity-100[data-state="open"] {
|
|
1513
1535
|
opacity: 1
|
|
1514
1536
|
}
|
|
@@ -14,6 +14,12 @@ export interface FilterFormPanelLabels {
|
|
|
14
14
|
compoundSubRangeToAriaLabel: (subLabel: string) => string;
|
|
15
15
|
compoundSubRangeFromPlaceholder: (subLabel: string) => string;
|
|
16
16
|
compoundSubRangeToPlaceholder: (subLabel: string) => string;
|
|
17
|
+
/** Shown under read-only multi-value text fields in the form panel. */
|
|
18
|
+
multiValueFormHint: string;
|
|
19
|
+
valuesCount: (count: number) => string;
|
|
20
|
+
selectedCount: (count: number) => string;
|
|
21
|
+
/** Clears a multi-value field so the user can enter a single value. */
|
|
22
|
+
clearFieldForSingleEdit: string;
|
|
17
23
|
}
|
|
18
24
|
export interface FilterFormPanelProps {
|
|
19
25
|
/** All known filter configs (same shape used by GroupedFilterAdd). */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ActiveFilterValue, ColumnFilterConfig, CompoundFilterValue, CompoundSubFilter } from "./filter-types.js";
|
|
2
|
+
export interface MultiValueSummaryLabels {
|
|
3
|
+
valuesCount: (count: number) => string;
|
|
4
|
+
selectedCount: (count: number) => string;
|
|
5
|
+
}
|
|
6
|
+
export declare function compoundSubTouchKey(fieldId: string, subKey: string): string;
|
|
7
|
+
export declare function recordFilterFieldTouch(touched: Set<string>, fieldId: string, compoundSubKey?: string): void;
|
|
8
|
+
export declare function singleStringDraftValue(strings: string[]): string;
|
|
9
|
+
export declare function enumComboboxSelection(strings: string[]): string | null;
|
|
10
|
+
export declare function patchFieldState<T extends Record<string, ActiveFilterValue | undefined>>(prev: T, fieldId: string, value: ActiveFilterValue | undefined): T;
|
|
11
|
+
export declare function mergeCompoundSubDraft(compound: CompoundFilterValue, subKey: string, next: ActiveFilterValue | undefined): ActiveFilterValue | undefined;
|
|
12
|
+
export declare function formatTextMultiSummary(values: string[], labels: MultiValueSummaryLabels): string;
|
|
13
|
+
export declare function formatEnumMultiSummary(values: string[], options: {
|
|
14
|
+
label: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}[] | undefined, labels: MultiValueSummaryLabels): string;
|
|
17
|
+
export declare function shouldPreserveMultiSubOnApply(sub: CompoundSubFilter, subCommitted: ActiveFilterValue | undefined, touchKey: string, touched: Set<string>): boolean;
|
|
18
|
+
export declare function mergeCompoundValueForApply(config: ColumnFilterConfig, fieldId: string, committed: ActiveFilterValue | undefined, draft: ActiveFilterValue | undefined, touched: Set<string>, hiddenSubSet: Set<string>): ActiveFilterValue | undefined;
|
|
19
|
+
export type ApplyResolution = {
|
|
20
|
+
kind: "skip";
|
|
21
|
+
} | {
|
|
22
|
+
kind: "remove";
|
|
23
|
+
} | {
|
|
24
|
+
kind: "set";
|
|
25
|
+
value: ActiveFilterValue;
|
|
26
|
+
};
|
|
27
|
+
export declare function resolveFieldApplyChange(config: ColumnFilterConfig, fieldId: string, committed: ActiveFilterValue | undefined, draft: ActiveFilterValue | undefined, touched: Set<string>, hiddenSubSet: Set<string>, isActive: boolean): ApplyResolution;
|
|
@@ -34,6 +34,14 @@ export type NumberRangeValue = {
|
|
|
34
34
|
export type CompoundFilterValue = {
|
|
35
35
|
[key: string]: ActiveFilterValue;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Stored filter payload for one column. Text and enum filters are usually `string[]`
|
|
39
|
+
* (one element for a single selection; multiple for popover/chip multi-select).
|
|
40
|
+
*
|
|
41
|
+
* `FilterFormPanel` edits at most one string per text/enum field (and per compound
|
|
42
|
+
* sub-filter of those types). Multi-value text/enum filters are edited via column
|
|
43
|
+
* filter chips and `ColumnFilterPopover`, not the form panel.
|
|
44
|
+
*/
|
|
37
45
|
export type ActiveFilterValue = string | string[] | DateRangeValue | NumberRangeValue | CompoundFilterValue;
|
|
38
46
|
export interface ActiveFilter {
|
|
39
47
|
columnId: string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ActiveFilterValue, CompoundFilterValue, DateRangeValue, NumberRangeValue } from "./filter-types.js";
|
|
2
2
|
/** Read a text/enum filter value. Non-array values read as `[]`. */
|
|
3
3
|
export declare function getStringArray(value: ActiveFilterValue | undefined): string[];
|
|
4
|
+
/** True when a text/enum value selects more than one string (popover/chip multi-select). */
|
|
5
|
+
export declare function isMultiStringFilterValue(value: ActiveFilterValue | undefined): boolean;
|
|
4
6
|
/** Read a date filter value. Non-string bounds are dropped. */
|
|
5
7
|
export declare function getDateRange(value: ActiveFilterValue | undefined): DateRangeValue;
|
|
6
8
|
/** Read a number-range filter value. Non-number bounds are dropped. */
|
|
@@ -7,4 +7,8 @@ declare const DropdownMenuPortal: React.FC<DropdownMenuPrimitive.DropdownMenuPor
|
|
|
7
7
|
declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
8
|
declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
9
9
|
declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
-
|
|
10
|
+
declare const DropdownMenuLabel: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
11
|
+
inset?: boolean;
|
|
12
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
13
|
+
declare const DropdownMenuSeparator: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuTrigger, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undevy-org/tablewright",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "React data-table and application-shell components: filters, drawers, row controls, sortable and resizable columns, built on Radix UI and TanStack Table.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|