@linzjs/step-ag-grid 29.1.6 → 29.2.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.1.6",
5
+ "version": "29.2.1",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -83,6 +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
87
  "@rollup/plugin-commonjs": "^28.0.6",
87
88
  "@rollup/plugin-json": "^6.1.0",
88
89
  "@rollup/plugin-node-resolve": "^16.0.1",
@@ -14,6 +14,8 @@ import {
14
14
  GetRowIdParams,
15
15
  GridOptions,
16
16
  GridReadyEvent,
17
+ GridSizeChangedEvent,
18
+ IColumnLimit,
17
19
  ModelUpdatedEvent,
18
20
  ModuleRegistry,
19
21
  RowClickedEvent,
@@ -124,6 +126,7 @@ export interface GridProps<TData extends GridBaseRow = GridBaseRow> {
124
126
  pinnedBottomRowData?: GridOptions['pinnedBottomRowData'];
125
127
  onRowClicked?: (event: RowClickedEvent) => void;
126
128
  onRowDoubleClicked?: (event: RowDoubleClickedEvent) => void;
129
+ allowResizeInStorybook?: boolean;
127
130
  }
128
131
 
129
132
  /**
@@ -142,6 +145,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
142
145
  rowData,
143
146
  rowHeight = theme === 'ag-theme-step-default' ? 40 : theme === 'ag-theme-step-compact' ? 36 : 40,
144
147
  selectable,
148
+ allowResizeInStorybook,
145
149
  onCellFocused: paramsOnCellFocused,
146
150
  ...params
147
151
  }: GridProps<TData>): ReactElement => {
@@ -204,7 +208,11 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
204
208
 
205
209
  const skipHeader = sizeColumns === 'auto-skip-headers' && gridRendered === 'rows-visible';
206
210
  if (sizeColumns === 'auto' || skipHeader) {
207
- const result = autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current, includeFlex: true });
211
+ const result = autoSizeColumns({
212
+ skipHeader,
213
+ userSizedColIds: new Set(userSizedColIds.current.keys()),
214
+ includeFlex: true,
215
+ });
208
216
  if (!result) {
209
217
  needsAutoSize.current = true;
210
218
  return;
@@ -238,11 +246,29 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
238
246
  }, [autoSizeColumns, gridRenderState, params, rowData, sizeColumns, sizeColumnsToFit]);
239
247
 
240
248
  const lastOwnerDocumentRef = useRef<Document>();
249
+ const wasVisibleRef = useRef(false);
241
250
 
242
251
  /**
243
252
  * Auto-size windows that had deferred auto-size
253
+ * Reset focus if panel went from invisible to visible.
244
254
  */
245
255
  useInterval(() => {
256
+ // If grid has become visible after previously being hidden, then refocus the last focused cell.
257
+ const visible = !!gridDivRef.current?.checkVisibility();
258
+ if (visible && !wasVisibleRef.current) {
259
+ wasVisibleRef.current = true;
260
+ const el = (window as any).__stepaggrid_lastfocuseventtarget;
261
+ if (el) {
262
+ // Setting this to null will cause a new refocus event
263
+ (window as any).__stepaggrid_lastfocuseventtarget = null;
264
+ // Check element is still part of document
265
+ if (el.checkVisibility()) {
266
+ el.focus();
267
+ }
268
+ }
269
+ }
270
+ wasVisibleRef.current = visible;
271
+
246
272
  // Check if window has been popped out and needs resize
247
273
  const currentDocument = gridDivRef.current?.ownerDocument;
248
274
  if (currentDocument !== lastOwnerDocumentRef.current) {
@@ -407,7 +433,11 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
407
433
 
408
434
  const skipHeader = sizeColumns === 'auto-skip-headers';
409
435
  if (hasSetContentSize.current) {
410
- autoSizeColumns({ skipHeader, userSizedColIds: userSizedColIds.current, colIds: colIdsEdited.current });
436
+ autoSizeColumns({
437
+ skipHeader,
438
+ userSizedColIds: new Set(userSizedColIds.current.keys()),
439
+ colIds: colIdsEdited.current,
440
+ });
411
441
  }
412
442
  colIdsEdited.current.clear();
413
443
  }, [autoSizeColumns, rowData?.length, setInitialContentSize, sizeColumns, updatedDep, updatingCols]);
@@ -525,7 +555,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
525
555
  if (hasSetContentSize.current) {
526
556
  autoSizeColumns({
527
557
  skipHeader,
528
- userSizedColIds: userSizedColIds.current,
558
+ userSizedColIds: new Set(userSizedColIds.current.keys()),
529
559
  colIds: colIdsEdited.current,
530
560
  });
531
561
  }
@@ -555,26 +585,43 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
555
585
  /**
556
586
  * Resize columns to fit if required on window/container resize
557
587
  */
558
- const onGridSizeChanged = useCallback(() => {
559
- if (sizeColumns !== 'none') {
560
- sizeColumnsToFit();
561
- }
562
- }, [sizeColumns, sizeColumnsToFit]);
588
+ const onGridSizeChanged = useCallback(
589
+ (event: GridSizeChangedEvent<TData>) => {
590
+ if (sizeColumns !== 'none' && (!(window as any).__STORYBOOK_PREVIEW__ || allowResizeInStorybook)) {
591
+ const columnLimits = [
592
+ ...userSizedColIds.current.entries().map(
593
+ ([c, w]): IColumnLimit => ({
594
+ key: c,
595
+ minWidth: w,
596
+ }),
597
+ ),
598
+ ];
599
+ event.api.sizeColumnsToFit({ columnLimits });
600
+ }
601
+ },
602
+ [allowResizeInStorybook, sizeColumns],
603
+ );
563
604
 
564
605
  /**
565
606
  * Set of column I'd's that are prevented from auto-sizing as they are user set
566
607
  */
567
- const userSizedColIds = useRef(new Set<string>());
608
+ const userSizedColIds = useRef(new Map<string, number>());
568
609
 
569
610
  /**
570
611
  * Lock/unlock column width on user edit/reset.
571
612
  */
572
613
  const onColumnResized = useCallback((e: ColumnResizedEvent) => {
573
614
  const colId = e.column?.getColId();
574
- if (colId == null) return;
615
+ if (colId == null) {
616
+ return;
617
+ }
618
+ const width = e.column?.getActualWidth();
619
+ if (width == null) {
620
+ return;
621
+ }
575
622
  switch (e.source) {
576
- case 'uiColumnDragged':
577
- userSizedColIds.current.add(colId);
623
+ case 'uiColumnResized':
624
+ userSizedColIds.current.set(colId, width);
578
625
  break;
579
626
  case 'autosizeColumns':
580
627
  userSizedColIds.current.delete(colId);
@@ -625,7 +672,7 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
625
672
 
626
673
  const onCellFocused = useCallback(
627
674
  (event: CellFocusedEvent<TData>) => {
628
- if (!paramsOnCellFocused || event.rowIndex == null) {
675
+ if (event.rowIndex == null) {
629
676
  return;
630
677
  }
631
678
  const api = event.api;
@@ -639,15 +686,17 @@ export const Grid = <TData extends GridBaseRow = GridBaseRow>({
639
686
  if (!colDef || typeof colDef === 'string') {
640
687
  return;
641
688
  }
642
- // Prevent repeated callbacks to cell focus when it focus didn't change
689
+ // Prevent repeated callbacks to cell focus when focus didn't change
643
690
  const { sourceEvent } = event;
644
691
  if (sourceEvent) {
645
- if ((window as any).__stepaggrid_lastfocuseventtarget === sourceEvent.target) {
692
+ const cell = (sourceEvent.target as Element | undefined)?.closest?.('.ag-cell') ?? null;
693
+ if ((window as any).__stepaggrid_lastfocuseventtarget === cell) {
646
694
  return;
647
695
  }
648
- (window as any).__stepaggrid_lastfocuseventtarget = sourceEvent.target;
696
+ (window as any).__stepaggrid_lastfocuseventtarget = cell;
649
697
  }
650
- paramsOnCellFocused({ colDef, data });
698
+
699
+ paramsOnCellFocused?.({ colDef, data });
651
700
  },
652
701
  [paramsOnCellFocused],
653
702
  );
@@ -424,7 +424,9 @@ export const GridContextProvider = <TData extends GridBaseRow>(props: PropsWithC
424
424
  */
425
425
  const autoSizeColumns = useCallback(
426
426
  ({ skipHeader, colIds, userSizedColIds, includeFlex }: AutoSizeColumnsProps = {}): AutoSizeColumnsResult => {
427
- if (!gridApi || !gridApi.getColumnState()) return null;
427
+ if (!gridApi || !gridApi.getColumnState()) {
428
+ return null;
429
+ }
428
430
  const colIdsSet = colIds instanceof Set ? colIds : new Set(colIds);
429
431
  const colsToResize = gridApi.getColumnState()?.filter?.((colState) => {
430
432
  const colId = colState.colId;
@@ -0,0 +1,28 @@
1
+ import { PanelsContextProvider } from '@linzjs/windows';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+
4
+ import { TestShowPanelResizingAgGrid } from './ShowPanelResizingStepAgGrid';
5
+
6
+ const meta: Meta<typeof TestShowPanelResizingAgGrid> = {
7
+ title: 'Components/Panel',
8
+ component: TestShowPanelResizingAgGrid,
9
+ argTypes: {
10
+ backgroundColor: { control: 'color' },
11
+ },
12
+ decorators: [
13
+ (Story) => (
14
+ <div>
15
+ <PanelsContextProvider baseZIndex={500}>
16
+ <Story />
17
+ </PanelsContextProvider>
18
+ </div>
19
+ ),
20
+ ],
21
+ };
22
+
23
+ export default meta;
24
+ type Story = StoryObj<typeof meta>;
25
+
26
+ export const PanelResizing: Story = {
27
+ args: {},
28
+ };
@@ -0,0 +1,169 @@
1
+ // import '../story.scss';
2
+ import '@linzjs/lui/dist/scss/base.scss';
3
+ import '../../../../styles/Grid.scss';
4
+
5
+ import {
6
+ OpenPanelButton,
7
+ Panel,
8
+ PanelContent,
9
+ PanelContext,
10
+ PanelHeader,
11
+ PanelsContextProvider,
12
+ } from '@linzjs/windows';
13
+ import { useContext, useMemo, useState } from 'react';
14
+
15
+ import {
16
+ ColDefT,
17
+ Grid,
18
+ GridCell,
19
+ GridContextProvider,
20
+ GridIcon,
21
+ GridPopoverMenu,
22
+ GridPopoverMessage,
23
+ GridUpdatingContextProvider,
24
+ GridWrapper,
25
+ } from '../../../../../src';
26
+
27
+ // #Example: Panel Context Provider
28
+ // Don't forget to add a PanelContextProvider at the root of your project
29
+ export const App = () => (
30
+ <PanelsContextProvider baseZIndex={500}>
31
+ <div>...the rest of your app...</div>
32
+ </PanelsContextProvider>
33
+ );
34
+
35
+ // #Example: Panel Component
36
+ export interface TestPanelProps {
37
+ data: number;
38
+ }
39
+
40
+ export const TestPanelResizing = ({ data }: TestPanelProps) => {
41
+ return (
42
+ <Panel title={`Panel resizing demo ${data}`} size={{ width: 320, height: 400 }} className={'WindowPanel-blue'}>
43
+ <PanelHeader />
44
+ <PanelContent>
45
+ <PanelContentsWithResize />
46
+ </PanelContent>
47
+ </Panel>
48
+ );
49
+ };
50
+
51
+ // #Example: Panel Invocation
52
+ export const TestShowPanelResizingAgGrid = () => (
53
+ <>
54
+ <OpenPanelButton buttonText={'TestPanel resizing 1'} componentFn={() => <TestPanelResizing data={1} />} />{' '}
55
+ <OpenPanelButton buttonText={'TestPanel resizing 2'} componentFn={() => <TestPanelResizing data={2} />} />
56
+ </>
57
+ );
58
+
59
+ /* exclude */
60
+ interface ITestRow {
61
+ id: number;
62
+ position: string;
63
+ age: number;
64
+ desc: string;
65
+ dd: string;
66
+ }
67
+
68
+ /* exclude */
69
+
70
+ // #Example: Resizing panel to content after load
71
+ // Note: Resize can only be used from within the panel content.
72
+ export const PanelContentsWithResize = () => {
73
+ // This is the first important bit
74
+ const { resizePanel } = useContext(PanelContext);
75
+
76
+ const columnDefs: ColDefT<ITestRow>[] = useMemo(
77
+ () => [
78
+ /* Your grid ColDefs */
79
+ /* exclude */
80
+ GridCell({
81
+ field: 'id',
82
+ headerName: 'Id',
83
+ lockVisible: true,
84
+ }),
85
+ GridCell({
86
+ field: 'position',
87
+ headerName: 'Position',
88
+ cellRendererParams: {
89
+ warning: (props) => props.value === 'Tester' && 'Testers are testing',
90
+ info: (props) => props.value === 'Developer' && 'Developers are awesome',
91
+ },
92
+ }),
93
+ GridCell({
94
+ field: 'age',
95
+ headerName: 'Age',
96
+ }),
97
+ GridCell({
98
+ field: 'desc',
99
+ headerName: 'Description',
100
+ flex: 1,
101
+ }),
102
+ GridPopoverMessage(
103
+ {
104
+ headerName: 'Popout message',
105
+ cellRenderer: () => <>Single Click me!</>,
106
+ exportable: false,
107
+ },
108
+ {
109
+ multiEdit: true,
110
+ editorParams: {
111
+ // eslint-disable-next-line @typescript-eslint/require-await
112
+ message: async (selectedRows): Promise<string> => {
113
+ return `There are ${selectedRows.length} row(s) selected`;
114
+ },
115
+ },
116
+ },
117
+ ),
118
+ GridCell({
119
+ headerName: 'Custom edit',
120
+ editable: true,
121
+ valueFormatter: () => 'Press E',
122
+ cellRendererParams: {
123
+ rightHoverElement: (
124
+ <GridIcon icon={'ic_launch_modal'} title={'Title text'} className={'GridCell-editableIcon'} />
125
+ ),
126
+ editAction: (selectedRows) => {
127
+ alert(`Custom edit ${selectedRows.map((r) => r.id).join()} rowId(s) selected`);
128
+ },
129
+ shortcutKeys: {
130
+ e: () => {
131
+ alert('Hi');
132
+ },
133
+ },
134
+ },
135
+ }),
136
+ GridPopoverMenu(
137
+ {},
138
+ {
139
+ multiEdit: true,
140
+ editorParams: {
141
+ // eslint-disable-next-line @typescript-eslint/require-await
142
+ options: async () => [],
143
+ },
144
+ },
145
+ ),
146
+ /* exclude */
147
+ ],
148
+ [],
149
+ );
150
+
151
+ const [rowData] = useState([
152
+ /* Your grid row data */
153
+ /* exclude */
154
+ { id: 1000, position: 'Tester', age: 30, desc: 'Tests application', dd: '1' },
155
+ { id: 1001, position: 'Developer', age: 12, desc: 'Develops application', dd: '2' },
156
+ { id: 1002, position: 'Manager', age: 65, desc: 'Manages', dd: '3' },
157
+ /* exclude */
158
+ ]);
159
+
160
+ return (
161
+ <GridUpdatingContextProvider>
162
+ <GridContextProvider>
163
+ <GridWrapper>
164
+ <Grid columnDefs={columnDefs} rowData={rowData} onContentSize={resizePanel} allowResizeInStorybook={true} />
165
+ </GridWrapper>
166
+ </GridContextProvider>
167
+ </GridUpdatingContextProvider>
168
+ );
169
+ };