@tensor_1/react 2.50.0 → 2.51.1
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/components/DataWorkspace.d.ts +40 -4
- package/dist/components/DataWorkspace.js +115 -39
- package/dist/components/FilterEditor.d.ts +3 -1
- package/dist/components/FilterEditor.js +22 -6
- package/dist/components/Table.d.ts +4 -0
- package/dist/components/Table.js +2 -2
- package/dist/filter-model.d.ts +15 -1
- package/dist/filter-model.js +8 -2
- package/dist/index.cjs +152 -48
- package/dist/index.d.cts +73 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -0
- package/dist/labels-context.d.ts +6 -1
- package/dist/table-engine.d.ts +10 -1
- package/dist/table-engine.js +4 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import * as react from 'react';
|
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
import { UixRenderLink } from '../link.js';
|
|
4
4
|
import { FilterField, FilterValue, SummarizeFilterOptions } from '../filter-model.js';
|
|
5
|
+
import { PaginationLabels } from './Pagination.js';
|
|
5
6
|
import { TableDensity } from './Table.js';
|
|
6
7
|
import { SavedViewMenuProps, ViewMenuProps } from './TableControls.js';
|
|
7
8
|
|
|
@@ -110,6 +111,17 @@ interface DataWorkspaceIdColumn<T> {
|
|
|
110
111
|
width?: string;
|
|
111
112
|
/** The column's floor in a fixed layout. Default: the `md` column tier. */
|
|
112
113
|
minWidth?: string;
|
|
114
|
+
/**
|
|
115
|
+
* The sort key the id column answers to in `sort` / `onSortChange` (HAR-2631), e.g. `title`
|
|
116
|
+
* when the id shows the title. Default `'id'`. Keep it distinct from the data columns' ids.
|
|
117
|
+
*/
|
|
118
|
+
id?: string;
|
|
119
|
+
/**
|
|
120
|
+
* The id header sorts like a data column (HAR-2631): a click cycles it, Shift + click (or
|
|
121
|
+
* Shift + Enter / Space) adds it as a further key in a multi-sort, with its ordinal and
|
|
122
|
+
* `aria-sort`. Off: the header is plain, as in 2.50.0.
|
|
123
|
+
*/
|
|
124
|
+
sortable?: boolean;
|
|
113
125
|
renderLink?: UixRenderLink;
|
|
114
126
|
}
|
|
115
127
|
/** A group of rows under one header row (HAR-2625). The product computes the groups. */
|
|
@@ -125,6 +137,13 @@ interface DataWorkspaceGroup<T> {
|
|
|
125
137
|
/** Aggregated cells in the header row, by column id (a sum, a range), each under its column. */
|
|
126
138
|
aggregates?: Readonly<Record<string, ReactNode>>;
|
|
127
139
|
}
|
|
140
|
+
/** Where a row's actions are shown (HAR-2634), the second argument of `rowActions`. */
|
|
141
|
+
interface DataWorkspaceRowActionsContext {
|
|
142
|
+
/** The ladder's step: `null` for the full table (or without `mobile`). */
|
|
143
|
+
ladder: 'cards' | 'priority' | 'priority-mid' | null;
|
|
144
|
+
/** The narrowest table step (`priority-mid`): render only the always-on control. Cards keep the full set, on a line of their own. */
|
|
145
|
+
compact: boolean;
|
|
146
|
+
}
|
|
128
147
|
/** Windowing for a long unpaged list (HAR-2626), over `useVirtualRows`. */
|
|
129
148
|
interface DataWorkspaceVirtualize {
|
|
130
149
|
/** Window only above this many rows. Default 100. */
|
|
@@ -136,7 +155,11 @@ interface DataWorkspaceVirtualize {
|
|
|
136
155
|
/** Rows rendered beyond each edge. Default 8. */
|
|
137
156
|
overscan?: number;
|
|
138
157
|
}
|
|
139
|
-
|
|
158
|
+
/**
|
|
159
|
+
* The footer's pager. `labels` words the pager itself; without them it takes `UixLabelsProvider`
|
|
160
|
+
* `pagination`, then English (HAR-2631).
|
|
161
|
+
*/
|
|
162
|
+
type DataWorkspacePagination = ({
|
|
140
163
|
mode: 'offset';
|
|
141
164
|
page: number;
|
|
142
165
|
pageSize: number;
|
|
@@ -155,12 +178,20 @@ type DataWorkspacePagination = {
|
|
|
155
178
|
pageSize?: number;
|
|
156
179
|
pageSizes?: readonly number[];
|
|
157
180
|
onPageSizeChange?: (size: number) => void;
|
|
181
|
+
}) & {
|
|
182
|
+
labels?: Partial<PaginationLabels>;
|
|
158
183
|
};
|
|
159
184
|
interface DataWorkspaceProps<T, S extends DataWorkspaceSortState = DataWorkspaceSort | null> extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
|
|
160
185
|
/** Names the workspace region and the table. */
|
|
161
186
|
label: string;
|
|
162
187
|
rows: readonly T[];
|
|
163
188
|
getRowId: (row: T) => string;
|
|
189
|
+
/**
|
|
190
|
+
* A row's name in words, where the kit names a row (its checkbox, HAR-2632) and there is no
|
|
191
|
+
* `idColumn.text`: a title or a person, never a key. Without it the row id is used, which is
|
|
192
|
+
* a UUID on many tables. Keep it stable (`useCallback`), as rows are memoised.
|
|
193
|
+
*/
|
|
194
|
+
rowLabel?: (row: T) => string;
|
|
164
195
|
columns: readonly DataWorkspaceColumn<T>[];
|
|
165
196
|
idColumn?: DataWorkspaceIdColumn<T>;
|
|
166
197
|
/**
|
|
@@ -186,8 +217,13 @@ interface DataWorkspaceProps<T, S extends DataWorkspaceSortState = DataWorkspace
|
|
|
186
217
|
/**
|
|
187
218
|
* A row's actions (a `Menu`, quick buttons), in a column frozen at the right edge and sized to
|
|
188
219
|
* them. Return `null` for a row without actions; with none on the page there is no column.
|
|
220
|
+
* The second argument says where the row is shown (HAR-2634): at the narrowest step of the
|
|
221
|
+
* ladder (`compact`, a workspace under 600 px with `mobile`), render only the always-on control,
|
|
222
|
+
* the peek or a kebab; the peek or the record carries the verbs. A frozen actions cell is for
|
|
223
|
+
* compact controls: word verbs (wider than a quarter of the table) unfreeze and scroll with
|
|
224
|
+
* their row, at any width.
|
|
189
225
|
*/
|
|
190
|
-
rowActions?: (row: T) => ReactNode;
|
|
226
|
+
rowActions?: (row: T, context: DataWorkspaceRowActionsContext) => ReactNode;
|
|
191
227
|
/** More props for a row, e.g. a data attribute that marks it changed (see ChangeHighlight, PR #164). */
|
|
192
228
|
getRowProps?: (row: T) => HTMLAttributes<HTMLTableRowElement> & Record<`data-${string}`, string | undefined>;
|
|
193
229
|
density?: TableDensity;
|
|
@@ -333,6 +369,6 @@ interface DataWorkspaceProps<T, S extends DataWorkspaceSortState = DataWorkspace
|
|
|
333
369
|
* rich id cell, multi-column sort, the product's state bodies and `beforeBody`, row groups,
|
|
334
370
|
* windowing (`virtualize`) and a keyboard row model (`onRowActivate`).
|
|
335
371
|
*/
|
|
336
|
-
declare function DataWorkspace<T, S extends DataWorkspaceSortState = DataWorkspaceSort | null>({ label, rows, getRowId, columns, idColumn, sort, onSortChange, selection, onSelectionChange, maxSelection, bulkActions, rowActions, getRowProps, density, zebra, freezeFirstColumn, fixed, mobile, search, filters, savedViews, viewMenu, viewChanged, onSaveView, onSaveViewAs, saveViewAs, maximized, onMaximizedChange, toolbarEnd, beforeBody, pagination, status, refreshing, onRetry, loadingRows, empty, emptyAction, filteredEmpty, filteredEmptyAction, error, stateOverride, groups, collapsedGroups, onCollapsedGroupsChange, virtualize, rowNavigation, onRowActivate, activateOnClick, activeRowId, onActiveRowChange, multiSort, labels: labelOverrides, className, ...props }: DataWorkspaceProps<T, S>): react.JSX.Element;
|
|
372
|
+
declare function DataWorkspace<T, S extends DataWorkspaceSortState = DataWorkspaceSort | null>({ label, rows, getRowId, rowLabel, columns, idColumn, sort, onSortChange, selection, onSelectionChange, maxSelection, bulkActions, rowActions, getRowProps, density, zebra, freezeFirstColumn, fixed, mobile, search, filters, savedViews, viewMenu, viewChanged, onSaveView, onSaveViewAs, saveViewAs, maximized, onMaximizedChange, toolbarEnd, beforeBody, pagination, status, refreshing, onRetry, loadingRows, empty, emptyAction, filteredEmpty, filteredEmptyAction, error, stateOverride, groups, collapsedGroups, onCollapsedGroupsChange, virtualize, rowNavigation, onRowActivate, activateOnClick, activeRowId, onActiveRowChange, multiSort, labels: labelOverrides, className, ...props }: DataWorkspaceProps<T, S>): react.JSX.Element;
|
|
337
373
|
|
|
338
|
-
export { DEFAULT_DATA_WORKSPACE_LABELS, DataWorkspace, type DataWorkspaceColumn, type DataWorkspaceGroup, type DataWorkspaceIdColumn, type DataWorkspaceLabels, type DataWorkspacePagination, type DataWorkspaceProps, type DataWorkspaceSort, type DataWorkspaceSortState, type DataWorkspaceSortValue, type DataWorkspaceVirtualize };
|
|
374
|
+
export { DEFAULT_DATA_WORKSPACE_LABELS, DataWorkspace, type DataWorkspaceColumn, type DataWorkspaceGroup, type DataWorkspaceIdColumn, type DataWorkspaceLabels, type DataWorkspacePagination, type DataWorkspaceProps, type DataWorkspaceRowActionsContext, type DataWorkspaceSort, type DataWorkspaceSortState, type DataWorkspaceSortValue, type DataWorkspaceVirtualize };
|
|
@@ -85,6 +85,7 @@ function DataWorkspace({
|
|
|
85
85
|
label,
|
|
86
86
|
rows,
|
|
87
87
|
getRowId,
|
|
88
|
+
rowLabel,
|
|
88
89
|
columns,
|
|
89
90
|
idColumn,
|
|
90
91
|
sort,
|
|
@@ -246,7 +247,11 @@ function DataWorkspace({
|
|
|
246
247
|
onSelectionChange(apply(new Set(selected), rowIds, on));
|
|
247
248
|
};
|
|
248
249
|
const atCap = selected.size >= cap;
|
|
249
|
-
const actionsByRow = useMemo(() =>
|
|
250
|
+
const actionsByRow = useMemo(() => {
|
|
251
|
+
if (!rowActions) return [];
|
|
252
|
+
const context = { ladder, compact: ladder === "priority-mid" };
|
|
253
|
+
return shownRows.map((row) => rowActions(row, context));
|
|
254
|
+
}, [shownRows, rowActions, ladder]);
|
|
250
255
|
const hasActions = actionsByRow.some((node) => node != null && node !== false);
|
|
251
256
|
const activeFilters = filters ? filters.fields.filter((field) => !isFilterEmpty(filters.values[field.id])) : [];
|
|
252
257
|
const searching = (search?.value.trim() ?? "") !== "";
|
|
@@ -273,7 +278,8 @@ function DataWorkspace({
|
|
|
273
278
|
onSortChange(next);
|
|
274
279
|
};
|
|
275
280
|
const showSaveAs = onSaveViewAs != null && (viewChanged || saveViewAs === "always");
|
|
276
|
-
const
|
|
281
|
+
const hasToolbar = savedViews != null || viewChanged && onSaveView != null || showSaveAs || filters != null || search != null || viewMenu != null || onMaximizedChange != null || toolbarEnd != null && toolbarEnd !== false;
|
|
282
|
+
const toolbar = hasToolbar && /* @__PURE__ */ jsxs("div", { className: "uix-toolbar uix-data-workspace__toolbar", role: "toolbar", "aria-label": label, children: [
|
|
277
283
|
savedViews && /* @__PURE__ */ jsx(PopoverButton, { id: `${id}-views`, label: savedViews.current, title: typeof savedViews.current === "string" ? fillLabel(labels.savedView, { name: savedViews.current }) : void 0, variant: "secondary", children: (close) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
278
284
|
savedViews.note != null && /* @__PURE__ */ jsx("div", { className: "uix-data-workspace__views-note", children: savedViews.note }),
|
|
279
285
|
/* @__PURE__ */ jsx(SavedViewMenu, { ...savedViews.menu, onSelect: (viewId) => {
|
|
@@ -333,6 +339,7 @@ function DataWorkspace({
|
|
|
333
339
|
collapsed,
|
|
334
340
|
onToggleGroup: toggleGroupOpen,
|
|
335
341
|
getRowId,
|
|
342
|
+
rowLabel,
|
|
336
343
|
columns,
|
|
337
344
|
idColumn,
|
|
338
345
|
getRowProps,
|
|
@@ -424,6 +431,7 @@ function WorkspaceTable({
|
|
|
424
431
|
collapsed,
|
|
425
432
|
onToggleGroup,
|
|
426
433
|
getRowId,
|
|
434
|
+
rowLabel,
|
|
427
435
|
columns,
|
|
428
436
|
idColumn,
|
|
429
437
|
getRowProps,
|
|
@@ -645,20 +653,75 @@ function WorkspaceTable({
|
|
|
645
653
|
const selectRowLabel = labels.selectRow;
|
|
646
654
|
const openLabel = labels.open;
|
|
647
655
|
const fixedOn = fixed && !cards;
|
|
656
|
+
const [actionsTooWide, setActionsTooWide] = useState(false);
|
|
648
657
|
const [actionsWidth, setActionsWidth] = useState(null);
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
658
|
+
const measureActions = !cards && hasActions;
|
|
659
|
+
const measureConfig = useRef({ measureActions, fixedOn, hasActions });
|
|
660
|
+
measureConfig.current = { measureActions, fixedOn, hasActions };
|
|
661
|
+
const measureActionsNow = useRef(() => {
|
|
662
|
+
});
|
|
663
|
+
measureActionsNow.current = () => {
|
|
664
|
+
const wrap = wrapRef.current;
|
|
665
|
+
const { measureActions: on, fixedOn: fixedNow, hasActions: any } = measureConfig.current;
|
|
666
|
+
if (!wrap) return;
|
|
667
|
+
const cells = Array.from(wrap.querySelectorAll("tbody td.uix-table__actions"));
|
|
668
|
+
if (!on) setActionsTooWide(false);
|
|
669
|
+
else {
|
|
670
|
+
let widest = 0;
|
|
671
|
+
for (const td of cells) if (typeof td.getBoundingClientRect === "function") widest = Math.max(widest, td.getBoundingClientRect().width, td.scrollWidth);
|
|
672
|
+
if (wrap.clientWidth > 0 && widest > 0) setActionsTooWide(widest > wrap.clientWidth / 4);
|
|
673
|
+
}
|
|
674
|
+
if (!fixedNow || !any || typeof document.createRange !== "function" || typeof getComputedStyle !== "function") return;
|
|
675
|
+
if (typeof document.createRange().getBoundingClientRect !== "function") return;
|
|
676
|
+
let content = 0;
|
|
677
|
+
for (const td of cells) {
|
|
653
678
|
const range = document.createRange();
|
|
654
679
|
range.selectNodeContents(td);
|
|
655
|
-
const content = range.getBoundingClientRect().width;
|
|
656
680
|
const style = getComputedStyle(td);
|
|
657
|
-
|
|
681
|
+
content = Math.max(content, range.getBoundingClientRect().width + parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0"));
|
|
658
682
|
}
|
|
659
|
-
const next =
|
|
660
|
-
|
|
661
|
-
}
|
|
683
|
+
const next = content > 0 ? Math.ceil(content) : null;
|
|
684
|
+
setActionsWidth((current) => current === next ? current : next);
|
|
685
|
+
};
|
|
686
|
+
useIsomorphicLayoutEffect(() => {
|
|
687
|
+
const wrap = wrapRef.current;
|
|
688
|
+
if (!wrap) return;
|
|
689
|
+
let frame = 0;
|
|
690
|
+
const schedule = () => {
|
|
691
|
+
if (frame || typeof requestAnimationFrame !== "function") return;
|
|
692
|
+
frame = requestAnimationFrame(() => {
|
|
693
|
+
frame = 0;
|
|
694
|
+
measureActionsNow.current();
|
|
695
|
+
});
|
|
696
|
+
};
|
|
697
|
+
const resize = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
|
|
698
|
+
const collect = () => {
|
|
699
|
+
if (!resize) return;
|
|
700
|
+
resize.disconnect();
|
|
701
|
+
resize.observe(wrap);
|
|
702
|
+
const table = wrap.querySelector("table");
|
|
703
|
+
if (table) resize.observe(table);
|
|
704
|
+
for (const td of Array.from(wrap.querySelectorAll("tbody td.uix-table__actions"))) {
|
|
705
|
+
resize.observe(td);
|
|
706
|
+
for (const child of Array.from(td.children)) resize.observe(child);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
collect();
|
|
710
|
+
const mutation = typeof MutationObserver === "undefined" ? null : new MutationObserver(() => {
|
|
711
|
+
collect();
|
|
712
|
+
schedule();
|
|
713
|
+
});
|
|
714
|
+
mutation?.observe(wrap, { childList: true, subtree: true });
|
|
715
|
+
return () => {
|
|
716
|
+
resize?.disconnect();
|
|
717
|
+
mutation?.disconnect();
|
|
718
|
+
if (frame && typeof cancelAnimationFrame === "function") cancelAnimationFrame(frame);
|
|
719
|
+
};
|
|
720
|
+
}, []);
|
|
721
|
+
const rowSet = ids.join("\0");
|
|
722
|
+
useIsomorphicLayoutEffect(() => {
|
|
723
|
+
measureActionsNow.current();
|
|
724
|
+
}, [measureActions, fixedOn, ladder, rowSet]);
|
|
662
725
|
const actionsCol = actionsWidth != null ? `${actionsWidth}px` : "5.25rem";
|
|
663
726
|
const floors = fixedOn ? [
|
|
664
727
|
...selectable ? ["var(--uix-selcol-w)"] : [],
|
|
@@ -667,6 +730,32 @@ function WorkspaceTable({
|
|
|
667
730
|
...hasActions ? [actionsCol] : []
|
|
668
731
|
] : [];
|
|
669
732
|
const tableStyle = fixedOn && floors.length ? { minWidth: floors.length === 1 ? floors[0] : `calc(${floors.join(" + ")})` } : void 0;
|
|
733
|
+
const sortProps = (column, columnSortable) => {
|
|
734
|
+
const order = keys.findIndex((key) => key.columnId === column.id);
|
|
735
|
+
const canSort = columnSortable && sortable;
|
|
736
|
+
const ordinal = keys.length > 1 && order >= 0 ? order + 1 : void 0;
|
|
737
|
+
return {
|
|
738
|
+
sortable: canSort,
|
|
739
|
+
sortDirection: sortFor(column),
|
|
740
|
+
sortOrder: ordinal,
|
|
741
|
+
sortOrderLabel: ordinal != null ? fillLabel(labels.sortPriority, { n: ordinal }) : void 0,
|
|
742
|
+
onSort: () => {
|
|
743
|
+
cycleSort(column, multi && additive.current);
|
|
744
|
+
additive.current = false;
|
|
745
|
+
},
|
|
746
|
+
...canSort && multi ? {
|
|
747
|
+
onKeyDownCapture: (event) => {
|
|
748
|
+
if (event.key === "Enter" || event.key === " ") shiftKey.current = event.shiftKey;
|
|
749
|
+
},
|
|
750
|
+
// A click made by Enter or Space has no pointer (detail 0) and may not carry Shift itself.
|
|
751
|
+
onClickCapture: (event) => {
|
|
752
|
+
additive.current = event.shiftKey || event.detail === 0 && shiftKey.current;
|
|
753
|
+
shiftKey.current = false;
|
|
754
|
+
}
|
|
755
|
+
} : {}
|
|
756
|
+
};
|
|
757
|
+
};
|
|
758
|
+
const idSortColumn = idColumn ? { id: idColumn.id ?? "id", header: idColumn.header, cell: () => null } : null;
|
|
670
759
|
const renderRow = (row, index) => {
|
|
671
760
|
const rowId = ids[index];
|
|
672
761
|
const isSelected = selected.has(rowId);
|
|
@@ -675,6 +764,7 @@ function WorkspaceTable({
|
|
|
675
764
|
{
|
|
676
765
|
row,
|
|
677
766
|
rowId,
|
|
767
|
+
rowLabel,
|
|
678
768
|
columns,
|
|
679
769
|
idColumn,
|
|
680
770
|
getRowProps,
|
|
@@ -769,7 +859,7 @@ function WorkspaceTable({
|
|
|
769
859
|
density,
|
|
770
860
|
zebra,
|
|
771
861
|
pinnedCol: frozen,
|
|
772
|
-
pinnedActions: hasActions && !cards,
|
|
862
|
+
pinnedActions: hasActions && !cards && !actionsTooWide,
|
|
773
863
|
fixed: fixedOn || void 0,
|
|
774
864
|
style: tableStyle,
|
|
775
865
|
className: cx(
|
|
@@ -802,36 +892,16 @@ function WorkspaceTable({
|
|
|
802
892
|
onChange: () => toggleMany(ids, onPage < ids.length && !atCap)
|
|
803
893
|
}
|
|
804
894
|
) }),
|
|
805
|
-
idColumn && /* @__PURE__ */ jsx(Th, { scope: "col", "data-col": ladderOn ? "id" : void 0, children: idColumn.header }),
|
|
895
|
+
idColumn && (idColumn.sortable && idSortColumn ? /* @__PURE__ */ jsx(Th, { scope: "col", "data-col": ladderOn ? "id" : void 0, ...sortProps(idSortColumn, true), children: idColumn.header }) : /* @__PURE__ */ jsx(Th, { scope: "col", "data-col": ladderOn ? "id" : void 0, children: idColumn.header })),
|
|
806
896
|
columns.map((column) => {
|
|
807
|
-
const order = keys.findIndex((key) => key.columnId === column.id);
|
|
808
|
-
const canSort = column.sortable && sortable;
|
|
809
897
|
const style = !fixedOn && (column.width || column.minWidth) ? { width: column.width, minWidth: column.minWidth } : void 0;
|
|
810
|
-
const ordinal = keys.length > 1 && order >= 0 ? order + 1 : void 0;
|
|
811
898
|
return /* @__PURE__ */ jsx(
|
|
812
899
|
Th,
|
|
813
900
|
{
|
|
814
901
|
scope: "col",
|
|
815
902
|
className: cx(column.align === "end" && "uix-col--num", column.className) || void 0,
|
|
816
903
|
style,
|
|
817
|
-
sortable
|
|
818
|
-
sortDirection: sortFor(column),
|
|
819
|
-
sortOrder: ordinal,
|
|
820
|
-
sortOrderLabel: ordinal != null ? fillLabel(labels.sortPriority, { n: ordinal }) : void 0,
|
|
821
|
-
onSort: () => {
|
|
822
|
-
cycleSort(column, multi && additive.current);
|
|
823
|
-
additive.current = false;
|
|
824
|
-
},
|
|
825
|
-
...canSort && multi ? {
|
|
826
|
-
onKeyDownCapture: (event) => {
|
|
827
|
-
if (event.key === "Enter" || event.key === " ") shiftKey.current = event.shiftKey;
|
|
828
|
-
},
|
|
829
|
-
// A click made by Enter or Space has no pointer (detail 0) and may not carry Shift itself.
|
|
830
|
-
onClickCapture: (event) => {
|
|
831
|
-
additive.current = event.shiftKey || event.detail === 0 && shiftKey.current;
|
|
832
|
-
shiftKey.current = false;
|
|
833
|
-
}
|
|
834
|
-
} : {},
|
|
904
|
+
...sortProps(column, column.sortable === true),
|
|
835
905
|
...ladderAttrs(ladderOn, column, false),
|
|
836
906
|
children: column.header
|
|
837
907
|
},
|
|
@@ -850,6 +920,7 @@ function WorkspaceTable({
|
|
|
850
920
|
function WorkspaceRowInner({
|
|
851
921
|
row,
|
|
852
922
|
rowId,
|
|
923
|
+
rowLabel,
|
|
853
924
|
columns,
|
|
854
925
|
idColumn,
|
|
855
926
|
getRowProps,
|
|
@@ -867,7 +938,7 @@ function WorkspaceRowInner({
|
|
|
867
938
|
tabStop,
|
|
868
939
|
handlers
|
|
869
940
|
}) {
|
|
870
|
-
const text = idColumn?.text(row) ?? rowId;
|
|
941
|
+
const text = idColumn?.text(row) ?? rowLabel?.(row) ?? rowId;
|
|
871
942
|
const extra = getRowProps?.(row);
|
|
872
943
|
const navProps = nav ? {
|
|
873
944
|
tabIndex: tabStop ? 0 : -1,
|
|
@@ -908,6 +979,7 @@ function WorkspaceRowInner({
|
|
|
908
979
|
const WorkspaceRow = memo(WorkspaceRowInner);
|
|
909
980
|
function IdCell({ row, text, idColumn, openLabel }) {
|
|
910
981
|
const rich = idColumn.render ? /* @__PURE__ */ jsx("span", { className: "uix-data-workspace__id-text", children: idColumn.render(row) }) : null;
|
|
982
|
+
const label = /* @__PURE__ */ jsx("span", { className: "uix-data-workspace__id-label", children: text });
|
|
911
983
|
const arrow = idColumn.href && renderUixLink(idColumn.renderLink, {
|
|
912
984
|
href: idColumn.href(row),
|
|
913
985
|
className: "uix-data-workspace__open",
|
|
@@ -919,18 +991,20 @@ function IdCell({ row, text, idColumn, openLabel }) {
|
|
|
919
991
|
href: idColumn.href(row),
|
|
920
992
|
className: "uix-id-cell__btn uix-data-workspace__id-link",
|
|
921
993
|
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
922
|
-
rich ??
|
|
994
|
+
rich ?? label,
|
|
923
995
|
/* @__PURE__ */ jsx(ArrowUpRight, {})
|
|
924
996
|
] })
|
|
925
997
|
}) });
|
|
926
998
|
}
|
|
927
999
|
return /* @__PURE__ */ jsxs("span", { className: "uix-data-workspace__id", children: [
|
|
928
|
-
idColumn.onPeek ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-id-cell__btn", "aria-haspopup": "dialog", onClick: () => idColumn.onPeek?.(row), children: rich ??
|
|
1000
|
+
idColumn.onPeek ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-id-cell__btn", "aria-haspopup": "dialog", onClick: () => idColumn.onPeek?.(row), children: rich ?? label }) : rich ?? /* @__PURE__ */ jsx("span", { className: "uix-cell-strong uix-data-workspace__id-label", children: text }),
|
|
929
1001
|
arrow
|
|
930
1002
|
] });
|
|
931
1003
|
}
|
|
932
1004
|
const cssEscape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
|
|
933
1005
|
function Footer({ pagination, labels, rowCount }) {
|
|
1006
|
+
const provided = useUixLabels().pagination;
|
|
1007
|
+
const pagerLabels = provided || pagination.labels ? { ...provided, ...pagination.labels } : void 0;
|
|
934
1008
|
const sizes = pagination.pageSizes;
|
|
935
1009
|
const size = pagination.mode === "offset" ? pagination.pageSize : pagination.pageSize;
|
|
936
1010
|
const rangeText = pagination.mode === "offset" ? fillLabel(labels.range, {
|
|
@@ -945,7 +1019,7 @@ function Footer({ pagination, labels, rowCount }) {
|
|
|
945
1019
|
/* @__PURE__ */ jsx(Select, { size: "sm", value: String(size), onValueChange: (v) => pagination.onPageSizeChange?.(Number(v)), options: sizes.map((s) => ({ value: String(s), label: String(s) })) })
|
|
946
1020
|
] }),
|
|
947
1021
|
/* @__PURE__ */ jsx("span", { className: "uix-toolbar__spacer" }),
|
|
948
|
-
pagination.mode === "offset" ? /* @__PURE__ */ jsx(Pagination, { mode: "offset", page: pagination.page, pageCount: Math.max(1, Math.ceil(pagination.total / pagination.pageSize)), onChange: pagination.onPageChange }) : /* @__PURE__ */ jsx(Pagination, { mode: "cursor", hasPrevious: pagination.hasPrevious, hasNext: pagination.hasNext, onFirst: pagination.onFirst, onPrevious: pagination.onPrevious, onNext: pagination.onNext, summary: pagination.summary })
|
|
1022
|
+
pagination.mode === "offset" ? /* @__PURE__ */ jsx(Pagination, { mode: "offset", page: pagination.page, pageCount: Math.max(1, Math.ceil(pagination.total / pagination.pageSize)), onChange: pagination.onPageChange, labels: pagerLabels }) : /* @__PURE__ */ jsx(Pagination, { mode: "cursor", hasPrevious: pagination.hasPrevious, hasNext: pagination.hasNext, onFirst: pagination.onFirst, onPrevious: pagination.onPrevious, onNext: pagination.onNext, summary: pagination.summary, labels: pagerLabels })
|
|
949
1023
|
] });
|
|
950
1024
|
}
|
|
951
1025
|
function PopoverButton({ id, label, title, variant, children }) {
|
|
@@ -970,7 +1044,9 @@ function PopoverButton({ id, label, title, variant, children }) {
|
|
|
970
1044
|
}
|
|
971
1045
|
function ChipSummary({ field, value, options }) {
|
|
972
1046
|
const resolved = useResolvedFilterLabels(field, value);
|
|
973
|
-
|
|
1047
|
+
const provided = useUixLabels().filterSummary;
|
|
1048
|
+
const merged = provided ? { ...options, labels: { ...provided, ...options?.labels, operators: { ...provided.operators, ...options?.labels?.operators } } } : options;
|
|
1049
|
+
return /* @__PURE__ */ jsx(Fragment, { children: summarizeFilter(resolved.field, resolved.value, merged) });
|
|
974
1050
|
}
|
|
975
1051
|
function openInside(root) {
|
|
976
1052
|
try {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { TextFilterOperator, NumberFilterOperator, DateFilterOperator, FilterField, FilterValue } from '../filter-model.js';
|
|
2
|
+
import { TextFilterOperator, NumberFilterOperator, DateFilterOperator, EnumFilterOperator, FilterField, FilterValue } from '../filter-model.js';
|
|
3
3
|
|
|
4
4
|
interface FilterEditorLabels {
|
|
5
5
|
apply: string;
|
|
@@ -35,6 +35,8 @@ interface FilterEditorLabels {
|
|
|
35
35
|
operators: Record<TextFilterOperator | NumberFilterOperator, string>;
|
|
36
36
|
/** `date` conditions (HAR-1832). Optional, like `presets`. */
|
|
37
37
|
dateOperators?: Partial<Record<DateFilterOperator, string>>;
|
|
38
|
+
/** `enum` conditions (HAR-2631), when the field offers both. Optional, like `presets`. */
|
|
39
|
+
enumOperators?: Partial<Record<EnumFilterOperator, string>>;
|
|
38
40
|
}
|
|
39
41
|
declare const DEFAULT_FILTER_EDITOR_LABELS: FilterEditorLabels;
|
|
40
42
|
interface FilterEditorProps {
|
|
@@ -12,7 +12,7 @@ import { cx } from "../cx.js";
|
|
|
12
12
|
import { useUixLabels } from "../labels-context.js";
|
|
13
13
|
import { useDelayedPending } from "../hooks/useDelayedPending.js";
|
|
14
14
|
import { foldForSearch } from "../search-suggest-model.js";
|
|
15
|
-
import { DATE_FILTER_OPERATORS, NUMBER_FILTER_OPERATORS, TEXT_FILTER_OPERATORS, emptyFilterValue, isUnresolvedChoice, matchFilterPreset, orderFilterRange } from "../filter-model.js";
|
|
15
|
+
import { DATE_FILTER_OPERATORS, ENUM_FILTER_OPERATORS, NUMBER_FILTER_OPERATORS, TEXT_FILTER_OPERATORS, emptyFilterValue, isUnresolvedChoice, matchFilterPreset, orderFilterRange } from "../filter-model.js";
|
|
16
16
|
const DEFAULT_FILTER_EDITOR_LABELS = {
|
|
17
17
|
apply: "Apply",
|
|
18
18
|
clear: "Clear",
|
|
@@ -38,6 +38,7 @@ const DEFAULT_FILTER_EDITOR_LABELS = {
|
|
|
38
38
|
addTagHint: "Enter or a comma adds it.",
|
|
39
39
|
date: "Date",
|
|
40
40
|
dateOperators: { on: "on", before: "before", after: "after", between: "between" },
|
|
41
|
+
enumOperators: { "is-any-of": "is any of", "is-none-of": "is none of" },
|
|
41
42
|
operators: {
|
|
42
43
|
contains: "contains",
|
|
43
44
|
"not-contains": "does not contain",
|
|
@@ -60,7 +61,8 @@ function FilterEditor({ field, value: valueProp, onValueChange, onApply, onClear
|
|
|
60
61
|
...provided,
|
|
61
62
|
...labelOverrides,
|
|
62
63
|
operators: { ...DEFAULT_FILTER_EDITOR_LABELS.operators, ...provided?.operators, ...labelOverrides?.operators },
|
|
63
|
-
dateOperators: { ...DEFAULT_FILTER_EDITOR_LABELS.dateOperators, ...provided?.dateOperators, ...labelOverrides?.dateOperators }
|
|
64
|
+
dateOperators: { ...DEFAULT_FILTER_EDITOR_LABELS.dateOperators, ...provided?.dateOperators, ...labelOverrides?.dateOperators },
|
|
65
|
+
enumOperators: { ...DEFAULT_FILTER_EDITOR_LABELS.enumOperators, ...provided?.enumOperators, ...labelOverrides?.enumOperators }
|
|
64
66
|
};
|
|
65
67
|
const id = useId();
|
|
66
68
|
let value = valueProp?.kind === field.kind ? valueProp : emptyFilterValue(field.kind);
|
|
@@ -163,16 +165,30 @@ function EnumBody({ field, value, onChange, labels, threshold }) {
|
|
|
163
165
|
return q ? options.filter((o) => foldForSearch(o.label).text.includes(q)) : options;
|
|
164
166
|
}, [options, query]);
|
|
165
167
|
const selected = new Set(value.values);
|
|
166
|
-
const
|
|
168
|
+
const ops = (field.enumOperators ?? []).filter((op) => ENUM_FILTER_OPERATORS.includes(op));
|
|
169
|
+
const condition = value.operator ?? (ops.length === 1 ? ops[0] : void 0);
|
|
170
|
+
const withValues = (values) => condition && condition !== "is-any-of" ? { kind: "enum", values, operator: condition } : { kind: "enum", values };
|
|
171
|
+
const toggle = (v, on) => onChange(withValues(on ? [...value.values, v] : value.values.filter((x) => x !== v)));
|
|
167
172
|
const presets = field.presets ?? [];
|
|
168
173
|
const active = matchFilterPreset(field, value);
|
|
174
|
+
const words = labels.enumOperators ?? {};
|
|
169
175
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
170
|
-
|
|
176
|
+
ops.length > 1 && /* @__PURE__ */ jsx("div", { className: "uix-filter-editor__row", children: /* @__PURE__ */ jsx(
|
|
177
|
+
Select,
|
|
178
|
+
{
|
|
179
|
+
size: "sm",
|
|
180
|
+
"aria-label": labels.operator,
|
|
181
|
+
value: value.operator ?? "is-any-of",
|
|
182
|
+
options: ops.map((op) => ({ value: op, label: words[op] ?? DEFAULT_FILTER_EDITOR_LABELS.enumOperators[op] })),
|
|
183
|
+
onValueChange: (op) => onChange(op === "is-none-of" ? { kind: "enum", values: value.values, operator: "is-none-of" } : { kind: "enum", values: value.values })
|
|
184
|
+
}
|
|
185
|
+
) }),
|
|
186
|
+
presets.length > 0 && /* @__PURE__ */ jsx("div", { className: "uix-chip-group uix-filter-editor__presets", role: "group", "aria-label": labels.presets ?? DEFAULT_FILTER_EDITOR_LABELS.presets, children: presets.map((p) => /* @__PURE__ */ jsx(Chip, { pressed: p === active, onPressedChange: () => onChange(withValues([...p.values])), children: p.label }, p.id)) }),
|
|
171
187
|
options.length > threshold && /* @__PURE__ */ jsx(Input, { type: "search", size: "sm", "aria-label": labels.search, placeholder: labels.search, value: query, onChange: (e) => setQuery(e.currentTarget.value), autoFocus: true }),
|
|
172
188
|
/* @__PURE__ */ jsxs("div", { className: "uix-filter-editor__bulk", children: [
|
|
173
189
|
/* @__PURE__ */ jsx("span", { className: "uix-filter-editor__count", "aria-live": "polite", children: fillLabel(labels.selected, { count: value.values.length }) }),
|
|
174
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange(
|
|
175
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange(
|
|
190
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange(withValues([.../* @__PURE__ */ new Set([...value.values, ...shown.map((o) => o.value)])])), children: labels.selectAll }),
|
|
191
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "link", size: "sm", onClick: () => onChange(withValues([])), children: labels.selectNone })
|
|
176
192
|
] }),
|
|
177
193
|
/* @__PURE__ */ jsx("div", { className: "uix-filter-editor__list", children: shown.length === 0 ? /* @__PURE__ */ jsx("p", { className: "uix-filter-editor__note", children: labels.noMatches }) : shown.map((o) => /* @__PURE__ */ jsx(Checkbox, { label: o.label, checked: selected.has(o.value), onChange: (e) => toggle(o.value, e.currentTarget.checked) }, o.value)) })
|
|
178
194
|
] });
|
|
@@ -20,6 +20,10 @@ declare const TableWrap: react.ForwardRefExoticComponent<TableWrapProps & react.
|
|
|
20
20
|
type TableDensity = 'compact' | 'standard' | 'comfortable';
|
|
21
21
|
type SortDirection = 'ascending' | 'descending' | 'none';
|
|
22
22
|
interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
23
|
+
/**
|
|
24
|
+
* The row height. Named, the table holds it, `standard` included, whatever the page sets
|
|
25
|
+
* `--uix-row-h` to (a density preference); left out, the table follows `--uix-row-h`.
|
|
26
|
+
*/
|
|
23
27
|
density?: TableDensity;
|
|
24
28
|
zebra?: boolean;
|
|
25
29
|
pinnedCol?: boolean;
|
package/dist/components/Table.js
CHANGED
|
@@ -13,7 +13,7 @@ const TableWrap = forwardRef(function TableWrap2({ announcement, refreshing, chi
|
|
|
13
13
|
] });
|
|
14
14
|
});
|
|
15
15
|
function Table({
|
|
16
|
-
density
|
|
16
|
+
density,
|
|
17
17
|
zebra = true,
|
|
18
18
|
pinnedCol,
|
|
19
19
|
pinnedActions,
|
|
@@ -35,7 +35,7 @@ function Table({
|
|
|
35
35
|
fixed && "uix-table--fixed",
|
|
36
36
|
className
|
|
37
37
|
),
|
|
38
|
-
"data-density": density
|
|
38
|
+
"data-density": density,
|
|
39
39
|
"data-uix-rowpeek": rowPeek || void 0,
|
|
40
40
|
"data-uix-table-v2": v2 || void 0,
|
|
41
41
|
...props,
|
package/dist/filter-model.d.ts
CHANGED
|
@@ -15,9 +15,15 @@ type NumberFilterOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'betwee
|
|
|
15
15
|
* `dateFilterBounds` turns each into the half-open range a query needs.
|
|
16
16
|
*/
|
|
17
17
|
type DateFilterOperator = 'on' | 'before' | 'after' | 'between';
|
|
18
|
+
/**
|
|
19
|
+
* An `enum` condition (HAR-2631): the row's value is one of the chosen values, or none of them
|
|
20
|
+
* ("status is none of Withdrawn, Lost"). Absent means `is-any-of`.
|
|
21
|
+
*/
|
|
22
|
+
type EnumFilterOperator = 'is-any-of' | 'is-none-of';
|
|
18
23
|
type FilterValue = {
|
|
19
24
|
kind: 'enum';
|
|
20
25
|
values: string[];
|
|
26
|
+
operator?: EnumFilterOperator;
|
|
21
27
|
} | {
|
|
22
28
|
kind: 'text';
|
|
23
29
|
operator: TextFilterOperator;
|
|
@@ -79,6 +85,11 @@ interface FilterField {
|
|
|
79
85
|
operators?: readonly (TextFilterOperator | NumberFilterOperator)[];
|
|
80
86
|
/** `date`: the conditions offered, in order. Default: on, before, after, between. */
|
|
81
87
|
dateOperators?: readonly DateFilterOperator[];
|
|
88
|
+
/**
|
|
89
|
+
* `enum`: the conditions offered, in order (HAR-2631). Default: `is-any-of` alone, so the editor
|
|
90
|
+
* shows no condition control; with both, it shows one above the options.
|
|
91
|
+
*/
|
|
92
|
+
enumOperators?: readonly EnumFilterOperator[];
|
|
82
93
|
/** `number`: a unit shown after the value, e.g. "days". */
|
|
83
94
|
unit?: string;
|
|
84
95
|
/** `boolean`: words for true and false, e.g. Escalated / Not escalated. Default from labels. */
|
|
@@ -105,6 +116,8 @@ interface FilterSummaryLabels {
|
|
|
105
116
|
more: string;
|
|
106
117
|
/** An `enum` value equal to a preset: `{label}` (the preset), `{field}`. Default `{label}`. */
|
|
107
118
|
preset?: string;
|
|
119
|
+
/** An `enum` value that excludes (`is-none-of`, HAR-2631): `{values}`. Default "not {values}" ("Status: not Open, Closed"). */
|
|
120
|
+
noneOf?: string;
|
|
108
121
|
/** `date` conditions (HAR-1832), `{date}`. Default "{date}", "before {date}", "after {date}". */
|
|
109
122
|
on?: string;
|
|
110
123
|
before?: string;
|
|
@@ -114,6 +127,7 @@ declare const DEFAULT_FILTER_SUMMARY_LABELS: FilterSummaryLabels;
|
|
|
114
127
|
declare const TEXT_FILTER_OPERATORS: readonly TextFilterOperator[];
|
|
115
128
|
declare const NUMBER_FILTER_OPERATORS: readonly NumberFilterOperator[];
|
|
116
129
|
declare const DATE_FILTER_OPERATORS: readonly DateFilterOperator[];
|
|
130
|
+
declare const ENUM_FILTER_OPERATORS: readonly EnumFilterOperator[];
|
|
117
131
|
/** The empty value for a field kind (what Clear resets to). */
|
|
118
132
|
declare function emptyFilterValue(kind: FilterValueKind): FilterValue;
|
|
119
133
|
/** True when the value filters nothing (so the chip goes away). */
|
|
@@ -165,4 +179,4 @@ interface SummarizeFilterOptions {
|
|
|
165
179
|
*/
|
|
166
180
|
declare function summarizeFilter(field: FilterField, input: FilterValue | undefined, options?: SummarizeFilterOptions): string;
|
|
167
181
|
|
|
168
|
-
export { DATE_FILTER_OPERATORS, DEFAULT_FILTER_SUMMARY_LABELS, type DateFilterOperator, type FilterChoice, type FilterField, type FilterPreset, type FilterSummaryLabels, type FilterValue, type FilterValueKind, NUMBER_FILTER_OPERATORS, type NumberFilterOperator, type SummarizeFilterOptions, TEXT_FILTER_OPERATORS, type TextFilterOperator, dateFilterBounds, emptyFilterValue, isFilterEmpty, isUnresolvedChoice, matchFilterPreset, orderFilterRange, resolveFilterLabels, summarizeFilter };
|
|
182
|
+
export { DATE_FILTER_OPERATORS, DEFAULT_FILTER_SUMMARY_LABELS, type DateFilterOperator, ENUM_FILTER_OPERATORS, type EnumFilterOperator, type FilterChoice, type FilterField, type FilterPreset, type FilterSummaryLabels, type FilterValue, type FilterValueKind, NUMBER_FILTER_OPERATORS, type NumberFilterOperator, type SummarizeFilterOptions, TEXT_FILTER_OPERATORS, type TextFilterOperator, dateFilterBounds, emptyFilterValue, isFilterEmpty, isUnresolvedChoice, matchFilterPreset, orderFilterRange, resolveFilterLabels, summarizeFilter };
|
package/dist/filter-model.js
CHANGED
|
@@ -20,6 +20,7 @@ const DEFAULT_FILTER_SUMMARY_LABELS = {
|
|
|
20
20
|
until: "until {to}",
|
|
21
21
|
more: "{first} +{count}",
|
|
22
22
|
preset: "{label}",
|
|
23
|
+
noneOf: "not {values}",
|
|
23
24
|
on: "{date}",
|
|
24
25
|
before: "before {date}",
|
|
25
26
|
after: "after {date}"
|
|
@@ -27,6 +28,7 @@ const DEFAULT_FILTER_SUMMARY_LABELS = {
|
|
|
27
28
|
const TEXT_FILTER_OPERATORS = ["contains", "not-contains", "is", "is-not", "starts-with"];
|
|
28
29
|
const NUMBER_FILTER_OPERATORS = ["eq", "neq", "lt", "lte", "gt", "gte", "between"];
|
|
29
30
|
const DATE_FILTER_OPERATORS = ["on", "before", "after", "between"];
|
|
31
|
+
const ENUM_FILTER_OPERATORS = ["is-any-of", "is-none-of"];
|
|
30
32
|
function emptyFilterValue(kind) {
|
|
31
33
|
switch (kind) {
|
|
32
34
|
case "enum":
|
|
@@ -108,7 +110,7 @@ async function resolveFilterLabels(field, value) {
|
|
|
108
110
|
return { kind: "reference", values: value.values.map((v) => resolved.has(v.value) ? { value: v.value, label: resolved.get(v.value) } : v) };
|
|
109
111
|
}
|
|
110
112
|
function matchFilterPreset(field, value) {
|
|
111
|
-
if (value?.kind !== "enum" || !field.presets?.length) return void 0;
|
|
113
|
+
if (value?.kind !== "enum" || value.operator === "is-none-of" || !field.presets?.length) return void 0;
|
|
112
114
|
const chosen = new Set(value.values);
|
|
113
115
|
return field.presets.find((preset) => {
|
|
114
116
|
const wanted = new Set(preset.values);
|
|
@@ -131,7 +133,10 @@ function summarizeFilter(field, input, options = {}) {
|
|
|
131
133
|
const preset = matchFilterPreset(field, value);
|
|
132
134
|
if (preset) return fill(labels.preset ?? DEFAULT_FILTER_SUMMARY_LABELS.preset, { label: preset.label, field: field.label });
|
|
133
135
|
const byValue = new Map((field.options ?? []).map((o) => [o.value, o.label]));
|
|
134
|
-
|
|
136
|
+
const named = list(value.values.map((v) => byValue.get(v) ?? v));
|
|
137
|
+
const condition = value.operator ?? (field.enumOperators?.length === 1 ? field.enumOperators[0] : "is-any-of");
|
|
138
|
+
if (condition === "is-none-of") return `${field.label}: ${fill(labels.noneOf ?? DEFAULT_FILTER_SUMMARY_LABELS.noneOf, { values: named, field: field.label })}`;
|
|
139
|
+
return `${field.label}: ${named}`;
|
|
135
140
|
}
|
|
136
141
|
case "reference":
|
|
137
142
|
return `${field.label}: ${list(value.values.map((v) => v.label))}`;
|
|
@@ -155,6 +160,7 @@ function summarizeFilter(field, input, options = {}) {
|
|
|
155
160
|
export {
|
|
156
161
|
DATE_FILTER_OPERATORS,
|
|
157
162
|
DEFAULT_FILTER_SUMMARY_LABELS,
|
|
163
|
+
ENUM_FILTER_OPERATORS,
|
|
158
164
|
NUMBER_FILTER_OPERATORS,
|
|
159
165
|
TEXT_FILTER_OPERATORS,
|
|
160
166
|
dateFilterBounds,
|