@adaptabletools/adaptable-react-aggrid 20.0.0-canary.3 → 20.0.0-canary.5

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
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@adaptabletools/adaptable-react-aggrid",
3
- "version": "20.0.0-canary.3",
3
+ "version": "20.0.0-canary.5",
4
4
  "description": "React version of AdapTable - the powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements",
5
5
  "keywords": [],
6
6
  "license": "contact sales@adaptabletools.com for details",
7
7
  "typings": "src/index.d.ts",
8
8
  "dependencies": {
9
9
  "tslib": "^2.8.1",
10
- "@adaptabletools/adaptable": "20.0.0-canary.3"
10
+ "@adaptabletools/adaptable": "20.0.0-canary.5"
11
11
  },
12
12
  "peerDependencies": {
13
13
  "ag-grid-enterprise": ">=33.0.0",
@@ -9,6 +9,7 @@ declare enum AdaptableAgGridStateTransitions {
9
9
  INITIALIZE_AG_GRID = "INITIALIZE_AG_GRID"
10
10
  }
11
11
  export declare const useAdaptableAgGridContext: () => {
12
+ id: string;
12
13
  adaptableProps: AdaptableReactProps;
13
14
  modules: Module[];
14
15
  onAdaptableReady: (adaptableReadyInfo: AdaptableReadyInfo) => void;
@@ -21,6 +22,7 @@ export declare const useAdaptableAgGridContext: () => {
21
22
  setAgGridProps: (props: AgGridReactPropsWithoutGridOptionsAndModules) => void;
22
23
  gridOptions: GridOptions;
23
24
  setGridOptions: (gridOptions: GridOptions) => void;
25
+ gridApiPromiseReject: (reason?: any) => void;
24
26
  };
25
27
  export interface AdaptableProviderProps<TData = any> {
26
28
  gridOptions: GridOptions<TData>;
@@ -3,6 +3,7 @@ import { AgGridReact } from 'ag-grid-react';
3
3
  import AdaptableReactComponent from './AdaptableReact';
4
4
  import { _ALL_GRID_OPTIONS, } from 'ag-grid-enterprise';
5
5
  import { diff } from '@adaptabletools/adaptable/src/Utilities/Extensions/ObjectExtensions';
6
+ import { useId, useState } from 'react';
6
7
  var AdaptableAgGridStateTransitions;
7
8
  (function (AdaptableAgGridStateTransitions) {
8
9
  AdaptableAgGridStateTransitions["AG_GRID_EMIT_PROPS"] = "AG_GRID_EMIT_PROPS";
@@ -19,23 +20,36 @@ export const useAdaptableAgGridContext = () => {
19
20
  }
20
21
  return context;
21
22
  };
23
+ function usePromise() {
24
+ const [promise] = useState(() => {
25
+ let resolve;
26
+ let reject;
27
+ const promise = new Promise((a, b) => {
28
+ resolve = a;
29
+ reject = b;
30
+ });
31
+ return {
32
+ promise,
33
+ resolve,
34
+ reject,
35
+ };
36
+ });
37
+ return promise;
38
+ }
22
39
  const AdaptableProvider = (props) => {
23
40
  const [currentTransition, setCurrentTransition] = React.useState(AdaptableAgGridStateTransitions.AG_GRID_EMIT_PROPS);
41
+ const useIdResult = useId();
42
+ const id = `${props.adaptableOptions.adaptableId || 'adaptable'}-${useIdResult}`;
24
43
  const [agGridProps, setAgGridProps] = React.useState();
25
44
  const [gridOptions, setGridOptions] = React.useState(props.gridOptions);
26
45
  const [agGridApi, setAgGridApi] = React.useState();
27
46
  const [adaptableApi, setAdaptableApi] = React.useState();
28
47
  const adaptableAgGridReactRenderedRef = React.useRef(false);
29
- const gridApiPromiseResolve = React.useRef(null);
30
- const gridApiPromise = React.useMemo(() => {
31
- return new Promise((resolve) => {
32
- gridApiPromiseResolve.current = resolve;
33
- });
34
- }, []);
48
+ const { promise: gridApiPromise, resolve: gridApiPromiseResolve, reject: gridApiPromiseReject, } = usePromise();
35
49
  const handleSetGridApi = React.useCallback((api) => {
36
50
  setAgGridApi(api);
37
- gridApiPromiseResolve.current(api);
38
- }, []);
51
+ gridApiPromiseResolve(api);
52
+ }, [gridApiPromiseResolve]);
39
53
  const onAdaptableReady = React.useCallback((adaptableReadyInfo) => {
40
54
  setAdaptableApi(adaptableReadyInfo.adaptableApi);
41
55
  props.onAdaptableReady?.(adaptableReadyInfo);
@@ -65,6 +79,7 @@ const AdaptableProvider = (props) => {
65
79
  };
66
80
  }, []);
67
81
  return (React.createElement(AdaptableAgGridContext.Provider, { value: {
82
+ id,
68
83
  onAdaptableReady,
69
84
  adaptableApi,
70
85
  transition: currentTransition,
@@ -74,6 +89,7 @@ const AdaptableProvider = (props) => {
74
89
  setGridOptions,
75
90
  agGridApi,
76
91
  setAgGridApi: handleSetGridApi,
92
+ gridApiPromiseReject,
77
93
  agGridProps,
78
94
  setAgGridProps: (agGridProps) => {
79
95
  adaptableAgGridReactRenderedRef.current = true;
@@ -99,7 +115,7 @@ const AdaptableProvider = (props) => {
99
115
  };
100
116
  // -- ADAPTABLE UI WRAPPER -
101
117
  const AdaptableUI = (props) => {
102
- const { adaptableProps, modules, gridOptions, transition, onAdaptableReady } = useAdaptableAgGridContext();
118
+ const { adaptableProps, modules, gridOptions, transition, onAdaptableReady, id } = useAdaptableAgGridContext();
103
119
  if (transition === AdaptableAgGridStateTransitions.AG_GRID_EMIT_PROPS) {
104
120
  return null;
105
121
  }
@@ -107,10 +123,12 @@ const AdaptableUI = (props) => {
107
123
  // This needs to be first, so gridOptions is the one passed and not from props
108
124
  , { ...adaptableProps, onAdaptableReady: onAdaptableReady, style: props.style, className: props.className, gridOptions: gridOptions, modules: modules }));
109
125
  };
126
+ const readyAGGridAdaptableInstances = new Map();
127
+ const destroyCalledInstances = new Map();
110
128
  const AdaptableAgGridReact = (props) => {
111
129
  const agGridRef = React.useRef(null);
112
130
  const [isAgGridReady, setAgGridIsReady] = React.useState(false);
113
- const { modules, gridOptions, setAgGridApi, transition, setAgGridProps, agGridApi } = useAdaptableAgGridContext();
131
+ const { modules, gridOptions, setAgGridApi, transition, setAgGridProps, agGridApi, gridApiPromiseReject, id, } = useAdaptableAgGridContext();
114
132
  const propsRef = React.useRef(props);
115
133
  React.useEffect(() => {
116
134
  const prevProps = propsRef.current;
@@ -155,6 +173,7 @@ const AdaptableAgGridReact = (props) => {
155
173
  return null;
156
174
  }
157
175
  return (React.createElement(AgGridReact, { ref: agGridRef, ...agGridPassThroughProps, modules: modules, onGridReady: (event) => {
176
+ // this is not called extra in strict mode
158
177
  setAgGridApi(event.api);
159
178
  if (props.onGridReady) {
160
179
  props.onGridReady(event);
@@ -163,6 +182,38 @@ const AdaptableAgGridReact = (props) => {
163
182
  gridOptions.onGridReady(event);
164
183
  }
165
184
  setAgGridIsReady(true);
185
+ readyAGGridAdaptableInstances.set(id, true);
186
+ }, onGridPreDestroyed: () => {
187
+ // this is extra called in strict mode
188
+ // when doing the double render
189
+ // when we are in strict mode,
190
+ // if gridPreDestroyed is called the second time
191
+ // then we know it's a real destroy
192
+ if (destroyCalledInstances.get(id) === true) {
193
+ // so go ahead and reject the promise
194
+ gridApiPromiseReject('ag grid destroy');
195
+ destroyCalledInstances.delete(id);
196
+ return;
197
+ }
198
+ destroyCalledInstances.set(id, true);
199
+ if (readyAGGridAdaptableInstances.get(id) === true) {
200
+ // in strict mode, gridPreDestroyed is called extra on mount, before the onGridReady
201
+ // so if the instance is ready, we know it's a real destroy, not due to strict mode
202
+ // so we can reject the promise
203
+ readyAGGridAdaptableInstances.delete(id);
204
+ gridApiPromiseReject('ag grid destroy');
205
+ }
206
+ else {
207
+ // STILL, we might have an exception
208
+ // when strict mode is not enabled,
209
+ // then we really need to destroy the instance
210
+ // but we'll only do this if the instance is not in readyAGGridAdaptableInstances
211
+ setTimeout(() => {
212
+ if (!readyAGGridAdaptableInstances.get(id)) {
213
+ gridApiPromiseReject('ag grid destroy');
214
+ }
215
+ }, 1000);
216
+ }
166
217
  }, gridOptions: gridOptions }));
167
218
  };
168
219
  export const Adaptable = {
@@ -1,77 +1,71 @@
1
1
  import * as React from 'react';
2
- import { useMemo, useState, useRef } from 'react';
2
+ import { useMemo, useState, useRef, useCallback } from 'react';
3
3
  import { ColumnApiModule } from 'ag-grid-enterprise';
4
4
  import { _AdaptableAgGrid } from '@adaptabletools/adaptable/agGrid';
5
5
  import { setupFrameworkComponents } from '../setupFrameworkComponents';
6
6
  import join from '../utils/join';
7
+ import { useAdaptableAgGridContext, } from './AdaptableProvider';
7
8
  const getRandomInt = (max) => Math.floor(Math.random() * Math.floor(max));
8
9
  const columnApiModule = ColumnApiModule;
10
+ const startedInstances = new Map();
11
+ const successInstances = new Map();
12
+ const isInstanceStarted = (id) => {
13
+ return startedInstances.get(id) === true;
14
+ };
9
15
  const AdaptableReact = ({ adaptableOptions, gridOptions, onAdaptableReady, ...props }) => {
16
+ const { id } = useAdaptableAgGridContext();
10
17
  const seedId = useMemo(() => `${getRandomInt(1000)}-${Date.now()}`, []);
11
- const adaptableContainerId = `adaptable-${seedId}`;
18
+ const adaptableContainerId = `adaptable-${id}-${seedId}`;
12
19
  const [adaptableJSXElement, setAdaptableJSXElement] = useState(null);
13
- const isMounted = useRef(true);
14
20
  const adaptableApiRef = useRef(null);
15
- const containerRef = useMemo(() => {
16
- const cleanup = () => {
17
- if (adaptableApiRef.current) {
18
- adaptableApiRef.current.destroy({
19
- unmount: true,
20
- destroyAgGrid: false,
21
- });
22
- adaptableApiRef.current = null;
23
- }
24
- };
25
- return (node) => {
26
- if (node) {
27
- isMounted.current = true;
28
- _AdaptableAgGrid
29
- ._initInternal({
30
- modules: props.modules,
31
- adaptableOptions: {
32
- ...adaptableOptions,
33
- containerOptions: {
34
- ...adaptableOptions.containerOptions,
35
- adaptableContainer: node,
36
- },
37
- },
38
- gridOptions,
39
- variant: 'react',
40
- renderAgGridFrameworkComponent: props.renderAgGridFrameworkComponent,
41
- getAgGridColumnApiModuleReference: () => {
42
- return columnApiModule;
43
- },
44
- })
45
- .then((api) => {
46
- // If the component was unmounted while initializing, cleanup immediately
47
- if (!isMounted.current) {
48
- api.destroy({
49
- unmount: true,
50
- destroyAgGrid: false,
51
- });
52
- return;
53
- }
54
- adaptableApiRef.current = api;
55
- //see #no_additional_react_root
56
- setAdaptableJSXElement(api.internalApi.getAdaptableJSXElement());
57
- if (onAdaptableReady) {
58
- api.eventApi.on('AdaptableReady', onAdaptableReady);
59
- }
60
- setupFrameworkComponents(api);
61
- });
62
- }
63
- else {
64
- isMounted.current = false;
65
- cleanup();
21
+ const cleanup = useCallback(() => {
22
+ if (adaptableApiRef.current && successInstances.get(id) === true) {
23
+ adaptableApiRef.current.destroy({
24
+ unmount: true,
25
+ destroyAgGrid: false,
26
+ });
27
+ adaptableApiRef.current = null;
28
+ }
29
+ }, [id]);
30
+ const containerRef = useCallback((node) => {
31
+ if (!node) {
32
+ cleanup();
33
+ return;
34
+ }
35
+ if (isInstanceStarted(id)) {
36
+ return;
37
+ }
38
+ startedInstances.set(id, true);
39
+ _AdaptableAgGrid
40
+ ._initInternal({
41
+ modules: props.modules,
42
+ adaptableOptions: {
43
+ ...adaptableOptions,
44
+ containerOptions: {
45
+ ...adaptableOptions.containerOptions,
46
+ adaptableContainer: node,
47
+ },
48
+ },
49
+ gridOptions,
50
+ variant: 'react',
51
+ renderAgGridFrameworkComponent: props.renderAgGridFrameworkComponent,
52
+ getAgGridColumnApiModuleReference: () => {
53
+ return columnApiModule;
54
+ },
55
+ })
56
+ .then((api) => {
57
+ adaptableApiRef.current = api;
58
+ //see #no_additional_react_root
59
+ setAdaptableJSXElement(api.internalApi.getAdaptableJSXElement());
60
+ if (onAdaptableReady) {
61
+ api.eventApi.on('AdaptableReady', onAdaptableReady);
66
62
  }
67
- };
68
- }, []);
69
- // Cleanup on component unmount
70
- React.useEffect(() => {
71
- return () => {
72
- isMounted.current = false;
73
- };
74
- }, []);
63
+ setupFrameworkComponents(api);
64
+ successInstances.set(id, true);
65
+ }, (err) => {
66
+ console.error('Failed to initialize AdaptableReact component', err);
67
+ });
68
+ }, [id]);
75
69
  return (React.createElement("div", { style: props.style, ref: containerRef, id: adaptableContainerId, className: join(props.className, 'ab__react-wrapper') }, adaptableJSXElement));
76
70
  };
77
71
  export default AdaptableReact;