@azrico/reactgrid 1.1.11 → 1.1.13

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.
@@ -1,478 +1,519 @@
1
- import '@inovua/reactdatagrid-enterprise/index.css';
2
- import '@inovua/reactdatagrid-enterprise/theme/default-dark.css';
3
- import '@inovua/reactdatagrid-enterprise/theme/amber-dark.css';
4
- import '../themes/base_styles.css';
5
- import '../themes/grid.css';
6
-
7
- import { ContextMenu, ConvertableToMenuItem } from '@azrico/controls';
8
- import React, { useCallback, useState } from 'react';
9
- import {
10
- TypeColumn,
11
- TypeDataGridProps,
12
- TypeDataSource,
13
- TypeFilterTypes,
14
- TypeRowDetailsInfo,
15
- TypeRowProps,
16
- TypeRowSelection,
17
- } from '@inovua/reactdatagrid-community/types';
18
- import { fix_parent_child_relations, func_selection } from '../func/helpers';
19
-
20
- import ReactDataGrid from '@inovua/reactdatagrid-enterprise';
21
- import { TypeOnSelectionChangeArg } from '@inovua/reactdatagrid-community/types/TypeDataGridProps';
22
- import clsx from 'clsx';
23
- import { object_equals } from '@azrico/object';
24
- import { useResizeDetector } from 'react-resize-detector';
25
-
26
- /* -------------------------------------------------------------------------- */
27
- type ModifyFields<T, R> = Omit<T, keyof R> & R;
28
- type MakeWriteable<Type> = {
29
- -readonly [Key in keyof Type]: Type[Key];
30
- };
31
- /* -------------------------------------------------------------------------- */
32
- export type ColumnDefinition = TypeColumn;
33
- export type CellDefinition = any;
34
- export type RowDefinition = any;
35
- export type RowProps = TypeRowProps;
36
- export type RowSelectionValue = TypeRowSelection;
37
- export type DataRowDefinition = any;
38
- export type FilterTypes = TypeFilterTypes;
39
- export type DataSourceDefinition = TypeDataSource;
40
- export type OnSelectionChangeFunc = (config: TypeOnSelectionChangeArg) => void;
41
- export type RowDetailsFunc = (rowDetailsInfo: TypeRowDetailsInfo) => React.ReactNode;
42
- /* -------------------------------------------------------------------------- */
43
- type loose_column_names = {
44
- key: string;
45
- field: string;
46
- columnName: string;
47
- name: string;
48
- };
49
- type loose_column_fields = loose_column_names & { title: string };
50
- export type LooseColumnDefinition = Partial<loose_column_fields & ColumnDefinition>;
51
- //
52
- type columnOrString = string | LooseColumnDefinition;
53
- type customColumnData = {
54
- name: string;
55
- render: (cell: any, formatterParams, onRendered) => React.ReactNode | undefined | null;
56
- };
57
- type GridOptions = Partial<TypeDataGridProps>;
58
- /* -------------------------------------------------------------------------- */
59
- export type AzGridProps = {
60
- columns?: columnOrString[];
61
- dataSource?: any[] | ((args: any) => any[] | Promise<any[] | any>);
62
-
63
- licenseKey?: string;
64
-
65
- gridOptions?: GridOptions;
66
-
67
- columnExtensions?: Array<{ name: string } & Partial<ColumnDefinition>>;
68
- editableColumns?: string[];
69
- leftColumns?: string[];
70
- rightColumns?: string[];
71
- customColumns?: customColumnData[];
72
-
73
- contextMenuItems?: ConvertableToMenuItem[];
74
-
75
- highlightedRow?: any;
76
- RowDetail?: RowDetailsFunc;
77
-
78
- selected?: RowSelectionValue;
79
- /* -------------------------------------------------------------------------- */
80
- onSelectionChange?: OnSelectionChangeFunc;
81
-
82
- onRowClick?: (rowIndex: number, row: any) => void;
83
- onRowDoubleClick?: (rowIndex: number, row: any) => void;
84
- onLimitChange?: (v: number) => void;
85
- onSkipChange?: (v: number) => void;
86
- /* -------------------------------------------------------------------------- */
87
- skip?: number;
88
- limit?: number;
89
- //
90
- loading?: boolean;
91
- dark?: boolean;
92
- allowChildRows?: boolean;
93
- looseCacheCheck?: boolean;
94
- className?: string;
95
- };
96
-
97
- type DataGridProps = ModifyFields<
98
- AzGridProps,
99
- {
100
- columns: ColumnDefinition[];
101
- dataSource: any;
102
- }
103
- >;
104
- /* -------------------------------------------------------------------------- */
105
- // const defaultfilterTypes: FilterTypes = {
106
- // string: {
107
- // type: 'string',
108
- // emptyValue: '',
109
- // operators: [
110
- // {
111
- // name: 'contains',
112
- // fn: ({ value, filterValue, data }) => {
113
- // console.log('fn');
114
- // return !filterValue ? true : value.indexOf(filterValue) != -1;
115
- // },
116
- // },
117
- // ],
118
- // },
119
- // };
120
- const defaultOptions: GridOptions = {
121
- //filterTypes: defaultfilterTypes,
122
- remoteFilter: false,
123
- remoteSort: false,
124
-
125
- rowReorderColumn: false,
126
- resizable: true,
127
- rowIndexColumn: true,
128
- };
129
- const forcedOptions: GridOptions = {
130
- idProperty: '__table_id',
131
- nodesProperty: '__children',
132
- };
133
- const defaultColumnOptions: ColumnDefinition = {
134
- draggable: false,
135
- lockable: false,
136
- };
137
- const dynamic_option_names = ['selected', 'loading', 'limit', 'skip'];
138
- /* -------------------------------------------------------------------------- */
139
- export function AzGrid(props: AzGridProps) {
140
- /* -------------------------------------------------------------------------- */
141
- /* Columns */
142
- /* -------------------------------------------------------------------------- */
143
- const cached_columns = React.useMemo(() => {
144
- return ApplyColumnData(props);
145
- }, [
146
- props.columns,
147
- props.leftColumns,
148
- props.rightColumns,
149
- props.editableColumns,
150
- props.columnExtensions,
151
- props.customColumns,
152
- ]);
153
- /* -------------------------------------------------------------------------- */
154
- /* Row changes */
155
- /* -------------------------------------------------------------------------- */
156
-
157
- const cached_data = React.useRef<any>();
158
- const last_cache_info = React.useRef<any>();
159
-
160
- const dataSource = React.useCallback(
161
- async (dt) => {
162
- let result_data: any = undefined;
163
-
164
- let isUsingCache = false;
165
-
166
- const cacheInfo = { ds: props.dataSource, skip: dt.skip, limit: dt.limit };
167
- //check if we can used cached data
168
- //if the datasource is changed since last render, we should invalidate cache
169
- if (!validateCache(last_cache_info.current, cacheInfo, props.looseCacheCheck)) {
170
- cached_data.current = undefined;
171
- last_cache_info.current = cacheInfo;
172
- }
173
-
174
- if (cached_data.current) {
175
- //cache is valid, use the cache
176
- isUsingCache = true;
177
- result_data = cached_data.current;
178
- } else {
179
- //cache is invalid, use the datasource again
180
-
181
- isUsingCache = false;
182
- if (typeof props.dataSource === 'function') {
183
- result_data = await props.dataSource(dt);
184
- } else result_data = props.dataSource;
185
- }
186
-
187
- //making sure the datasource is a valid object :
188
- if (!result_data) result_data = [];
189
- if (Array.isArray(result_data) || typeof result_data !== 'object')
190
- result_data = { data: result_data };
191
-
192
- if (!Array.isArray(result_data.data)) result_data.data = [result_data.data];
193
-
194
- //no need to fix parent/child when using cache (it should be already fixed)
195
- if (!isUsingCache && props.allowChildRows)
196
- result_data.data = fix_parent_child_relations(result_data.data);
197
-
198
- if (!result_data.count) result_data.count = (result_data.data as Array<any>).length;
199
-
200
- //update cache
201
- cached_data.current = result_data;
202
- return result_data;
203
- },
204
- [props.dataSource]
205
- );
206
-
207
- return (
208
- <DataGrid
209
- {...props}
210
- columns={cached_columns}
211
- dataSource={dataSource}
212
- highlightedRow={props.highlightedRow}
213
- />
214
- );
215
- }
216
-
217
- /* -------------------------------------------------------------------------- */
218
- function DataGrid(props: DataGridProps) {
219
- const { columns, dataSource, dark, contextMenuItems } = props;
220
-
221
- /* -------------------------------------------------------------------------- */
222
- const gridRef = React.useRef<any>(null);
223
- const click_data = React.useRef<any>(null);
224
-
225
- const [menuAnchorEl, set_menuAnchorEl] = React.useState<any>(null);
226
- const [menuRow, set_menuRow] = React.useState<any>(null);
227
-
228
- React.useEffect(() => {
229
- click_data.current = {};
230
- }, [dataSource]);
231
-
232
- /* --------------------------------- events --------------------------------- */
233
-
234
- const onRowContextMenu = React.useCallback((rowProps, event) => {
235
- if (func_selection.has_selection()) return;
236
- update_menuAnchorEl(func_selection.getVirtualElement(event.clientX, event.clientY));
237
- update_menuRow(rowProps.data);
238
- event.preventDefault();
239
- }, []);
240
- const onRowClick = React.useCallback((rowProps, event) => {
241
- const nowDate = Date.now();
242
- const lastclick: number = click_data.current[get_row_id(rowProps)];
243
- if (lastclick) {
244
- const diffTime = Math.abs(nowDate - lastclick);
245
- if (diffTime < 400) {
246
- props.onRowDoubleClick &&
247
- props.onRowDoubleClick(rowProps.rowIndex, rowProps.data);
248
- }
249
- }
250
-
251
- click_data.current[get_row_id(rowProps)] = nowDate;
252
- props.onRowClick && props.onRowClick(rowProps.rowIndex, rowProps.data);
253
- }, []);
254
- /* ------------------------------- ContextMenu ------------------------------ */
255
-
256
- const update_menuAnchorEl = React.useCallback((el) => {
257
- if (el) {
258
- set_menuAnchorEl(el);
259
- } else set_menuAnchorEl(null);
260
- }, []);
261
- const update_menuRow = React.useCallback((r) => {
262
- if (r) {
263
- set_menuRow(r);
264
- } else set_menuRow(null);
265
- }, []);
266
-
267
- /* -------------------------------------------------------------------------- */
268
- /* Create the grid */
269
- /* -------------------------------------------------------------------------- */
270
-
271
- const base_gridOptions = React.useMemo<GridOptions>(() => {
272
- const firstcolumn = columns[0];
273
- const licenseKey = props.licenseKey || get_default_key();
274
- const events: GridOptions = {
275
- onRowClick: onRowClick,
276
- onSkipChange: props.onSkipChange,
277
- onLimitChange: props.onLimitChange,
278
- onRowContextMenu: onRowContextMenu,
279
- };
280
- const extra_ops: GridOptions = {
281
- onSelectionChange: props.onSelectionChange,
282
- licenseKey: licenseKey,
283
- treeColumn: props.allowChildRows ? firstcolumn.name : undefined,
284
- renderRowDetails: props.RowDetail,
285
- theme: dark ? 'amber-dark' : 'default-light',
286
- };
287
- return { ...defaultOptions, ...events, ...extra_ops };
288
- }, [columns, props.allowChildRows, props.RowDetail]);
289
-
290
- const static_gridOptions = React.useMemo<GridOptions>(() => {
291
- return { ...base_gridOptions, ...props.gridOptions, ...forcedOptions };
292
- }, [props.gridOptions, base_gridOptions, forcedOptions]);
293
-
294
- const dynamic_gridOptions = React.useMemo<GridOptions>(
295
- () => {
296
- const res = {};
297
- dynamic_option_names.map((r) => {
298
- res[r] = props[r];
299
- });
300
- return res;
301
- },
302
- dynamic_option_names.map((r) => props[r])
303
- );
304
-
305
- // const { width, height, ref: resize_ref } = useResizeDetector();
306
-
307
- return (
308
- <div className={clsx(props.className, 'azgrid-nooverflow', 'azgrid-filler')}>
309
- <ReactDataGrid
310
- {...static_gridOptions}
311
- {...dynamic_gridOptions}
312
- handle={(ref) => (gridRef.current = ref ? ref.current : null)}
313
- className={'azgrid-filler'}
314
- columns={columns}
315
- dataSource={dataSource}
316
- />
317
- <ContextMenu
318
- menuAnchorEl={menuAnchorEl}
319
- set_menuAnchorEl={update_menuAnchorEl}
320
- menuRow={menuRow}
321
- items={contextMenuItems}
322
- />
323
- </div>
324
- );
325
- }
326
-
327
- /* -------------------------------------------------------------------------- */
328
- /* -------------------------------------------------------------------------- */
329
- /* -------------------------------------------------------------------------- */
330
- /* -------------------------------------------------------------------------- */
331
-
332
- /**
333
- * Gets name of a column from its column object
334
- * @param obj
335
- * @returns
336
- */
337
- function get_col_name(obj: any): string {
338
- if (!obj) return 'default';
339
- if (typeof obj == 'string') return obj;
340
- if (typeof obj != 'object') return 'default';
341
- return obj.name || obj.field || obj.key || obj.columnName || 'default';
342
- }
343
- /**
344
- * Gets name of a column from its column object
345
- * @param obj
346
- * @returns
347
- */
348
- function get_row_id(obj: any): any {
349
- if (!obj) return 'default';
350
- if (typeof obj == 'string') return obj;
351
- if (typeof obj != 'object') return 'default';
352
- return obj.__table_id || obj.id || 'default';
353
- }
354
-
355
- /**
356
- * Gets default activation key for grid plugin
357
- * @returns
358
- */
359
- function get_default_key() {
360
- return 'ExpiryDate=2999-01-01,Z=1040577552';
361
- }
362
-
363
- //check if two cache objects are the same
364
- function validateCache(current: any, old: any, looseCheck = false) {
365
- if (!old || !object_equals(current, old)) return false;
366
-
367
- if (looseCheck) return true;
368
-
369
- if (current.ds != old.ds) return false;
370
- return true;
371
- }
372
- /**
373
- * merges together all different props for columns
374
- * to build a single final column data array
375
- * @param props
376
- * @returns
377
- */
378
- function ApplyColumnData(props: any) {
379
- const final_columns: MakeWriteable<ColumnDefinition>[] = [];
380
- let mcols = props.columns || [];
381
- let mcolExts = props.columnExtensions || [];
382
- let rightColumns = props.rightColumns || [];
383
- let leftColumns = props.leftColumns || [];
384
- let editableColumns = props.editableColumns || [];
385
- let customColumns = props.customColumns || [];
386
-
387
- if (!Array.isArray(mcols)) return [];
388
- /* ------------------------------- fix columns ------------------------------ */
389
-
390
- mcols
391
- .map((r) => {
392
- if (r == null) return null;
393
- if (typeof r === 'string') return { field: r, header: r };
394
- if (typeof r === 'object') {
395
- let final_cl: MakeWriteable<ColumnDefinition> = {
396
- name: get_col_name(r),
397
- header: r.header || r.title || r.name || '-',
398
- width: r.width || undefined,
399
- };
400
- delete r.field;
401
- delete r.key;
402
- delete r.columnName;
403
- final_cl = {
404
- ...defaultColumnOptions,
405
- ...r,
406
- ...final_cl,
407
- };
408
- return final_cl;
409
- }
410
- return null;
411
- })
412
- .forEach((r) => {
413
- if (r != null) final_columns.push(r);
414
- });
415
-
416
- /* ---------------------------- apply extensions ---------------------------- */
417
- if (mcolExts && Array.isArray(mcolExts))
418
- for (let index = 0; index < mcolExts.length; index++) {
419
- const ext = mcolExts[index];
420
- const ext_cl = get_col_name(ext);
421
- const mcol = final_columns.find((s) => s.name === ext_cl);
422
- if (mcol) {
423
- for (const key in ext) {
424
- if (!mcol[key] && key != 'name') mcol[key] = ext[key];
425
- }
426
- }
427
- // if (ext.widthGrow) mcol.widthGrow = ext.widthGrow;
428
- }
429
-
430
- /* ------------------------------- frozen cols ------------------------------ */
431
- if (Array.isArray(leftColumns)) {
432
- for (let index = 0; index < leftColumns.length; index++) {
433
- const r = get_col_name(leftColumns[index]);
434
- const mcol = final_columns.find((s) => s.name === r);
435
- if (mcol) {
436
- mcol.locked = 'start';
437
- }
438
- }
439
- }
440
- if (Array.isArray(rightColumns)) {
441
- for (let index = 0; index < rightColumns.length; index++) {
442
- const r = get_col_name(rightColumns[index]);
443
- const mcol = final_columns.find((s) => s.name === r);
444
- if (mcol) {
445
- mcol.locked = 'end';
446
- }
447
- }
448
- }
449
- /* -------------------------------- ediatable ------------------------------- */
450
- if (Array.isArray(editableColumns)) {
451
- for (let index = 0; index < editableColumns.length; index++) {
452
- const r = get_col_name(editableColumns[index]);
453
- const mcol = final_columns.find((s) => s.name === r);
454
- if (mcol) {
455
- // mcol.editor = editorWrapper;
456
- // mcol.editorOptions = {
457
- // editOnClick: true,
458
- // };
459
- }
460
- }
461
- }
462
- if (Array.isArray(customColumns)) {
463
- for (let index = 0; index < customColumns.length; index++) {
464
- const r = customColumns[index];
465
- const rn = get_col_name(r);
466
- const mcol = final_columns.find((s) => s.name === rn);
467
- if (mcol) {
468
- //@ts-ignore
469
- mcol.render = r.render;
470
- }
471
- }
472
- }
473
- if (!Array.isArray(final_columns) || final_columns.length <= 0) return [];
474
- return final_columns;
475
- }
476
-
477
- /* -------------------------------------------------------------------------- */
478
- export default AzGrid;
1
+ import '@inovua/reactdatagrid-enterprise/index.css';
2
+ import '@inovua/reactdatagrid-enterprise/theme/default-dark.css';
3
+ import '@inovua/reactdatagrid-enterprise/theme/amber-dark.css';
4
+ import '../themes/base_styles.css';
5
+ import '../themes/grid.css';
6
+
7
+ import { ContextMenu, ConvertableToMenuItem } from '@azrico/controls';
8
+ import React, { useCallback, useState } from 'react';
9
+ import {
10
+ TypeColumn,
11
+ TypeDataGridProps,
12
+ TypeDataSource,
13
+ TypeFilterTypes,
14
+ TypeRowDetailsInfo,
15
+ TypeRowProps,
16
+ TypeRowSelection,
17
+ } from '@inovua/reactdatagrid-community/types';
18
+ import { fix_parent_child_relations, func_selection } from '../func/helpers';
19
+
20
+ import ReactDataGrid from '@inovua/reactdatagrid-enterprise';
21
+ import { TypeOnSelectionChangeArg } from '@inovua/reactdatagrid-community/types/TypeDataGridProps';
22
+ import clsx from 'clsx';
23
+ import { object_equals } from '@azrico/object';
24
+ import { useResizeDetector } from 'react-resize-detector';
25
+
26
+ /* -------------------------------------------------------------------------- */
27
+ type ModifyFields<T, R> = Omit<T, keyof R> & R;
28
+ type MakeWriteable<Type> = {
29
+ -readonly [Key in keyof Type]: Type[Key];
30
+ };
31
+ /* -------------------------------------------------------------------------- */
32
+ export type ColumnDefinition = TypeColumn;
33
+ export type CellDefinition = any;
34
+ export type RowDefinition = any;
35
+ export type RowProps = TypeRowProps;
36
+ export type RowSelectionValue = TypeRowSelection;
37
+ export type DataRowDefinition = any;
38
+ export type FilterTypes = TypeFilterTypes;
39
+ export type DataSourceDefinition = TypeDataSource;
40
+ export type OnSelectionChangeFunc = (config: TypeOnSelectionChangeArg) => void;
41
+ export type RowDetailsFunc = (rowDetailsInfo: TypeRowDetailsInfo) => React.ReactNode;
42
+ /* -------------------------------------------------------------------------- */
43
+ type loose_column_names = {
44
+ key: string;
45
+ field: string;
46
+ columnName: string;
47
+ name: string;
48
+ };
49
+ type loose_column_fields = loose_column_names & {
50
+ title: string;
51
+ type: string;
52
+ items: any[];
53
+ };
54
+ export type LooseColumnDefinition = Partial<loose_column_fields & ColumnDefinition>;
55
+ //
56
+ type columnOrString = string | LooseColumnDefinition;
57
+ type customColumnData = {
58
+ name: string;
59
+ render: (cell: any, formatterParams, onRendered) => React.ReactNode | undefined | null;
60
+ };
61
+ type GridOptions = Partial<TypeDataGridProps>;
62
+ /* -------------------------------------------------------------------------- */
63
+ export type AzGridProps = {
64
+ columns?: columnOrString[];
65
+ dataSource?: any[] | ((args: any) => any[] | Promise<any[] | any>);
66
+
67
+ licenseKey?: string;
68
+
69
+ gridOptions?: GridOptions;
70
+
71
+ columnExtensions?: Array<{ name: string } & Partial<ColumnDefinition>>;
72
+ editableColumns?: string[];
73
+ leftColumns?: string[];
74
+ rightColumns?: string[];
75
+ customColumns?: customColumnData[];
76
+
77
+ contextMenuItems?: ConvertableToMenuItem[];
78
+
79
+ highlightedRow?: any;
80
+ RowDetail?: RowDetailsFunc;
81
+
82
+ selected?: RowSelectionValue;
83
+ /* -------------------------------------------------------------------------- */
84
+ onSelectionChange?: OnSelectionChangeFunc;
85
+
86
+ onRowClick?: (rowIndex: number, row: any) => void;
87
+ onRowDoubleClick?: (rowIndex: number, row: any) => void;
88
+ onLimitChange?: (v: number) => void;
89
+ onSkipChange?: (v: number) => void;
90
+ /* -------------------------------------------------------------------------- */
91
+ skip?: number;
92
+ limit?: number;
93
+ //
94
+ loading?: boolean;
95
+ dark?: boolean;
96
+ allowChildRows?: boolean;
97
+ looseCacheCheck?: boolean;
98
+ className?: string;
99
+ };
100
+
101
+ type DataGridProps = ModifyFields<
102
+ AzGridProps,
103
+ {
104
+ columns: ColumnDefinition[];
105
+ dataSource: any;
106
+ }
107
+ >;
108
+ /* -------------------------------------------------------------------------- */
109
+ // const defaultfilterTypes: FilterTypes = {
110
+ // string: {
111
+ // type: 'string',
112
+ // emptyValue: '',
113
+ // operators: [
114
+ // {
115
+ // name: 'contains',
116
+ // fn: ({ value, filterValue, data }) => {
117
+ // console.log('fn');
118
+ // return !filterValue ? true : value.indexOf(filterValue) != -1;
119
+ // },
120
+ // },
121
+ // ],
122
+ // },
123
+ // };
124
+ const defaultOptions: GridOptions = {
125
+ //filterTypes: defaultfilterTypes,
126
+ remoteFilter: false,
127
+ remoteSort: false,
128
+
129
+ rowReorderColumn: false,
130
+ resizable: true,
131
+ rowIndexColumn: true,
132
+ };
133
+ const forcedOptions: GridOptions = {
134
+ idProperty: '__table_id',
135
+ nodesProperty: '__children',
136
+ };
137
+ const defaultColumnOptions: ColumnDefinition = {
138
+ draggable: false,
139
+ lockable: false,
140
+ };
141
+ const dynamic_option_names = ['selected', 'loading', 'limit', 'skip'];
142
+ /* -------------------------------------------------------------------------- */
143
+ export function AzGrid(props: AzGridProps) {
144
+ /* -------------------------------------------------------------------------- */
145
+ /* Columns */
146
+ /* -------------------------------------------------------------------------- */
147
+ const cached_columns = React.useMemo(() => {
148
+ return ApplyColumnData(props);
149
+ }, [
150
+ props.columns,
151
+ props.leftColumns,
152
+ props.rightColumns,
153
+ props.editableColumns,
154
+ props.columnExtensions,
155
+ props.customColumns,
156
+ ]);
157
+ /* -------------------------------------------------------------------------- */
158
+ /* Row changes */
159
+ /* -------------------------------------------------------------------------- */
160
+
161
+ const cached_data = React.useRef<any>();
162
+ const last_cache_info = React.useRef<any>();
163
+
164
+ const dataSource = React.useCallback(
165
+ async (dt) => {
166
+ let result_data: any = undefined;
167
+
168
+ let isUsingCache = false;
169
+
170
+ const cacheInfo = { ds: props.dataSource, skip: dt.skip, limit: dt.limit };
171
+ //check if we can used cached data
172
+ //if the datasource is changed since last render, we should invalidate cache
173
+ if (!validateCache(last_cache_info.current, cacheInfo, props.looseCacheCheck)) {
174
+ cached_data.current = undefined;
175
+ last_cache_info.current = cacheInfo;
176
+ }
177
+
178
+ if (cached_data.current) {
179
+ //cache is valid, use the cache
180
+ isUsingCache = true;
181
+ result_data = cached_data.current;
182
+ } else {
183
+ //cache is invalid, use the datasource again
184
+
185
+ isUsingCache = false;
186
+ if (typeof props.dataSource === 'function') {
187
+ result_data = await props.dataSource(dt);
188
+ } else result_data = props.dataSource;
189
+ }
190
+
191
+ //making sure the datasource is a valid object :
192
+ if (!result_data) result_data = [];
193
+ if (Array.isArray(result_data) || typeof result_data !== 'object')
194
+ result_data = { data: result_data };
195
+
196
+ if (!Array.isArray(result_data.data)) result_data.data = [result_data.data];
197
+
198
+ //no need to fix parent/child when using cache (it should be already fixed)
199
+ if (!isUsingCache && props.allowChildRows)
200
+ result_data.data = fix_parent_child_relations(result_data.data);
201
+
202
+ if (!result_data.count) result_data.count = (result_data.data as Array<any>).length;
203
+
204
+ //update cache
205
+ cached_data.current = result_data;
206
+ return result_data;
207
+ },
208
+ [props.dataSource]
209
+ );
210
+
211
+ return (
212
+ <DataGrid
213
+ {...props}
214
+ columns={cached_columns}
215
+ dataSource={dataSource}
216
+ highlightedRow={props.highlightedRow}
217
+ />
218
+ );
219
+ }
220
+
221
+ /* -------------------------------------------------------------------------- */
222
+ function DataGrid(props: DataGridProps) {
223
+ const { columns, dataSource, dark, contextMenuItems } = props;
224
+
225
+ /* -------------------------------------------------------------------------- */
226
+ const gridRef = React.useRef<any>(null);
227
+ const click_data = React.useRef<any>(null);
228
+
229
+ const [menuAnchorEl, set_menuAnchorEl] = React.useState<any>(null);
230
+ const [menuRow, set_menuRow] = React.useState<any>(null);
231
+
232
+ React.useEffect(() => {
233
+ click_data.current = {};
234
+ }, [dataSource]);
235
+
236
+ /* --------------------------------- events --------------------------------- */
237
+
238
+ const onRowContextMenu = React.useCallback((rowProps, event) => {
239
+ if (func_selection.has_selection()) return;
240
+ update_menuRow(rowProps.data);
241
+ update_menuAnchorEl(func_selection.getVirtualElement(event.clientX, event.clientY));
242
+ event.preventDefault();
243
+ }, []);
244
+ const onRowClick = React.useCallback((rowProps, event) => {
245
+ const nowDate = Date.now();
246
+ const lastclick: number = click_data.current[get_row_id(rowProps)];
247
+ if (lastclick) {
248
+ const diffTime = Math.abs(nowDate - lastclick);
249
+ if (diffTime < 400) {
250
+ props.onRowDoubleClick &&
251
+ props.onRowDoubleClick(rowProps.rowIndex, rowProps.data);
252
+ }
253
+ }
254
+
255
+ click_data.current[get_row_id(rowProps)] = nowDate;
256
+ props.onRowClick && props.onRowClick(rowProps.rowIndex, rowProps.data);
257
+ }, []);
258
+ /* ------------------------------- ContextMenu ------------------------------ */
259
+
260
+ const update_menuAnchorEl = React.useCallback((el) => {
261
+ if (el) {
262
+ set_menuAnchorEl(el);
263
+ } else set_menuAnchorEl(null);
264
+ }, []);
265
+ const update_menuRow = React.useCallback((r) => {
266
+ if (r) {
267
+ set_menuRow(r);
268
+ } else set_menuRow(null);
269
+ }, []);
270
+
271
+ /* -------------------------------------------------------------------------- */
272
+ /* Create the grid */
273
+ /* -------------------------------------------------------------------------- */
274
+
275
+ const base_gridOptions = React.useMemo<GridOptions>(() => {
276
+ const firstcolumn = columns[0];
277
+ const licenseKey = props.licenseKey || get_default_key();
278
+ const events: GridOptions = {
279
+ onRowClick: onRowClick,
280
+ onSkipChange: props.onSkipChange,
281
+ onLimitChange: props.onLimitChange,
282
+ onRowContextMenu: onRowContextMenu,
283
+ };
284
+ const extra_ops: GridOptions = {
285
+ onSelectionChange: props.onSelectionChange,
286
+ licenseKey: licenseKey,
287
+ treeColumn: props.allowChildRows ? firstcolumn.name : undefined,
288
+ renderRowDetails: props.RowDetail,
289
+ theme: dark ? 'amber-dark' : 'default-light',
290
+ };
291
+ return { ...defaultOptions, ...events, ...extra_ops };
292
+ }, [columns, props.allowChildRows, props.RowDetail]);
293
+
294
+ const static_gridOptions = React.useMemo<GridOptions>(() => {
295
+ return { ...base_gridOptions, ...props.gridOptions, ...forcedOptions };
296
+ }, [props.gridOptions, base_gridOptions, forcedOptions]);
297
+
298
+ const dynamic_gridOptions = React.useMemo<GridOptions>(
299
+ () => {
300
+ const res = {};
301
+ dynamic_option_names.map((r) => {
302
+ res[r] = props[r];
303
+ });
304
+ return res;
305
+ },
306
+ dynamic_option_names.map((r) => props[r])
307
+ );
308
+
309
+ // const { width, height, ref: resize_ref } = useResizeDetector();
310
+
311
+ return (
312
+ <div className={clsx(props.className, 'azgrid-nooverflow', 'azgrid-filler')}>
313
+ <ReactDataGrid
314
+ {...static_gridOptions}
315
+ {...dynamic_gridOptions}
316
+ handle={(ref) => (gridRef.current = ref ? ref.current : null)}
317
+ className={'azgrid-filler'}
318
+ columns={columns}
319
+ dataSource={dataSource}
320
+ />
321
+ <ContextMenu
322
+ menuAnchorEl={menuAnchorEl}
323
+ set_menuAnchorEl={update_menuAnchorEl}
324
+ menuRow={menuRow}
325
+ items={contextMenuItems}
326
+ />
327
+ </div>
328
+ );
329
+ }
330
+
331
+ /* -------------------------------------------------------------------------- */
332
+ /* -------------------------------------------------------------------------- */
333
+ /* -------------------------------------------------------------------------- */
334
+ /* -------------------------------------------------------------------------- */
335
+
336
+ /**
337
+ * merges together all different props for columns
338
+ * to build a single final column data array
339
+ * @param props
340
+ * @returns
341
+ */
342
+ function ApplyColumnData(props: any) {
343
+ const final_columns: MakeWriteable<ColumnDefinition>[] = [];
344
+ let mcols = props.columns || [];
345
+ let mcolExts = props.columnExtensions || [];
346
+ let rightColumns = props.rightColumns || [];
347
+ let leftColumns = props.leftColumns || [];
348
+ let editableColumns = props.editableColumns || [];
349
+ let customColumns = props.customColumns || [];
350
+
351
+ if (!Array.isArray(mcols)) return [];
352
+ /* ------------------------------- fix columns ------------------------------ */
353
+
354
+ mcols
355
+ .map((r) => {
356
+ if (r == null) return null;
357
+ if (typeof r === 'string') return { field: r, header: r };
358
+ if (typeof r === 'object') {
359
+ let final_cl: MakeWriteable<ColumnDefinition> = {
360
+ name: get_col_name(r),
361
+ header: get_col_header(r),
362
+ width: r.width || undefined,
363
+ };
364
+ final_cl = {
365
+ ...defaultColumnOptions,
366
+ ...r,
367
+ ...final_cl,
368
+ };
369
+ return final_cl;
370
+ }
371
+ return null;
372
+ })
373
+ .forEach((r) => {
374
+ if (r != null) final_columns.push(r);
375
+ });
376
+
377
+ /* ---------------------------- apply extensions ---------------------------- */
378
+ if (mcolExts && Array.isArray(mcolExts))
379
+ for (let index = 0; index < mcolExts.length; index++) {
380
+ const ext = mcolExts[index];
381
+ const ext_cl = get_col_name(ext);
382
+ const mcol = final_columns.find((s) => get_col_name(s) === ext_cl);
383
+ if (mcol) {
384
+ for (const key in ext) {
385
+ if (!mcol[key] && key != 'name') mcol[key] = ext[key];
386
+ }
387
+ }
388
+ // if (ext.widthGrow) mcol.widthGrow = ext.widthGrow;
389
+ }
390
+
391
+ /* ------------------------------- frozen cols ------------------------------ */
392
+ if (Array.isArray(leftColumns)) {
393
+ for (let index = 0; index < leftColumns.length; index++) {
394
+ const r = get_col_name(leftColumns[index]);
395
+ const mcol = final_columns.find((s) => get_col_name(s) === r);
396
+ if (mcol) {
397
+ mcol.locked = 'start';
398
+ }
399
+ }
400
+ }
401
+ if (Array.isArray(rightColumns)) {
402
+ for (let index = 0; index < rightColumns.length; index++) {
403
+ const r = get_col_name(rightColumns[index]);
404
+ const mcol = final_columns.find((s) => get_col_name(s) === r);
405
+ if (mcol) {
406
+ mcol.locked = 'end';
407
+ }
408
+ }
409
+ }
410
+ /* -------------------------------- ediatable ------------------------------- */
411
+ if (Array.isArray(editableColumns)) {
412
+ for (let index = 0; index < editableColumns.length; index++) {
413
+ const r = get_col_name(editableColumns[index]);
414
+ const mcol = final_columns.find((s) => get_col_name(s) === r);
415
+ if (mcol) {
416
+ // mcol.editor = editorWrapper;
417
+ // mcol.editorOptions = {
418
+ // editOnClick: true,
419
+ // };
420
+ }
421
+ }
422
+ }
423
+ if (Array.isArray(customColumns)) {
424
+ for (let index = 0; index < customColumns.length; index++) {
425
+ const r = customColumns[index];
426
+ const rn = get_col_name(r);
427
+ const mcol = final_columns.find((s) => get_col_name(s) === rn);
428
+ if (mcol) {
429
+ //@ts-ignore
430
+ mcol.render = r.render;
431
+ }
432
+ }
433
+ }
434
+
435
+ /* ------------------------------ apply renames ----------------------------- */
436
+ for (let index = 0; index < final_columns.length; index++) {
437
+ const col = final_columns[index];
438
+
439
+ if (!col.name) {
440
+ col.name = get_col_name(col);
441
+ delete col.field;
442
+ delete col.key;
443
+ delete col.columnName;
444
+ }
445
+ //
446
+ if (col.type) {
447
+ col.editor = col.type;
448
+ delete col.type;
449
+ }
450
+ //
451
+ if (col.items) {
452
+ if (!col.editorParams) col.editorParams = { valuesLookup: true, allowEmpty: true };
453
+ col.editorParams.values = {};
454
+ for (let index = 0; index < col.items; index++) {
455
+ const ci = col.items[index];
456
+ col.editorParams.values[String(ci).toLowerCase()] = ci;
457
+ }
458
+ delete col.items;
459
+ }
460
+ }
461
+ /* -------------------------------------------------------------------------- */
462
+
463
+ return final_columns;
464
+ }
465
+
466
+ /**
467
+ * Gets name of a column from its column object
468
+ * @param obj
469
+ * @returns
470
+ */
471
+ function get_col_name(obj: any): string {
472
+ if (!obj) return 'default';
473
+ if (typeof obj == 'string') return obj;
474
+ if (typeof obj != 'object') return 'default';
475
+ return obj.name || obj.field || obj.key || obj.columnName || 'default';
476
+ }
477
+ /**
478
+ * Gets header of a column
479
+ * @param obj
480
+ * @returns
481
+ */
482
+ function get_col_header(obj: any): string {
483
+ if (!obj) return 'default';
484
+ if (typeof obj == 'string') return obj;
485
+ if (typeof obj != 'object') return 'default';
486
+ return obj.header || obj.title || get_col_name(obj);
487
+ }
488
+ /**
489
+ * Gets name of a column from its column object
490
+ * @param obj
491
+ * @returns
492
+ */
493
+ function get_row_id(obj: any): any {
494
+ if (!obj) return 'default';
495
+ if (typeof obj == 'string') return obj;
496
+ if (typeof obj != 'object') return 'default';
497
+ return obj.__table_id || obj.id || 'default';
498
+ }
499
+
500
+ /**
501
+ * Gets default activation key for grid plugin
502
+ * @returns
503
+ */
504
+ function get_default_key() {
505
+ return 'ExpiryDate=2999-01-01,Z=1040577552';
506
+ }
507
+
508
+ //check if two cache objects are the same
509
+ function validateCache(current: any, old: any, looseCheck = false) {
510
+ if (!old || !object_equals(current, old)) return false;
511
+
512
+ if (looseCheck) return true;
513
+
514
+ if (current.ds != old.ds) return false;
515
+ return true;
516
+ }
517
+
518
+ /* -------------------------------------------------------------------------- */
519
+ export default AzGrid;