@linzjs/step-ag-grid 29.2.3 → 29.3.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/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": "29.2.3",
5
+ "version": "29.3.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -83,7 +83,7 @@
83
83
  "@chromatic-com/storybook": "^4.1.1",
84
84
  "@linzjs/lui": "^23.11.1",
85
85
  "@linzjs/style": "^5.4.0",
86
- "@linzjs/windows": "^5.5.1",
86
+ "@linzjs/windows": "^5.6.0",
87
87
  "@rollup/plugin-commonjs": "^28.0.6",
88
88
  "@rollup/plugin-json": "^6.1.0",
89
89
  "@rollup/plugin-node-resolve": "^16.0.1",
@@ -14,7 +14,6 @@ import {
14
14
  GetRowIdParams,
15
15
  GridOptions,
16
16
  GridReadyEvent,
17
- GridSizeChangedEvent,
18
17
  IColumnLimit,
19
18
  ModelUpdatedEvent,
20
19
  ModuleRegistry,
@@ -31,7 +30,7 @@ import { defer, difference, isEmpty, last, omit, xorBy } from 'lodash-es';
31
30
  import { ReactElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
32
31
  import { useInterval } from 'usehooks-ts';
33
32
 
34
- import { useGridContext } from '../contexts/GridContext';
33
+ import { AutoSizeColumnsResult, useGridContext } from '../contexts/GridContext';
35
34
  import { GridUpdatingContext } from '../contexts/GridUpdatingContext';
36
35
  import { fnOrVar, isNotEmpty } from '../utils/util';
37
36
  import { clickInputWhenContainingCellClicked } from './clickInputWhenContainingCellClicked';
@@ -99,6 +98,10 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
99
98
  * If you want to stretch to container width if width is greater than the container add a flex column.
100
99
  */
101
100
  sizeColumns?: 'fit' | 'auto' | 'auto-skip-headers' | 'none';
101
+ /**
102
+ * On first don't return a content size larger than this.
103
+ */
104
+ maxInitialWidth?: number;
102
105
  /**
103
106
  * When pressing tab whilst editing the grid will select and edit the next cell if available.
104
107
  * Once the last cell to edit closes this callback is called.
@@ -145,6 +148,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
145
148
  rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40,
146
149
  selectable,
147
150
  onCellFocused: paramsOnCellFocused,
151
+ maxInitialWidth,
148
152
  ...params
149
153
  }: GridProps<TData>): ReactElement => {
150
154
  const {
@@ -190,6 +194,8 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
190
194
 
191
195
  const lastFullResize = useRef<number>();
192
196
 
197
+ const autoSizeResultRef = useRef<AutoSizeColumnsResult | null>(null);
198
+ const prevRowsVisibleRef = useRef(false);
193
199
  const setInitialContentSize = useCallback(() => {
194
200
  if (!gridDivRef.current?.clientWidth || rowData == null) {
195
201
  // Don't resize grids if they are offscreen as it doesn't work.
@@ -204,31 +210,50 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
204
210
  return;
205
211
  }
206
212
 
207
- const skipHeader = sizeColumns === 'auto-skip-headers' && gridRendered === 'rows-visible';
208
- if (sizeColumns === 'auto' || skipHeader) {
209
- const result = autoSizeColumns({
210
- skipHeader,
211
- userSizedColIds: new Set(userSizedColIds.current.keys()),
212
- includeFlex: true,
213
- });
214
- if (!result) {
213
+ // 1. First we autosize to get the size of the columns on an infinite grid.
214
+ if (sizeColumns === 'auto' || sizeColumns === 'auto-skip-headers') {
215
+ // You can't skip headers until the grid has content
216
+ const rowsVisible = gridRendered === 'rows-visible';
217
+ const skipHeader = sizeColumns === 'auto-skip-headers' && rowsVisible;
218
+ // If grid was empty and now has content we need to autosize
219
+ if (rowsVisible !== prevRowsVisibleRef.current && rowsVisible) {
220
+ prevRowsVisibleRef.current = rowsVisible;
221
+ autoSizeResultRef.current = null;
222
+ }
223
+ const autoSizeResult =
224
+ autoSizeResultRef.current ??
225
+ autoSizeColumns({
226
+ skipHeader,
227
+ userSizedColIds: new Set(userSizedColIds.current.keys()),
228
+ });
229
+ // Auto-size failed retry later
230
+ if (!autoSizeResult) {
215
231
  needsAutoSize.current = true;
216
232
  return;
217
233
  }
234
+
235
+ autoSizeResultRef.current = autoSizeResult;
236
+ // Calculate the auto-sized width, limit it to maxInitialWidth
237
+ autoSizeResult.width = maxInitialWidth ? Math.min(autoSizeResult.width, maxInitialWidth) : autoSizeResult.width;
218
238
  if (gridRendered === 'empty') {
239
+ // If the grid is empty we still do an onContentSize callback, we will do another callback when grid has data
240
+ // We don't do this callback if we have previously had row data, or have already called back for empty
219
241
  if (!hasSetContentSizeEmpty.current && !hasSetContentSize.current) {
220
242
  hasSetContentSizeEmpty.current = true;
221
- params.onContentSize?.(result);
243
+ params.onContentSize?.(autoSizeResult);
222
244
  }
223
245
  } else if (gridRendered === 'rows-visible') {
246
+ // we have rows now so callback grid size
224
247
  if (!hasSetContentSize.current) {
225
- if (lastFullResize.current === result.width) {
248
+ // Only callback if grid size has settled
249
+ if (lastFullResize.current === autoSizeResult.width) {
226
250
  hasSetContentSize.current = true;
227
- params.onContentSize?.(result);
251
+ params.onContentSize?.(autoSizeResult);
228
252
  } else {
229
- needsAutoSize.current = true;
253
+ // Need to retry callback when size has settelled
254
+ lastFullResize.current = autoSizeResult.width;
255
+ return;
230
256
  }
231
- lastFullResize.current = result.width;
232
257
  }
233
258
  } else {
234
259
  // It should be impossible to get here
@@ -236,12 +261,13 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
236
261
  }
237
262
  }
238
263
 
264
+ // 2. Now we size columns to fit the grid width
239
265
  if (sizeColumns !== 'none') {
240
266
  sizeColumnsToFit();
241
267
  }
242
268
  setAutoSized(true);
243
269
  needsAutoSize.current = false;
244
- }, [autoSizeColumns, gridRenderState, params, rowData, sizeColumns, sizeColumnsToFit]);
270
+ }, [autoSizeColumns, gridRenderState, maxInitialWidth, params, rowData, sizeColumns, sizeColumnsToFit]);
245
271
 
246
272
  const lastOwnerDocumentRef = useRef<Document>();
247
273
  const wasVisibleRef = useRef(false);
@@ -419,6 +445,8 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
419
445
  const onRowDataChanged = useCallback(() => {
420
446
  const length = rowData?.length ?? 0;
421
447
  if (previousRowDataLength.current !== length) {
448
+ // We need to autosize all cells again
449
+ autoSizeResultRef.current = null;
422
450
  setInitialContentSize();
423
451
  previousRowDataLength.current = length;
424
452
  }
@@ -583,14 +611,17 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
583
611
  /**
584
612
  * Resize columns to fit if required on window/container resize
585
613
  */
586
- const onGridSizeChanged = useCallback(
587
- (event: GridSizeChangedEvent<TData>) => {
614
+ const onGridResize = useCallback(
615
+ (event: AgGridEvent<TData>) => {
588
616
  if (sizeColumns !== 'none') {
617
+ // Flex columns can expand to fit after resize, but they cannot shrink less than use resized value
618
+ // Double click column resize handle to reset this behaviour
589
619
  const columnLimits = [
590
620
  ...userSizedColIds.current.entries().map(
591
621
  ([c, w]): IColumnLimit => ({
592
622
  key: c,
593
623
  minWidth: w,
624
+ maxWidth: w,
594
625
  }),
595
626
  ),
596
627
  ];
@@ -608,24 +639,32 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
608
639
  /**
609
640
  * Lock/unlock column width on user edit/reset.
610
641
  */
611
- const onColumnResized = useCallback((e: ColumnResizedEvent) => {
612
- const colId = e.column?.getColId();
613
- if (colId == null) {
614
- return;
615
- }
616
- const width = e.column?.getActualWidth();
617
- if (width == null) {
618
- return;
619
- }
620
- switch (e.source) {
621
- case 'uiColumnResized':
622
- userSizedColIds.current.set(colId, width);
623
- break;
624
- case 'autosizeColumns':
625
- userSizedColIds.current.delete(colId);
626
- break;
627
- }
628
- }, []);
642
+ const onColumnResized = useCallback(
643
+ (e: ColumnResizedEvent) => {
644
+ const colId = e.column?.getColId();
645
+ if (colId == null) {
646
+ return;
647
+ }
648
+ const width = e.column?.getActualWidth();
649
+ if (width == null) {
650
+ return;
651
+ }
652
+ switch (e.source) {
653
+ case 'uiColumnResized':
654
+ userSizedColIds.current.set(colId, width);
655
+ const colDef = e.column?.getColDef();
656
+ if (!colDef?.flex) {
657
+ onGridResize(e);
658
+ }
659
+ break;
660
+ case 'autosizeColumns':
661
+ userSizedColIds.current.delete(colId);
662
+ onGridResize(e);
663
+ break;
664
+ }
665
+ },
666
+ [onGridResize],
667
+ );
629
668
 
630
669
  const gridContextMenu = useGridContextMenu({ contextMenu: params.contextMenu, contextMenuSelectRow });
631
670
 
@@ -762,6 +801,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
762
801
  );
763
802
 
764
803
  const selectionColumnDef = useMemo((): SelectionColumnDef => {
804
+ // Note this has to be 1 for hidden otherwise ag-grid does crazy things whilst resizing
765
805
  const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
766
806
  return {
767
807
  suppressNavigable: params.hideSelectColumn,
@@ -829,7 +869,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
829
869
  animateRows={params.animateRows ?? false}
830
870
  rowClassRules={params.rowClassRules}
831
871
  getRowId={getRowId}
832
- onGridSizeChanged={onGridSizeChanged}
872
+ onGridSizeChanged={onGridResize}
833
873
  suppressColumnVirtualisation={suppressColumnVirtualization}
834
874
  suppressClickEdit={true}
835
875
  onColumnVisible={setInitialContentSize}
@@ -1,11 +1,18 @@
1
- import { PropsWithChildren } from 'react';
1
+ import clsx from 'clsx';
2
+ import { forwardRef, PropsWithChildren } from 'react';
2
3
 
3
4
  export interface GridWrapperProps {
5
+ className?: string | undefined;
4
6
  maxHeight?: number | string;
5
7
  }
6
8
 
7
- export const GridWrapper = ({ children, maxHeight }: PropsWithChildren<GridWrapperProps>) => (
8
- <div className={'Grid-wrapper'} style={{ maxHeight }}>
9
- {children}
10
- </div>
11
- );
9
+ export const GridWrapper = forwardRef<HTMLDivElement, PropsWithChildren<GridWrapperProps>>(function GridWrapperFr(
10
+ { children, maxHeight, className },
11
+ ref,
12
+ ) {
13
+ return (
14
+ <div className={clsx('Grid-wrapper', className)} style={{ maxHeight }} ref={ref}>
15
+ {children}
16
+ </div>
17
+ );
18
+ });
@@ -1,4 +1,4 @@
1
- import { ColDef, GridApi, IRowNode } from 'ag-grid-community';
1
+ import { ColDef, GridApi, IRowNode, ISizeColumnsToFitParams } from 'ag-grid-community';
2
2
  import { CsvExportParams } from 'ag-grid-community';
3
3
  import { createContext, useContext } from 'react';
4
4
 
@@ -47,7 +47,7 @@ export interface GridContextType<TData extends GridBaseRow> {
47
47
  ensureSelectedRowIsVisible: () => void;
48
48
  getFirstRowId: () => number;
49
49
  autoSizeColumns: (props?: AutoSizeColumnsProps) => AutoSizeColumnsResult;
50
- sizeColumnsToFit: () => void;
50
+ sizeColumnsToFit: (paramsOrGridWidth?: ISizeColumnsToFitParams) => void;
51
51
  startCellEditing: ({ rowId, colId }: StartCellEditingProps) => Promise<void>;
52
52
  // Restores the previous focus after cell editing
53
53
  resetFocusedCellAfterCellEditing: () => void;
@@ -4,6 +4,7 @@ import {
4
4
  CsvExportParams,
5
5
  GridApi,
6
6
  IRowNode,
7
+ ISizeColumnsToFitParams,
7
8
  ProcessCellForExportParams,
8
9
  RowNode,
9
10
  } from 'ag-grid-community';
@@ -455,11 +456,14 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
455
456
  /**
456
457
  * Resize columns to fit container
457
458
  */
458
- const sizeColumnsToFit = useCallback((): void => {
459
- if (gridApi && !gridApi?.isDestroyed()) {
460
- gridApi.sizeColumnsToFit();
461
- }
462
- }, [gridApi]);
459
+ const sizeColumnsToFit = useCallback(
460
+ (paramsOrGridWidth?: ISizeColumnsToFitParams): void => {
461
+ if (gridApi && !gridApi?.isDestroyed()) {
462
+ gridApi.sizeColumnsToFit(paramsOrGridWidth);
463
+ }
464
+ },
465
+ [gridApi],
466
+ );
463
467
 
464
468
  /**
465
469
  *
@@ -71,7 +71,7 @@ interface ITestRow {
71
71
  // Note: Resize can only be used from within the panel content.
72
72
  export const PanelContentsWithResize = () => {
73
73
  // This is the first important bit
74
- const { resizePanel } = useContext(PanelContext);
74
+ const { initialResizePanel } = useContext(PanelContext);
75
75
 
76
76
  const columnDefs: ColDefT<ITestRow>[] = useMemo(
77
77
  () => [
@@ -151,7 +151,13 @@ export const PanelContentsWithResize = () => {
151
151
  const [rowData] = useState([
152
152
  /* Your grid row data */
153
153
  /* exclude */
154
- { id: 1000, position: 'Tester', age: 30, desc: 'Tests application', dd: '1' },
154
+ {
155
+ id: 1000,
156
+ position: 'Tester',
157
+ age: 30,
158
+ desc: 'Tests application',
159
+ dd: '1',
160
+ },
155
161
  { id: 1001, position: 'Developer', age: 12, desc: 'Develops application', dd: '2' },
156
162
  { id: 1002, position: 'Manager', age: 65, desc: 'Manages', dd: '3' },
157
163
  /* exclude */
@@ -161,7 +167,12 @@ export const PanelContentsWithResize = () => {
161
167
  <GridUpdatingContextProvider>
162
168
  <GridContextProvider>
163
169
  <GridWrapper>
164
- <Grid columnDefs={columnDefs} rowData={rowData} onContentSize={resizePanel} />
170
+ <Grid
171
+ columnDefs={columnDefs}
172
+ rowData={rowData}
173
+ onContentSize={initialResizePanel}
174
+ sizeColumns={'auto-skip-headers'}
175
+ />
165
176
  </GridWrapper>
166
177
  </GridContextProvider>
167
178
  </GridUpdatingContextProvider>