gbs-add-block 1.2.7 → 1.2.8
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/README.md +3 -3
- package/index.cjs +9 -9
- package/package.json +1 -1
- package/source/components/datagridbeta/README.md +232 -0
- package/source/components/datagridbeta/__tests__/core.test.ts +341 -0
- package/source/components/datagridbeta/__tests__/export.test.ts +70 -0
- package/source/components/datagridbeta/core/columnHelper.ts +21 -0
- package/source/components/datagridbeta/core/columns.ts +288 -0
- package/source/components/datagridbeta/core/filtering.ts +237 -0
- package/source/components/datagridbeta/core/grid.ts +616 -0
- package/source/components/datagridbeta/core/index.ts +14 -0
- package/source/components/datagridbeta/core/rows.ts +64 -0
- package/source/components/datagridbeta/core/sorting.ts +111 -0
- package/source/components/datagridbeta/core/state.ts +82 -0
- package/source/components/datagridbeta/core/store.ts +52 -0
- package/source/components/datagridbeta/core/types.ts +266 -0
- package/source/components/datagridbeta/core/values.ts +98 -0
- package/source/components/datagridbeta/core/virtual.ts +85 -0
- package/source/components/datagridbeta/export/csv.ts +32 -0
- package/source/components/datagridbeta/export/download.ts +12 -0
- package/source/components/datagridbeta/export/pdf.ts +61 -0
- package/source/components/datagridbeta/export/table.ts +73 -0
- package/source/components/datagridbeta/export/xlsx.ts +145 -0
- package/source/components/datagridbeta/export/zip.ts +87 -0
- package/source/components/datagridbeta/index.ts +7 -0
- package/source/components/datagridbeta/react/Cell.tsx +282 -0
- package/source/components/datagridbeta/react/ColumnMenu.tsx +135 -0
- package/source/components/datagridbeta/react/DataGrid.tsx +296 -0
- package/source/components/datagridbeta/react/FilterForm.tsx +142 -0
- package/source/components/datagridbeta/react/HeaderRow.tsx +306 -0
- package/source/components/datagridbeta/react/Pagination.tsx +107 -0
- package/source/components/datagridbeta/react/Popover.tsx +79 -0
- package/source/components/datagridbeta/react/Row.tsx +85 -0
- package/source/components/datagridbeta/react/Toolbar.tsx +266 -0
- package/source/components/datagridbeta/react/Viewport.tsx +167 -0
- package/source/components/datagridbeta/react/context.ts +57 -0
- package/source/components/datagridbeta/react/hooks.ts +80 -0
- package/source/components/datagridbeta/react/icons.tsx +72 -0
- package/source/components/datagridbeta/react/keyboard.ts +110 -0
- package/source/components/datagridbeta/react/locale.ts +128 -0
- package/source/components/datagridbeta/styles.css +717 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useDeferredValue,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useLayoutEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState,
|
|
9
|
+
type CSSProperties,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
type Ref,
|
|
12
|
+
} from "react";
|
|
13
|
+
import { computeLayout, createSelectionColumn, resolveColumns } from "../core/columns";
|
|
14
|
+
import { filterRows } from "../core/filtering";
|
|
15
|
+
import { createGridEngine, type GridApi, type GridSnapshot } from "../core/grid";
|
|
16
|
+
import { buildRows, createRowIdGetter, paginate } from "../core/rows";
|
|
17
|
+
import { sortRows } from "../core/sorting";
|
|
18
|
+
import { shallowEqual } from "../core/store";
|
|
19
|
+
import type { Density, GridOptions } from "../core/types";
|
|
20
|
+
import { createFormatters } from "../core/values";
|
|
21
|
+
import { ColumnMenu } from "./ColumnMenu";
|
|
22
|
+
import { GridContext, cx, type GridContextValue, type GridSlot } from "./context";
|
|
23
|
+
import { usePopoverState, useStoreSelector } from "./hooks";
|
|
24
|
+
import { defaultLocaleText, type LocaleText } from "./locale";
|
|
25
|
+
import { Pagination } from "./Pagination";
|
|
26
|
+
import { Popover } from "./Popover";
|
|
27
|
+
import { Toolbar, type ToolbarOptions } from "./Toolbar";
|
|
28
|
+
import { GridViewport } from "./Viewport";
|
|
29
|
+
|
|
30
|
+
export interface DataGridProps<T> extends GridOptions<T> {
|
|
31
|
+
ref?: Ref<GridApi<T>>;
|
|
32
|
+
/** Height of the scrolling area. Ignored with `autoHeight`. Default 520. */
|
|
33
|
+
height?: number | string;
|
|
34
|
+
/** Grow to fit all rows instead of scrolling (use with pagination or small data). */
|
|
35
|
+
autoHeight?: boolean;
|
|
36
|
+
/** Fixed row height in px. Defaults to the density's height. */
|
|
37
|
+
rowHeight?: number;
|
|
38
|
+
headerHeight?: number;
|
|
39
|
+
loading?: boolean;
|
|
40
|
+
toolbar?: boolean | ToolbarOptions;
|
|
41
|
+
pageSizeOptions?: number[];
|
|
42
|
+
emptyState?: ReactNode;
|
|
43
|
+
getRowClassName?(row: T, rowIndex: number): string | undefined;
|
|
44
|
+
classNames?: Partial<Record<GridSlot, string>>;
|
|
45
|
+
className?: string;
|
|
46
|
+
style?: CSSProperties;
|
|
47
|
+
/** BCP 47 locale for number and date formatting. */
|
|
48
|
+
locale?: string;
|
|
49
|
+
localeText?: Partial<LocaleText>;
|
|
50
|
+
"aria-label"?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ROW_HEIGHTS: Record<Density, number> = { compact: 32, standard: 40, comfortable: 52 };
|
|
54
|
+
const DEFAULT_PAGE_SIZES = [25, 50, 100, 250];
|
|
55
|
+
const EMPTY_CLASSNAMES: Partial<Record<GridSlot, string>> = {};
|
|
56
|
+
|
|
57
|
+
const selectModelState = ({ state }: GridSnapshot) => ({
|
|
58
|
+
sorting: state.sorting,
|
|
59
|
+
filters: state.filters,
|
|
60
|
+
globalFilter: state.globalFilter,
|
|
61
|
+
pagination: state.pagination,
|
|
62
|
+
columnOrder: state.columnOrder,
|
|
63
|
+
columnVisibility: state.columnVisibility,
|
|
64
|
+
columnSizing: state.columnSizing,
|
|
65
|
+
columnPinning: state.columnPinning,
|
|
66
|
+
density: state.density,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export function DataGrid<T>(props: DataGridProps<T>) {
|
|
70
|
+
const {
|
|
71
|
+
ref,
|
|
72
|
+
data,
|
|
73
|
+
columns: columnDefs,
|
|
74
|
+
getRowId,
|
|
75
|
+
mode = "client",
|
|
76
|
+
rowCount,
|
|
77
|
+
state: controlled,
|
|
78
|
+
enablePagination = true,
|
|
79
|
+
enableSorting = true,
|
|
80
|
+
enableFiltering = true,
|
|
81
|
+
enableColumnResizing = true,
|
|
82
|
+
enableColumnReordering = true,
|
|
83
|
+
enableColumnPinning = true,
|
|
84
|
+
enableColumnHiding = true,
|
|
85
|
+
enableRowSelection = false,
|
|
86
|
+
selectionMode = "multiple",
|
|
87
|
+
height = 520,
|
|
88
|
+
autoHeight = false,
|
|
89
|
+
loading = false,
|
|
90
|
+
toolbar = true,
|
|
91
|
+
pageSizeOptions = DEFAULT_PAGE_SIZES,
|
|
92
|
+
emptyState,
|
|
93
|
+
getRowClassName,
|
|
94
|
+
classNames = EMPTY_CLASSNAMES,
|
|
95
|
+
className,
|
|
96
|
+
style,
|
|
97
|
+
locale,
|
|
98
|
+
localeText: localeOverrides,
|
|
99
|
+
} = props;
|
|
100
|
+
|
|
101
|
+
const [engine] = useState(() => createGridEngine<T>(props));
|
|
102
|
+
useImperativeHandle(ref, () => engine.api, [engine]);
|
|
103
|
+
|
|
104
|
+
// Selection and UI state are deliberately not selected here: changing them
|
|
105
|
+
// re-renders only the rows and cells that subscribe to them.
|
|
106
|
+
const internal = useStoreSelector(engine.store, selectModelState, shallowEqual);
|
|
107
|
+
const sorting = controlled?.sorting ?? internal.sorting;
|
|
108
|
+
const filters = controlled?.filters ?? internal.filters;
|
|
109
|
+
const globalFilter = controlled?.globalFilter ?? internal.globalFilter;
|
|
110
|
+
const pagination = controlled?.pagination ?? internal.pagination;
|
|
111
|
+
const columnOrder = controlled?.columnOrder ?? internal.columnOrder;
|
|
112
|
+
const columnVisibility = controlled?.columnVisibility ?? internal.columnVisibility;
|
|
113
|
+
const columnSizing = controlled?.columnSizing ?? internal.columnSizing;
|
|
114
|
+
const columnPinning = controlled?.columnPinning ?? internal.columnPinning;
|
|
115
|
+
const density = controlled?.density ?? internal.density;
|
|
116
|
+
|
|
117
|
+
const localeText = useMemo(() => ({ ...defaultLocaleText, ...localeOverrides }), [localeOverrides]);
|
|
118
|
+
const formatters = useMemo(
|
|
119
|
+
() => createFormatters(locale, { yes: localeText.yes, no: localeText.no }),
|
|
120
|
+
[locale, localeText.yes, localeText.no],
|
|
121
|
+
);
|
|
122
|
+
const numberFormat = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
123
|
+
|
|
124
|
+
// Row pipeline: each stage recomputes only when its own inputs change.
|
|
125
|
+
const server = mode === "server";
|
|
126
|
+
const rowIdGetter = useMemo(() => createRowIdGetter(getRowId), [getRowId]);
|
|
127
|
+
const columns = useMemo(() => resolveColumns(columnDefs), [columnDefs]);
|
|
128
|
+
const coreRows = useMemo(() => buildRows(data, rowIdGetter), [data, rowIdGetter]);
|
|
129
|
+
|
|
130
|
+
const liveQuery = useMemo(
|
|
131
|
+
() => ({ sorting, filters, globalFilter, pagination }),
|
|
132
|
+
[sorting, filters, globalFilter, pagination],
|
|
133
|
+
);
|
|
134
|
+
// Filtering and sorting large data render at low priority, so typing stays responsive.
|
|
135
|
+
const query = useDeferredValue(liveQuery);
|
|
136
|
+
const stale = query !== liveQuery;
|
|
137
|
+
|
|
138
|
+
const filteredRows = useMemo(
|
|
139
|
+
() => (server ? coreRows : filterRows(coreRows, columns, query.filters, query.globalFilter, formatters)),
|
|
140
|
+
[server, coreRows, columns, query.filters, query.globalFilter, formatters],
|
|
141
|
+
);
|
|
142
|
+
const sortedRows = useMemo(
|
|
143
|
+
() => (server ? filteredRows : sortRows(filteredRows, columns, query.sorting)),
|
|
144
|
+
[server, filteredRows, columns, query.sorting],
|
|
145
|
+
);
|
|
146
|
+
const page = useMemo(
|
|
147
|
+
() => paginate(sortedRows, query.pagination, { enabled: enablePagination, server, rowCount }),
|
|
148
|
+
[sortedRows, query.pagination, enablePagination, server, rowCount],
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const hasSelection = enableRowSelection !== false;
|
|
152
|
+
const leadingColumns = useMemo(() => (hasSelection ? [createSelectionColumn<T>()] : []), [hasSelection]);
|
|
153
|
+
const layout = useMemo(
|
|
154
|
+
() => computeLayout(columns, { columnOrder, columnVisibility, columnSizing, columnPinning }, leadingColumns),
|
|
155
|
+
[columns, columnOrder, columnVisibility, columnSizing, columnPinning, leadingColumns],
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const rowHeight = props.rowHeight ?? ROW_HEIGHTS[density];
|
|
159
|
+
const headerHeight = props.headerHeight ?? Math.max(40, rowHeight);
|
|
160
|
+
|
|
161
|
+
// Hand the committed props and model to the engine for event handlers.
|
|
162
|
+
useLayoutEffect(() => {
|
|
163
|
+
engine.sync(props, { columns, coreRows, sortedRows, page, layout, rowHeight, headerHeight, formatters });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const menu = usePopoverState<string>();
|
|
167
|
+
const openMenu = menu.open;
|
|
168
|
+
|
|
169
|
+
const isRowSelectable = useMemo(
|
|
170
|
+
() =>
|
|
171
|
+
typeof enableRowSelection === "function" ? enableRowSelection : () => enableRowSelection === true,
|
|
172
|
+
[enableRowSelection],
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const context = useMemo<GridContextValue<T>>(
|
|
176
|
+
() => ({
|
|
177
|
+
engine,
|
|
178
|
+
locale: localeText,
|
|
179
|
+
classNames,
|
|
180
|
+
formatters,
|
|
181
|
+
numberFormat,
|
|
182
|
+
controlledSelection: controlled?.rowSelection,
|
|
183
|
+
isRowSelectable,
|
|
184
|
+
openColumnMenu: (columnId, anchor) => openMenu(anchor, columnId),
|
|
185
|
+
features: {
|
|
186
|
+
sorting: enableSorting,
|
|
187
|
+
filtering: enableFiltering,
|
|
188
|
+
resizing: enableColumnResizing,
|
|
189
|
+
reordering: enableColumnReordering,
|
|
190
|
+
pinning: enableColumnPinning,
|
|
191
|
+
hiding: enableColumnHiding,
|
|
192
|
+
selection: hasSelection,
|
|
193
|
+
selectionMode,
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
196
|
+
[
|
|
197
|
+
engine,
|
|
198
|
+
localeText,
|
|
199
|
+
classNames,
|
|
200
|
+
formatters,
|
|
201
|
+
numberFormat,
|
|
202
|
+
controlled?.rowSelection,
|
|
203
|
+
isRowSelectable,
|
|
204
|
+
openMenu,
|
|
205
|
+
enableSorting,
|
|
206
|
+
enableFiltering,
|
|
207
|
+
enableColumnResizing,
|
|
208
|
+
enableColumnReordering,
|
|
209
|
+
enableColumnPinning,
|
|
210
|
+
enableColumnHiding,
|
|
211
|
+
hasSelection,
|
|
212
|
+
selectionMode,
|
|
213
|
+
],
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const toolbarOptions: ToolbarOptions | null =
|
|
217
|
+
toolbar === false
|
|
218
|
+
? null
|
|
219
|
+
: {
|
|
220
|
+
search: true,
|
|
221
|
+
filterChips: true,
|
|
222
|
+
columns: true,
|
|
223
|
+
density: true,
|
|
224
|
+
export: true,
|
|
225
|
+
...(toolbar === true ? {} : toolbar),
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const menuAnchor = menu.state?.anchor ?? null;
|
|
229
|
+
const menuItem = menu.state ? layout.columns.find((i) => i.column.id === menu.state?.payload) : undefined;
|
|
230
|
+
const closeMenu = () => menu.close(menuAnchor);
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<GridContext value={context as unknown as GridContextValue<unknown>}>
|
|
234
|
+
<div
|
|
235
|
+
className={cx("dg-root", classNames.root, className)}
|
|
236
|
+
style={
|
|
237
|
+
{
|
|
238
|
+
...style,
|
|
239
|
+
"--dg-row-height": `${rowHeight}px`,
|
|
240
|
+
"--dg-header-height": `${headerHeight}px`,
|
|
241
|
+
} as CSSProperties
|
|
242
|
+
}
|
|
243
|
+
data-density={density}
|
|
244
|
+
data-stale={stale || undefined}
|
|
245
|
+
>
|
|
246
|
+
{toolbarOptions && (
|
|
247
|
+
<Toolbar
|
|
248
|
+
options={toolbarOptions}
|
|
249
|
+
globalFilter={globalFilter}
|
|
250
|
+
filters={filters}
|
|
251
|
+
density={density}
|
|
252
|
+
columns={columns}
|
|
253
|
+
columnVisibility={columnVisibility}
|
|
254
|
+
/>
|
|
255
|
+
)}
|
|
256
|
+
|
|
257
|
+
<GridViewport
|
|
258
|
+
layout={layout}
|
|
259
|
+
page={page}
|
|
260
|
+
sortedRows={sortedRows}
|
|
261
|
+
sorting={sorting}
|
|
262
|
+
filters={filters}
|
|
263
|
+
hasQuery={filters.length > 0 || globalFilter !== ""}
|
|
264
|
+
rowHeight={rowHeight}
|
|
265
|
+
headerHeight={headerHeight}
|
|
266
|
+
height={autoHeight ? undefined : height}
|
|
267
|
+
loading={loading}
|
|
268
|
+
emptyState={emptyState}
|
|
269
|
+
ariaLabel={props["aria-label"] ?? localeText.gridLabel}
|
|
270
|
+
getRowClassName={getRowClassName}
|
|
271
|
+
/>
|
|
272
|
+
|
|
273
|
+
{enablePagination && (
|
|
274
|
+
<Pagination page={page} pageSize={pagination.pageSize} pageSizeOptions={pageSizeOptions} />
|
|
275
|
+
)}
|
|
276
|
+
|
|
277
|
+
{menuAnchor && menuItem && (
|
|
278
|
+
<Popover
|
|
279
|
+
anchor={menuAnchor}
|
|
280
|
+
onClose={closeMenu}
|
|
281
|
+
label={localeText.columnMenu(menuItem.column.header)}
|
|
282
|
+
className="dg-column-menu-popover"
|
|
283
|
+
>
|
|
284
|
+
<ColumnMenu
|
|
285
|
+
item={menuItem}
|
|
286
|
+
layout={layout}
|
|
287
|
+
sort={sorting.find((s) => s.columnId === menuItem.column.id)}
|
|
288
|
+
filter={filters.find((f) => f.columnId === menuItem.column.id)}
|
|
289
|
+
onClose={closeMenu}
|
|
290
|
+
/>
|
|
291
|
+
</Popover>
|
|
292
|
+
)}
|
|
293
|
+
</div>
|
|
294
|
+
</GridContext>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type FormEvent } from "react";
|
|
4
|
+
import { getFilterOperators } from "../core/filtering";
|
|
5
|
+
import type { ColumnFilter, FilterOperator, FilterValue, ResolvedColumn } from "../core/types";
|
|
6
|
+
import { useGridContext } from "./context";
|
|
7
|
+
|
|
8
|
+
interface FilterFormProps<T> {
|
|
9
|
+
column: ResolvedColumn<T>;
|
|
10
|
+
filter: ColumnFilter | undefined;
|
|
11
|
+
onDone(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const toInput = (value: FilterValue | undefined) =>
|
|
15
|
+
value === null || value === undefined || typeof value === "object" ? "" : String(value);
|
|
16
|
+
|
|
17
|
+
export function FilterForm<T>({ column, filter, onDone }: FilterFormProps<T>) {
|
|
18
|
+
const { engine, locale } = useGridContext<T>();
|
|
19
|
+
const operators = getFilterOperators(column);
|
|
20
|
+
const { options } = column.def;
|
|
21
|
+
|
|
22
|
+
const [operator, setOperator] = useState<FilterOperator>(
|
|
23
|
+
filter && operators.includes(filter.operator) ? filter.operator : operators[0],
|
|
24
|
+
);
|
|
25
|
+
const [value, setValue] = useState(() => toInput(filter?.value));
|
|
26
|
+
const [value2, setValue2] = useState(() => toInput(filter?.value2));
|
|
27
|
+
const [chosen, setChosen] = useState<ReadonlyArray<string | number>>(() =>
|
|
28
|
+
filter && Array.isArray(filter.value) ? (filter.value as ReadonlyArray<string | number>) : [],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const needsValue = operator !== "isEmpty" && operator !== "isNotEmpty";
|
|
32
|
+
const inputType = column.type === "number" ? "number" : column.type === "date" ? "date" : "text";
|
|
33
|
+
const parse = (raw: string): FilterValue =>
|
|
34
|
+
raw === "" ? null : column.type === "number" ? Number(raw) : raw;
|
|
35
|
+
|
|
36
|
+
const submit = (event: FormEvent<HTMLFormElement>) => {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
let next: Omit<ColumnFilter, "columnId"> | null;
|
|
39
|
+
if (!needsValue) next = { operator };
|
|
40
|
+
else if (operator === "in") next = chosen.length > 0 ? { operator, value: chosen } : null;
|
|
41
|
+
else if (column.type === "boolean") next = { operator, value: value !== "false" };
|
|
42
|
+
else if (operator === "between") {
|
|
43
|
+
next = value || value2 ? { operator, value: parse(value), value2: parse(value2) } : null;
|
|
44
|
+
} else next = value === "" ? null : { operator, value: parse(value) };
|
|
45
|
+
|
|
46
|
+
engine.api.setFilter(column.id, next);
|
|
47
|
+
onDone();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const clear = () => {
|
|
51
|
+
engine.api.setFilter(column.id, null);
|
|
52
|
+
onDone();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<form className="dg-filter-form" onSubmit={submit}>
|
|
57
|
+
<div className="dg-menu-label">{locale.filter}</div>
|
|
58
|
+
|
|
59
|
+
{operators.length > 1 && (
|
|
60
|
+
<select
|
|
61
|
+
className="dg-select"
|
|
62
|
+
aria-label={locale.filterOperator}
|
|
63
|
+
value={operator}
|
|
64
|
+
onChange={(e) => setOperator(e.target.value as FilterOperator)}
|
|
65
|
+
>
|
|
66
|
+
{operators.map((op) => (
|
|
67
|
+
<option key={op} value={op}>
|
|
68
|
+
{locale.operators[op]}
|
|
69
|
+
</option>
|
|
70
|
+
))}
|
|
71
|
+
</select>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{needsValue && operator === "in" && options && (
|
|
75
|
+
<div className="dg-check-list" role="group" aria-label={locale.filterValue}>
|
|
76
|
+
{options.map((option) => {
|
|
77
|
+
const checked = chosen.includes(option.value);
|
|
78
|
+
return (
|
|
79
|
+
<label key={String(option.value)}>
|
|
80
|
+
<input
|
|
81
|
+
type="checkbox"
|
|
82
|
+
className="dg-checkbox"
|
|
83
|
+
checked={checked}
|
|
84
|
+
onChange={() =>
|
|
85
|
+
setChosen(checked ? chosen.filter((v) => v !== option.value) : [...chosen, option.value])
|
|
86
|
+
}
|
|
87
|
+
/>
|
|
88
|
+
{option.label}
|
|
89
|
+
</label>
|
|
90
|
+
);
|
|
91
|
+
})}
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{needsValue && column.type === "boolean" && (
|
|
96
|
+
<select
|
|
97
|
+
className="dg-select"
|
|
98
|
+
aria-label={locale.filterValue}
|
|
99
|
+
value={value === "false" ? "false" : "true"}
|
|
100
|
+
onChange={(e) => setValue(e.target.value)}
|
|
101
|
+
>
|
|
102
|
+
<option value="true">{locale.yes}</option>
|
|
103
|
+
<option value="false">{locale.no}</option>
|
|
104
|
+
</select>
|
|
105
|
+
)}
|
|
106
|
+
|
|
107
|
+
{needsValue && operator !== "in" && column.type !== "boolean" && (
|
|
108
|
+
<>
|
|
109
|
+
<input
|
|
110
|
+
className="dg-input"
|
|
111
|
+
type={inputType}
|
|
112
|
+
value={value}
|
|
113
|
+
aria-label={operator === "between" ? locale.filterFrom : locale.filterValue}
|
|
114
|
+
placeholder={operator === "between" ? locale.filterFrom : locale.filterValue}
|
|
115
|
+
onChange={(e) => setValue(e.target.value)}
|
|
116
|
+
/>
|
|
117
|
+
{operator === "between" && (
|
|
118
|
+
<input
|
|
119
|
+
className="dg-input"
|
|
120
|
+
type={inputType}
|
|
121
|
+
value={value2}
|
|
122
|
+
aria-label={locale.filterTo}
|
|
123
|
+
placeholder={locale.filterTo}
|
|
124
|
+
onChange={(e) => setValue2(e.target.value)}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
|
|
130
|
+
<div className="dg-form-actions">
|
|
131
|
+
{filter && (
|
|
132
|
+
<button type="button" className="dg-button" onClick={clear}>
|
|
133
|
+
{locale.clearFilter}
|
|
134
|
+
</button>
|
|
135
|
+
)}
|
|
136
|
+
<button type="submit" className="dg-button" data-variant="primary">
|
|
137
|
+
{locale.applyFilter}
|
|
138
|
+
</button>
|
|
139
|
+
</div>
|
|
140
|
+
</form>
|
|
141
|
+
);
|
|
142
|
+
}
|