@linzjs/step-ag-grid 14.9.4 → 15.0.0
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 +5 -0
- package/dist/src/components/GridCellFiller.d.ts +4 -0
- package/dist/src/components/gridUtil.d.ts +3 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/contexts/GridContext.d.ts +2 -1
- package/dist/step-ag-grid.cjs.js +27801 -0
- package/dist/step-ag-grid.cjs.js.map +1 -0
- package/dist/step-ag-grid.esm.js +60 -42
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/GridCellFiller.tsx +11 -0
- package/src/components/gridFilter/GridFilterColumnsToggle.tsx +43 -53
- package/src/components/gridUtil.ts +6 -0
- package/src/components/index.ts +1 -0
- package/src/contexts/GridContext.tsx +8 -1
- package/src/contexts/GridContextProvider.tsx +53 -31
- package/src/stories/grid/GridReadOnly.stories.tsx +2 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@linzjs/step-ag-grid",
|
|
3
3
|
"repository": "github:linz/step-ag-grid.git",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "15.0.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"aggrid",
|
|
8
8
|
"ag-grid",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"react",
|
|
13
13
|
"react-component"
|
|
14
14
|
],
|
|
15
|
-
"main": "dist/step-ag-grid.
|
|
15
|
+
"main": "dist/step-ag-grid.cjs.js",
|
|
16
16
|
"type": "module",
|
|
17
17
|
"typings": "dist/src/index.d.ts",
|
|
18
18
|
"module": "dist/step-ag-grid.esm.js",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ColDef } from "ag-grid-community";
|
|
2
|
+
|
|
3
|
+
export const GridCellFillerColId = "gridCellFiller";
|
|
4
|
+
|
|
5
|
+
export const isGridCellFiller = (col: ColDef) => col.colId === GridCellFillerColId;
|
|
6
|
+
|
|
7
|
+
export const GridCellFiller = (): ColDef => ({
|
|
8
|
+
colId: GridCellFillerColId,
|
|
9
|
+
headerName: "",
|
|
10
|
+
flex: 1,
|
|
11
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isEmpty } from "lodash-es";
|
|
2
2
|
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
3
3
|
|
|
4
4
|
import { LuiCheckboxInput, LuiIcon } from "@linzjs/lui";
|
|
@@ -16,17 +16,20 @@ export interface GridFilterColumnsToggleProps {
|
|
|
16
16
|
|
|
17
17
|
export const GridFilterColumnsToggle = ({ saveState = true }: GridFilterColumnsToggleProps): JSX.Element => {
|
|
18
18
|
const [loaded, setLoaded] = useState(false);
|
|
19
|
-
const { getColumns, invisibleColumnIds, setInvisibleColumnIds } = useContext(GridContext);
|
|
19
|
+
const { getColumns, getColumnIds, invisibleColumnIds, setInvisibleColumnIds } = useContext(GridContext);
|
|
20
20
|
|
|
21
21
|
const columnStorageKey = useMemo(
|
|
22
22
|
() =>
|
|
23
|
-
isEmpty(
|
|
23
|
+
isEmpty(getColumnIds())
|
|
24
24
|
? null // Grid hasn't been initialised yet
|
|
25
|
-
: "stepAgGrid_invisibleColumnIds_" +
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
: "stepAgGrid_invisibleColumnIds_" + getColumnIds().join("_"),
|
|
26
|
+
[getColumnIds],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// infer the invisible ids from colDefs
|
|
30
|
+
const resetColumns = useCallback(
|
|
31
|
+
() => setInvisibleColumnIds(getColumnIds("initialHide")),
|
|
32
|
+
[getColumnIds, setInvisibleColumnIds],
|
|
30
33
|
);
|
|
31
34
|
|
|
32
35
|
// Load state on start
|
|
@@ -36,14 +39,7 @@ export const GridFilterColumnsToggle = ({ saveState = true }: GridFilterColumnsT
|
|
|
36
39
|
try {
|
|
37
40
|
const stored = window.localStorage.getItem(columnStorageKey);
|
|
38
41
|
if (!stored) {
|
|
39
|
-
|
|
40
|
-
setInvisibleColumnIds(
|
|
41
|
-
compact(
|
|
42
|
-
getColumns()
|
|
43
|
-
.filter((col) => col.initialHide)
|
|
44
|
-
.map((col) => col.colId),
|
|
45
|
-
),
|
|
46
|
-
);
|
|
42
|
+
resetColumns();
|
|
47
43
|
} else {
|
|
48
44
|
const invisibleIds = JSON.parse(stored ?? "[]");
|
|
49
45
|
if (!Array.isArray(invisibleIds)) {
|
|
@@ -59,7 +55,7 @@ export const GridFilterColumnsToggle = ({ saveState = true }: GridFilterColumnsT
|
|
|
59
55
|
}
|
|
60
56
|
setLoaded(true);
|
|
61
57
|
}
|
|
62
|
-
}, [columnStorageKey, getColumns, loaded, saveState, setInvisibleColumnIds]);
|
|
58
|
+
}, [columnStorageKey, getColumns, loaded, resetColumns, saveState, setInvisibleColumnIds]);
|
|
63
59
|
|
|
64
60
|
// Save state on column visibility change
|
|
65
61
|
useEffect(() => {
|
|
@@ -81,10 +77,6 @@ export const GridFilterColumnsToggle = ({ saveState = true }: GridFilterColumnsT
|
|
|
81
77
|
[invisibleColumnIds, setInvisibleColumnIds],
|
|
82
78
|
);
|
|
83
79
|
|
|
84
|
-
const resetColumns = () => {
|
|
85
|
-
setInvisibleColumnIds([]);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
80
|
const numericRegExp = /^\d+$/;
|
|
89
81
|
const isNonManageableColumn = (col: ColDefT<GridBaseRow>) => {
|
|
90
82
|
return col.lockVisible || col.colId == null || numericRegExp.test(col.colId);
|
|
@@ -98,40 +90,38 @@ export const GridFilterColumnsToggle = ({ saveState = true }: GridFilterColumnsT
|
|
|
98
90
|
unmountOnClose={true}
|
|
99
91
|
>
|
|
100
92
|
<div className={"GridFilterColumnsToggle-container"}>
|
|
101
|
-
{getColumns()
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (e.key !== "
|
|
110
|
-
|
|
111
|
-
if (e.key !== "Enter") {
|
|
112
|
-
toggleColumn(col.colId);
|
|
113
|
-
}
|
|
93
|
+
{getColumns("headerName").map((col) => (
|
|
94
|
+
<MenuItem
|
|
95
|
+
key={col.colId}
|
|
96
|
+
disabled={isNonManageableColumn(col)}
|
|
97
|
+
onClick={(e: ClickEvent) => {
|
|
98
|
+
// Global react-menu MenuItem handler handles tabs
|
|
99
|
+
if (e.key !== "Tab") {
|
|
100
|
+
e.keepOpen = true;
|
|
101
|
+
if (e.key !== "Enter") {
|
|
102
|
+
toggleColumn(col.colId);
|
|
114
103
|
}
|
|
104
|
+
}
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
<LuiCheckboxInput
|
|
108
|
+
isChecked={!!invisibleColumnIds && !invisibleColumnIds.includes(col.colId ?? "")}
|
|
109
|
+
value={`${col.colId}`}
|
|
110
|
+
label={col.headerName ?? ""}
|
|
111
|
+
isDisabled={isNonManageableColumn(col)}
|
|
112
|
+
inputProps={{
|
|
113
|
+
onClick: (e) => {
|
|
114
|
+
// Click is handled by MenuItem onClick so keyboard events work
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
e.stopPropagation();
|
|
117
|
+
},
|
|
118
|
+
}}
|
|
119
|
+
onChange={() => {
|
|
120
|
+
/*Do nothing, change handled by menuItem*/
|
|
115
121
|
}}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
value={`${col.colId}`}
|
|
120
|
-
label={col.headerName ?? ""}
|
|
121
|
-
isDisabled={isNonManageableColumn(col)}
|
|
122
|
-
inputProps={{
|
|
123
|
-
onClick: (e) => {
|
|
124
|
-
// Click is handled by MenuItem onClick so keyboard events work
|
|
125
|
-
e.preventDefault();
|
|
126
|
-
e.stopPropagation();
|
|
127
|
-
},
|
|
128
|
-
}}
|
|
129
|
-
onChange={() => {
|
|
130
|
-
/*Do nothing, change handled by menuItem*/
|
|
131
|
-
}}
|
|
132
|
-
/>
|
|
133
|
-
</MenuItem>
|
|
134
|
-
))}
|
|
122
|
+
/>
|
|
123
|
+
</MenuItem>
|
|
124
|
+
))}
|
|
135
125
|
</div>
|
|
136
126
|
<MenuDivider key={`$$divider_reset_columns`} />
|
|
137
127
|
<MenuItem
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ColDef } from "ag-grid-community";
|
|
2
|
+
|
|
3
|
+
import { isGridCellFiller } from "./GridCellFiller";
|
|
4
|
+
|
|
5
|
+
export const getColId = (colDef: ColDef): string => colDef.colId ?? "";
|
|
6
|
+
export const isFlexColumn = (colDef: ColDef): boolean => !!colDef.flex && !isGridCellFiller(colDef);
|
package/src/components/index.ts
CHANGED
|
@@ -17,7 +17,10 @@ export type AutoSizeColumnsResult = { width: number } | null;
|
|
|
17
17
|
export interface GridContextType<RowType extends GridBaseRow> {
|
|
18
18
|
gridReady: boolean;
|
|
19
19
|
getColDef: (colId?: string) => ColDef | undefined;
|
|
20
|
-
getColumns: (
|
|
20
|
+
getColumns: (
|
|
21
|
+
filter?: keyof ColDef | ((r: ColDef) => boolean | undefined | null | number | string),
|
|
22
|
+
) => ColDefT<RowType>[];
|
|
23
|
+
getColumnIds: (filter?: keyof ColDef | ((r: ColDef) => boolean | undefined | null | number | string)) => string[];
|
|
21
24
|
setApis: (gridApi: GridApi | undefined, columnApi: ColumnApi | undefined, dataTestId?: string) => void;
|
|
22
25
|
prePopupOps: () => void;
|
|
23
26
|
setQuickFilter: (quickFilter: string) => void;
|
|
@@ -71,6 +74,10 @@ export const GridContext = createContext<GridContextType<any>>({
|
|
|
71
74
|
console.error("no context provider for getColumns");
|
|
72
75
|
return [];
|
|
73
76
|
},
|
|
77
|
+
getColumnIds: () => {
|
|
78
|
+
console.error("no context provider for getColumnIds");
|
|
79
|
+
return [];
|
|
80
|
+
},
|
|
74
81
|
invisibleColumnIds: undefined,
|
|
75
82
|
setInvisibleColumnIds: () => {
|
|
76
83
|
console.error("no context provider for setInvisibleColumnIds");
|
|
@@ -3,10 +3,12 @@ import { CellPosition } from "ag-grid-community/dist/lib/entities/cellPosition";
|
|
|
3
3
|
import { ValueFormatterParams } from "ag-grid-community/dist/lib/entities/colDef";
|
|
4
4
|
import { CsvExportParams, ProcessCellForExportParams } from "ag-grid-community/dist/lib/interfaces/exportParams";
|
|
5
5
|
import debounce from "debounce-promise";
|
|
6
|
-
import { compact, defer, delay, difference, isEmpty, last, remove, sortBy, sumBy } from "lodash-es";
|
|
6
|
+
import { compact, defer, delay, difference, filter, isEmpty, last, pull, remove, sortBy, sumBy } from "lodash-es";
|
|
7
7
|
import { ReactElement, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
|
|
9
9
|
import { ColDefT, GridBaseRow } from "../components";
|
|
10
|
+
import { GridCellFillerColId, isGridCellFiller } from "../components/GridCellFiller";
|
|
11
|
+
import { getColId, isFlexColumn } from "../components/gridUtil";
|
|
10
12
|
import { isNotEmpty, sanitiseFileName, wait } from "../utils/util";
|
|
11
13
|
import { AutoSizeColumnsProps, AutoSizeColumnsResult, GridContext, GridFilterExternal } from "./GridContext";
|
|
12
14
|
import { GridUpdatingContext } from "./GridUpdatingContext";
|
|
@@ -26,13 +28,19 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
26
28
|
const [columnApi, setColumnApi] = useState<ColumnApi>();
|
|
27
29
|
const [gridReady, setGridReady] = useState(false);
|
|
28
30
|
const [quickFilter, setQuickFilter] = useState("");
|
|
29
|
-
const [invisibleColumnIds,
|
|
31
|
+
const [invisibleColumnIds, _setInvisibleColumnIds] = useState<string[]>();
|
|
30
32
|
const testId = useRef<string | undefined>();
|
|
31
33
|
const idsBeforeUpdate = useRef<number[]>([]);
|
|
32
34
|
const prePopupFocusedCell = useRef<CellPosition>();
|
|
33
35
|
const [externallySelectedItemsAreInSync, setExternallySelectedItemsAreInSync] = useState(false);
|
|
34
36
|
const externalFilters = useRef<GridFilterExternal<RowType>[]>([]);
|
|
35
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Make extra sure the GridCellFillerColId never gets added to invisibleColumnIds as it's dynamically determined
|
|
40
|
+
*/
|
|
41
|
+
const setInvisibleColumnIds = (invisibleColumnIds: string[]) =>
|
|
42
|
+
_setInvisibleColumnIds(pull(invisibleColumnIds, GridCellFillerColId));
|
|
43
|
+
|
|
36
44
|
/**
|
|
37
45
|
* Set quick filter directly on grid, based on previously save quickFilter state.
|
|
38
46
|
*/
|
|
@@ -152,9 +160,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
152
160
|
const _getNewNodes = useCallback((): RowNode[] => {
|
|
153
161
|
return gridApiOp(
|
|
154
162
|
(gridApi) =>
|
|
155
|
-
difference(_getAllRowIds(), idsBeforeUpdate.current)
|
|
156
|
-
.map((rowId) => gridApi.getRowNode("" + rowId)) //
|
|
157
|
-
.filter((r) => r) as RowNode[],
|
|
163
|
+
compact(difference(_getAllRowIds(), idsBeforeUpdate.current).map((rowId) => gridApi.getRowNode("" + rowId))),
|
|
158
164
|
() => [],
|
|
159
165
|
);
|
|
160
166
|
}, [_getAllRowIds, gridApiOp]);
|
|
@@ -168,10 +174,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
168
174
|
const _rowIdsToNodes = useCallback(
|
|
169
175
|
(rowIds: number[]): RowNode[] => {
|
|
170
176
|
return gridApiOp(
|
|
171
|
-
(gridApi) =>
|
|
172
|
-
rowIds
|
|
173
|
-
.map((rowId) => gridApi.getRowNode("" + rowId)) //
|
|
174
|
-
.filter((r) => r) as RowNode[],
|
|
177
|
+
(gridApi) => compact(rowIds.map((rowId) => gridApi.getRowNode("" + rowId))),
|
|
175
178
|
() => [] as RowNode[],
|
|
176
179
|
);
|
|
177
180
|
},
|
|
@@ -181,11 +184,20 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
181
184
|
/**
|
|
182
185
|
* Get ColDefs, with flattened ColGroupDefs
|
|
183
186
|
*/
|
|
184
|
-
const getColumns
|
|
185
|
-
(
|
|
187
|
+
const getColumns = useCallback(
|
|
188
|
+
(
|
|
189
|
+
filterDef: keyof ColDef | ((r: ColDef) => boolean | undefined | null | number | string) = () => true,
|
|
190
|
+
): ColDefT<RowType>[] =>
|
|
191
|
+
filter(columnApi?.getAllColumns()?.map((col) => col.getColDef()) ?? [], filterDef) as ColDefT<RowType>[],
|
|
186
192
|
[columnApi],
|
|
187
193
|
);
|
|
188
194
|
|
|
195
|
+
const getColumnIds = useCallback(
|
|
196
|
+
(filterDef: keyof ColDef | ((r: ColDef) => boolean | undefined | null | number | string) = () => true): string[] =>
|
|
197
|
+
compact(getColumns(filterDef).map(getColId)),
|
|
198
|
+
[getColumns],
|
|
199
|
+
);
|
|
200
|
+
|
|
189
201
|
/**
|
|
190
202
|
* Internal method for selecting and flashing rows.
|
|
191
203
|
*
|
|
@@ -544,9 +556,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
544
556
|
|
|
545
557
|
const redrawRows = useCallback(
|
|
546
558
|
(rowNodes?: RowNode[]) => {
|
|
547
|
-
gridApiOp((gridApi) => {
|
|
548
|
-
gridApi.redrawRows(rowNodes ? { rowNodes } : undefined);
|
|
549
|
-
});
|
|
559
|
+
gridApiOp((gridApi) => gridApi.redrawRows(rowNodes ? { rowNodes } : undefined));
|
|
550
560
|
},
|
|
551
561
|
[gridApiOp],
|
|
552
562
|
);
|
|
@@ -582,31 +592,39 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
582
592
|
onFilterChanged().then();
|
|
583
593
|
};
|
|
584
594
|
|
|
585
|
-
const isExternalFilterPresent = (): boolean => externalFilters.current
|
|
595
|
+
const isExternalFilterPresent = (): boolean => !isEmpty(externalFilters.current);
|
|
586
596
|
|
|
587
|
-
const doesExternalFilterPass = (node: RowNode): boolean =>
|
|
588
|
-
|
|
589
|
-
};
|
|
597
|
+
const doesExternalFilterPass = (node: RowNode): boolean =>
|
|
598
|
+
externalFilters.current.every((filter) => filter(node.data, node));
|
|
590
599
|
|
|
591
600
|
const getColDef = useCallback(
|
|
592
601
|
(colId?: string): ColDef | undefined => (!!colId && gridApi?.getColumnDef(colId)) || undefined,
|
|
593
602
|
[gridApi],
|
|
594
603
|
);
|
|
595
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Apply column visibility
|
|
607
|
+
*/
|
|
596
608
|
useEffect(() => {
|
|
597
|
-
if (columnApi
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
);
|
|
607
|
-
|
|
608
|
-
columnApi.setColumnsVisible(invisibleColumnIds, false);
|
|
609
|
+
if (!columnApi || !invisibleColumnIds) return;
|
|
610
|
+
|
|
611
|
+
// show all columns that aren't invisible
|
|
612
|
+
const newVisibleColumns = getColumns(
|
|
613
|
+
(col) => !col.lockVisible && col.colId && !invisibleColumnIds.includes(col.colId) && !isGridCellFiller(col),
|
|
614
|
+
);
|
|
615
|
+
// If there's no flex column showing add the filler column if defined
|
|
616
|
+
const visibleColumnsContainsAFlex = newVisibleColumns.some(isFlexColumn);
|
|
617
|
+
if (!visibleColumnsContainsAFlex) {
|
|
618
|
+
const fillerColumn = getColumns(isGridCellFiller)[0];
|
|
619
|
+
fillerColumn && newVisibleColumns.push(fillerColumn);
|
|
609
620
|
}
|
|
621
|
+
columnApi.setColumnsVisible(compact(newVisibleColumns.map(getColId)), true);
|
|
622
|
+
|
|
623
|
+
// Hide the filler column if there's already a flex column
|
|
624
|
+
const invisibleColumnIdsWithOptionalFiller = visibleColumnsContainsAFlex
|
|
625
|
+
? [...invisibleColumnIds, GridCellFillerColId]
|
|
626
|
+
: invisibleColumnIds;
|
|
627
|
+
columnApi.setColumnsVisible(invisibleColumnIdsWithOptionalFiller, false);
|
|
610
628
|
}, [invisibleColumnIds, columnApi, getColumns]);
|
|
611
629
|
|
|
612
630
|
/**
|
|
@@ -620,7 +638,10 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
620
638
|
|
|
621
639
|
const columnKeys = columnApi
|
|
622
640
|
?.getColumnState()
|
|
623
|
-
.filter((cs) =>
|
|
641
|
+
.filter((cs) => {
|
|
642
|
+
const colDef = gridApi.getColumnDef(cs.colId);
|
|
643
|
+
return !cs.hide && colDef && !isGridCellFiller(colDef) && colDef.headerComponentParams?.exportable !== false;
|
|
644
|
+
})
|
|
624
645
|
.map((cs) => cs.colId);
|
|
625
646
|
gridApi.exportDataAsCsv({
|
|
626
647
|
columnKeys,
|
|
@@ -637,6 +658,7 @@ export const GridContextProvider = <RowType extends GridBaseRow>(props: GridCont
|
|
|
637
658
|
value={{
|
|
638
659
|
getColDef,
|
|
639
660
|
getColumns,
|
|
661
|
+
getColumnIds,
|
|
640
662
|
invisibleColumnIds,
|
|
641
663
|
setInvisibleColumnIds,
|
|
642
664
|
gridReady,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../..";
|
|
26
26
|
import { GridFilterColumnsToggle } from "../../components";
|
|
27
27
|
import { GridFilterDownloadCsvButton } from "../../components";
|
|
28
|
+
import { GridCellFiller } from "../../components/GridCellFiller";
|
|
28
29
|
import "../../styles/GridTheme.scss";
|
|
29
30
|
import "../../styles/index.scss";
|
|
30
31
|
|
|
@@ -97,6 +98,7 @@ const GridReadOnlyTemplate: ComponentStory<typeof Grid> = (props: GridProps) =>
|
|
|
97
98
|
flex: 1,
|
|
98
99
|
initialHide: true,
|
|
99
100
|
}),
|
|
101
|
+
GridCellFiller(),
|
|
100
102
|
GridPopoverMessage(
|
|
101
103
|
{
|
|
102
104
|
headerName: "Popout message",
|