@linzjs/step-ag-grid 29.2.4 → 29.3.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/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.4",
5
+ "version": "29.3.1",
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.6.0",
86
+ "@linzjs/windows": "^5.7.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,
@@ -27,15 +26,14 @@ import {
27
26
  } from 'ag-grid-community';
28
27
  import { AgGridReact } from 'ag-grid-react';
29
28
  import clsx from 'clsx';
30
- import { defer, difference, isEmpty, last, omit, sum, xorBy } from 'lodash-es';
29
+ import { defer, delay, 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';
38
- import { ColDefT } from './GridCell';
39
37
  import { GridHeaderSelect } from './gridHeader';
40
38
  import { GridContextMenuComponent, useGridContextMenu } from './gridHook';
41
39
  import { GridNoRowsOverlay } from './GridNoRowsOverlay';
@@ -194,8 +192,9 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
194
192
  const hasSetContentSizeEmpty = useRef(false);
195
193
  const needsAutoSize = useRef(true);
196
194
 
197
- const lastFullResize = useRef<number>();
198
-
195
+ const requiresInitialSizeToFitRef = useRef(true);
196
+ const autoSizeResultRef = useRef<AutoSizeColumnsResult | null>(null);
197
+ const prevRowsVisibleRef = useRef(false);
199
198
  const setInitialContentSize = useCallback(() => {
200
199
  if (!gridDivRef.current?.clientWidth || rowData == null) {
201
200
  // Don't resize grids if they are offscreen as it doesn't work.
@@ -210,46 +209,63 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
210
209
  return;
211
210
  }
212
211
 
213
- const skipHeader = sizeColumns === 'auto-skip-headers' && gridRendered === 'rows-visible';
214
- if (sizeColumns === 'auto' || skipHeader) {
215
- const result = autoSizeColumns({
216
- skipHeader,
217
- userSizedColIds: new Set(userSizedColIds.current.keys()),
218
- includeFlex: true,
219
- });
220
- if (!result) {
212
+ // 1. First we autosize to get the size of the columns on an infinite grid.
213
+ if (sizeColumns === 'auto' || sizeColumns === 'auto-skip-headers') {
214
+ // You can't skip headers until the grid has content
215
+ const rowsVisible = gridRendered === 'rows-visible';
216
+ const skipHeader = sizeColumns === 'auto-skip-headers' && rowsVisible;
217
+ // If grid was empty and now has content we need to autosize
218
+ if (rowsVisible !== prevRowsVisibleRef.current && rowsVisible) {
219
+ prevRowsVisibleRef.current = rowsVisible;
220
+ autoSizeResultRef.current = null;
221
+ }
222
+ const autoSizeResult =
223
+ autoSizeResultRef.current ??
224
+ autoSizeColumns({
225
+ skipHeader,
226
+ userSizedColIds: new Set(userSizedColIds.current.keys()),
227
+ includeFlex: true,
228
+ });
229
+ // Auto-size failed retry later
230
+ if (!autoSizeResult) {
221
231
  needsAutoSize.current = true;
222
232
  return;
223
233
  }
224
- // Default max intial width is 256x initial visible column count, max of 80% window width
225
- const maxWidth =
226
- maxInitialWidth ||
227
- sum(params.columnDefs.map((c: ColDefT<TData>) => (!(c as any).hide ? c.maxInitialWidth || 128 : 0)));
228
- result.width = Math.min(result.width, maxWidth);
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;
229
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
230
241
  if (!hasSetContentSizeEmpty.current && !hasSetContentSize.current) {
231
242
  hasSetContentSizeEmpty.current = true;
232
- params.onContentSize?.(result);
243
+ requiresInitialSizeToFitRef.current = true;
244
+ params.onContentSize?.(autoSizeResult);
233
245
  }
234
246
  } else if (gridRendered === 'rows-visible') {
247
+ // we have rows now so callback grid size
235
248
  if (!hasSetContentSize.current) {
236
- if (lastFullResize.current === result.width) {
237
- hasSetContentSize.current = true;
238
- params.onContentSize?.(result);
239
- } else {
240
- needsAutoSize.current = true;
241
- }
242
- lastFullResize.current = result.width;
249
+ hasSetContentSize.current = true;
250
+ requiresInitialSizeToFitRef.current = true;
251
+ params.onContentSize?.(autoSizeResult);
243
252
  }
244
253
  } else {
245
254
  // It should be impossible to get here
246
255
  console.error('Unknown value returned from hasGridRendered');
247
256
  }
248
- }
249
257
 
250
- if (sizeColumns !== 'none') {
251
- sizeColumnsToFit();
258
+ // If there's no contentSize callback there'll be on onGridResize callback
259
+ // which is required to run sizeColumnsToFit.
260
+ // There's also the possibility that the panel was already the right size so didn't trigger onGridResize.
261
+ delay(() => {
262
+ if (requiresInitialSizeToFitRef.current) {
263
+ requiresInitialSizeToFitRef.current = false;
264
+ sizeColumnsToFit();
265
+ }
266
+ }, 50);
252
267
  }
268
+
253
269
  setAutoSized(true);
254
270
  needsAutoSize.current = false;
255
271
  }, [autoSizeColumns, gridRenderState, maxInitialWidth, params, rowData, sizeColumns, sizeColumnsToFit]);
@@ -430,6 +446,8 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
430
446
  const onRowDataChanged = useCallback(() => {
431
447
  const length = rowData?.length ?? 0;
432
448
  if (previousRowDataLength.current !== length) {
449
+ // We need to autosize all cells again
450
+ autoSizeResultRef.current = null;
433
451
  setInitialContentSize();
434
452
  previousRowDataLength.current = length;
435
453
  }
@@ -594,18 +612,22 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
594
612
  /**
595
613
  * Resize columns to fit if required on window/container resize
596
614
  */
597
- const onGridSizeChanged = useCallback(
598
- (event: GridSizeChangedEvent<TData>) => {
615
+ const onGridResize = useCallback(
616
+ (event: AgGridEvent<TData>) => {
599
617
  if (sizeColumns !== 'none') {
618
+ // Flex columns can expand to fit after resize, but they cannot shrink less than use resized value
619
+ // Double click column resize handle to reset this behaviour
600
620
  const columnLimits = [
601
621
  ...userSizedColIds.current.entries().map(
602
622
  ([c, w]): IColumnLimit => ({
603
623
  key: c,
604
624
  minWidth: w,
625
+ maxWidth: w,
605
626
  }),
606
627
  ),
607
628
  ];
608
- event.api.sizeColumnsToFit({ columnLimits });
629
+ requiresInitialSizeToFitRef.current = false;
630
+ defer(() => event.api.sizeColumnsToFit({ columnLimits }));
609
631
  }
610
632
  },
611
633
  [sizeColumns],
@@ -619,24 +641,32 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
619
641
  /**
620
642
  * Lock/unlock column width on user edit/reset.
621
643
  */
622
- const onColumnResized = useCallback((e: ColumnResizedEvent) => {
623
- const colId = e.column?.getColId();
624
- if (colId == null) {
625
- return;
626
- }
627
- const width = e.column?.getActualWidth();
628
- if (width == null) {
629
- return;
630
- }
631
- switch (e.source) {
632
- case 'uiColumnResized':
633
- userSizedColIds.current.set(colId, width);
634
- break;
635
- case 'autosizeColumns':
636
- userSizedColIds.current.delete(colId);
637
- break;
638
- }
639
- }, []);
644
+ const onColumnResized = useCallback(
645
+ (e: ColumnResizedEvent) => {
646
+ const colId = e.column?.getColId();
647
+ if (colId == null) {
648
+ return;
649
+ }
650
+ const width = e.column?.getActualWidth();
651
+ if (width == null) {
652
+ return;
653
+ }
654
+ switch (e.source) {
655
+ case 'uiColumnResized':
656
+ userSizedColIds.current.set(colId, width);
657
+ const colDef = e.column?.getColDef();
658
+ if (!colDef?.flex) {
659
+ onGridResize(e);
660
+ }
661
+ break;
662
+ case 'autosizeColumns':
663
+ userSizedColIds.current.delete(colId);
664
+ onGridResize(e);
665
+ break;
666
+ }
667
+ },
668
+ [onGridResize],
669
+ );
640
670
 
641
671
  const gridContextMenu = useGridContextMenu({ contextMenu: params.contextMenu, contextMenuSelectRow });
642
672
 
@@ -773,6 +803,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
773
803
  );
774
804
 
775
805
  const selectionColumnDef = useMemo((): SelectionColumnDef => {
806
+ // Note this has to be 1 for hidden otherwise ag-grid does crazy things whilst resizing
776
807
  const selectWidth = params.hideSelectColumn ? 0 : selectable && params.onRowDragEnd ? 76 : 48;
777
808
  return {
778
809
  suppressNavigable: params.hideSelectColumn,
@@ -840,7 +871,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
840
871
  animateRows={params.animateRows ?? false}
841
872
  rowClassRules={params.rowClassRules}
842
873
  getRowId={getRowId}
843
- onGridSizeChanged={onGridSizeChanged}
874
+ onGridSizeChanged={onGridResize}
844
875
  suppressColumnVirtualisation={suppressColumnVirtualization}
845
876
  suppressClickEdit={true}
846
877
  onColumnVisible={setInitialContentSize}
@@ -71,7 +71,6 @@ export interface ColDefT<TData extends GridBaseRow, ValueType = any> extends Col
71
71
  editable?: boolean | EditableCallback<TData, ValueType>;
72
72
  valueGetter?: string | ValueGetterFunc<TData, ValueType>;
73
73
  valueFormatter?: string | ValueFormatterFunc<TData, ValueType>;
74
- maxInitialWidth?: number;
75
74
  cellRenderer?:
76
75
  | ((props: ICellRendererParams<TData, ValueType>) => ReactElement | string | false | null | undefined)
77
76
  | string;
@@ -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
+ });
@@ -12,7 +12,7 @@ const meta: Meta<typeof TestShowPanelResizingAgGrid> = {
12
12
  decorators: [
13
13
  (Story) => (
14
14
  <div>
15
- <PanelsContextProvider baseZIndex={500}>
15
+ <PanelsContextProvider baseZIndex={500} panelStateOptions={null}>
16
16
  <Story />
17
17
  </PanelsContextProvider>
18
18
  </div>