@linzjs/step-ag-grid 29.13.0 → 29.14.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/dist/index.css +26 -2
- package/dist/src/components/Grid.d.ts +36 -61
- package/dist/src/components/GridRangeSelectContextMenu.d.ts +10 -0
- package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
- package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +2 -1
- package/dist/src/components/gridHook/useGridContextMenu.d.ts +12 -7
- package/dist/src/components/gridHook/useGridCopy.d.ts +15 -0
- package/dist/src/components/gridHook/useGridCopySettings.d.ts +11 -0
- package/dist/src/components/gridHook/useGridRangeSelection.d.ts +25 -0
- package/dist/src/components/gridPopoverEdit/GridPopoverEditBearing.d.ts +2 -0
- package/dist/src/components/types.d.ts +1 -0
- package/dist/src/contexts/GridContext.d.ts +1 -0
- package/dist/src/lui/timeoutHook.d.ts +0 -6
- package/dist/src/lui/tsUtils.d.ts +5 -0
- package/dist/src/utils/__tests__/random.ts +19 -0
- package/dist/src/utils/util.d.ts +1 -0
- package/dist/step-ag-grid.cjs +560 -79
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +561 -82
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Grid.tsx +249 -179
- package/src/components/GridRangeSelectContextMenu.tsx +73 -0
- package/src/components/gridForm/GridFormDropDown.tsx +1 -3
- package/src/components/gridForm/GridFormPopoverMenu.tsx +2 -1
- package/src/components/gridHook/useGridContextMenu.tsx +22 -9
- package/src/components/gridHook/useGridCopy.ts +279 -0
- package/src/components/gridHook/useGridCopySettings.ts +28 -0
- package/src/components/gridHook/useGridRangeSelection.ts +235 -0
- package/src/components/gridPopoverEdit/GridPopoverEditBearing.ts +3 -0
- package/src/components/types.ts +2 -0
- package/src/contexts/GridContext.tsx +2 -0
- package/src/contexts/GridContextProvider.tsx +5 -2
- package/src/lui/timeoutHook.tsx +0 -19
- package/src/lui/tsUtils.ts +8 -0
- package/src/stories/grid/GridCopy.stories.tsx +175 -0
- package/src/stories/grid/GridPopoverEditBearing.stories.tsx +4 -4
- package/src/stories/grid/GridReadOnly.stories.tsx +1 -0
- package/src/stories/grid/interactions/GridKeyboardInteractions.stories.tsx +2 -2
- package/src/styles/ContextMenu.scss +61 -0
- package/src/styles/Grid.scss +26 -2
- package/src/utils/__tests__/random.ts +19 -0
- package/src/utils/bearing.ts +2 -2
- package/src/utils/util.ts +2 -0
package/package.json
CHANGED
package/src/components/Grid.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
CellKeyDownEvent,
|
|
9
9
|
ColDef,
|
|
10
10
|
ColGroupDef,
|
|
11
|
+
ColumnMovedEvent,
|
|
11
12
|
ColumnResizedEvent,
|
|
12
13
|
EditableCallback,
|
|
13
14
|
EditableCallbackParams,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
GridOptions,
|
|
16
17
|
GridReadyEvent,
|
|
17
18
|
GridSizeChangedEvent,
|
|
19
|
+
IRowNode,
|
|
18
20
|
ModelUpdatedEvent,
|
|
19
21
|
ModuleRegistry,
|
|
20
22
|
RowClickedEvent,
|
|
@@ -37,6 +39,8 @@ import { compareNaturalInsensitive, fnOrVar, isNotEmpty } from '../utils/util';
|
|
|
37
39
|
import { clickInputWhenContainingCellClicked } from './clickInputWhenContainingCellClicked';
|
|
38
40
|
import { GridHeaderSelect } from './gridHeader';
|
|
39
41
|
import { GridContextMenuComponent, useGridContextMenu } from './gridHook';
|
|
42
|
+
import { useGridCopy } from './gridHook/useGridCopy';
|
|
43
|
+
import { CellLocation, useGridRangeSelection } from './gridHook/useGridRangeSelection';
|
|
40
44
|
import { GridNoRowsOverlay } from './GridNoRowsOverlay';
|
|
41
45
|
import { usePostSortRowsHook } from './PostSortRowsHook';
|
|
42
46
|
import { GridBaseRow, GridOnRowDragEndProps } from './types';
|
|
@@ -44,135 +48,148 @@ import { GridBaseRow, GridOnRowDragEndProps } from './types';
|
|
|
44
48
|
ModuleRegistry.registerModules([AllCommunityModule]);
|
|
45
49
|
|
|
46
50
|
export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
|
|
47
|
-
readOnly?: boolean; // set all editables to false when read only, make all styles black, otherwise style is gray for not editable
|
|
48
|
-
defaultPostSort?: boolean; // Retain sort order after edit, Defaults to true.
|
|
49
|
-
selectable?: boolean;
|
|
50
|
-
enableClickSelection?: boolean;
|
|
51
|
-
enableSelectionWithoutKeys?: boolean;
|
|
52
|
-
hideSelectColumn?: boolean;
|
|
53
|
-
theme?: string; // should have prefix ag-theme-
|
|
54
51
|
['data-testid']?: string;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
theme?: string;
|
|
53
|
+
|
|
54
|
+
// ─── Grid State ────────────────────────────────────────────────
|
|
55
|
+
loading?: boolean;
|
|
56
|
+
readOnly?: boolean;
|
|
57
|
+
suppressReadOnlyStyle?: boolean;
|
|
58
|
+
|
|
59
|
+
// ─── Data & Columns ────────────────────────────────────────────
|
|
61
60
|
columnDefs: ColDef<TData>[] | ColGroupDef<TData>[];
|
|
61
|
+
defaultColDef?: GridOptions['defaultColDef'];
|
|
62
|
+
defaultPostSort?: boolean;
|
|
63
|
+
domLayout?: GridOptions['domLayout'];
|
|
64
|
+
pinnedBottomRowData?: GridOptions['pinnedBottomRowData'];
|
|
65
|
+
pinnedTopRowData?: GridOptions['pinnedTopRowData'];
|
|
62
66
|
rowData: GridOptions['rowData'];
|
|
63
|
-
selectColumnPinned?: ColDef['pinned'];
|
|
64
|
-
noRowsOverlayText?: string;
|
|
65
|
-
noRowsMatchingOverlayText?: string;
|
|
66
|
-
animateRows?: boolean;
|
|
67
|
-
rowHeight?: number;
|
|
68
67
|
rowClassRules?: GridOptions['rowClassRules'];
|
|
68
|
+
rowHeight?: number;
|
|
69
69
|
rowSelection?: 'single' | 'multiple';
|
|
70
|
+
sizeColumns?: 'fit' | 'auto' | 'auto-skip-headers' | 'none';
|
|
71
|
+
|
|
72
|
+
// ─── Selection ────────────────────────────────────────────────
|
|
70
73
|
autoSelectFirstRow?: boolean;
|
|
74
|
+
enableClickSelection?: boolean;
|
|
75
|
+
enableRangeSelection?: boolean;
|
|
76
|
+
enableSelectionWithoutKeys?: boolean;
|
|
77
|
+
externalSelectedIds?: TData['id'][];
|
|
78
|
+
externalSelectedItems?: TData[];
|
|
79
|
+
hideSelectColumn?: boolean;
|
|
80
|
+
selectColumnPinned?: ColDef['pinned'];
|
|
81
|
+
selectable?: boolean;
|
|
82
|
+
setExternalSelectedIds?: (ids: TData['id'][]) => void;
|
|
83
|
+
setExternalSelectedItems?: (items: TData[]) => void;
|
|
84
|
+
|
|
85
|
+
// ─── Editing ──────────────────────────────────────────────────
|
|
86
|
+
onBulkEditingComplete?: () => Promise<void> | void;
|
|
87
|
+
singleClickEdit?: boolean;
|
|
88
|
+
|
|
89
|
+
// ─── Context Menu ─────────────────────────────────────────────
|
|
90
|
+
contextMenu?: GridContextMenuComponent<TData>;
|
|
91
|
+
contextMenuSelectRow?: boolean;
|
|
92
|
+
|
|
93
|
+
// ─── Callbacks / Events ───────────────────────────────────────
|
|
71
94
|
onCellFocused?: (props: { colDef: ColDef<TData>; data: TData }) => void;
|
|
72
95
|
onColumnMoved?: GridOptions['onColumnMoved'];
|
|
73
|
-
rowDragText?: GridOptions['rowDragText'];
|
|
74
|
-
onRowDragEnd?: (props: GridOnRowDragEndProps<TData>) => Promise<void> | void;
|
|
75
|
-
alwaysShowVerticalScroll?: boolean;
|
|
76
|
-
suppressColumnVirtualization?: GridOptions['suppressColumnVirtualisation'];
|
|
77
|
-
suppressReadOnlyStyle?: boolean;
|
|
78
|
-
/**
|
|
79
|
-
* When the grid is rendered using sizeColumns=="auto" this is called initially with the required container size to fit all content.
|
|
80
|
-
* This allows you set the size of the panel to fit perfectly.
|
|
81
|
-
*/
|
|
82
96
|
onContentSize?: (props: { width: number }) => void;
|
|
83
|
-
/**
|
|
84
|
-
* <ul>
|
|
85
|
-
* <li>"none" to use aggrid defaults.</li>
|
|
86
|
-
* <li>"fit" will adjust columns to fit within panel via min/max/initial sizing.
|
|
87
|
-
* <b>Note:</b> This is only really needed if you have auto-height columns which prevents "auto" from working.
|
|
88
|
-
* </li>
|
|
89
|
-
* <li>"auto" will size columns based on their content but still obeying min/max sizing.</li>
|
|
90
|
-
* <li>"auto-skip-headers" (default) same as auto but does not take headers into account.</li>
|
|
91
|
-
* </ul>
|
|
92
|
-
*
|
|
93
|
-
* If you want to stretch to container width if width is greater than the container add a flex column.
|
|
94
|
-
*/
|
|
95
|
-
sizeColumns?: 'fit' | 'auto' | 'auto-skip-headers' | 'none';
|
|
96
|
-
/**
|
|
97
|
-
* On first don't return a content size larger than this.
|
|
98
|
-
*/
|
|
99
|
-
maxInitialWidth?: number;
|
|
100
|
-
/**
|
|
101
|
-
* When pressing tab whilst editing the grid will select and edit the next cell if available.
|
|
102
|
-
* Once the last cell to edit closes this callback is called.
|
|
103
|
-
*/
|
|
104
|
-
onBulkEditingComplete?: () => Promise<void> | void;
|
|
105
97
|
|
|
106
98
|
/**
|
|
107
|
-
*
|
|
99
|
+
* @deprecated You should drive your app off selection states. This will be deleted.
|
|
108
100
|
*/
|
|
109
|
-
|
|
110
|
-
|
|
101
|
+
onRowClicked?: (event: RowClickedEvent) => void;
|
|
111
102
|
/**
|
|
112
|
-
*
|
|
103
|
+
* @deprecated You should drive your app off selection states. This will be deleted.
|
|
113
104
|
*/
|
|
114
|
-
|
|
105
|
+
onRowDoubleClicked?: (event: RowDoubleClickedEvent) => void;
|
|
106
|
+
onRowDragEnd?: (props: GridOnRowDragEndProps<TData>) => Promise<void> | void;
|
|
115
107
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
108
|
+
// ─── Row Behavior ─────────────────────────────────────────────
|
|
109
|
+
animateRows?: boolean;
|
|
110
|
+
alwaysShowVerticalScroll?: boolean;
|
|
111
|
+
rowDragText?: GridOptions['rowDragText'];
|
|
120
112
|
|
|
121
|
-
|
|
113
|
+
// ─── Overlays / Messages ──────────────────────────────────────
|
|
114
|
+
noRowsOverlayText?: string;
|
|
115
|
+
noRowsMatchingOverlayText?: string;
|
|
116
|
+
|
|
117
|
+
// ─── Miscellaneous ────────────────────────────────────────────
|
|
118
|
+
maxInitialWidth?: number;
|
|
122
119
|
suppressCellFocus?: boolean;
|
|
123
|
-
|
|
124
|
-
pinnedBottomRowData?: GridOptions['pinnedBottomRowData'];
|
|
125
|
-
onRowClicked?: (event: RowClickedEvent) => void;
|
|
126
|
-
onRowDoubleClicked?: (event: RowDoubleClickedEvent) => void;
|
|
120
|
+
suppressColumnVirtualization?: GridOptions['suppressColumnVirtualisation'];
|
|
127
121
|
}
|
|
128
122
|
|
|
129
123
|
/**
|
|
130
124
|
* Wrapper for AgGrid to add commonly used functionality.
|
|
131
125
|
*/
|
|
132
126
|
export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
127
|
+
theme = 'ag-theme-step-default',
|
|
133
128
|
'data-testid': dataTestId,
|
|
129
|
+
|
|
130
|
+
// ─── Grid State ───────────────────────────────
|
|
131
|
+
suppressReadOnlyStyle = false,
|
|
132
|
+
|
|
133
|
+
// ─── Data & Columns ───────────────────────────
|
|
134
134
|
defaultPostSort = true,
|
|
135
|
+
rowData,
|
|
136
|
+
rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40,
|
|
135
137
|
rowSelection = 'multiple',
|
|
136
|
-
suppressColumnVirtualization = true,
|
|
137
|
-
theme = 'ag-theme-step-default',
|
|
138
138
|
sizeColumns = 'auto',
|
|
139
|
+
|
|
140
|
+
// ─── Selection ────────────────────────────────
|
|
141
|
+
autoSelectFirstRow,
|
|
142
|
+
enableRangeSelection = true,
|
|
143
|
+
externalSelectedIds,
|
|
144
|
+
externalSelectedItems,
|
|
139
145
|
selectColumnPinned = 'left',
|
|
140
|
-
contextMenuSelectRow = false,
|
|
141
|
-
singleClickEdit = false,
|
|
142
|
-
rowData,
|
|
143
|
-
rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40,
|
|
144
146
|
selectable,
|
|
145
|
-
|
|
147
|
+
setExternalSelectedIds,
|
|
148
|
+
setExternalSelectedItems,
|
|
149
|
+
|
|
150
|
+
// ─── Editing ──────────────────────────────────
|
|
151
|
+
singleClickEdit = false,
|
|
152
|
+
|
|
153
|
+
// ─── Context Menu ─────────────────────────────
|
|
154
|
+
contextMenuSelectRow = false,
|
|
155
|
+
contextMenu,
|
|
156
|
+
|
|
157
|
+
// ─── Callbacks / Events ───────────────────────
|
|
146
158
|
onCellFocused: paramsOnCellFocused,
|
|
159
|
+
onColumnMoved,
|
|
160
|
+
|
|
161
|
+
// ─── Row Behavior ─────────────────────────────
|
|
162
|
+
suppressColumnVirtualization = true,
|
|
163
|
+
|
|
164
|
+
// ─── Miscellaneous ────────────────────────────
|
|
147
165
|
maxInitialWidth,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
setExternalSelectedItems,
|
|
151
|
-
externalSelectedIds,
|
|
152
|
-
setExternalSelectedIds,
|
|
166
|
+
|
|
167
|
+
// ─── Spread Remaining Params ──────────────────
|
|
153
168
|
...params
|
|
154
169
|
}: GridProps<TData>): ReactElement => {
|
|
155
170
|
const {
|
|
171
|
+
setApis,
|
|
172
|
+
setExternallySelectedItemsAreInSync,
|
|
173
|
+
setOnBulkEditingComplete,
|
|
156
174
|
gridReady,
|
|
157
175
|
gridRenderState,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
getFirstRowId,
|
|
161
|
-
selectRowsById,
|
|
162
|
-
focusByRowById,
|
|
163
|
-
ensureSelectedRowIsVisible,
|
|
176
|
+
externallySelectedItemsAreInSync,
|
|
177
|
+
showNoRowsOverlay,
|
|
164
178
|
autoSizeColumns,
|
|
165
179
|
sizeColumnsToFit,
|
|
166
|
-
externallySelectedItemsAreInSync,
|
|
167
|
-
setExternallySelectedItemsAreInSync,
|
|
168
|
-
isExternalFilterPresent,
|
|
169
180
|
doesExternalFilterPass,
|
|
170
|
-
|
|
181
|
+
ensureRowVisible,
|
|
182
|
+
ensureSelectedRowIsVisible,
|
|
183
|
+
focusByRowById,
|
|
171
184
|
getColDef,
|
|
172
|
-
|
|
185
|
+
getFirstRowId,
|
|
186
|
+
isExternalFilterPresent,
|
|
173
187
|
prePopupOps,
|
|
188
|
+
selectRowsById,
|
|
189
|
+
|
|
174
190
|
startCellEditing: propStartCellEditing,
|
|
175
191
|
} = useGridContext<TData>();
|
|
192
|
+
|
|
176
193
|
// CellEditingStop event happens too much for one edit
|
|
177
194
|
const startedEditRef = useRef(false);
|
|
178
195
|
const startCellEditing = useCallback(
|
|
@@ -328,7 +345,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
328
345
|
return;
|
|
329
346
|
}
|
|
330
347
|
hasSelectedFirstItem.current = true;
|
|
331
|
-
if (isNotEmpty(rowData) && isEmpty(externalSelectedItems)) {
|
|
348
|
+
if (isNotEmpty(rowData) && isEmpty(externalSelectedItems) && isEmpty(externalSelectedIds)) {
|
|
332
349
|
const firstRowId = getFirstRowId();
|
|
333
350
|
if (autoSelectFirstRow && selectable) {
|
|
334
351
|
selectRowsById([firstRowId]);
|
|
@@ -346,6 +363,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
346
363
|
selectRowsById,
|
|
347
364
|
getFirstRowId,
|
|
348
365
|
selectable,
|
|
366
|
+
externalSelectedIds,
|
|
349
367
|
]);
|
|
350
368
|
|
|
351
369
|
/**
|
|
@@ -544,19 +562,6 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
544
562
|
[startCellEditing],
|
|
545
563
|
);
|
|
546
564
|
|
|
547
|
-
/**
|
|
548
|
-
* Handle single click edits
|
|
549
|
-
*/
|
|
550
|
-
const onCellClicked = useCallback(
|
|
551
|
-
(event: CellClickedEvent) => {
|
|
552
|
-
const editable = fnOrVar(event.colDef?.editable, event);
|
|
553
|
-
if ((editable && event.colDef.singleClickEdit) ?? singleClickEdit) {
|
|
554
|
-
void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
|
|
555
|
-
}
|
|
556
|
-
},
|
|
557
|
-
[singleClickEdit, startCellEditing],
|
|
558
|
-
);
|
|
559
|
-
|
|
560
565
|
const onCellEditingStopped = useCallback(
|
|
561
566
|
(event: AgGridEvent<TData>) => {
|
|
562
567
|
if (!startedEditRef.current) {
|
|
@@ -613,6 +618,93 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
613
618
|
[prePopupOps, startCellEditing],
|
|
614
619
|
);
|
|
615
620
|
|
|
621
|
+
const { cellContextMenu, contextMenuComponent } = useGridContextMenu({
|
|
622
|
+
contextMenu,
|
|
623
|
+
contextMenuSelectRow,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
const hasSelectedMoreThanOneCellRef = useRef(false);
|
|
627
|
+
const rangeStartRef = useRef<CellLocation | null>(null);
|
|
628
|
+
const rangeEndRef = useRef<CellLocation | null>(null);
|
|
629
|
+
const rangeSortedNodesRef = useRef<IRowNode<TData>[] | null>(null);
|
|
630
|
+
|
|
631
|
+
const { clearRangeSelection, ranges, onCellMouseDown, onCellMouseOver } = useGridRangeSelection({
|
|
632
|
+
enableRangeSelection,
|
|
633
|
+
gridDivRef,
|
|
634
|
+
rangeStartRef,
|
|
635
|
+
rangeEndRef,
|
|
636
|
+
hasSelectedMoreThanOneCellRef,
|
|
637
|
+
rangeSortedNodesRef,
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
const { onCopyEvent, rangeSelectInterceptContextMenu, rangeSelectContextMenuComponent } = useGridCopy({
|
|
641
|
+
ranges,
|
|
642
|
+
rangeStartRef,
|
|
643
|
+
rangeEndRef,
|
|
644
|
+
hasSelectedMoreThanOneCellRef,
|
|
645
|
+
cellContextMenu,
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
const onSortChanged = useCallback(() => {
|
|
649
|
+
clearRangeSelection();
|
|
650
|
+
ensureSelectedRowIsVisible();
|
|
651
|
+
}, [clearRangeSelection, ensureSelectedRowIsVisible]);
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Handle single click edits
|
|
655
|
+
*/
|
|
656
|
+
const onCellClicked = useCallback(
|
|
657
|
+
(event: CellClickedEvent) => {
|
|
658
|
+
if (rangeStartRef.current && rangeEndRef.current) {
|
|
659
|
+
// This is to detect difference between a single click and a click drag return to cell.
|
|
660
|
+
if (rangeEndRef.current.timestamp - rangeStartRef.current.timestamp < 100) {
|
|
661
|
+
clearRangeSelection();
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const editable = fnOrVar(event.colDef?.editable, event);
|
|
666
|
+
if ((editable && event.colDef.singleClickEdit) ?? singleClickEdit) {
|
|
667
|
+
void startCellEditing({ rowId: event.data.id, colId: event.column.getColId() });
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
[clearRangeSelection, singleClickEdit, startCellEditing],
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Set of column I'd's that are prevented from auto-sizing as they are user set
|
|
675
|
+
*/
|
|
676
|
+
const userSizedColIds = useRef(new Map<string, number>());
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Lock/unlock column width on user edit/reset.
|
|
680
|
+
*/
|
|
681
|
+
const onColumnResized = useCallback((e: ColumnResizedEvent) => {
|
|
682
|
+
const colId = e.column?.getColId();
|
|
683
|
+
if (colId == null) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
const width = e.column?.getActualWidth();
|
|
687
|
+
if (width == null) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
switch (e.source) {
|
|
691
|
+
case 'uiColumnResized':
|
|
692
|
+
userSizedColIds.current.set(colId, width);
|
|
693
|
+
break;
|
|
694
|
+
case 'autosizeColumns':
|
|
695
|
+
userSizedColIds.current.delete(colId);
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
}, []);
|
|
699
|
+
|
|
700
|
+
const columnMoved = useCallback(
|
|
701
|
+
(e: ColumnMovedEvent<TData>) => {
|
|
702
|
+
clearRangeSelection();
|
|
703
|
+
onColumnMoved?.(e);
|
|
704
|
+
},
|
|
705
|
+
[clearRangeSelection, onColumnMoved],
|
|
706
|
+
);
|
|
707
|
+
|
|
616
708
|
/**
|
|
617
709
|
* Once the grid has auto-sized we want to run fit to fit the grid in its container,
|
|
618
710
|
* but we don't want the non-flex auto-sized columns to "fit" size, so suppressSizeToFit is set to true.
|
|
@@ -730,40 +822,6 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
730
822
|
prevLoading.current = newLoading;
|
|
731
823
|
}, [params.loading, rowData, showNoRowsOverlay]);
|
|
732
824
|
|
|
733
|
-
/**
|
|
734
|
-
* Set of column I'd's that are prevented from auto-sizing as they are user set
|
|
735
|
-
*/
|
|
736
|
-
const userSizedColIds = useRef(new Map<string, number>());
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* Lock/unlock column width on user edit/reset.
|
|
740
|
-
*/
|
|
741
|
-
const onColumnResized = useCallback((e: ColumnResizedEvent) => {
|
|
742
|
-
const colId = e.column?.getColId();
|
|
743
|
-
if (colId == null) {
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
const width = e.column?.getActualWidth();
|
|
747
|
-
if (width == null) {
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
switch (e.source) {
|
|
751
|
-
case 'uiColumnResized':
|
|
752
|
-
userSizedColIds.current.set(colId, width);
|
|
753
|
-
/*const colDef = e.column?.getColDef();
|
|
754
|
-
if (!colDef?.flex) {
|
|
755
|
-
onGridResize(e);
|
|
756
|
-
}*/
|
|
757
|
-
break;
|
|
758
|
-
case 'autosizeColumns':
|
|
759
|
-
userSizedColIds.current.delete(colId);
|
|
760
|
-
//onGridResize(e);
|
|
761
|
-
break;
|
|
762
|
-
}
|
|
763
|
-
}, []);
|
|
764
|
-
|
|
765
|
-
const gridContextMenu = useGridContextMenu({ contextMenu: params.contextMenu, contextMenuSelectRow });
|
|
766
|
-
|
|
767
825
|
const startDragYRef = useRef<number | null>(null);
|
|
768
826
|
|
|
769
827
|
const clearHighlightRowClasses = useCallback(() => {
|
|
@@ -929,9 +987,18 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
929
987
|
}
|
|
930
988
|
return false;
|
|
931
989
|
},
|
|
932
|
-
|
|
990
|
+
// enableClickSelection = true means ag-grid auto selects row if you click outside the checkbox
|
|
991
|
+
// if it's false we have to do it.
|
|
992
|
+
onCellClicked: params.enableClickSelection ? undefined : clickInputWhenContainingCellClicked,
|
|
933
993
|
};
|
|
934
|
-
}, [
|
|
994
|
+
}, [
|
|
995
|
+
params.hideSelectColumn,
|
|
996
|
+
params.onRowDragEnd,
|
|
997
|
+
rowSelection,
|
|
998
|
+
selectColumnPinned,
|
|
999
|
+
selectable,
|
|
1000
|
+
params.enableClickSelection,
|
|
1001
|
+
]);
|
|
935
1002
|
|
|
936
1003
|
const onGridSizeChanged = useCallback(
|
|
937
1004
|
(event: GridSizeChangedEvent<TData>) => {
|
|
@@ -953,10 +1020,52 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
953
1020
|
gridReady && rowData && autoSized && 'Grid-ready',
|
|
954
1021
|
)}
|
|
955
1022
|
>
|
|
956
|
-
{
|
|
957
|
-
|
|
1023
|
+
{contextMenuComponent}
|
|
1024
|
+
{rangeSelectContextMenuComponent}
|
|
1025
|
+
<div style={{ flex: 1 }} ref={gridDivRef} onCopy={onCopyEvent}>
|
|
958
1026
|
<AgGridReact
|
|
959
1027
|
theme={'legacy'}
|
|
1028
|
+
domLayout={params.domLayout}
|
|
1029
|
+
defaultColDef={defaultColDef}
|
|
1030
|
+
columnDefs={columnDefsAdjusted}
|
|
1031
|
+
getRowId={getRowId}
|
|
1032
|
+
rowData={rowData}
|
|
1033
|
+
alwaysShowVerticalScroll={params.alwaysShowVerticalScroll}
|
|
1034
|
+
animateRows={params.animateRows ?? false}
|
|
1035
|
+
doesExternalFilterPass={doesExternalFilterPass}
|
|
1036
|
+
isExternalFilterPresent={isExternalFilterPresent}
|
|
1037
|
+
maintainColumnOrder={true}
|
|
1038
|
+
noRowsOverlayComponent={noRowsOverlayComponent}
|
|
1039
|
+
onCellClicked={onCellClicked}
|
|
1040
|
+
onCellContextMenu={rangeSelectInterceptContextMenu}
|
|
1041
|
+
onCellDoubleClicked={onCellDoubleClick}
|
|
1042
|
+
onCellEditingStopped={onCellEditingStopped}
|
|
1043
|
+
onCellFocused={onCellFocused}
|
|
1044
|
+
onCellKeyDown={onCellKeyPress}
|
|
1045
|
+
onCellMouseDown={onCellMouseDown}
|
|
1046
|
+
onCellMouseOver={onCellMouseOver}
|
|
1047
|
+
onColumnMoved={columnMoved}
|
|
1048
|
+
onColumnResized={onColumnResized}
|
|
1049
|
+
onColumnVisible={() => void setInitialContentSize()}
|
|
1050
|
+
onGridReady={onGridReady}
|
|
1051
|
+
onGridSizeChanged={onGridSizeChanged}
|
|
1052
|
+
onModelUpdated={onModelUpdated}
|
|
1053
|
+
onRowClicked={params.onRowClicked}
|
|
1054
|
+
onRowDataUpdated={onRowDataUpdated}
|
|
1055
|
+
onRowDoubleClicked={params.onRowDoubleClicked}
|
|
1056
|
+
onRowDragCancel={clearHighlightRowClasses}
|
|
1057
|
+
onRowDragEnd={onRowDragEnd}
|
|
1058
|
+
onRowDragMove={onRowDragMove}
|
|
1059
|
+
onSelectionChanged={synchroniseExternalStateToGridSelection}
|
|
1060
|
+
onSortChanged={onSortChanged}
|
|
1061
|
+
pinnedBottomRowData={params.pinnedBottomRowData}
|
|
1062
|
+
pinnedTopRowData={params.pinnedTopRowData}
|
|
1063
|
+
postSortRows={params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows}
|
|
1064
|
+
preventDefaultOnContextMenu={true}
|
|
1065
|
+
quickFilterParser={quickFilterParser}
|
|
1066
|
+
rowClassRules={params.rowClassRules}
|
|
1067
|
+
rowDragText={params.rowDragText}
|
|
1068
|
+
rowHeight={rowHeight}
|
|
960
1069
|
rowSelection={
|
|
961
1070
|
selectable
|
|
962
1071
|
? {
|
|
@@ -971,48 +1080,9 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
|
|
|
971
1080
|
: undefined
|
|
972
1081
|
}
|
|
973
1082
|
selectionColumnDef={selectionColumnDef}
|
|
974
|
-
rowHeight={rowHeight}
|
|
975
|
-
animateRows={params.animateRows ?? false}
|
|
976
|
-
rowClassRules={params.rowClassRules}
|
|
977
|
-
getRowId={getRowId}
|
|
978
|
-
suppressColumnVirtualisation={suppressColumnVirtualization}
|
|
979
|
-
suppressClickEdit={true}
|
|
980
|
-
onGridSizeChanged={onGridSizeChanged}
|
|
981
|
-
onColumnVisible={() => void setInitialContentSize()}
|
|
982
|
-
onRowDataUpdated={onRowDataUpdated}
|
|
983
|
-
onCellFocused={onCellFocused}
|
|
984
|
-
onCellKeyDown={onCellKeyPress}
|
|
985
|
-
onCellClicked={onCellClicked}
|
|
986
|
-
onCellDoubleClicked={onCellDoubleClick}
|
|
987
|
-
onCellEditingStopped={onCellEditingStopped}
|
|
988
|
-
domLayout={params.domLayout}
|
|
989
|
-
onColumnResized={onColumnResized}
|
|
990
|
-
defaultColDef={defaultColDef}
|
|
991
|
-
columnDefs={columnDefsAdjusted}
|
|
992
|
-
rowData={rowData}
|
|
993
|
-
postSortRows={params.onRowDragEnd || !defaultPostSort ? undefined : postSortRows}
|
|
994
|
-
onModelUpdated={onModelUpdated}
|
|
995
|
-
onGridReady={onGridReady}
|
|
996
|
-
onSortChanged={ensureSelectedRowIsVisible}
|
|
997
|
-
quickFilterParser={quickFilterParser}
|
|
998
|
-
onSelectionChanged={synchroniseExternalStateToGridSelection}
|
|
999
|
-
onColumnMoved={params.onColumnMoved}
|
|
1000
|
-
noRowsOverlayComponent={noRowsOverlayComponent}
|
|
1001
|
-
alwaysShowVerticalScroll={params.alwaysShowVerticalScroll}
|
|
1002
|
-
isExternalFilterPresent={isExternalFilterPresent}
|
|
1003
|
-
doesExternalFilterPass={doesExternalFilterPass}
|
|
1004
|
-
maintainColumnOrder={true}
|
|
1005
|
-
preventDefaultOnContextMenu={true}
|
|
1006
|
-
onCellContextMenu={gridContextMenu.cellContextMenu}
|
|
1007
|
-
rowDragText={params.rowDragText}
|
|
1008
|
-
onRowDragCancel={clearHighlightRowClasses}
|
|
1009
|
-
onRowDragMove={onRowDragMove}
|
|
1010
|
-
onRowDragEnd={onRowDragEnd}
|
|
1011
1083
|
suppressCellFocus={params.suppressCellFocus}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
onRowClicked={params.onRowClicked}
|
|
1015
|
-
onRowDoubleClicked={params.onRowDoubleClicked}
|
|
1084
|
+
suppressClickEdit={true}
|
|
1085
|
+
suppressColumnVirtualisation={suppressColumnVirtualization}
|
|
1016
1086
|
suppressStartEditOnTab={true}
|
|
1017
1087
|
/>
|
|
1018
1088
|
</div>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { LuiIcon } from '@linzjs/lui';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { ReactElement, useCallback, useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
import { typedEntries } from '../lui/tsUtils';
|
|
6
|
+
import { MenuDivider, MenuHeader, MenuItem } from '../react-menu3';
|
|
7
|
+
import { GridContextMenuComponentProps } from './gridHook';
|
|
8
|
+
import { CopyOptionsKey, gridCopyOptions } from './gridHook/useGridCopySettings';
|
|
9
|
+
import { GridBaseRow } from './types';
|
|
10
|
+
|
|
11
|
+
export interface CopyOptionsContext {
|
|
12
|
+
onCopy: (type?: CopyOptionsKey) => void;
|
|
13
|
+
copyType: CopyOptionsKey;
|
|
14
|
+
setCopyType: (copyType: CopyOptionsKey) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const GridRangeSelectContextMenu = <TData extends GridBaseRow>({
|
|
18
|
+
event,
|
|
19
|
+
context,
|
|
20
|
+
}: GridContextMenuComponentProps<TData, CopyOptionsContext>): ReactElement => {
|
|
21
|
+
const developerContextMenu: boolean =
|
|
22
|
+
!!(event?.event as { ctrlKey?: boolean })?.ctrlKey && !!(event?.event as { shiftKey?: boolean })?.shiftKey;
|
|
23
|
+
const onCopy = useMemo(() => context?.onCopy, [context?.onCopy]);
|
|
24
|
+
const onClick = useCallback(
|
|
25
|
+
(type: CopyOptionsKey) => {
|
|
26
|
+
onCopy?.(type);
|
|
27
|
+
},
|
|
28
|
+
[onCopy],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<MenuHeader>Copy as</MenuHeader>
|
|
34
|
+
{typedEntries(gridCopyOptions).map(([key, { icon, text, developer }]) => {
|
|
35
|
+
return (
|
|
36
|
+
(!developer || developerContextMenu) && (
|
|
37
|
+
<MenuItem key={key} onClick={() => void onClick(key)}>
|
|
38
|
+
<div className={'copyMenuMenuItem'}>
|
|
39
|
+
<LuiIcon name={icon} alt={text} size={'md'} />
|
|
40
|
+
<div className={'copyMenuMenuItem__text'}>{text}</div>
|
|
41
|
+
</div>
|
|
42
|
+
</MenuItem>
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
})}
|
|
46
|
+
<MenuDivider />
|
|
47
|
+
<MenuHeader>Set copy default</MenuHeader>
|
|
48
|
+
{typedEntries(gridCopyOptions).map(([key, { text, developer }]) => {
|
|
49
|
+
return (
|
|
50
|
+
!developer && (
|
|
51
|
+
<MenuItem
|
|
52
|
+
key={'default_' + key}
|
|
53
|
+
onClick={(e) => {
|
|
54
|
+
context?.setCopyType(key);
|
|
55
|
+
e.keepOpen = true;
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<div
|
|
59
|
+
className={clsx(
|
|
60
|
+
'copyMenuMenuItem',
|
|
61
|
+
context?.copyType === key ? '' : 'copyMenuMenuItem__buttonDefault--hidden',
|
|
62
|
+
)}
|
|
63
|
+
>
|
|
64
|
+
<LuiIcon name={'ic_tick'} alt={'CSV'} size={'sm'} />
|
|
65
|
+
<div className={'copyMenuMenuItem__text'}>{text}</div>
|
|
66
|
+
</div>
|
|
67
|
+
</MenuItem>
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
})}
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -9,7 +9,7 @@ import { usePrevious } from '../../lui/reactUtils';
|
|
|
9
9
|
import { FocusableItem, MenuDivider, MenuHeader, MenuItem } from '../../react-menu3';
|
|
10
10
|
import { ClickEvent } from '../../react-menu3/types';
|
|
11
11
|
import { textMatch } from '../../utils/textMatcher';
|
|
12
|
-
import { isNotEmpty } from '../../utils/util';
|
|
12
|
+
import { isNotEmpty, MaybePromise } from '../../utils/util';
|
|
13
13
|
import { ComponentLoadingWrapper } from '../ComponentLoadingWrapper';
|
|
14
14
|
import { CellEditorCommon } from '../GridCell';
|
|
15
15
|
import { useGridPopoverHook } from '../GridPopoverHook';
|
|
@@ -47,8 +47,6 @@ export const MenuHeaderItem = (title: string) => {
|
|
|
47
47
|
|
|
48
48
|
export type SelectOption<TOptionValue = any> = FinalSelectOption<TOptionValue>;
|
|
49
49
|
|
|
50
|
-
export type MaybePromise<T> = T | Promise<T>;
|
|
51
|
-
|
|
52
50
|
export interface GridFormDropDownProps<TData extends GridBaseRow, TOptionValue> extends CellEditorCommon {
|
|
53
51
|
// This overrides CellEditorCommon to provide some common class options
|
|
54
52
|
className?: // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
@@ -5,6 +5,7 @@ import { useGridPopoverContext } from '../../contexts/GridPopoverContext';
|
|
|
5
5
|
import { GridSubComponentContext } from '../../contexts/GridSubComponentContext';
|
|
6
6
|
import { FocusableItem, MenuDivider, MenuItem, SubMenu } from '../../react-menu3';
|
|
7
7
|
import { ClickEvent } from '../../react-menu3/types';
|
|
8
|
+
import { MaybePromise } from '../../utils/util';
|
|
8
9
|
import { ComponentLoadingWrapper } from '../ComponentLoadingWrapper';
|
|
9
10
|
import { CellEditorCommon } from '../GridCell';
|
|
10
11
|
import { useGridPopoverHook } from '../GridPopoverHook';
|
|
@@ -33,7 +34,7 @@ export interface SelectedMenuOptionResult<TData extends GridBaseRow> extends Men
|
|
|
33
34
|
export interface MenuOption<TData extends GridBaseRow> {
|
|
34
35
|
label: ReactElement | string | MenuSeparatorType;
|
|
35
36
|
subMenu?: () => ReactElement;
|
|
36
|
-
action?: (props: { selectedRows: TData[]; menuOption: SelectedMenuOptionResult<TData> }) =>
|
|
37
|
+
action?: (props: { selectedRows: TData[]; menuOption: SelectedMenuOptionResult<TData> }) => MaybePromise<void>;
|
|
37
38
|
disabled?: string | boolean;
|
|
38
39
|
hidden?: boolean;
|
|
39
40
|
subComponent?: (props: any) => ReactElement;
|