slickgrid-react 5.8.0 → 5.9.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.
Files changed (83) hide show
  1. package/README.md +8 -0
  2. package/dist/cjs/components/slickgrid-react.js +44 -32
  3. package/dist/cjs/components/slickgrid-react.js.map +1 -1
  4. package/dist/cjs/extensions/slickRowDetailView.js +316 -0
  5. package/dist/cjs/extensions/slickRowDetailView.js.map +1 -0
  6. package/dist/cjs/global-grid-options.js +9 -0
  7. package/dist/cjs/global-grid-options.js.map +1 -1
  8. package/dist/cjs/index.js +3 -2
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/models/rowDetailView.interface.js +3 -0
  11. package/dist/cjs/models/rowDetailView.interface.js.map +1 -0
  12. package/dist/cjs/models/viewModelBindableData.interface.js +3 -0
  13. package/dist/cjs/models/viewModelBindableData.interface.js.map +1 -0
  14. package/dist/cjs/models/viewModelBindableInputData.interface.js +3 -0
  15. package/dist/cjs/models/viewModelBindableInputData.interface.js.map +1 -0
  16. package/dist/cjs/services/index.js +0 -1
  17. package/dist/cjs/services/index.js.map +1 -1
  18. package/dist/cjs/services/reactUtils.js +20 -0
  19. package/dist/cjs/services/reactUtils.js.map +1 -0
  20. package/dist/esm/components/slickgrid-react.js +43 -31
  21. package/dist/esm/components/slickgrid-react.js.map +1 -1
  22. package/dist/esm/extensions/slickRowDetailView.js +312 -0
  23. package/dist/esm/extensions/slickRowDetailView.js.map +1 -0
  24. package/dist/esm/global-grid-options.js +9 -0
  25. package/dist/esm/global-grid-options.js.map +1 -1
  26. package/dist/esm/index.js +3 -2
  27. package/dist/esm/index.js.map +1 -1
  28. package/dist/esm/models/rowDetailView.interface.js +2 -0
  29. package/dist/esm/models/rowDetailView.interface.js.map +1 -0
  30. package/dist/esm/models/viewModelBindableData.interface.js +2 -0
  31. package/dist/esm/models/viewModelBindableData.interface.js.map +1 -0
  32. package/dist/esm/models/viewModelBindableInputData.interface.js +2 -0
  33. package/dist/esm/models/viewModelBindableInputData.interface.js.map +1 -0
  34. package/dist/esm/services/index.js +0 -1
  35. package/dist/esm/services/index.js.map +1 -1
  36. package/dist/esm/services/reactUtils.js +14 -0
  37. package/dist/esm/services/reactUtils.js.map +1 -0
  38. package/dist/types/components/slickgrid-react.d.ts +13 -14
  39. package/dist/types/components/slickgrid-react.d.ts.map +1 -1
  40. package/dist/types/components/slickgridReactProps.d.ts +0 -2
  41. package/dist/types/components/slickgridReactProps.d.ts.map +1 -1
  42. package/dist/types/extensions/slickRowDetailView.d.ts +82 -0
  43. package/dist/types/extensions/slickRowDetailView.d.ts.map +1 -0
  44. package/dist/types/global-grid-options.d.ts.map +1 -1
  45. package/dist/types/index.d.ts +4 -3
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/dist/types/models/gridOption.interface.d.ts +3 -0
  48. package/dist/types/models/gridOption.interface.d.ts.map +1 -1
  49. package/dist/types/models/index.d.ts +5 -2
  50. package/dist/types/models/index.d.ts.map +1 -1
  51. package/dist/types/models/rowDetailView.interface.d.ts +13 -0
  52. package/dist/types/models/rowDetailView.interface.d.ts.map +1 -0
  53. package/dist/types/models/slickgridReactInstance.interface.d.ts +4 -2
  54. package/dist/types/models/slickgridReactInstance.interface.d.ts.map +1 -1
  55. package/dist/types/models/viewModelBindableData.interface.d.ts +10 -0
  56. package/dist/types/models/viewModelBindableData.interface.d.ts.map +1 -0
  57. package/dist/types/models/viewModelBindableInputData.interface.d.ts +9 -0
  58. package/dist/types/models/viewModelBindableInputData.interface.d.ts.map +1 -0
  59. package/dist/types/services/index.d.ts +0 -1
  60. package/dist/types/services/index.d.ts.map +1 -1
  61. package/dist/types/services/reactUtils.d.ts +6 -0
  62. package/dist/types/services/reactUtils.d.ts.map +1 -0
  63. package/package.json +48 -69
  64. package/src/slickgrid-react/components/slickgrid-react.tsx +53 -44
  65. package/src/slickgrid-react/components/slickgridReactProps.ts +0 -2
  66. package/src/slickgrid-react/extensions/slickRowDetailView.ts +383 -0
  67. package/src/slickgrid-react/global-grid-options.ts +10 -1
  68. package/src/slickgrid-react/index.ts +5 -2
  69. package/src/slickgrid-react/models/gridOption.interface.ts +5 -0
  70. package/src/slickgrid-react/models/index.ts +5 -2
  71. package/src/slickgrid-react/models/rowDetailView.interface.ts +15 -0
  72. package/src/slickgrid-react/models/slickgridReactInstance.interface.ts +5 -2
  73. package/src/slickgrid-react/models/viewModelBindableData.interface.ts +10 -0
  74. package/src/slickgrid-react/models/viewModelBindableInputData.interface.ts +9 -0
  75. package/src/slickgrid-react/services/index.ts +0 -1
  76. package/src/slickgrid-react/services/reactUtils.ts +15 -0
  77. package/dist/cjs/services/reactUtil.service.js +0 -30
  78. package/dist/cjs/services/reactUtil.service.js.map +0 -1
  79. package/dist/esm/services/reactUtil.service.js +0 -23
  80. package/dist/esm/services/reactUtil.service.js.map +0 -1
  81. package/dist/types/services/reactUtil.service.d.ts +0 -6
  82. package/dist/types/services/reactUtil.service.d.ts.map +0 -1
  83. package/src/slickgrid-react/services/reactUtil.service.ts +0 -26
@@ -1,13 +1,10 @@
1
- // import 3rd party vendor libs
2
- import i18next from 'i18next';
3
- import React from 'react';
4
-
5
1
  import {
6
2
  // interfaces/types
7
3
  type AutocompleterEditor,
8
4
  type BackendService,
9
5
  type BackendServiceApi,
10
6
  type BackendServiceOption,
7
+ type BasePaginationComponent,
11
8
  type Column,
12
9
  type DataViewOption,
13
10
  type EventSubscription,
@@ -18,8 +15,8 @@ import {
18
15
  type Locale,
19
16
  type Metrics,
20
17
  type Pagination,
18
+ type PaginationMetadata,
21
19
  type SelectEditor,
22
- type ServicePagination,
23
20
  SlickDataView,
24
21
  SlickEventHandler,
25
22
  SlickGrid,
@@ -35,7 +32,7 @@ import {
35
32
  GridEventService,
36
33
  GridService,
37
34
  GridStateService,
38
- GroupingAndColspanService,
35
+ HeaderGroupingService,
39
36
  type Observable,
40
37
  PaginationService,
41
38
  ResizerService,
@@ -56,22 +53,21 @@ import { SlickFooterComponent } from '@slickgrid-universal/custom-footer-compone
56
53
  import { SlickEmptyWarningComponent } from '@slickgrid-universal/empty-warning-component';
57
54
  import { SlickPaginationComponent } from '@slickgrid-universal/pagination-component';
58
55
  import { extend } from '@slickgrid-universal/utils';
59
-
60
56
  import { dequal } from 'dequal/lite';
57
+ import i18next from 'i18next';
58
+ import React from 'react';
59
+ import type { Subscription } from 'rxjs';
60
+
61
61
  import { Constants } from '../constants';
62
62
  import { GlobalGridOptions } from '../global-grid-options';
63
63
  import type { SlickgridReactInstance, GridOption, } from '../models/index';
64
- import {
65
- ReactUtilService,
66
- disposeAllSubscriptions,
67
- TranslaterService,
68
- } from '../services/index';
69
- import type { Subscription } from 'rxjs';
70
-
64
+ import { disposeAllSubscriptions } from '../services/utilities';
71
65
  import { GlobalContainerService } from '../services/singletons';
66
+ import { loadReactComponentDynamically } from '../services/reactUtils';
67
+ import { TranslaterService } from '../services/translater.service';
72
68
  import type { SlickgridReactProps } from './slickgridReactProps';
73
69
 
74
- const WARN_NO_PREPARSE_DATE_SIZE = 5000; // data size to warn user when pre-parse isn't enabled
70
+ const WARN_NO_PREPARSE_DATE_SIZE = 10000; // data size to warn user when pre-parse isn't enabled
75
71
 
76
72
  interface State {
77
73
  showPagination: boolean;
@@ -153,7 +149,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
153
149
  // components
154
150
  slickEmptyWarning: SlickEmptyWarningComponent | undefined;
155
151
  slickFooter: SlickFooterComponent | undefined;
156
- slickPagination: SlickPaginationComponent | undefined;
152
+ slickPagination: BasePaginationComponent | undefined;
157
153
 
158
154
  // services
159
155
  backendUtilityService!: BackendUtilityService;
@@ -165,13 +161,8 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
165
161
  gridEventService: GridEventService;
166
162
  gridService: GridService;
167
163
  gridStateService: GridStateService;
168
- groupingService: GroupingAndColspanService;
169
- protected get paginationService(): PaginationService {
170
- return this.state?.paginationService;
171
- }
172
- protected set paginationService(value: PaginationService) {
173
- this.setStateValue('paginationService', value);
174
- }
164
+ groupingService!: HeaderGroupingService;
165
+ headerGroupingService: HeaderGroupingService;
175
166
  resizerService!: ResizerService;
176
167
  rxjs?: RxJsFacade;
177
168
  sharedService: SharedService;
@@ -186,7 +177,6 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
186
177
  instances: SlickgridReactInstance | null = null;
187
178
 
188
179
  static defaultProps = {
189
- reactUtilService: new ReactUtilService(),
190
180
  containerService: GlobalContainerService,
191
181
  translaterService: new TranslaterService(),
192
182
  dataset: [],
@@ -251,6 +241,13 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
251
241
  this._isDatasetHierarchicalInitialized = true;
252
242
  }
253
243
 
244
+ protected get paginationService(): PaginationService {
245
+ return this.state?.paginationService;
246
+ }
247
+ protected set paginationService(value: PaginationService) {
248
+ this.setStateValue('paginationService', value);
249
+ }
250
+
254
251
  constructor(public readonly props: SlickgridReactProps) {
255
252
  super(props);
256
253
  const slickgridConfig = new SlickgridConfig();
@@ -293,7 +290,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
293
290
 
294
291
  this.gridStateService = new GridStateService(this.extensionService, this.filterService, this._eventPubSubService, this.sharedService, this.sortService, this.treeDataService);
295
292
  this.gridService = new GridService(this.gridStateService, this.filterService, this._eventPubSubService, this.paginationService, this.sharedService, this.sortService, this.treeDataService);
296
- this.groupingService = new GroupingAndColspanService(this.extensionUtility, this._eventPubSubService);
293
+ this.headerGroupingService = new HeaderGroupingService(this.extensionUtility);
297
294
 
298
295
  this.serviceList = [
299
296
  this.extensionService,
@@ -301,7 +298,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
301
298
  this.gridEventService,
302
299
  this.gridService,
303
300
  this.gridStateService,
304
- this.groupingService,
301
+ this.headerGroupingService,
305
302
  this.paginationService,
306
303
  this.resizerService,
307
304
  this.sortService,
@@ -322,7 +319,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
322
319
  this.props.containerService.registerInstance('GridEventService', this.gridEventService);
323
320
  this.props.containerService.registerInstance('GridService', this.gridService);
324
321
  this.props.containerService.registerInstance('GridStateService', this.gridStateService);
325
- this.props.containerService.registerInstance('GroupingAndColspanService', this.groupingService);
322
+ this.props.containerService.registerInstance('HeaderGroupingService', this.headerGroupingService);
326
323
  this.props.containerService.registerInstance('PaginationService', this.paginationService);
327
324
  this.props.containerService.registerInstance('ResizerService', this.resizerService);
328
325
  this.props.containerService.registerInstance('SharedService', this.sharedService);
@@ -591,12 +588,13 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
591
588
  // return all available Services (non-singleton)
592
589
  backendService: this.backendService,
593
590
  eventPubSubService: this._eventPubSubService,
591
+ extensionService: this.extensionService,
594
592
  filterService: this.filterService,
595
593
  gridEventService: this.gridEventService,
596
594
  gridStateService: this.gridStateService,
597
595
  gridService: this.gridService,
598
- groupingService: this.groupingService,
599
- extensionService: this.extensionService,
596
+ groupingService: this.headerGroupingService,
597
+ headerGroupingService: this.headerGroupingService,
600
598
  paginationService: this.paginationService,
601
599
  resizerService: this.resizerService,
602
600
  sortService: this.sortService,
@@ -765,7 +763,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
765
763
  if (gridOptions.enableTranslate) {
766
764
  this.extensionService.translateAllExtensions(lang);
767
765
  if ((gridOptions.createPreHeaderPanel && gridOptions.createTopHeaderPanel) || (gridOptions.createPreHeaderPanel && !gridOptions.enableDraggableGrouping)) {
768
- this.groupingService.translateGroupingAndColSpan();
766
+ this.headerGroupingService.translateHeaderGrouping();
769
767
  }
770
768
  }
771
769
  });
@@ -1021,7 +1019,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1021
1019
  * On a Pagination changed, we will trigger a Grid State changed with the new pagination info
1022
1020
  * Also if we use Row Selection or the Checkbox Selector with a Backend Service (Odata, GraphQL), we need to reset any selection
1023
1021
  */
1024
- paginationChanged(pagination: ServicePagination) {
1022
+ paginationChanged(pagination: PaginationMetadata) {
1025
1023
  const isSyncGridSelectionEnabled = this.gridStateService?.needToPreserveRowSelection() ?? false;
1026
1024
  if (this.grid && !isSyncGridSelectionEnabled && this.gridOptions?.backendServiceApi && (this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector)) {
1027
1025
  this.grid.setSelectedRows([]);
@@ -1134,7 +1132,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1134
1132
  protected setPaginationOptionsWhenPresetDefined(gridOptions: GridOption, paginationOptions: Pagination): Pagination {
1135
1133
  if (gridOptions.presets?.pagination && gridOptions.pagination) {
1136
1134
  if (this.hasBackendInfiniteScroll()) {
1137
- console.warn('[Aurelia-Slickgrid] `presets.pagination` is not supported with Infinite Scroll, reverting to first page.');
1135
+ console.warn('[Slickgrid-React] `presets.pagination` is not supported with Infinite Scroll, reverting to first page.');
1138
1136
  } else {
1139
1137
  paginationOptions.pageSize = gridOptions.presets.pagination.pageSize;
1140
1138
  paginationOptions.pageNumber = gridOptions.presets.pagination.pageNumber;
@@ -1226,8 +1224,8 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1226
1224
  this.paginationService.totalItems = this.totalItems;
1227
1225
  this.paginationService.init(this.grid, paginationOptions, this.backendServiceApi);
1228
1226
  this.subscriptions.push(
1229
- this._eventPubSubService.subscribe<ServicePagination>('onPaginationChanged', paginationChanges => this.paginationChanged(paginationChanges)),
1230
- this._eventPubSubService.subscribe<ServicePagination>('onPaginationOptionsChanged', paginationChanges => this.paginationOptionsChanged(paginationChanges)),
1227
+ this._eventPubSubService.subscribe<PaginationMetadata>('onPaginationChanged', paginationChanges => this.paginationChanged(paginationChanges)),
1228
+ this._eventPubSubService.subscribe<PaginationMetadata>('onPaginationOptionsChanged', paginationChanges => this.paginationOptionsChanged(paginationChanges)),
1231
1229
  this._eventPubSubService.subscribe<{ visible: boolean; }>('onPaginationVisibilityChanged', (visibility: { visible: boolean }) => {
1232
1230
  this.showPagination = visibility?.visible ?? false;
1233
1231
  if (this.gridOptions?.backendServiceApi) {
@@ -1249,11 +1247,22 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1249
1247
  * @param {Boolean} showPagination - show (new render) or not (dispose) the Pagination
1250
1248
  * @param {Boolean} shouldDisposePaginationService - when disposing the Pagination, do we also want to dispose of the Pagination Service? (defaults to True)
1251
1249
  */
1252
- protected renderPagination(showPagination = true) {
1253
- if (this._gridOptions?.enablePagination && !this._isPaginationInitialized && showPagination) {
1254
- this.slickPagination = new SlickPaginationComponent(this.paginationService, this._eventPubSubService, this.sharedService, this.props.translaterService);
1255
- this.slickPagination.renderPagination(this._elm as HTMLDivElement);
1256
- this._isPaginationInitialized = true;
1250
+ protected async renderPagination(showPagination = true) {
1251
+ if (this.grid && this._gridOptions?.enablePagination && !this._isPaginationInitialized && showPagination) {
1252
+ if (this.gridOptions.customPaginationComponent) {
1253
+ const paginationContainer = document.createElement('section');
1254
+ this._elm!.appendChild(paginationContainer);
1255
+ const { component } = await loadReactComponentDynamically<BasePaginationComponent>(this.gridOptions.customPaginationComponent, paginationContainer);
1256
+ this.slickPagination = component;
1257
+ } else {
1258
+ this.slickPagination = new SlickPaginationComponent();
1259
+ }
1260
+
1261
+ if (this.slickPagination) {
1262
+ this.slickPagination.init(this.grid, this.paginationService, this._eventPubSubService, this.props.translaterService);
1263
+ this.slickPagination.renderPagination(this._elm as HTMLDivElement);
1264
+ this._isPaginationInitialized = true;
1265
+ }
1257
1266
  } else if (!showPagination) {
1258
1267
  this.slickPagination?.dispose();
1259
1268
  this._isPaginationInitialized = false;
@@ -1268,7 +1277,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1268
1277
 
1269
1278
  if (collectionAsync instanceof Promise) {
1270
1279
  // wait for the "collectionAsync", once resolved we will save it into the "collection"
1271
- // the collectionAsync can be of 3 types HttpClient, HttpFetch or a Promise
1280
+ // the collectionAsync can be of 3 types Fetch, Promise or RxJS when available
1272
1281
  collectionAsync.then((response: any | any[]) => {
1273
1282
  if (Array.isArray(response)) {
1274
1283
  this.updateEditorCollection(column, response); // from Promise
@@ -1446,7 +1455,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1446
1455
  this._registeredResources = [];
1447
1456
  }
1448
1457
 
1449
- /** Pre-Register any Resource that don't require SlickGrid to be instantiated (for example RxJS Resource) */
1458
+ /** Pre-Register any Resource that don't require SlickGrid to be instantiated (for example RxJS Resource & RowDetail) */
1450
1459
  protected preRegisterResources() {
1451
1460
  this._registeredResources = this.gridOptions.externalResources || [];
1452
1461
 
@@ -1482,7 +1491,7 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1482
1491
 
1483
1492
  // when using Grouping/DraggableGrouping/Colspan register its Service
1484
1493
  if ((this.gridOptions.createPreHeaderPanel && this.gridOptions.createTopHeaderPanel) || (this.gridOptions.createPreHeaderPanel && !this.gridOptions.enableDraggableGrouping)) {
1485
- this._registeredResources.push(this.groupingService);
1494
+ this._registeredResources.push(this.headerGroupingService);
1486
1495
  }
1487
1496
 
1488
1497
  // when using Tree Data View, register its Service
@@ -1564,10 +1573,10 @@ export class SlickgridReact<TData = any> extends React.Component<SlickgridReactP
1564
1573
  }
1565
1574
 
1566
1575
  protected suggestDateParsingWhenHelpful() {
1567
- if (this.dataView?.getItemCount() > WARN_NO_PREPARSE_DATE_SIZE && !this.gridOptions.preParseDateColumns && this.grid.getColumns().some(c => isColumnDateType(c.type))) {
1576
+ if (this.dataView?.getItemCount() > WARN_NO_PREPARSE_DATE_SIZE && !this.gridOptions.silenceWarnings && !this.gridOptions.preParseDateColumns && this.grid.getColumns().some(c => isColumnDateType(c.type))) {
1568
1577
  console.warn(
1569
1578
  '[Slickgrid-Universal] For getting better perf, we suggest you enable the `preParseDateColumns` grid option, ' +
1570
- 'for more info visit:: https://ghiscoding.gitbook.io/slickgrid-universal/column-functionalities/sorting#pre-parse-date-columns-for-better-perf'
1579
+ 'for more info visit => https://ghiscoding.gitbook.io/slickgrid-react/column-functionalities/sorting#pre-parse-date-columns-for-better-perf'
1571
1580
  );
1572
1581
  }
1573
1582
  }
@@ -54,12 +54,10 @@ import type {
54
54
  SlickGrid,
55
55
  } from '@slickgrid-universal/common';
56
56
  import type { SlickgridReactInstance } from '../models';
57
- import type { ReactUtilService } from '../services';
58
57
 
59
58
  export interface SlickgridReactProps {
60
59
  header?: JSX.Element;
61
60
  footer?: JSX.Element;
62
- reactUtilService: ReactUtilService;
63
61
  containerService: ContainerService;
64
62
  translaterService: TranslaterService;
65
63
  customDataView?: SlickDataView;
@@ -0,0 +1,383 @@
1
+ import {
2
+ addToArrayWhenNotExists,
3
+ type EventSubscription,
4
+ type OnBeforeRowDetailToggleArgs,
5
+ type OnRowBackToViewportRangeArgs,
6
+ SlickEventData,
7
+ type SlickEventHandler,
8
+ type SlickGrid,
9
+ SlickRowSelectionModel,
10
+ unsubscribeAll,
11
+ } from '@slickgrid-universal/common';
12
+ import { type EventPubSubService } from '@slickgrid-universal/event-pub-sub';
13
+ import { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';
14
+ import type { Root } from 'react-dom/client';
15
+
16
+ import type { GridOption, RowDetailView, ViewModelBindableInputData } from '../models/index';
17
+ import { loadReactComponentDynamically } from '../services/reactUtils';
18
+
19
+ const ROW_DETAIL_CONTAINER_PREFIX = 'container_';
20
+ const PRELOAD_CONTAINER_PREFIX = 'container_loading';
21
+
22
+ export interface CreatedView {
23
+ id: string | number;
24
+ dataContext: any;
25
+ root: Root | null;
26
+ }
27
+ // interface SRDV extends React.Component<Props, State>, UniversalSlickRowDetailView {}s
28
+
29
+ export class SlickRowDetailView extends UniversalSlickRowDetailView {
30
+ protected _component?: any;
31
+ protected _preloadComponent?: any;
32
+ protected _views: CreatedView[] = [];
33
+ protected _subscriptions: EventSubscription[] = [];
34
+ protected _userProcessFn?: (item: any) => Promise<any>;
35
+ protected gridContainerElement!: HTMLElement;
36
+ _root?: Root;
37
+
38
+ constructor(
39
+ private readonly eventPubSubService: EventPubSubService,
40
+ ) {
41
+ super(eventPubSubService);
42
+ }
43
+
44
+ get addonOptions() {
45
+ return this.getOptions();
46
+ }
47
+
48
+ protected get datasetIdPropName(): string {
49
+ return this.gridOptions.datasetIdPropertyName || 'id';
50
+ }
51
+
52
+ get eventHandler(): SlickEventHandler {
53
+ return this._eventHandler;
54
+ }
55
+ set eventHandler(eventHandler: SlickEventHandler) {
56
+ this._eventHandler = eventHandler;
57
+ }
58
+
59
+ get gridOptions(): GridOption {
60
+ return (this._grid?.getOptions() || {}) as GridOption;
61
+ }
62
+
63
+ get rowDetailViewOptions(): RowDetailView | undefined {
64
+ return this.gridOptions.rowDetailView;
65
+ }
66
+
67
+ /** Dispose of the RowDetailView Extension */
68
+ dispose() {
69
+ this.disposeAllViewComponents();
70
+ unsubscribeAll(this._subscriptions);
71
+ super.dispose();
72
+ }
73
+
74
+ /** Dispose of all the opened Row Detail Panels Components */
75
+ disposeAllViewComponents() {
76
+ if (Array.isArray(this._views)) {
77
+ this._views.forEach((view) => this.disposeViewComponent(view));
78
+ }
79
+ this._views = [];
80
+ }
81
+
82
+ /** Get the instance of the SlickGrid addon (control or plugin). */
83
+ getAddonInstance(): SlickRowDetailView | null {
84
+ return this;
85
+ }
86
+
87
+ init(grid: SlickGrid) {
88
+ this._grid = grid;
89
+ super.init(this._grid);
90
+ this.gridContainerElement = grid.getContainerNode();
91
+ this.register(grid?.getSelectionModel() as SlickRowSelectionModel);
92
+ }
93
+
94
+ /**
95
+ * Create the plugin before the Grid creation, else it will behave oddly.
96
+ * Mostly because the column definitions might change after the grid creation
97
+ */
98
+ register(rowSelectionPlugin?: SlickRowSelectionModel) {
99
+ if (typeof this.gridOptions.rowDetailView?.process === 'function') {
100
+ // we need to keep the user "process" method and replace it with our own execution method
101
+ // we do this because when we get the item detail, we need to call "onAsyncResponse.notify" for the plugin to work
102
+ this._userProcessFn = this.gridOptions.rowDetailView.process as (item: any) => Promise<any>; // keep user's process method
103
+ this.addonOptions.process = (item) => this.onProcessing(item); // replace process method & run our internal one
104
+ } else {
105
+ throw new Error('[Slickgrid-React] You need to provide a "process" function for the Row Detail Extension to work properly');
106
+ }
107
+
108
+ if (this._grid && this.gridOptions?.rowDetailView) {
109
+ // load the Preload & RowDetail Templates (could be straight HTML or React Components)
110
+ // when those are React Components, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)
111
+ if (!this.gridOptions.rowDetailView.preTemplate) {
112
+ this._preloadComponent = this.gridOptions?.rowDetailView?.preloadComponent;
113
+ this.addonOptions.preTemplate = () => this._grid.sanitizeHtmlString(`<div class="${PRELOAD_CONTAINER_PREFIX}"></div>`) as string;
114
+ }
115
+ if (!this.gridOptions.rowDetailView.postTemplate) {
116
+ this._component = this.gridOptions?.rowDetailView?.viewComponent;
117
+ this.addonOptions.postTemplate = (itemDetail: any) => {
118
+ return this._grid.sanitizeHtmlString(`<div class="${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}"></div>`) as string
119
+ };
120
+ }
121
+
122
+ if (this._grid && this.gridOptions) {
123
+ // this also requires the Row Selection Model to be registered as well
124
+ if (!rowSelectionPlugin || !this._grid.getSelectionModel()) {
125
+ rowSelectionPlugin = new SlickRowSelectionModel(this.gridOptions.rowSelectionOptions || { selectActiveRow: true });
126
+ this._grid.setSelectionModel(rowSelectionPlugin);
127
+ }
128
+
129
+ // hook all events
130
+ if (this._grid && this.rowDetailViewOptions) {
131
+ if (this.rowDetailViewOptions.onExtensionRegistered) {
132
+ this.rowDetailViewOptions.onExtensionRegistered(this);
133
+ }
134
+
135
+ if (this.onAsyncResponse) {
136
+ this._eventHandler.subscribe(this.onAsyncResponse, (event, args) => {
137
+ if (typeof this.rowDetailViewOptions?.onAsyncResponse === 'function') {
138
+ this.rowDetailViewOptions.onAsyncResponse(event, args);
139
+ }
140
+ });
141
+ }
142
+
143
+ if (this.onAsyncEndUpdate) {
144
+ this._eventHandler.subscribe(this.onAsyncEndUpdate, async (event, args) => {
145
+ // triggers after backend called "onAsyncResponse.notify()"
146
+ await this.renderViewModel(args?.item);
147
+
148
+ if (typeof this.rowDetailViewOptions?.onAsyncEndUpdate === 'function') {
149
+ this.rowDetailViewOptions.onAsyncEndUpdate(event, args);
150
+ }
151
+ });
152
+ }
153
+
154
+ if (this.onAfterRowDetailToggle) {
155
+ this._eventHandler.subscribe(this.onAfterRowDetailToggle, async (event, args) => {
156
+ // display preload template & re-render all the other Detail Views after toggling
157
+ // the preload View will eventually go away once the data gets loaded after the "onAsyncEndUpdate" event
158
+ await this.renderPreloadView(args.item);
159
+ this.renderAllViewModels();
160
+
161
+ if (typeof this.rowDetailViewOptions?.onAfterRowDetailToggle === 'function') {
162
+ this.rowDetailViewOptions.onAfterRowDetailToggle(event, args);
163
+ }
164
+ });
165
+ }
166
+
167
+ if (this.onBeforeRowDetailToggle) {
168
+ this._eventHandler.subscribe(this.onBeforeRowDetailToggle, (event, args) => {
169
+ // before toggling row detail, we need to create View Component if it doesn't exist
170
+ this.handleOnBeforeRowDetailToggle(event, args);
171
+
172
+ if (typeof this.rowDetailViewOptions?.onBeforeRowDetailToggle === 'function') {
173
+ return this.rowDetailViewOptions.onBeforeRowDetailToggle(event, args);
174
+ }
175
+ return true;
176
+ });
177
+ }
178
+
179
+ if (this.onRowBackToViewportRange) {
180
+ this._eventHandler.subscribe(this.onRowBackToViewportRange, async (event, args) => {
181
+ // when row is back to viewport range, we will re-render the View Component(s)
182
+ await this.handleOnRowBackToViewportRange(event, args);
183
+
184
+ if (typeof this.rowDetailViewOptions?.onRowBackToViewportRange === 'function') {
185
+ this.rowDetailViewOptions.onRowBackToViewportRange(event, args);
186
+ }
187
+ });
188
+ }
189
+
190
+ if (this.onRowOutOfViewportRange) {
191
+ this._eventHandler.subscribe(this.onRowOutOfViewportRange, (event, args) => {
192
+ if (typeof this.rowDetailViewOptions?.onRowOutOfViewportRange === 'function') {
193
+ this.rowDetailViewOptions.onRowOutOfViewportRange(event, args);
194
+ }
195
+ });
196
+ }
197
+
198
+ // --
199
+ // hook some events needed by the Plugin itself
200
+
201
+ // we need to redraw the open detail views if we change column position (column reorder)
202
+ this.eventHandler.subscribe(this._grid.onColumnsReordered, this.redrawAllViewComponents.bind(this));
203
+
204
+ // on row selection changed, we also need to redraw
205
+ if (this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector) {
206
+ this._eventHandler.subscribe(this._grid.onSelectedRowsChanged, this.redrawAllViewComponents.bind(this));
207
+ }
208
+
209
+ // on column sort/reorder, all row detail are collapsed so we can dispose of all the Views as well
210
+ this._eventHandler.subscribe(this._grid.onSort, this.disposeAllViewComponents.bind(this));
211
+
212
+ // on filter changed, we need to re-render all Views
213
+ this._subscriptions.push(
214
+ this.eventPubSubService?.subscribe(['onFilterChanged', 'onGridMenuColumnsChanged', 'onColumnPickerColumnsChanged'], this.redrawAllViewComponents.bind(this)),
215
+ this.eventPubSubService?.subscribe(['onGridMenuClearAllFilters', 'onGridMenuClearAllSorting'], () => window.setTimeout(() => this.redrawAllViewComponents())),
216
+ );
217
+ }
218
+ }
219
+ }
220
+
221
+ return this;
222
+ }
223
+
224
+ /** Redraw (re-render) all the expanded row detail View Components */
225
+ async redrawAllViewComponents() {
226
+ await Promise.all(this._views.map(async x => this.redrawViewComponent(x)));
227
+ }
228
+
229
+ /** Render all the expanded row detail View Components */
230
+ async renderAllViewModels() {
231
+ await Promise.all(this._views.filter(x => x?.dataContext).map(async x => this.renderViewModel(x.dataContext)));
232
+ }
233
+
234
+ /** Redraw the necessary View Component */
235
+ async redrawViewComponent(view: CreatedView) {
236
+ const containerElement = this.gridContainerElement.getElementsByClassName(`${ROW_DETAIL_CONTAINER_PREFIX}${view.id}`);
237
+ if (containerElement?.length >= 0) {
238
+ await this.renderViewModel(view.dataContext);
239
+ }
240
+ }
241
+
242
+ /** Render (or re-render) the View Component (Row Detail) */
243
+ async renderPreloadView(item: any) {
244
+ const containerElements = this.gridContainerElement.getElementsByClassName(`${PRELOAD_CONTAINER_PREFIX}`);
245
+ if (this._preloadComponent && containerElements?.length) {
246
+ const detailContainer = document.createElement('section');
247
+ containerElements[containerElements.length - 1]!.appendChild(detailContainer);
248
+
249
+ console.log('elm', containerElements[containerElements.length - 1])
250
+ const { root } = await loadReactComponentDynamically(this._preloadComponent, detailContainer as HTMLElement);
251
+ const viewObj = this._views.find(obj => obj.id === item[this.datasetIdPropName]);
252
+ this._root = root;
253
+ if (viewObj) {
254
+ viewObj.root = root;
255
+ }
256
+ }
257
+ }
258
+
259
+ /** Render (or re-render) the View Component (Row Detail) */
260
+ async renderViewModel(item: any) {
261
+ const containerElements = this.gridContainerElement.getElementsByClassName(`${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`);
262
+ if (this._component && containerElements?.length) {
263
+ const bindableData = {
264
+ model: item,
265
+ addon: this,
266
+ grid: this._grid,
267
+ dataView: this.dataView,
268
+ parent: this.rowDetailViewOptions?.parent,
269
+ } as ViewModelBindableInputData;
270
+ const viewObj = this._views.find(obj => obj.id === item[this.datasetIdPropName]);
271
+
272
+ // load our Row Detail React Component dynamically, typically we would want to use `root.render()` after the preload component (last argument below)
273
+ // BUT the root render doesn't seem to work and shows a blank element, so we'll use `createRoot()` every time even though it shows a console log in Dev
274
+ // that is the only way I got it working so let's use it anyway and console warnings are removed in production anyway
275
+ const { root } = await loadReactComponentDynamically(this._component, containerElements[containerElements.length - 1] as HTMLElement, bindableData /*, viewObj?.root */);
276
+ if (!viewObj) {
277
+ this.addViewInfoToViewsRef(item, root);
278
+ }
279
+ }
280
+ }
281
+
282
+ // --
283
+ // protected functions
284
+ // ------------------
285
+
286
+ protected addViewInfoToViewsRef(item: any, root: Root | null) {
287
+ const viewInfo: CreatedView = {
288
+ id: item[this.datasetIdPropName],
289
+ dataContext: item,
290
+ root
291
+ };
292
+ const idPropName = this.gridOptions.datasetIdPropertyName || 'id';
293
+ addToArrayWhenNotExists(this._views, viewInfo, idPropName);
294
+ }
295
+
296
+ protected disposeViewComponent(expandedView: CreatedView): CreatedView | void {
297
+ if (expandedView) {
298
+ if (expandedView?.root) {
299
+ const container = this.gridContainerElement.getElementsByClassName(`${ROW_DETAIL_CONTAINER_PREFIX}${this._views[0].id}`);
300
+ if (container?.length) {
301
+ expandedView.root.unmount();
302
+ container[0].textContent = '';
303
+ return expandedView;
304
+ }
305
+ }
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Just before the row get expanded or collapsed we will do the following
311
+ * First determine if the row is expanding or collapsing,
312
+ * if it's expanding we will add it to our View Components reference array if we don't already have it
313
+ * or if it's collapsing we will remove it from our View Components reference array
314
+ */
315
+ protected handleOnBeforeRowDetailToggle(_e: SlickEventData<OnBeforeRowDetailToggleArgs>, args: { grid: SlickGrid; item: any; }) {
316
+ // expanding
317
+ if (args?.item?.__collapsed) {
318
+ // expanding row detail
319
+ this.addViewInfoToViewsRef(args.item, null);
320
+ } else {
321
+ // collapsing, so dispose of the View
322
+ const foundViewIdx = this._views.findIndex((view: CreatedView) => view.id === args.item[this.datasetIdPropName]);
323
+ if (foundViewIdx >= 0 && this.disposeViewComponent(this._views[foundViewIdx])) {
324
+ this._views.splice(foundViewIdx, 1);
325
+ }
326
+ }
327
+ }
328
+
329
+ /** When Row comes back to Viewport Range, we need to redraw the View */
330
+ protected async handleOnRowBackToViewportRange(_e: SlickEventData<OnRowBackToViewportRangeArgs>, args: {
331
+ item: any;
332
+ rowId: string | number;
333
+ rowIndex: number;
334
+ expandedRows: (string | number)[];
335
+ rowIdsOutOfViewport: (string | number)[];
336
+ grid: SlickGrid;
337
+ }) {
338
+ if (args?.item) {
339
+ await this.redrawAllViewComponents();
340
+ }
341
+ }
342
+
343
+ /**
344
+ * notify the onAsyncResponse with the "args.item" (required property)
345
+ * the plugin will then use item to populate the row detail panel with the "postTemplate"
346
+ * @param item
347
+ */
348
+ protected notifyTemplate(item: any) {
349
+ if (this.onAsyncResponse) {
350
+ this.onAsyncResponse.notify({ item, itemDetail: item }, new SlickEventData(), this);
351
+ }
352
+ }
353
+
354
+ /**
355
+ * On Processing, we will notify the plugin with the new item detail once backend server call completes
356
+ * @param item
357
+ */
358
+ protected async onProcessing(item: any) {
359
+ if (item && typeof this._userProcessFn === 'function') {
360
+ let awaitedItemDetail: any;
361
+ const userProcessFn = this._userProcessFn(item);
362
+
363
+ // wait for the "userProcessFn", once resolved we will save it into the "collection"
364
+ const response: any | any[] = await userProcessFn;
365
+
366
+ if (response.hasOwnProperty(this.datasetIdPropName)) {
367
+ awaitedItemDetail = response; // from Promise
368
+ } else if (response instanceof Response && typeof response['json'] === 'function') {
369
+ awaitedItemDetail = await response['json'](); // from Fetch
370
+ } else if (response && response['content']) {
371
+ awaitedItemDetail = response['content']; // from http-client
372
+ }
373
+
374
+ if (!awaitedItemDetail || !awaitedItemDetail.hasOwnProperty(this.datasetIdPropName)) {
375
+ throw new Error('[Slickgrid-React] could not process the Row Detail, please make sure that your "process" callback ' +
376
+ '(a Promise or an HttpClient call returning an Observable) returns an item object that has an "${this.datasetIdPropName}" property');
377
+ }
378
+
379
+ // notify the plugin with the new item details
380
+ this.notifyTemplate(awaitedItemDetail || {});
381
+ }
382
+ }
383
+ }
@@ -1,5 +1,5 @@
1
1
  import { type Column, DelimiterType, EventNamingStyle, FileType, Filters, OperatorType, type TreeDataOption } from '@slickgrid-universal/common';
2
- import type { GridOption } from './models/index';
2
+ import type { GridOption, RowDetailView } from './models/index';
3
3
 
4
4
  /**
5
5
  * Default Options that can be passed to the Slickgrid-React
@@ -227,6 +227,15 @@ export const GlobalGridOptions: Partial<GridOption> = {
227
227
  pageSize: 25,
228
228
  totalItems: 0
229
229
  },
230
+ rowDetailView: {
231
+ collapseAllOnSort: true,
232
+ cssClass: 'detail-view-toggle',
233
+ panelRows: 1,
234
+ keyPrefix: '__',
235
+ useRowClick: false,
236
+ useSimpleViewportCalc: true,
237
+ saveDetailViewOnScroll: false,
238
+ } as RowDetailView,
230
239
  headerRowHeight: 35,
231
240
  rowHeight: 35,
232
241
  topPanelHeight: 30,