From fb327a6abe85b86683572cde2a550de43a01f9e1 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 26 Oct 2021 00:41:13 -0400 Subject: [PATCH] feat(plugins): move Row Detail View plugin to universal --- packages/common/src/controls/slickGridMenu.ts | 2 +- .../common/src/enums/slickPluginList.enum.ts | 6 +- .../__tests__/rowDetailViewExtension.spec.ts | 44 -- .../common/src/extensions/extensionUtility.ts | 2 +- packages/common/src/extensions/index.ts | 1 - .../src/extensions/rowDetailViewExtension.ts | 27 - .../src/formatters/bsDropdownFormatter.ts | 5 +- .../common/src/formatters/formatters.index.ts | 1 + .../common/src/interfaces/column.interface.ts | 3 + packages/common/src/interfaces/index.ts | 1 - .../src/interfaces/rowDetailView.interface.ts | 6 +- .../rowDetailViewOption.interface.ts | 2 +- .../interfaces/slickNamespace.interface.ts | 2 +- .../slickRowDetailView.interface.ts | 78 -- .../__tests__/slickContextMenu.spec.ts | 8 +- .../plugins/__tests__/slickRowDetail.spec.ts | 69 ++ packages/common/src/plugins/index.ts | 1 + .../common/src/plugins/slickContextMenu.ts | 5 +- .../common/src/plugins/slickRowDetailView.ts | 109 +++ .../__tests__/extension.service.spec.ts | 54 +- .../common/src/services/extension.service.ts | 36 +- packages/row-detail-view-plugin/README.md | 24 + packages/row-detail-view-plugin/package.json | 58 ++ .../row-detail-view-plugin/src/.npmignore | 2 + .../row-detail-view-plugin/src/index.spec.ts | 11 + packages/row-detail-view-plugin/src/index.ts | 1 + .../src/slickRowDetailView.ts | 720 ++++++++++++++++++ .../tsconfig.bundle.json | 14 + packages/row-detail-view-plugin/tsconfig.json | 24 + .../components/slick-vanilla-grid-bundle.ts | 12 +- 30 files changed, 1110 insertions(+), 218 deletions(-) delete mode 100644 packages/common/src/extensions/__tests__/rowDetailViewExtension.spec.ts delete mode 100644 packages/common/src/extensions/rowDetailViewExtension.ts delete mode 100644 packages/common/src/interfaces/slickRowDetailView.interface.ts create mode 100644 packages/common/src/plugins/__tests__/slickRowDetail.spec.ts create mode 100644 packages/common/src/plugins/slickRowDetailView.ts create mode 100644 packages/row-detail-view-plugin/README.md create mode 100644 packages/row-detail-view-plugin/package.json create mode 100644 packages/row-detail-view-plugin/src/.npmignore create mode 100644 packages/row-detail-view-plugin/src/index.spec.ts create mode 100644 packages/row-detail-view-plugin/src/index.ts create mode 100644 packages/row-detail-view-plugin/src/slickRowDetailView.ts create mode 100644 packages/row-detail-view-plugin/tsconfig.bundle.json create mode 100644 packages/row-detail-view-plugin/tsconfig.json diff --git a/packages/common/src/controls/slickGridMenu.ts b/packages/common/src/controls/slickGridMenu.ts index ca207b09b..bdbac18a4 100644 --- a/packages/common/src/controls/slickGridMenu.ts +++ b/packages/common/src/controls/slickGridMenu.ts @@ -11,7 +11,7 @@ import { SlickEventHandler, SlickNamespace, } from '../interfaces/index'; -import { DelimiterType, FileType } from '../enums'; +import { DelimiterType, FileType } from '../enums/index'; import { ExtensionUtility } from '../extensions/extensionUtility'; import { emptyElement, findWidthOrDefault, getHtmlElementOffset, getTranslationPrefix, } from '../services/index'; import { ExcelExportService } from '../services/excelExport.service'; diff --git a/packages/common/src/enums/slickPluginList.enum.ts b/packages/common/src/enums/slickPluginList.enum.ts index bf135b134..7716f82ed 100644 --- a/packages/common/src/enums/slickPluginList.enum.ts +++ b/packages/common/src/enums/slickPluginList.enum.ts @@ -1,7 +1,4 @@ -import { - SlickEditorLock, - SlickRowDetailView, -} from '../interfaces/index'; +import { SlickEditorLock } from '../interfaces/index'; import { SlickAutoTooltip, SlickCellExternalCopyManager, @@ -15,6 +12,7 @@ import { SlickGroupItemMetadataProvider, SlickHeaderButtons, SlickHeaderMenu, + SlickRowDetailView, SlickRowMoveManager, SlickRowSelectionModel, } from '../plugins/index'; diff --git a/packages/common/src/extensions/__tests__/rowDetailViewExtension.spec.ts b/packages/common/src/extensions/__tests__/rowDetailViewExtension.spec.ts deleted file mode 100644 index ec5bf262e..000000000 --- a/packages/common/src/extensions/__tests__/rowDetailViewExtension.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { RowDetailViewExtension } from '../rowDetailViewExtension'; -import { Column, GridOption, SlickNamespace } from '../../interfaces/index'; - -declare const Slick: SlickNamespace; - -const mockAddon = jest.fn().mockImplementation(() => ({ - init: jest.fn(), - destroy: jest.fn(), - getColumnDefinition: jest.fn(), - onAsyncResponse: new Slick.Event(), - onAsyncEndUpdate: new Slick.Event(), - onAfterRowDetailToggle: new Slick.Event(), - onBeforeRowDetailToggle: new Slick.Event(), - onRowOutOfViewportRange: new Slick.Event(), - onRowBackToViewportRange: new Slick.Event() -})); - -const mockSelectionModel = jest.fn().mockImplementation(() => ({ - init: jest.fn(), - destroy: jest.fn() -})); - -jest.mock('slickgrid/plugins/slick.rowdetailview', () => mockAddon); -Slick.Plugins = { - RowDetailView: mockAddon -} as any; - -describe('rowDetailViewExtension', () => { - it('should display a not implemented when calling "create" method', () => { - expect(() => RowDetailViewExtension.prototype.create!([] as Column[], {} as GridOption)).toThrow('[Slickgrid-Universal] RowDetailViewExtension "create" method is not yet implemented'); - }); - - it('should display a not implemented when calling "dispose" method', () => { - expect(() => RowDetailViewExtension.prototype.dispose!()).toThrow('[Slickgrid-Universal] RowDetailViewExtension "dispose" method is not yet implemented'); - }); - - it('should display a not implemented when calling "getAddonInstance" method', () => { - expect(() => RowDetailViewExtension.prototype.getAddonInstance()).toThrow('[Slickgrid-Universal] RowDetailViewExtension "getAddonInstance" method is not yet implemented'); - }); - - it('should display a not implemented when calling "register" method', () => { - expect(() => RowDetailViewExtension.prototype.register!()).toThrow('[Slickgrid-Universal] RowDetailViewExtension "register" method is not yet implemented'); - }); -}); diff --git a/packages/common/src/extensions/extensionUtility.ts b/packages/common/src/extensions/extensionUtility.ts index 0165cdf66..157c3afbe 100644 --- a/packages/common/src/extensions/extensionUtility.ts +++ b/packages/common/src/extensions/extensionUtility.ts @@ -1,5 +1,5 @@ import { Constants } from '../constants'; -import { Column, GridOption, Locale, MenuCommandItem, MenuOptionItem, } from '../interfaces'; +import { Column, GridOption, Locale, MenuCommandItem, MenuOptionItem, } from '../interfaces/index'; import { BackendUtilityService } from '../services/backendUtility.service'; import { SharedService } from '../services/shared.service'; import { TranslaterService } from '../services/translater.service'; diff --git a/packages/common/src/extensions/index.ts b/packages/common/src/extensions/index.ts index 96f5604da..2fb5d0e2d 100644 --- a/packages/common/src/extensions/index.ts +++ b/packages/common/src/extensions/index.ts @@ -1,2 +1 @@ export * from './extensionUtility'; -export * from './rowDetailViewExtension'; diff --git a/packages/common/src/extensions/rowDetailViewExtension.ts b/packages/common/src/extensions/rowDetailViewExtension.ts deleted file mode 100644 index 57342598d..000000000 --- a/packages/common/src/extensions/rowDetailViewExtension.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { Column, Extension, GridOption, SlickRowDetailView, } from '../interfaces/index'; -import { SlickRowSelectionModel } from '../plugins/slickRowSelectionModel'; - -export class RowDetailViewExtension implements Extension { - /** Dispose of the RowDetailView Extension */ - dispose() { - throw new Error('[Slickgrid-Universal] RowDetailViewExtension "dispose" method is not yet implemented'); - } - - /** - * Create the plugin before the Grid creation, else it will behave oddly. - * Mostly because the column definitions might change after the grid creation - */ - create(_columnDefinitions: Column[], _gridOptions: GridOption) { - throw new Error('[Slickgrid-Universal] RowDetailViewExtension "create" method is not yet implemented'); - } - - /** Get the instance of the SlickGrid addon (control or plugin). */ - getAddonInstance() { - throw new Error('[Slickgrid-Universal] RowDetailViewExtension "getAddonInstance" method is not yet implemented'); - } - - register(_rowSelectionPlugin?: SlickRowSelectionModel): SlickRowDetailView | null { - throw new Error('[Slickgrid-Universal] RowDetailViewExtension "register" method is not yet implemented'); - } -} diff --git a/packages/common/src/formatters/bsDropdownFormatter.ts b/packages/common/src/formatters/bsDropdownFormatter.ts index 2f519990a..2ffb2a126 100644 --- a/packages/common/src/formatters/bsDropdownFormatter.ts +++ b/packages/common/src/formatters/bsDropdownFormatter.ts @@ -1,6 +1,9 @@ import { Formatter } from '../interfaces/formatter.interface'; -/** A simple Bootstrap Dropdown Formatter which requires a Formatter Label */ +/** + * @deprecated @use `CellMenu`, which is a lot more generic instead of `bsDropdownFormatter`. + * A simple Bootstrap Dropdown Formatter which requires a Formatter Label. + */ export const bsDropdownFormatter: Formatter = (row, cell, _val, columnDef) => { const columnParams = columnDef && columnDef.params || {}; const label = columnParams.label || columnParams.formatterLabel; diff --git a/packages/common/src/formatters/formatters.index.ts b/packages/common/src/formatters/formatters.index.ts index c8f8036a1..638aadb6d 100644 --- a/packages/common/src/formatters/formatters.index.ts +++ b/packages/common/src/formatters/formatters.index.ts @@ -62,6 +62,7 @@ export const Formatters = { bold: boldFormatter, /** + * @deprecated @use `CellMenu`, which is a lot more generic instead of `bsDropdownFormatter`. * a simple Bootstrap Dropdown Formatter which requires a Formatter Label * example:: { formatter: Formatters.bsDropdown, params: { formatterLabel: 'Label' }} */ diff --git a/packages/common/src/interfaces/column.interface.ts b/packages/common/src/interfaces/column.interface.ts index 492c01eaa..1f62aab54 100644 --- a/packages/common/src/interfaces/column.interface.ts +++ b/packages/common/src/interfaces/column.interface.ts @@ -28,6 +28,9 @@ type Join = /* eslint-enable @typescript-eslint/indent */ export interface Column { + /** do we want to always render the column? */ + alwaysRenderColumn?: boolean; + /** async background post-rendering formatter */ asyncPostRender?: (domCellNode: any, row: number, dataContext: T, columnDef: Column) => void; diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts index 180ac2917..9567196cf 100644 --- a/packages/common/src/interfaces/index.ts +++ b/packages/common/src/interfaces/index.ts @@ -145,7 +145,6 @@ export * from './slickNamespace.interface'; export * from './slickRange.interface'; export * from './slickRemoteModel.interface'; export * from './slickResizer.interface'; -export * from './slickRowDetailView.interface'; export * from './sorter.interface'; export * from './textExportOption.interface'; export * from './treeDataOption.interface'; diff --git a/packages/common/src/interfaces/rowDetailView.interface.ts b/packages/common/src/interfaces/rowDetailView.interface.ts index 72d4fc295..57734228b 100644 --- a/packages/common/src/interfaces/rowDetailView.interface.ts +++ b/packages/common/src/interfaces/rowDetailView.interface.ts @@ -1,7 +1,5 @@ -import { RowDetailViewOption } from './rowDetailViewOption.interface'; -import { SlickGrid } from './slickGrid.interface'; -import { SlickEventData } from './slickEventData.interface'; -import { SlickRowDetailView } from './slickRowDetailView.interface'; +import { RowDetailViewOption, SlickEventData, SlickGrid } from './index'; +import { SlickRowDetailView } from '../plugins/slickRowDetailView'; export interface RowDetailView extends RowDetailViewOption { // -- diff --git a/packages/common/src/interfaces/rowDetailViewOption.interface.ts b/packages/common/src/interfaces/rowDetailViewOption.interface.ts index 587ee1a9b..b90301fbc 100644 --- a/packages/common/src/interfaces/rowDetailViewOption.interface.ts +++ b/packages/common/src/interfaces/rowDetailViewOption.interface.ts @@ -63,7 +63,7 @@ export interface RowDetailViewOption { * It's preferable to use the "preloadView" property to use a framework View instead of plain HTML. * If you still wish to use these methods, we strongly suggest you to sanitize your HTML, e.g. "DOMPurify.sanitize()" */ - preTemplate?: () => string; + preTemplate?: (item?: any) => string; /** * HTML Post Template (when Row Detail data is available) that will be loaded once the async function finishes diff --git a/packages/common/src/interfaces/slickNamespace.interface.ts b/packages/common/src/interfaces/slickNamespace.interface.ts index aba802203..bb17ae6ba 100644 --- a/packages/common/src/interfaces/slickNamespace.interface.ts +++ b/packages/common/src/interfaces/slickNamespace.interface.ts @@ -27,7 +27,6 @@ import { SlickRange, SlickRemoteModel, SlickResizer, - SlickRowDetailView, } from './index'; import { SlickGridMenu, } from '../controls/index'; import { @@ -43,6 +42,7 @@ import { SlickGroupItemMetadataProvider, SlickHeaderButtons, SlickHeaderMenu, + SlickRowDetailView, SlickRowMoveManager, SlickRowSelectionModel, } from '../plugins/index'; diff --git a/packages/common/src/interfaces/slickRowDetailView.interface.ts b/packages/common/src/interfaces/slickRowDetailView.interface.ts deleted file mode 100644 index 0c937830d..000000000 --- a/packages/common/src/interfaces/slickRowDetailView.interface.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { UsabilityOverrideFn } from '../enums/usabilityOverrideFn.type'; -import { - Column, - RowDetailViewOption, - SlickEvent, - SlickGrid, -} from './index'; - -/** A plugin to add row detail panel. */ -export interface SlickRowDetailView { - pluginName: 'RowDetailView'; - - /** Constructor of the SlickGrid 3rd party plugin, it can optionally receive options */ - constructor: (options: RowDetailViewOption) => void; - - /** Initialize the SlickGrid 3rd party plugin */ - init(grid: SlickGrid): void; - - /** Destroy (dispose) the SlickGrid 3rd party plugin */ - destroy(): void; - - /** Collapse all of the open items */ - collapseAll(): void; - - /** Colapse an Item so it is not longer seen */ - collapseDetailView(item: any, isMultipleCollapsing: boolean): void; - - /** Expand a row given the dataview item that is to be expanded */ - expandDetailView(item: any): void; - - /** Override the logic for showing (or not) the expand icon (use case example: only every 2nd row is expandable) */ - expandableOverride?: UsabilityOverrideFn; - - /** Get the Column Definition of the first column dedicated to toggling the Row Detail View */ - getColumnDefinition(): Column; - - /** return the currently expanded rows */ - getExpandedRows(): Array; - - /** Takes in the item we are filtering and if it is an expanded row returns it's parents row to filter on */ - getFilterItem(item: any): any; - - /** Get current plugin options */ - getOptions(): RowDetailViewOption; - - /** Resize the Row Detail View */ - resizeDetailView(item: any): void; - - /** Saves the current state of the detail view */ - saveDetailView(item: any): void; - - /** - * Change plugin options - * @options An object with configuration options. - */ - setOptions(options: RowDetailViewOption): void; - - // -- - // Events - - /** Fired when the async response finished */ - onAsyncEndUpdate?: SlickEvent<{ item: any; grid: SlickGrid; }>; - - /** This event must be used with the "notify" by the end user once the Asynchronous Server call returns the item detail */ - onAsyncResponse?: SlickEvent<{ item: any; detailView?: any }>; - - /** Fired after the row detail gets toggled */ - onAfterRowDetailToggle?: SlickEvent<{ item: any; expandedRows: Array; grid: SlickGrid; }>; - - /** Fired before the row detail gets toggled */ - onBeforeRowDetailToggle?: SlickEvent<{ item: any; grid: SlickGrid; }>; - - /** Fired after the row detail gets toggled */ - onRowBackToViewportRange?: SlickEvent<{ item: any; rowId: number | string; rowIndex: number; expandedRows: Array; rowIdsOutOfViewport: Array; grid: SlickGrid; }>; - - /** Fired after a row becomes out of viewport range (user can't see the row anymore) */ - onRowOutOfViewportRange?: SlickEvent<{ item: any; rowId: number | string; rowIndex: number; expandedRows: Array; rowIdsOutOfViewport: Array; grid: SlickGrid; }>; -} diff --git a/packages/common/src/plugins/__tests__/slickContextMenu.spec.ts b/packages/common/src/plugins/__tests__/slickContextMenu.spec.ts index d363f3741..7f32a78dd 100644 --- a/packages/common/src/plugins/__tests__/slickContextMenu.spec.ts +++ b/packages/common/src/plugins/__tests__/slickContextMenu.spec.ts @@ -1,10 +1,10 @@ +import { DelimiterType, FileType } from '../../enums/index'; import { ContextMenu, Column, ElementPosition, GridOption, MenuCommandItem, MenuOptionItem, SlickDataView, SlickGrid, SlickNamespace, } from '../../interfaces/index'; -import { SlickContextMenu } from '../slickContextMenu'; -import { BackendUtilityService, deepCopy, ExcelExportService, PubSubService, SharedService, TextExportService, TreeDataService, } from '../../services'; +import { BackendUtilityService, deepCopy, ExcelExportService, PubSubService, SharedService, TextExportService, TreeDataService, } from '../../services/index'; import { ExtensionUtility } from '../../extensions/extensionUtility'; -import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; import { Formatters } from '../../formatters'; -import { DelimiterType, FileType } from '../../enums'; +import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; +import { SlickContextMenu } from '../slickContextMenu'; declare const Slick: SlickNamespace; diff --git a/packages/common/src/plugins/__tests__/slickRowDetail.spec.ts b/packages/common/src/plugins/__tests__/slickRowDetail.spec.ts new file mode 100644 index 000000000..7746739e8 --- /dev/null +++ b/packages/common/src/plugins/__tests__/slickRowDetail.spec.ts @@ -0,0 +1,69 @@ +import { SlickRowDetailView } from '../slickRowDetailView'; + +describe('SlickRowDetailView plugin', () => { + let plugin: SlickRowDetailView; + + beforeEach(() => { + plugin = new SlickRowDetailView(); + }); + + it('should throw a not implemented error when calling "init" method', () => { + expect(() => plugin.init({} as any)).toThrow('SlickRowDetailView the "init" method must be implemented'); + }); + + it('should throw a not implemented error when calling "create" method', () => { + expect(() => plugin.create([], {} as any)).toThrow('SlickRowDetailView the "create" method must be implemented'); + }); + + it('should throw a not implemented error when calling "destroy" method', () => { + expect(() => plugin.destroy()).toThrow('SlickRowDetailView the "destroy" method must be implemented'); + }); + + it('should throw a not implemented error when calling "dispose" method', () => { + expect(() => plugin.dispose()).toThrow('SlickRowDetailView the "dispose" method must be implemented'); + }); + + it('should throw a not implemented error when calling "collapseAll" method', () => { + expect(() => plugin.collapseAll()).toThrow('SlickRowDetailView the "collapseAll" method must be implemented'); + }); + + it('should throw a not implemented error when calling "collapseDetailView" method', () => { + expect(() => plugin.collapseDetailView({} as any, true)).toThrow('SlickRowDetailView the "collapseDetailView" method must be implemented'); + }); + + it('should throw a not implemented error when calling "expandDetailView" method', () => { + expect(() => plugin.expandDetailView({} as any)).toThrow('SlickRowDetailView the "expandDetailView" method must be implemented'); + }); + + it('should throw a not implemented error when calling "expandableOverride" method', () => { + expect(() => plugin.expandableOverride({} as any)).toThrow('SlickRowDetailView the "expandableOverride" method must be implemented'); + }); + + it('should throw a not implemented error when calling "getColumnDefinition" method', () => { + expect(() => plugin.getColumnDefinition()).toThrow('SlickRowDetailView the "getColumnDefinition" method must be implemented'); + }); + + it('should throw a not implemented error when calling "getExpandedRows" method', () => { + expect(() => plugin.getExpandedRows()).toThrow('SlickRowDetailView the "getExpandedRows" method must be implemented'); + }); + + it('should throw a not implemented error when calling "getFilterItem" method', () => { + expect(() => plugin.getFilterItem({} as any)).toThrow('SlickRowDetailView the "getFilterItem" method must be implemented'); + }); + + it('should throw a not implemented error when calling "getOptions" method', () => { + expect(() => plugin.getOptions()).toThrow('SlickRowDetailView the "getOptions" method must be implemented'); + }); + + it('should throw a not implemented error when calling "resizeDetailView" method', () => { + expect(() => plugin.resizeDetailView({} as any)).toThrow('SlickRowDetailView the "resizeDetailView" method must be implemented'); + }); + + it('should throw a not implemented error when calling "saveDetailView" method', () => { + expect(() => plugin.saveDetailView({} as any)).toThrow('SlickRowDetailView the "saveDetailView" method must be implemented'); + }); + + it('should throw a not implemented error when calling "setOptions" method', () => { + expect(() => plugin.setOptions({} as any)).toThrow('SlickRowDetailView the "setOptions" method must be implemented'); + }); +}); diff --git a/packages/common/src/plugins/index.ts b/packages/common/src/plugins/index.ts index e2617a92d..d31e15942 100644 --- a/packages/common/src/plugins/index.ts +++ b/packages/common/src/plugins/index.ts @@ -11,5 +11,6 @@ export * from './slickDraggableGrouping'; export * from './slickGroupItemMetadataProvider'; export * from './slickHeaderButtons'; export * from './slickHeaderMenu'; +export * from './slickRowDetailView'; export * from './slickRowMoveManager'; export * from './slickRowSelectionModel'; \ No newline at end of file diff --git a/packages/common/src/plugins/slickContextMenu.ts b/packages/common/src/plugins/slickContextMenu.ts index 5aa5d8d37..a6bbc66f0 100644 --- a/packages/common/src/plugins/slickContextMenu.ts +++ b/packages/common/src/plugins/slickContextMenu.ts @@ -10,14 +10,13 @@ import { MenuOptionItem, SlickEventHandler, } from '../interfaces/index'; -import { getCellValueFromQueryFieldGetter, getTranslationPrefix, } from '../services/index'; +import { DelimiterType, FileType } from '../enums/index'; +import { ExcelExportService, getCellValueFromQueryFieldGetter, getTranslationPrefix, TextExportService } from '../services/index'; import { exportWithFormatterWhenDefined } from '../formatters/formatterUtilities'; import { ExtensionUtility } from '../extensions/extensionUtility'; import { PubSubService } from '../services/pubSub.service'; import { SharedService } from '../services/shared.service'; import { TreeDataService } from '../services/treeData.service'; -import { ExcelExportService, TextExportService } from '../services'; -import { DelimiterType, FileType } from '../enums'; import { MenuFromCellBaseClass } from './menuFromCellBaseClass'; diff --git a/packages/common/src/plugins/slickRowDetailView.ts b/packages/common/src/plugins/slickRowDetailView.ts new file mode 100644 index 000000000..69aa916d9 --- /dev/null +++ b/packages/common/src/plugins/slickRowDetailView.ts @@ -0,0 +1,109 @@ +import { UsabilityOverrideFn } from '../enums/index'; +import { Column, GridOption, RowDetailViewOption, SlickEvent, SlickGrid } from '../interfaces/index'; + +/* eslint-disable @typescript-eslint/no-unused-vars */ +export class SlickRowDetailView { + pluginName: 'RowDetailView' = 'RowDetailView'; + + /** Fired when the async response finished */ + onAsyncEndUpdate?: SlickEvent<{ item: any; grid: SlickGrid; }>; + + /** This event must be used with the "notify" by the end user once the Asynchronous Server call returns the item detail */ + onAsyncResponse?: SlickEvent<{ item: any; detailView?: any }>; + + /** Fired after the row detail gets toggled */ + onAfterRowDetailToggle?: SlickEvent<{ item: any; expandedRows: Array; grid: SlickGrid; }>; + + /** Fired before the row detail gets toggled */ + onBeforeRowDetailToggle?: SlickEvent<{ item: any; grid: SlickGrid; }>; + + /** Fired after the row detail gets toggled */ + onRowBackToViewportRange?: SlickEvent<{ item: any; rowId: number | string; rowIndex: number; expandedRows: Array; rowIdsOutOfViewport: Array; grid: SlickGrid; }>; + + /** Fired after a row becomes out of viewport range (when user can't see the row anymore) */ + onRowOutOfViewportRange?: SlickEvent<{ item: any; rowId: number | string; rowIndex: number; expandedRows: Array; rowIdsOutOfViewport: Array; grid: SlickGrid; }>; + + /** Constructor of the SlickGrid 3rd party plugin, it can optionally receive options */ + constructor() { } + + /** + * Initialize the Export Service + * @param _grid + * @param _containerService + */ + init(_grid: SlickGrid): void { + throw new Error('SlickRowDetailView the "init" method must be implemented'); + } + + /** @deprecated use `dispose` Destroy the Slick Row Detail View */ + destroy() { + throw new Error('SlickRowDetailView the "destroy" method must be implemented'); + } + + /** Dispose of the Slick Row Detail View */ + dispose() { + throw new Error('SlickRowDetailView the "dispose" method must be implemented'); + } + + create(columnDefinitions: Column[], gridOptions: GridOption): SlickRowDetailView | null { + throw new Error('SlickRowDetailView the "create" method must be implemented'); + } + + /** Collapse all of the open items */ + collapseAll() { + throw new Error('SlickRowDetailView the "collapseAll" method must be implemented'); + } + + /** Colapse an Item so it is not longer seen */ + collapseDetailView(item: any, isMultipleCollapsing: boolean) { + throw new Error('SlickRowDetailView the "collapseDetailView" method must be implemented'); + } + + /** Expand a row given the dataview item that is to be expanded */ + expandDetailView(item: any) { + throw new Error('SlickRowDetailView the "expandDetailView" method must be implemented'); + } + + /** Override the logic for showing (or not) the expand icon (use case example: only every 2nd row is expandable) */ + expandableOverride(overrideFn: UsabilityOverrideFn) { + throw new Error('SlickRowDetailView the "expandableOverride" method must be implemented'); + } + + /** Get the Column Definition of the first column dedicated to toggling the Row Detail View */ + getColumnDefinition(): Column { + throw new Error('SlickRowDetailView the "getColumnDefinition" method must be implemented'); + } + + /** return the currently expanded rows */ + getExpandedRows(): Array { + throw new Error('SlickRowDetailView the "getExpandedRows" method must be implemented'); + } + + /** Takes in the item we are filtering and if it is an expanded row returns it's parents row to filter on */ + getFilterItem(item: any) { + throw new Error('SlickRowDetailView the "getFilterItem" method must be implemented'); + } + + /** Get current plugin options */ + getOptions(): RowDetailViewOption { + throw new Error('SlickRowDetailView the "getOptions" method must be implemented'); + } + + /** Resize the Row Detail View */ + resizeDetailView(item: any) { + throw new Error('SlickRowDetailView the "resizeDetailView" method must be implemented'); + } + + /** Saves the current state of the detail view */ + saveDetailView(item: any) { + throw new Error('SlickRowDetailView the "saveDetailView" method must be implemented'); + } + + /** + * Change plugin options + * @options An object with configuration options. + */ + setOptions(options: RowDetailViewOption) { + throw new Error('SlickRowDetailView the "setOptions" method must be implemented'); + } +} \ No newline at end of file diff --git a/packages/common/src/services/__tests__/extension.service.spec.ts b/packages/common/src/services/__tests__/extension.service.spec.ts index df60739be..fe563da58 100644 --- a/packages/common/src/services/__tests__/extension.service.spec.ts +++ b/packages/common/src/services/__tests__/extension.service.spec.ts @@ -2,7 +2,7 @@ import 'jest-extended'; import { ExtensionName } from '../../enums/index'; import { Column, ExtensionModel, GridOption, SlickGrid, SlickNamespace } from '../../interfaces/index'; -import { ExtensionUtility, RowDetailViewExtension } from '../../extensions'; +import { ExtensionUtility } from '../../extensions'; import { BackendUtilityService, ExtensionService, FilterService, PubSubService, SharedService, SortService, TreeDataService } from '../index'; import { TranslateServiceStub } from '../../../../../test/translateServiceStub'; import { SlickAutoTooltip, SlickCellExcelCopyManager, SlickCellMenu, SlickCheckboxSelectColumn, SlickContextMenu, SlickDraggableGrouping, SlickHeaderButtons, SlickHeaderMenu, SlickRowMoveManager, SlickRowSelectionModel } from '../../plugins/index'; @@ -68,6 +68,16 @@ const mockRowMoveManager = { jest.mock('../../plugins/slickRowMoveManager', () => ({ SlickRowMoveManager: jest.fn().mockImplementation(() => mockRowMoveManager), })); +const mockRowDetailView = { + constructor: jest.fn(), + init: jest.fn(), + create: jest.fn(), + destroy: jest.fn(), + dispose: jest.fn(), +} as unknown as SlickRowMoveManager; +jest.mock('../../plugins/slickRowDetailView', () => ({ + SlickRowDetailView: jest.fn().mockImplementation(() => mockRowDetailView), +})); const gridStub = { autosizeColumns: jest.fn(), @@ -172,11 +182,9 @@ describe('ExtensionService', () => { extensionUtilityStub, filterServiceStub, pubSubServiceStub, + sharedService, sortServiceStub, treeDataServiceStub, - // extensions - extensionStub as unknown as RowDetailViewExtension, - sharedService, translateService, ); }); @@ -409,22 +417,25 @@ describe('ExtensionService', () => { }); it('should register the RowDetailView addon when "enableRowDetailView" is set in the grid options', () => { + const onRegisteredMock = jest.fn(); const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[]; - const gridOptionsMock = { enableRowDetailView: true } as GridOption; - const extCreateSpy = jest.spyOn(extensionStub, 'create').mockReturnValue(instanceMock); - const extRegisterSpy = jest.spyOn(extensionStub, 'register'); + const gridOptionsMock = { + enableRowDetailView: true, + rowDetailView: { + onExtensionRegistered: onRegisteredMock + } + } as unknown as GridOption; + const extCreateSpy = jest.spyOn(mockRowDetailView, 'create').mockReturnValue(mockRowDetailView); const gridSpy = jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock); service.createExtensionsBeforeGridCreation(columnsMock, gridOptionsMock); service.bindDifferentExtensions(); - const rowSelectionInstance = service.getExtensionByName(ExtensionName.rowSelection); const output = service.getExtensionByName(ExtensionName.rowDetailView); expect(gridSpy).toHaveBeenCalled(); expect(extCreateSpy).toHaveBeenCalledWith(columnsMock, gridOptionsMock); - expect(rowSelectionInstance).not.toBeNull(); - expect(extRegisterSpy).toHaveBeenCalled(); - expect(output).toEqual({ name: ExtensionName.rowDetailView, instance: instanceMock as unknown, class: extensionStub } as ExtensionModel); + expect(onRegisteredMock).toHaveBeenCalledWith(expect.toBeObject()); + expect(output).toEqual({ name: ExtensionName.rowDetailView, instance: mockRowDetailView as unknown, class: mockRowDetailView } as ExtensionModel); }); it('should register the RowMoveManager addon when "enableRowMoveManager" is set in the grid options', () => { @@ -559,14 +570,21 @@ describe('ExtensionService', () => { expect(extSpy).toHaveBeenCalledWith(columnsMock, gridOptionsMock); }); - it('should call rowDetailViewExtension create when "enableRowDetailView" is set in the grid options provided', () => { - const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[]; - const gridOptionsMock = { enableRowDetailView: true } as GridOption; - const extSpy = jest.spyOn(extensionStub, 'create').mockReturnValue(instanceMock); + it('should call RowDetailView create when "enableRowDetailView" is set in the grid options provided', () => { + const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }, { id: 'field2', field: 'field2', width: 50, }] as Column[]; + const gridOptionsMock = { + enableCheckboxSelector: true, enableRowSelection: true, + checkboxSelector: { columnIndexPosition: 1 }, + enableRowDetailView: true, + rowDetailView: { columnIndexPosition: 0 } + } as GridOption; + const extCheckSelectSpy = jest.spyOn(mockCheckboxSelectColumn, 'create'); + const extRowDetailViewSpy = jest.spyOn(mockRowDetailView, 'create'); service.createExtensionsBeforeGridCreation(columnsMock, gridOptionsMock); - expect(extSpy).toHaveBeenCalledWith(columnsMock, gridOptionsMock); + expect(extCheckSelectSpy).toHaveBeenCalledWith(columnsMock, gridOptionsMock); + expect(extRowDetailViewSpy).toHaveBeenCalledWith(columnsMock, gridOptionsMock); }); it('should call draggableGroupingExtension create when "enableDraggableGrouping" is set in the grid options provided', () => { @@ -868,11 +886,9 @@ describe('ExtensionService', () => { extensionUtilityStub, filterServiceStub, pubSubServiceStub, + sharedService, sortServiceStub, treeDataServiceStub, - // extensions - extensionStub as unknown as RowDetailViewExtension, - sharedService, translateService, ); diff --git a/packages/common/src/services/extension.service.ts b/packages/common/src/services/extension.service.ts index 29858582c..f34076d0b 100644 --- a/packages/common/src/services/extension.service.ts +++ b/packages/common/src/services/extension.service.ts @@ -1,9 +1,6 @@ import { Column, Extension, ExtensionModel, GridOption, } from '../interfaces/index'; import { ColumnReorderFunction, ExtensionList, ExtensionName, SlickControlList, SlickPluginList } from '../enums/index'; -import { - ExtensionUtility, - RowDetailViewExtension, -} from '../extensions/index'; +import { ExtensionUtility } from '../extensions/index'; import { SharedService } from './shared.service'; import { TranslaterService } from './translater.service'; import { SlickColumnPicker, SlickGridMenu } from '../controls/index'; @@ -14,21 +11,22 @@ import { SlickCheckboxSelectColumn, SlickContextMenu, SlickDraggableGrouping, + SlickGroupItemMetadataProvider, SlickHeaderButtons, SlickHeaderMenu, SlickRowMoveManager, SlickRowSelectionModel } from '../plugins/index'; import { FilterService } from './filter.service'; -import { SlickGroupItemMetadataProvider } from '../plugins/slickGroupItemMetadataProvider'; import { PubSubService } from './pubSub.service'; import { SortService } from './sort.service'; import { TreeDataService } from './treeData.service'; +import { SlickRowDetailView } from '../plugins/slickRowDetailView'; interface ExtensionWithColumnIndexPosition { name: ExtensionName; columnIndexPosition: number; - extension: SlickCheckboxSelectColumn | RowDetailViewExtension | SlickRowMoveManager; + extension: SlickCheckboxSelectColumn | SlickRowDetailView | SlickRowMoveManager; } export class ExtensionService { @@ -44,6 +42,7 @@ export class ExtensionService { protected _gridMenuControl?: SlickGridMenu; protected _groupItemMetadataProviderService?: SlickGroupItemMetadataProvider; protected _headerMenuPlugin?: SlickHeaderMenu; + protected _rowDetailViewPlugin?: SlickRowDetailView; protected _rowMoveManagerPlugin?: SlickRowMoveManager; protected _rowSelectionModel?: SlickRowSelectionModel; @@ -59,11 +58,9 @@ export class ExtensionService { protected readonly extensionUtility: ExtensionUtility, protected readonly filterService: FilterService, protected readonly pubSubService: PubSubService, + protected readonly sharedService: SharedService, protected readonly sortService: SortService, protected readonly treeDataService: TreeDataService, - - protected readonly rowDetailViewExtension: RowDetailViewExtension, - protected readonly sharedService: SharedService, protected readonly translaterService?: TranslaterService, ) { } @@ -258,15 +255,17 @@ export class ExtensionService { // Row Detail View Plugin if (this.gridOptions.enableRowDetailView) { - if (this.rowDetailViewExtension && this.rowDetailViewExtension.register) { - const rowSelectionPlugin = this.getExtensionByName(ExtensionName.rowSelection); - this.rowDetailViewExtension.register(rowSelectionPlugin?.instance); - const createdExtension = this.getCreatedExtensionByName(ExtensionName.rowDetailView); // get the plugin from when it was really created earlier - const instance = createdExtension && createdExtension.instance; - if (instance) { - this._extensionList[ExtensionName.rowDetailView] = { name: ExtensionName.rowDetailView, class: this.rowDetailViewExtension, instance }; - } + this._rowDetailViewPlugin = new SlickRowDetailView(); + this._rowDetailViewPlugin.init(this.sharedService.slickGrid); + if (this.gridOptions.rowDetailView?.onExtensionRegistered) { + this.gridOptions.rowDetailView.onExtensionRegistered(this._rowDetailViewPlugin); + } + const createdExtension = this.getCreatedExtensionByName(ExtensionName.rowDetailView); // get the instance from when it was really created earlier + const instance = createdExtension?.instance; + if (instance) { + this._extensionList[ExtensionName.rowDetailView] = { name: ExtensionName.rowDetailView, class: this._rowDetailViewPlugin, instance: this._rowDetailViewPlugin }; } + // this._extensionList[ExtensionName.headerMenu] = { name: ExtensionName.headerMenu, class: this._headerMenuPlugin, instance: this._headerMenuPlugin }; } // Row Move Manager Plugin @@ -311,7 +310,8 @@ export class ExtensionService { } if (gridOptions.enableRowDetailView) { if (!this.getCreatedExtensionByName(ExtensionName.rowDetailView)) { - featureWithColumnIndexPositions.push({ name: ExtensionName.rowDetailView, extension: this.rowDetailViewExtension, columnIndexPosition: gridOptions?.rowDetailView?.columnIndexPosition ?? featureWithColumnIndexPositions.length }); + this._rowDetailViewPlugin = new SlickRowDetailView(); + featureWithColumnIndexPositions.push({ name: ExtensionName.rowDetailView, extension: this._rowDetailViewPlugin, columnIndexPosition: gridOptions?.rowDetailView?.columnIndexPosition ?? featureWithColumnIndexPositions.length }); } } diff --git a/packages/row-detail-view-plugin/README.md b/packages/row-detail-view-plugin/README.md new file mode 100644 index 000000000..23ba93ec0 --- /dev/null +++ b/packages/row-detail-view-plugin/README.md @@ -0,0 +1,24 @@ +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) +[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/) +[![npm](https://img.shields.io/npm/v/@slickgrid-universal/row-detail-view-plugin.svg?color=forest)](https://www.npmjs.com/package/@slickgrid-universal/row-detail-view-plugin) +[![npm](https://img.shields.io/npm/dy/@slickgrid-universal/row-detail-view-plugin?color=forest)](https://www.npmjs.com/package/@slickgrid-universal/row-detail-view-plugin) + +[![Actions Status](https://github.com/ghiscoding/slickgrid-universal/workflows/CI%20Build/badge.svg)](https://github.com/ghiscoding/slickgrid-universal/actions) +[![Cypress.io](https://img.shields.io/badge/tested%20with-Cypress-04C38E.svg)](https://www.cypress.io/) +[![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest) +[![codecov](https://codecov.io/gh/ghiscoding/slickgrid-universal/branch/master/graph/badge.svg)](https://codecov.io/gh/ghiscoding/slickgrid-universal) + +## Slick Row Detail View (plugin) +#### @slickgrid-universal/row-detail-view-plugin + +A plugin to add Row Detail View Panel that can be expanded/collapsed, the plugin was created from an idea that came out of this Original StackOverflow question & article making this possible (thanks to violet313). + * [Can SlickGrid's row height be dynamically altered? - on Stack Overflow](https://stackoverflow.com/questions/10535164/can-slickgrids-row-height-be-dynamically-altered#29399927) + * [a responsive slickgrid with dynamic row-heights by violet313](https://violet313.github.io) + +### Internal Dependencies +- [@slickgrid-universal/common](https://github.com/ghiscoding/slickgrid-universal/tree/master/packages/common) + + +### Installation +Follow the instruction provided in the main [README](https://github.com/ghiscoding/slickgrid-universal#installation) diff --git a/packages/row-detail-view-plugin/package.json b/packages/row-detail-view-plugin/package.json new file mode 100644 index 000000000..f8180e967 --- /dev/null +++ b/packages/row-detail-view-plugin/package.json @@ -0,0 +1,58 @@ +{ + "name": "@slickgrid-universal/row-detail-view-plugin", + "version": "0.18.0", + "description": "SlickRowDetail plugin - A plugin to add Row Detail Panel", + "main": "dist/commonjs/index.js", + "browser": "src/index.ts", + "module": "dist/esm/index.js", + "types": "dist/commonjs/index.d.ts", + "typings": "dist/commonjs/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "/dist" + ], + "scripts": { + "build": "cross-env tsc --build", + "postbuild": "npm-run-all bundle:commonjs", + "build:watch": "cross-env tsc --incremental --watch", + "dev": "run-s build", + "dev:watch": "run-p build:watch", + "bundle": "run-p bundle:commonjs bundle:esm", + "bundle:commonjs": "tsc --project tsconfig.bundle.json --outDir dist/commonjs --module commonjs", + "bundle:esm": "cross-env tsc --project tsconfig.bundle.json --outDir dist/esm --module esnext --target es2018", + "prebundle": "npm-run-all delete:dist", + "delete:dist": "cross-env rimraf --maxBusyTries=10 dist", + "package:add-browser-prop": "cross-env node ../change-package-browser.js --add-browser=true --folder-name=row-detail-view-plugin", + "package:remove-browser-prop": "cross-env node ../change-package-browser.js --remove-browser=true --folder-name=row-detail-view-plugin" + }, + "license": "MIT", + "author": "Ghislain B.", + "homepage": "https://github.com/ghiscoding/slickgrid-universal", + "repository": { + "type": "git", + "url": "https://github.com/ghiscoding/slickgrid-universal.git", + "directory": "packages/row-detail-view-plugin" + }, + "bugs": { + "url": "https://github.com/ghiscoding/slickgrid-universal/issues" + }, + "engines": { + "node": ">=14.15.0", + "npm": ">=6.14.8" + }, + "browserslist": [ + "last 2 version", + "> 1%", + "not dead" + ], + "dependencies": { + "@slickgrid-universal/common": "^0.18.0" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "npm-run-all": "^4.1.5", + "rimraf": "^3.0.2" + } +} diff --git a/packages/row-detail-view-plugin/src/.npmignore b/packages/row-detail-view-plugin/src/.npmignore new file mode 100644 index 000000000..083a3140f --- /dev/null +++ b/packages/row-detail-view-plugin/src/.npmignore @@ -0,0 +1,2 @@ +index.ts +**/*.* diff --git a/packages/row-detail-view-plugin/src/index.spec.ts b/packages/row-detail-view-plugin/src/index.spec.ts new file mode 100644 index 000000000..adcf8da0f --- /dev/null +++ b/packages/row-detail-view-plugin/src/index.spec.ts @@ -0,0 +1,11 @@ +import * as entry from './index'; + +describe('Testing library entry point', () => { + it('should have an index entry point defined', () => { + expect(entry).toBeTruthy(); + }); + + it('should have all exported object defined', () => { + expect(typeof entry.SlickRowDetailView).toBe('function'); + }); +}); diff --git a/packages/row-detail-view-plugin/src/index.ts b/packages/row-detail-view-plugin/src/index.ts new file mode 100644 index 000000000..4b676be57 --- /dev/null +++ b/packages/row-detail-view-plugin/src/index.ts @@ -0,0 +1 @@ +export * from './slickRowDetailView'; \ No newline at end of file diff --git a/packages/row-detail-view-plugin/src/slickRowDetailView.ts b/packages/row-detail-view-plugin/src/slickRowDetailView.ts new file mode 100644 index 000000000..8d4ba9fa8 --- /dev/null +++ b/packages/row-detail-view-plugin/src/slickRowDetailView.ts @@ -0,0 +1,720 @@ +import { + Column, + ExternalResource, + FormatterResultObject, + GridOption, + RowDetailView, + RowDetailViewOption, + SlickDataView, + SlickEventHandler, + SlickGrid, + SlickNamespace, + SlickRowDetailView as UniversalRowDetailView, + UsabilityOverrideFn, +} from '@slickgrid-universal/common'; + +// using external non-typed js libraries +declare const Slick: SlickNamespace; + +export class SlickRowDetailView implements ExternalResource, UniversalRowDetailView { + protected _addonOptions!: RowDetailView; + protected _eventHandler: SlickEventHandler; + protected _expandableOverride: any; + protected _expandedRows: any[] = []; + protected _grid!: SlickGrid; + protected _lastRange: any; + protected _rowIdsOutOfViewport: any; + protected _gridRowBuffer: any; + protected _outsideRange: any; + protected _keyPrefix = ''; + protected _dataViewIdProperty = ''; + protected _gridUid = ''; + protected _visibleRenderedCellCount: any; + protected _defaults = { + columnId: '_detail_selector', + cssClass: 'detailView-toggle', + expandedClass: null, + collapsedClass: null, + keyPrefix: '_', + loadOnce: false, + collapseAllOnSort: true, + saveDetailViewOnScroll: true, + singleRowExpand: false, + useSimpleViewportCalc: false, + alwaysRenderColumn: true, + toolTip: '', + width: 30, + maxRows: null + } as unknown as RowDetailView; + + pluginName: 'RowDetailView' = 'RowDetailView'; + + /** Fired when the async response finished */ + onAsyncEndUpdate = new Slick.Event(); + + /** This event must be used with the "notify" by the end user once the Asynchronous Server call returns the item detail */ + onAsyncResponse = new Slick.Event(); + + /** Fired after the row detail gets toggled */ + onAfterRowDetailToggle = new Slick.Event(); + + /** Fired before the row detail gets toggled */ + onBeforeRowDetailToggle = new Slick.Event(); + + /** Fired after the row detail gets toggled */ + onRowBackToViewportRange = new Slick.Event(); + + /** Fired after a row becomes out of viewport range (when user can't see the row anymore) */ + onRowOutOfViewportRange = new Slick.Event(); + + /** Constructor of the SlickGrid 3rd party plugin, it can optionally receive options */ + constructor() { + this._eventHandler = new Slick.EventHandler(); + } + + /** Getter of SlickGrid DataView object */ + get dataView(): SlickDataView { + return this._grid?.getData() || {} as SlickDataView; + } + + /** Getter for the Grid Options pulled through the Grid Object */ + get gridOptions(): GridOption { + return this._grid?.getOptions() || {}; + } + + protected gridUid() { + return this._gridUid || (this._grid?.getUID() || ''); + } + /** + * Initialize the Export Service + * @param _grid + * @param _containerService + */ + init(grid: SlickGrid): void { + this._grid = grid; + if (!grid) { + throw new Error('RowDetailView Plugin requires the Grid instance to be passed as argument to the "init()" method'); + } + this._grid = grid; + this._gridUid = grid.getUID(); + this._keyPrefix = this._addonOptions && this._addonOptions.keyPrefix || '_'; + + // Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3 + this._gridRowBuffer = this._grid.getOptions().minRowBuffer; + this._grid.getOptions().minRowBuffer = this._addonOptions.panelRows + 3; + + this._eventHandler + .subscribe(this._grid.onClick, this.handleClick.bind(this)) + .subscribe(this._grid.onScroll, this.handleScroll.bind(this)); + + // Sort will, by default, Collapse all of the open items (unless user implements his own onSort which deals with open row and padding) + if (this._addonOptions.collapseAllOnSort) { + this._eventHandler.subscribe(this._grid.onSort, this.collapseAll); + this._expandedRows = []; + this._rowIdsOutOfViewport = []; + } + + this._eventHandler.subscribe(this.dataView.onRowCountChanged, () => { + this._grid.updateRowCount(); + this._grid.render(); + }); + + this._eventHandler.subscribe(this.dataView.onRowsChanged, (e, a) => { + this._grid.invalidateRows(a.rows); + this._grid.render(); + }); + + // subscribe to the onAsyncResponse so that the plugin knows when the user server side calls finished + this.subscribeToOnAsyncResponse(); + + // after data is set, let's get the DataView Id Property name used (defaults to "id") + this._eventHandler.subscribe(this.dataView.onSetItemsCalled, () => { + this._dataViewIdProperty = this.dataView?.getIdPropertyName() || 'id'; + }); + + // if we use the alternative & simpler calculation of the out of viewport range + // we will need to know how many rows are rendered on the screen and we need to wait for grid to be rendered + // unfortunately there is no triggered event for knowing when grid is finished, so we use 250ms delay and it's typically more than enough + if (this._addonOptions.useSimpleViewportCalc) { + this._eventHandler.subscribe(this._grid.onRendered, (_e, args) => { + if (args && args.endRow) { + this._visibleRenderedCellCount = args.endRow - args.startRow; + } + }); + } + } + + /** @deprecated use `dispose` Destroy the Slick Row Detail View */ + destroy() { + this.dispose(); + } + + /** Dispose of the Slick Row Detail View */ + dispose() { + this._eventHandler?.unsubscribeAll(); + } + + create(columnDefinitions: Column[], gridOptions: GridOption): SlickRowDetailView | null { + this._addonOptions = { ...this._defaults, ...gridOptions.rowDetailView } as RowDetailView; + if (Array.isArray(columnDefinitions) && gridOptions) { + const newRowDetailViewColumn: Column = this.getColumnDefinition(); + const rowDetailColDef = Array.isArray(columnDefinitions) && columnDefinitions.find(col => col?.behavior === 'selectAndMove'); + const finalRowDetailViewColumn = rowDetailColDef ? rowDetailColDef : newRowDetailViewColumn; + + // column index position in the grid + const columnPosition = gridOptions?.rowDetailView?.columnIndexPosition ?? 0; + if (columnPosition > 0) { + columnDefinitions.splice(columnPosition, 0, finalRowDetailViewColumn); + } else { + columnDefinitions.unshift(finalRowDetailViewColumn); + } + } + return this; + } + + /** Get current plugin options */ + getOptions(): RowDetailViewOption { + return this._addonOptions; + } + + /** set or change some of the plugin options */ + setOptions(options: RowDetailViewOption) { + this._addonOptions = { ... this._addonOptions, ...options }; + if (this._addonOptions?.singleRowExpand) { + this.collapseAll(); + } + } + + /** Collapse all of the open items */ + collapseAll() { + this.dataView.beginUpdate(); + for (let i = this._expandedRows.length - 1; i >= 0; i--) { + this.collapseDetailView(this._expandedRows[i], true); + } + this.dataView.endUpdate(); + } + + /** Colapse an Item so it is not longer seen */ + collapseDetailView(item: any, isMultipleCollapsing = false) { + if (!isMultipleCollapsing) { + this.dataView.beginUpdate(); + } + // Save the details on the collapse assuming onetime loading + if (this._addonOptions.loadOnce) { + this.saveDetailView(item); + } + + item[this._keyPrefix + 'collapsed'] = true; + for (let idx = 1; idx <= item[this._keyPrefix + 'sizePadding']; idx++) { + this.dataView.deleteItem(item[this._dataViewIdProperty] + '.' + idx); + } + item[this._keyPrefix + 'sizePadding'] = 0; + this.dataView.updateItem(item[this._dataViewIdProperty], item); + + // Remove the item from the expandedRows + this._expandedRows = this._expandedRows.filter((r) => { + return r[this._dataViewIdProperty] !== item[this._dataViewIdProperty]; + }); + + if (!isMultipleCollapsing) { + this.dataView.endUpdate(); + } + } + + /** Expand a row given the dataview item that is to be expanded */ + expandDetailView(item: any) { + if (this._addonOptions?.singleRowExpand) { + this.collapseAll(); + } + + item[this._keyPrefix + 'collapsed'] = false; + this._expandedRows.push(item); + + // In the case something went wrong loading it the first time such a scroll of screen before loaded + if (!item[this._keyPrefix + 'detailContent']) { + item[this._keyPrefix + 'detailViewLoaded'] = false; + } + + // display pre-loading template + if (!item[this._keyPrefix + 'detailViewLoaded'] || this._addonOptions.loadOnce !== true) { + item[this._keyPrefix + 'detailContent'] = this._addonOptions.preTemplate!(item); + } else { + this.onAsyncResponse.notify({ + item, + itemDetail: item, + detailView: item[this._keyPrefix + 'detailContent'] + }); + this.applyTemplateNewLineHeight(item); + this.dataView.updateItem(item[this._dataViewIdProperty], item); + + return; + } + + this.applyTemplateNewLineHeight(item); + this.dataView.updateItem(item[this._dataViewIdProperty], item); + + // async server call + this._addonOptions.process(item); + } + + /** Saves the current state of the detail view */ + saveDetailView(item: any) { + const view = document.querySelector('.' + this._gridUid + ' .innerDetailView_' + item[this._dataViewIdProperty]); + if (view) { + const html = view.innerHTML; + if (html !== undefined) { + item[this._keyPrefix + 'detailContent'] = html; + } + } + } + + /** + * subscribe to the onAsyncResponse so that the plugin knows when the user server side calls finished + * the response has to be as "args.item" (or "args.itemDetail") with it's data back + */ + subscribeToOnAsyncResponse() { + this.onAsyncResponse.subscribe((_e, args) => { + if (!args || (!args.item && !args.itemDetail)) { + throw new Error('Slick.RowDetailView plugin requires the onAsyncResponse() to supply "args.item" property.'); + } + + // we accept item/itemDetail, just get the one which has data + const itemDetail = args.item || args.itemDetail; + + // If we just want to load in a view directly we can use detailView property to do so + if (args.detailView) { + itemDetail[this._keyPrefix + 'detailContent'] = args.detailView; + } else { + itemDetail[this._keyPrefix + 'detailContent'] = this._addonOptions.postTemplate!(itemDetail); + } + + itemDetail[this._keyPrefix + 'detailViewLoaded'] = true; + this.dataView.updateItem(itemDetail[this._dataViewIdProperty], itemDetail); + + // trigger an event once the post template is finished loading + this.onAsyncEndUpdate.notify({ + grid: this._grid, + item: itemDetail, + itemDetail, + }); + }); + } + + /** When row is getting toggled, we will handle the action of collapsing/expanding */ + protected handleAccordionShowHide(item: any) { + if (item) { + if (!item[this._keyPrefix + 'collapsed']) { + this.collapseDetailView(item); + } else { + this.expandDetailView(item); + } + } + } + + /** + * TODO interface only has a GETTER not a SETTER..why? + * Override the logic for showing (or not) the expand icon (use case example: only every 2nd row is expandable) + * Method that user can pass to override the default behavior or making every row an expandable row. + * In order word, user can choose which rows to be an available row detail (or not) by providing his own logic. + * @param overrideFn: override function callback + */ + setExpandableOverride(overrideFn: UsabilityOverrideFn) { + this._expandableOverride = overrideFn; + } + + expandableOverride(): UsabilityOverrideFn { + return this._expandableOverride; + } + + /** Get the Column Definition of the first column dedicated to toggling the Row Detail View */ + getColumnDefinition(): Column { + return { + id: (this._addonOptions as any).columnId, + name: '', + toolTip: (this._addonOptions as any).toolTip, + field: 'sel', + width: (this._addonOptions as any).width, + resizable: false, + sortable: false, + alwaysRenderColumn: (this._addonOptions as any).alwaysRenderColumn, + cssClass: this._addonOptions.cssClass, + formatter: this.detailSelectionFormatter.bind(this), + }; + } + + /** return the currently expanded rows */ + getExpandedRows(): Array { + return this._expandedRows; + } + + /** Takes in the item we are filtering and if it is an expanded row returns it's parents row to filter on */ + getFilterItem(item: any) { + if (item[this._keyPrefix + 'isPadding'] && item[this._keyPrefix + 'parent']) { + item = item[this._keyPrefix + 'parent']; + } + return item; + } + + /** Resize the Row Detail View */ + resizeDetailView(item: any) { + if (!item) { + return; + } + + // Grad each of the DOM elements + const mainContainer = document.querySelector('.' + this._gridUid + ' .detailViewContainer_' + item[this._dataViewIdProperty]) as HTMLDivElement; + const cellItem = document.querySelector('.' + this._gridUid + ' .cellDetailView_' + item[this._dataViewIdProperty]) as HTMLDivElement; + const inner = document.querySelector('.' + this._gridUid + ' .innerDetailView_' + item[this._dataViewIdProperty]) as HTMLDivElement; + + if (!mainContainer || !cellItem || !inner) { + return; + } + + for (let idx = 1; idx <= item[this._keyPrefix + 'sizePadding']; idx++) { + this.dataView.deleteItem(item[this._dataViewIdProperty] + '.' + idx); + } + + const rowHeight = this.gridOptions.rowHeight as number; // height of a row + const lineHeight = 13; // we know cuz we wrote the custom css innit ;) + + // remove the height so we can calculate the height + mainContainer.style.minHeight = ''; + + // Get the scroll height for the main container so we know the actual size of the view + const itemHeight = mainContainer.scrollHeight; + + // Now work out how many rows + const rowCount = Math.ceil(itemHeight / rowHeight); + + item[this._keyPrefix + 'sizePadding'] = Math.ceil(((rowCount * 2) * lineHeight) / rowHeight); + item[this._keyPrefix + 'height'] = itemHeight; + + let outterHeight = (item[this._keyPrefix + 'sizePadding'] * rowHeight); + if ((this._addonOptions as any).maxRows !== null && item[this._keyPrefix + 'sizePadding'] > (this._addonOptions as any).maxRows) { + outterHeight = (this._addonOptions as any).maxRows * rowHeight; + item[this._keyPrefix + 'sizePadding'] = (this._addonOptions as any).maxRows; + } + + // If the padding is now more than the original minRowBuff we need to increase it + if (this.gridOptions.minRowBuffer! < item[this._keyPrefix + 'sizePadding']) { + // Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3 + this.gridOptions.minRowBuffer = item[this._keyPrefix + 'sizePadding'] + 3; + } + + mainContainer.setAttribute('style', 'min-height: ' + item[this._keyPrefix + 'height'] + 'px'); + if (cellItem) { + cellItem.setAttribute('style', 'height: ' + outterHeight + 'px; top:' + rowHeight + 'px'); + } + + const idxParent = this.dataView.getIdxById(item[this._dataViewIdProperty]) as number; + for (let idx = 1; idx <= item[this._keyPrefix + 'sizePadding']; idx++) { + this.dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx)); + } + + // Lastly save the updated state + this.saveDetailView(item); + } + + /** + * create the detail ctr node. this belongs to the dev & can be custom-styled as per + */ + protected applyTemplateNewLineHeight(item: any) { + // the height is calculated by the template row count (how many line of items does the template view have) + const rowCount = this._addonOptions.panelRows; + + // calculate padding requirements based on detail-content.. + // ie. worst-case: create an invisible dom node now & find it's height. + const lineHeight = 13; // we know cuz we wrote the custom css init ;) + item[this._keyPrefix + 'sizePadding'] = Math.ceil(((rowCount * 2) * lineHeight) / this.gridOptions.rowHeight!); + item[this._keyPrefix + 'height'] = (item[this._keyPrefix + 'sizePadding'] * this.gridOptions.rowHeight!); + const idxParent = this.dataView.getIdxById(item[this._dataViewIdProperty]); + for (let idx = 1; idx <= item[this._keyPrefix + 'sizePadding']; idx++) { + this.dataView.insertItem((idxParent || 0) + idx, this.getPaddingItem(item, idx)); + } + } + + /** Find a value in an array and return the index when (or -1 when not found) */ + protected arrayFindIndex(sourceArray: any[], value: any) { + if (sourceArray) { + for (let i = 0; i < sourceArray.length; i++) { + if (sourceArray[i] === value) { + return i; + } + } + } + return -1; + } + + protected calculateOutOfRangeViews() { + if (this._grid) { + let scrollDir: any; + const renderedRange = this._grid.getRenderedRange(); + // Only check if we have expanded rows + if (this._expandedRows.length > 0) { + // Assume scroll direction is down by default. + scrollDir = 'DOWN'; + if (this._lastRange) { + // Some scrolling isn't anything as the range is the same + if (this._lastRange.top === renderedRange.top && this._lastRange.bottom === renderedRange.bottom) { + return; + } + + // If our new top is smaller we are scrolling up + if (this._lastRange.top > renderedRange.top || + // Or we are at very top but our bottom is increasing + (this._lastRange.top === 0 && renderedRange.top === 0) && this._lastRange.bottom > renderedRange.bottom) { + scrollDir = 'UP'; + } + } + } + + this._expandedRows.forEach((row) => { + const rowIndex = this.dataView.getRowById(row[this._dataViewIdProperty]) as number; + const rowPadding = row[`${this._keyPrefix}sizePadding`]; + const rowOutOfRange = this.arrayFindIndex(this._rowIdsOutOfViewport, row[this._dataViewIdProperty]) >= 0; + + if (scrollDir === 'UP') { + // save the view when asked + if (this._addonOptions.saveDetailViewOnScroll) { + // If the bottom item within buffer range is an expanded row save it. + if (rowIndex >= renderedRange.bottom - this._gridRowBuffer) { + this.saveDetailView(row); + } + } + + // If the row expanded area is within the buffer notify that it is back in range + if (rowOutOfRange && rowIndex - this._outsideRange < renderedRange.top && rowIndex >= renderedRange.top) { + this.notifyBackToViewportWhenDomExist(row, row[this._dataViewIdProperty]); + } else if (!rowOutOfRange && (rowIndex + rowPadding) > renderedRange.bottom) { + // if our first expanded row is about to go off the bottom + this.notifyOutOfViewport(row, row[this._dataViewIdProperty]); + } + } else if (scrollDir === 'DOWN') { + // save the view when asked + if (this._addonOptions.saveDetailViewOnScroll) { + // If the top item within buffer range is an expanded row save it. + if (rowIndex <= renderedRange.top + this._gridRowBuffer) { + this.saveDetailView(row); + } + } + + // If row index is i higher than bottom with some added value (To ignore top rows off view) and is with view and was our of range + if (rowOutOfRange && (rowIndex + rowPadding + this._outsideRange) > renderedRange.bottom && rowIndex < rowIndex + rowPadding) { + this.notifyBackToViewportWhenDomExist(row, row[this._dataViewIdProperty]); + } else if (!rowOutOfRange && rowIndex < renderedRange.top) { + // if our row is outside top of and the buffering zone but not in the array of outOfVisable range notify it + this.notifyOutOfViewport(row, row[this._dataViewIdProperty]); + } + } + }); + this._lastRange = renderedRange; + } + } + + protected calculateOutOfRangeViewsSimplerVersion() { + if (this._grid) { + const renderedRange = this._grid.getRenderedRange(); + + this._expandedRows.forEach((row) => { + const rowIndex = this.dataView.getRowById(row[this._dataViewIdProperty]) as number; + const isOutOfVisibility = this.checkIsRowOutOfViewportRange(rowIndex, renderedRange); + if (!isOutOfVisibility && this.arrayFindIndex(this._rowIdsOutOfViewport, row[this._dataViewIdProperty]) >= 0) { + this.notifyBackToViewportWhenDomExist(row, row[this._dataViewIdProperty]); + } else if (isOutOfVisibility) { + this.notifyOutOfViewport(row, row[this._dataViewIdProperty]); + } + }); + } + } + + protected checkExpandableOverride(row: number, dataContext: any, grid: SlickGrid) { + if (typeof this._expandableOverride === 'function') { + return this._expandableOverride(row, dataContext, grid); + } + return true; + } + + protected checkIsRowOutOfViewportRange(rowIndex: number, renderedRange: any) { + if (Math.abs(renderedRange.bottom - this._gridRowBuffer - rowIndex) > this._visibleRenderedCellCount * 2) { + return true; + } + return false; + } + + /** Get the Row Detail padding (which are the rows dedicated to the detail panel) */ + protected getPaddingItem(parent: any, offset: any) { + const item: any = {}; + + for (const prop in this.dataView) { + if (prop) { + item[prop] = null; + } + } + item[this._dataViewIdProperty] = parent[this._dataViewIdProperty] + '.' + offset; + + // additional hidden padding metadata fields + item[this._keyPrefix + 'collapsed'] = true; + item[this._keyPrefix + 'isPadding'] = true; + item[this._keyPrefix + 'parent'] = parent; + item[this._keyPrefix + 'offset'] = offset; + + return item; + } + + /** The Formatter of the toggling icon of the Row Detail */ + protected detailSelectionFormatter(row: number, cell: number, value: any, columnDef: Column, dataContext: any, grid: SlickGrid): FormatterResultObject | string { + if (!this.checkExpandableOverride(row, dataContext, grid)) { + return ''; + } else { + if (dataContext[this._keyPrefix + 'collapsed'] === undefined) { + dataContext[this._keyPrefix + 'collapsed'] = true; + dataContext[this._keyPrefix + 'sizePadding'] = 0; // the required number of pading rows + dataContext[this._keyPrefix + 'height'] = 0; // the actual height in pixels of the detail field + dataContext[this._keyPrefix + 'isPadding'] = false; + dataContext[this._keyPrefix + 'parent'] = undefined; + dataContext[this._keyPrefix + 'offset'] = 0; + } + + if (dataContext[this._keyPrefix + 'isPadding']) { + // render nothing + } else if (dataContext[this._keyPrefix + 'collapsed']) { + let collapsedClasses = this._addonOptions.cssClass + ' expand '; + if (this._addonOptions.collapsedClass) { + collapsedClasses += this._addonOptions.collapsedClass; + } + return '
'; + } else { + const html = []; + const rowHeight = this.gridOptions.rowHeight; + let outterHeight = dataContext[this._keyPrefix + 'sizePadding'] * this.gridOptions.rowHeight!; + + if ((this._addonOptions as any).maxRows !== null && dataContext[this._keyPrefix + 'sizePadding'] > (this._addonOptions as any).maxRows) { + outterHeight = (this._addonOptions as any).maxRows * rowHeight!; + dataContext[this._keyPrefix + 'sizePadding'] = (this._addonOptions as any).maxRows; + } + + // V313HAX: + // putting in an extra closing div after the closing toggle div and ommiting a + // final closing div for the detail ctr div causes the slickgrid renderer to + // insert our detail div as a new column ;) ~since it wraps whatever we provide + // in a generic div column container. so our detail becomes a child directly of + // the row not the cell. nice =) ~no need to apply a css change to the parent + // slick-cell to escape the cell overflow clipping. + + // sneaky extra inserted here-----------------v + let expandedClasses = this._addonOptions.cssClass + ' collapse '; + if (this._addonOptions.expandedClass) { + expandedClasses += this._addonOptions.expandedClass; + } + html.push('
'); + html.push('
'); // shift detail below 1st row + html.push('
'); // sub ctr for custom styling + html.push('
', dataContext[this._keyPrefix + 'detailContent'], '
'); + // omit a final closing detail container
that would come next + + return html.join(''); + } + } + return ''; + } + + /** Handle mouse click event */ + protected handleClick(e: any, args: any) { + const dataContext = this._grid.getDataItem(args.row); + if (!this.checkExpandableOverride(args.row, dataContext, this._grid)) { + return; + } + + // clicking on a row select checkbox + if (this._addonOptions.useRowClick || this._grid.getColumns()[args.cell]['id'] === (this._addonOptions as any).columnId && e.target.classList.contains(this._addonOptions.cssClass)) { + // if editing, try to commit + if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) { + e.preventDefault(); + e.stopImmediatePropagation(); + return; + } + + // trigger an event before toggling + this.onBeforeRowDetailToggle.notify({ + 'grid': this._grid, + 'item': dataContext + }); + + this.toggleRowSelection(args.row, dataContext); + + // trigger an event after toggling + this.onAfterRowDetailToggle.notify({ + grid: this._grid, + item: dataContext, + expandedRows: this._expandedRows, + }); + + e.stopPropagation(); + e.stopImmediatePropagation(); + } + } + + protected handleScroll() { + if (this._addonOptions.useSimpleViewportCalc) { + this.calculateOutOfRangeViewsSimplerVersion(); + } else { + this.calculateOutOfRangeViews(); + } + } + + protected notifyOutOfViewport(item: any, rowId: any) { + const rowIndex = item.rowIndex || this.dataView.getRowById(item[this._dataViewIdProperty]); + + this.onRowOutOfViewportRange.notify({ + grid: this._grid, + item, + rowId, + rowIndex, + expandedRows: this._expandedRows, + rowIdsOutOfViewport: this.syncOutOfViewportArray(rowId, true) + }); + } + + protected notifyBackToViewportWhenDomExist(item: any, rowId: any) { + const rowIndex = item.rowIndex || this.dataView.getRowById(item[this._dataViewIdProperty]); + + setTimeout(() => { + // make sure View Row DOM Element really exist before notifying that it's a row that is visible again + if (document.querySelector('.cellDetailView_' + item[this._dataViewIdProperty])) { + this.onRowBackToViewportRange.notify({ + grid: this._grid, + item, + rowId, + rowIndex, + expandedRows: this._expandedRows, + rowIdsOutOfViewport: this.syncOutOfViewportArray(rowId, false) + }); + } + }, 100); + } + + protected syncOutOfViewportArray(rowId: any, isAdding: boolean) { + const arrayRowIndex = this.arrayFindIndex(this._rowIdsOutOfViewport, rowId); + + if (isAdding && arrayRowIndex < 0) { + this._rowIdsOutOfViewport.push(rowId); + } else if (!isAdding && arrayRowIndex >= 0) { + this._rowIdsOutOfViewport.splice(arrayRowIndex, 1); + } + return this._rowIdsOutOfViewport; + } + + protected toggleRowSelection(rowNumber: number, dataContext: any) { + if (!this.checkExpandableOverride(rowNumber, dataContext, this._grid)) { + return; + } + + this.dataView.beginUpdate(); + this.handleAccordionShowHide(dataContext); + this.dataView.endUpdate(); + } + + +} diff --git a/packages/row-detail-view-plugin/tsconfig.bundle.json b/packages/row-detail-view-plugin/tsconfig.bundle.json new file mode 100644 index 000000000..7c6f72a9a --- /dev/null +++ b/packages/row-detail-view-plugin/tsconfig.bundle.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.bundle.json", + "compilerOptions": { + "typeRoots": [ + "../typings", + "../../node_modules/@types" + ], + "outDir": "dist/commonjs" + }, + "include": [ + "../typings", + "**/*" + ] +} \ No newline at end of file diff --git a/packages/row-detail-view-plugin/tsconfig.json b/packages/row-detail-view-plugin/tsconfig.json new file mode 100644 index 000000000..277dfa292 --- /dev/null +++ b/packages/row-detail-view-plugin/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../tsconfig.base.json", + "compileOnSave": false, + "compilerOptions": { + "rootDir": "src", + "declarationDir": "dist/esm", + "outDir": "dist/esm", + "typeRoots": [ + "typings" + ] + }, + "exclude": [ + "dist", + "node_modules", + "**/*.spec.ts" + ], + "filesGlob": [ + "./src/**/*.ts" + ], + "include": [ + "src/**/*.ts", + "typings/**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts index 46997f151..f5322da69 100644 --- a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts +++ b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts @@ -29,15 +29,11 @@ import { SlickGroupItemMetadataProvider, SlickNamespace, Subscription, - - // extensions - ExtensionUtility, - RowDetailViewExtension, - // services BackendUtilityService, CollectionService, ExtensionService, + ExtensionUtility, FilterFactory, FilterService, GridEventService, @@ -345,17 +341,13 @@ export class SlickVanillaGridBundle { this.treeDataService = services?.treeDataService ?? new TreeDataService(this._eventPubSubService, this.sharedService, this.sortService); this.paginationService = services?.paginationService ?? new PaginationService(this._eventPubSubService, this.sharedService, this.backendUtilityService); - // extensions - const rowDetailViewExtension = new RowDetailViewExtension(); - this.extensionService = services?.extensionService ?? new ExtensionService( this.extensionUtility, this.filterService, this._eventPubSubService, + this.sharedService, this.sortService, this.treeDataService, - rowDetailViewExtension, - this.sharedService, this.translaterService, );