UNPKG

angular-slickgrid

Version:

Slickgrid components made available in Angular

1 lines 190 kB
{"version":3,"file":"angular-slickgrid.mjs","sources":["../../src/app/modules/angular-slickgrid/services/angularUtil.service.ts","../../src/app/modules/angular-slickgrid/services/container.service.ts","../../src/app/modules/angular-slickgrid/services/translater.service.ts","../../src/app/modules/angular-slickgrid/services/utilities.ts","../../src/app/modules/angular-slickgrid/extensions/slickRowDetailView.ts","../../src/app/modules/angular-slickgrid/global-grid-options.ts","../../src/app/modules/angular-slickgrid/slickgrid-config.ts","../../src/app/modules/angular-slickgrid/constants.ts","../../src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts","../../src/app/modules/angular-slickgrid/components/angular-slickgrid.component.html","../../src/app/modules/angular-slickgrid/modules/angular-slickgrid.module.ts","../../src/angular-slickgrid.ts"],"sourcesContent":["import { Injectable, ViewContainerRef } from '@angular/core';\nimport type { EmbeddedViewRef, EnvironmentInjector, Injector, NgModuleRef, Type } from '@angular/core';\n\nimport type { AngularComponentOutput } from '../models/angularComponentOutput.interface';\n\ninterface CreateComponentOption {\n index?: number;\n injector?: Injector;\n ngModuleRef?: NgModuleRef<unknown>;\n environmentInjector?: EnvironmentInjector | NgModuleRef<unknown>;\n projectableNodes?: Node[][];\n sanitizer?: (dirtyHtml: string) => string;\n}\n\n@Injectable()\nexport class AngularUtilService {\n constructor(private vcr: ViewContainerRef) {}\n\n createInteractiveAngularComponent<C>(\n component: Type<C>,\n targetElement: Element,\n data?: any,\n createCompOptions?: CreateComponentOption\n ): AngularComponentOutput {\n // Create a component reference from the component\n const componentRef = this.vcr.createComponent(component, createCompOptions);\n\n // user could provide data to assign to the component instance\n if (componentRef?.instance && data) {\n Object.assign(componentRef.instance as any, data);\n }\n\n // Get DOM element from component\n let domElem: HTMLElement | null = null;\n const viewRef = componentRef.hostView as EmbeddedViewRef<any>;\n\n if (viewRef && Array.isArray(viewRef.rootNodes) && viewRef.rootNodes[0]) {\n domElem = viewRef.rootNodes[0] as HTMLElement;\n\n // when user provides the DOM element target, we will move the dynamic component into that target (aka portal-ing it)\n if (targetElement && domElem) {\n targetElement.replaceChildren(componentRef.location.nativeElement);\n }\n }\n\n return { componentRef, domElement: domElem as HTMLElement };\n }\n\n /**\n * Dynamically create an Angular component, user could also provide optional arguments for target, data & createComponent options\n * @param {Component} component\n * @param {HTMLElement} [targetElement]\n * @param {*} [data]\n * @param {CreateComponentOption} [createCompOptions]\n * @returns\n */\n createAngularComponent<C>(\n component: Type<C>,\n targetElement?: Element,\n data?: any,\n createCompOptions?: CreateComponentOption\n ): AngularComponentOutput {\n // Create a component reference from the component\n const componentRef = this.vcr.createComponent(component, createCompOptions);\n\n // user could provide data to assign to the component instance\n if (componentRef?.instance && data) {\n Object.assign(componentRef.instance as any, data);\n\n // NOTE: detectChanges() MUST be done BEFORE returning the DOM element in the next step,\n // because if we do it only after returning the rootNodes (domElement) then it won't have the instance data yet\n // and we would have to wait an extra cycle to see the result, this basically helps with Example22\n componentRef.changeDetectorRef.detectChanges();\n }\n\n // Get DOM element from component\n let domElem: HTMLElement | null = null;\n const viewRef = componentRef.hostView as EmbeddedViewRef<any>;\n\n // get DOM element from the new dynamic Component, make sure this is read after any data and detectChanges()\n if (viewRef && Array.isArray(viewRef.rootNodes) && viewRef.rootNodes[0]) {\n domElem = viewRef.rootNodes[0] as HTMLElement;\n\n // when user provides the DOM element target, we will read the new Component html and use it to replace the target html\n if (targetElement && domElem) {\n targetElement.innerHTML =\n typeof createCompOptions?.sanitizer === 'function'\n ? createCompOptions.sanitizer(domElem.innerHTML || '')\n : domElem.innerHTML;\n }\n }\n\n return { componentRef, domElement: domElem as HTMLElement };\n }\n\n /**\n * Dynamically create an Angular component and append it to the DOM unless a target element is provided,\n * user could also provide other optional arguments for data & createComponent options.\n * @param {Component} component\n * @param {HTMLElement} [targetElement]\n * @param {*} [data]\n * @param {CreateComponentOption} [createCompOptions]\n * @returns\n */\n createAngularComponentAppendToDom<C>(\n component: Type<C>,\n targetElement?: Element,\n data?: any,\n createCompOptions?: CreateComponentOption\n ): AngularComponentOutput {\n const componentOutput = this.createAngularComponent(component, targetElement, data, createCompOptions);\n\n // Append DOM element to the HTML element specified\n if (targetElement?.replaceChildren) {\n targetElement.replaceChildren(componentOutput.domElement);\n } else {\n document.body.appendChild(componentOutput.domElement); // when no target provided, we'll simply add it to the HTML Body\n }\n\n return componentOutput;\n }\n}\n","import { Injectable } from '@angular/core';\nimport { type ContainerInstance, ContainerService as UniversalContainerService } from '@slickgrid-universal/common';\n\n@Injectable()\nexport class ContainerService implements UniversalContainerService {\n dependencies: ContainerInstance[] = [];\n\n get<T = any>(key: string): T | null {\n const dependency = this.dependencies.find((dep) => dep.key === key);\n if (dependency?.instance) {\n return dependency.instance;\n }\n return null;\n }\n\n dispose() {\n this.dependencies = [];\n }\n\n registerInstance(key: string, instance: any) {\n const dependency = this.dependencies.some((dep) => dep.key === key);\n if (!dependency) {\n this.dependencies.push({ key, instance });\n }\n }\n}\n","import { Injectable, Optional } from '@angular/core';\nimport { TranslateService } from '@ngx-translate/core';\nimport { TranslaterService as UniversalTranslateService } from '@slickgrid-universal/common';\n\n/**\n * This is a Translate Service Wrapper for Slickgrid-Universal monorepo lib to work properly,\n * it must implement Slickgrid-Universal TranslaterService interface to work properly\n */\n@Injectable()\nexport class TranslaterService implements UniversalTranslateService {\n constructor(@Optional() private readonly translateService: TranslateService) {}\n\n /**\n * Method to return the current language used by the App\n * @return {string} current language\n */\n getCurrentLanguage(): string {\n return this.translateService?.currentLang ?? '';\n }\n\n /**\n * Method to set the language to use in the App and Translate Service\n * @param {string} language\n * @return {Promise} output\n */\n async use(newLang: string): Promise<any> {\n return this.translateService?.use?.(newLang);\n }\n\n /**\n * Method which receives a translation key and returns the translated value assigned to that key\n * @param {string} translation key\n * @return {string} translated value\n */\n translate(translationKey: string): string {\n return this.translateService?.instant?.(translationKey || ' ') as string;\n }\n}\n","/**\n * Unsubscribe all Observables Subscriptions\n * It will return an empty array if it all went well\n * @param subscriptions\n */\nexport function unsubscribeAllObservables(subscriptions: Array<{ unsubscribe: () => void }>): any[] {\n if (Array.isArray(subscriptions)) {\n let subscription = subscriptions.pop();\n while (subscription) {\n if (typeof subscription.unsubscribe === 'function') {\n subscription.unsubscribe();\n }\n subscription = subscriptions.pop();\n }\n }\n\n // TODO: deprecated, remove the return type in next major version\n return subscriptions;\n}\n","import type { ApplicationRef, ComponentRef, Type, ViewContainerRef } from '@angular/core';\nimport type {\n EventSubscription,\n OnBeforeRowDetailToggleArgs,\n OnRowBackToViewportRangeArgs,\n RxJsFacade,\n SlickGrid,\n} from '@slickgrid-universal/common';\nimport {\n addToArrayWhenNotExists,\n castObservableToPromise,\n createDomElement,\n SlickEventData,\n SlickRowSelectionModel,\n unsubscribeAll,\n} from '@slickgrid-universal/common';\nimport type { EventPubSubService } from '@slickgrid-universal/event-pub-sub';\nimport { SlickRowDetailView as UniversalSlickRowDetailView } from '@slickgrid-universal/row-detail-view-plugin';\nimport { Observable, type Subject } from 'rxjs';\n\nimport type { GridOption, RowDetailView } from '../models/index';\nimport type { AngularUtilService } from '../services/angularUtil.service';\n\nconst ROW_DETAIL_CONTAINER_PREFIX = 'container_';\nconst PRELOAD_CONTAINER_PREFIX = 'container_loading';\n\nexport interface CreatedView {\n id: string | number;\n dataContext: any;\n componentRef?: ComponentRef<any>;\n rendered?: boolean;\n}\n\nexport class SlickRowDetailView extends UniversalSlickRowDetailView {\n rowDetailContainer!: ViewContainerRef;\n protected _preloadComponent: Type<object> | undefined;\n protected _preloadCompRef?: ComponentRef<any>;\n protected _views: CreatedView[] = [];\n protected _viewComponent!: Type<object>;\n protected _subscriptions: EventSubscription[] = [];\n protected _userProcessFn!: (item: any) => Promise<any> | Observable<any> | Subject<any>;\n\n constructor(\n protected readonly angularUtilService: AngularUtilService,\n protected readonly appRef: ApplicationRef,\n protected readonly eventPubSubService: EventPubSubService,\n protected readonly gridContainerElement: HTMLDivElement,\n protected rxjs?: RxJsFacade\n ) {\n super(eventPubSubService);\n }\n\n get addonOptions() {\n return this.getOptions();\n }\n\n protected get datasetIdPropName(): string {\n return this.gridOptions.datasetIdPropertyName || 'id';\n }\n\n /** Getter for the Grid Options pulled through the Grid Object */\n get gridOptions(): GridOption {\n return (this._grid?.getOptions() || {}) as GridOption;\n }\n\n get rowDetailViewOptions(): RowDetailView | undefined {\n return this.gridOptions.rowDetailView;\n }\n\n addRxJsResource(rxjs: RxJsFacade) {\n this.rxjs = rxjs;\n }\n\n /** Dispose of the RowDetailView Extension */\n dispose() {\n this.disposeAllViewComponents();\n this._subscriptions = unsubscribeAll(this._subscriptions); // also unsubscribe all RxJS subscriptions\n super.dispose();\n }\n\n /** Dispose of all the opened Row Detail Panels Angular View Components */\n disposeAllViewComponents() {\n do {\n const view = this._views.pop();\n if (view) {\n this.disposeView(view);\n }\n } while (this._views.length > 0);\n }\n\n /** Get the instance of the SlickGrid addon (control or plugin). */\n getAddonInstance(): SlickRowDetailView | null {\n return this;\n }\n\n init(grid: SlickGrid) {\n this._grid = grid;\n super.init(this._grid);\n this.register(grid?.getSelectionModel() as SlickRowSelectionModel);\n }\n\n /**\n * Create the plugin before the Grid creation, else it will behave oddly.\n * Mostly because the column definitions might change after the grid creation\n */\n register(rowSelectionPlugin?: SlickRowSelectionModel) {\n if (typeof this.gridOptions.rowDetailView?.process === 'function') {\n // we need to keep the user \"process\" method and replace it with our own execution method\n // we do this because when we get the item detail, we need to call \"onAsyncResponse.notify\" for the plugin to work\n this._userProcessFn = this.gridOptions.rowDetailView.process as (item: any) => Promise<any>; // keep user's process method\n this.addonOptions.process = (item) => this.onProcessing(item); // replace process method & run our internal one\n } else {\n throw new Error(\n '[Angular-Slickgrid] You need to provide a \"process\" function for the Row Detail Extension to work properly'\n );\n }\n\n if (this._grid && this.gridOptions?.rowDetailView) {\n // load the Preload & RowDetail Templates (could be straight HTML or Angular View/ViewModel)\n // when those are Angular View/ViewModel, we need to create View Component & provide the html containers to the Plugin (preTemplate/postTemplate methods)\n if (!this.gridOptions.rowDetailView.preTemplate) {\n this._preloadComponent = this.gridOptions?.rowDetailView?.preloadComponent;\n this.addonOptions.preTemplate = () => createDomElement('div', { className: `${PRELOAD_CONTAINER_PREFIX}` });\n }\n if (!this.gridOptions.rowDetailView.postTemplate) {\n this._viewComponent = this.gridOptions?.rowDetailView?.viewComponent;\n this.addonOptions.postTemplate = (itemDetail: any) =>\n createDomElement('div', { className: `${ROW_DETAIL_CONTAINER_PREFIX}${itemDetail[this.datasetIdPropName]}` });\n }\n\n // this also requires the Row Selection Model to be registered as well\n if (!rowSelectionPlugin || !this._grid.getSelectionModel()) {\n rowSelectionPlugin = new SlickRowSelectionModel(this.gridOptions.rowSelectionOptions || { selectActiveRow: true });\n this._grid.setSelectionModel(rowSelectionPlugin);\n }\n\n // hook all events\n if (this._grid && this.rowDetailViewOptions) {\n if (this.rowDetailViewOptions.onExtensionRegistered) {\n this.rowDetailViewOptions.onExtensionRegistered(this);\n }\n\n this.eventHandler.subscribe(this.onAsyncResponse, (event, args) => {\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncResponse === 'function') {\n this.rowDetailViewOptions.onAsyncResponse(event, args);\n }\n });\n\n this.eventHandler.subscribe(this.onAsyncEndUpdate, (e, args) => {\n // destroy preload if exists\n this._preloadCompRef?.destroy();\n\n // triggers after backend called \"onAsyncResponse.notify()\"\n this.renderViewModel(args?.item);\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAsyncEndUpdate === 'function') {\n this.rowDetailViewOptions.onAsyncEndUpdate(e, args);\n }\n });\n\n this.eventHandler.subscribe(\n this.onAfterRowDetailToggle,\n (e: any, args: { grid: SlickGrid; item: any; expandedRows: Array<number | string> }) => {\n // display preload template & re-render all the other Detail Views after toggling\n // the preload View will eventually go away once the data gets loaded after the \"onAsyncEndUpdate\" event\n this.renderPreloadView();\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onAfterRowDetailToggle === 'function') {\n this.rowDetailViewOptions.onAfterRowDetailToggle(e, args);\n }\n }\n );\n\n this.eventHandler.subscribe(this.onBeforeRowDetailToggle, (e, args) => {\n // before toggling row detail, we need to create View Component if it doesn't exist\n this.handleOnBeforeRowDetailToggle(e, args);\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onBeforeRowDetailToggle === 'function') {\n return this.rowDetailViewOptions.onBeforeRowDetailToggle(e, args);\n }\n return true;\n });\n\n this.eventHandler.subscribe(this.onRowBackToViewportRange, (e, args) => {\n // when row is back to viewport range, we will re-render the View Component(s)\n this.handleOnRowBackToViewportRange(e, args);\n\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowBackToViewportRange === 'function') {\n this.rowDetailViewOptions.onRowBackToViewportRange(e, args);\n }\n });\n\n this._eventHandler.subscribe(this.onBeforeRowOutOfViewportRange, (event, args) => {\n if (typeof this.rowDetailViewOptions?.onBeforeRowOutOfViewportRange === 'function') {\n this.rowDetailViewOptions.onBeforeRowOutOfViewportRange(event, args);\n }\n this.disposeViewByItem(args.item);\n });\n\n this.eventHandler.subscribe(this.onRowOutOfViewportRange, (e, args) => {\n if (this.rowDetailViewOptions && typeof this.rowDetailViewOptions.onRowOutOfViewportRange === 'function') {\n this.rowDetailViewOptions.onRowOutOfViewportRange(e, args);\n }\n });\n\n // --\n // hook some events needed by the Plugin itself\n\n // we need to redraw the open detail views if we change column position (column reorder)\n this.eventHandler.subscribe(this._grid.onColumnsReordered, this.redrawAllViewComponents.bind(this, false));\n\n // on row selection changed, we also need to redraw\n if (this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector) {\n this.eventHandler.subscribe(this._grid.onSelectedRowsChanged, this.redrawAllViewComponents.bind(this, false));\n }\n\n // on sort, all row detail are collapsed so we can dispose of all the Views as well\n this.eventHandler.subscribe(this._grid.onSort, this.disposeAllViewComponents.bind(this));\n\n // redraw all Views whenever certain events are triggered\n this._subscriptions.push(\n this.eventPubSubService?.subscribe(\n ['onFilterChanged', 'onGridMenuColumnsChanged', 'onColumnPickerColumnsChanged'],\n this.redrawAllViewComponents.bind(this, false)\n ),\n this.eventPubSubService?.subscribe(['onGridMenuClearAllFilters', 'onGridMenuClearAllSorting'], () =>\n window.setTimeout(() => this.redrawAllViewComponents())\n )\n );\n }\n }\n return this;\n }\n\n /** Redraw (re-render) all the expanded row detail View Components */\n redrawAllViewComponents(forceRedraw = false) {\n this.resetRenderedRows();\n this._views.forEach((view) => {\n if (!view.rendered || forceRedraw) {\n forceRedraw && view.componentRef?.destroy();\n this.redrawViewComponent(view);\n }\n });\n }\n\n /** Redraw the necessary View Component */\n redrawViewComponent(createdView: CreatedView) {\n const containerElement = this.gridContainerElement.querySelector(`.${ROW_DETAIL_CONTAINER_PREFIX}${createdView.id}`);\n if (containerElement) {\n this.renderViewModel(createdView.dataContext);\n }\n }\n\n /** Render (or re-render) the View Component (Row Detail) */\n renderPreloadView() {\n const containerElement = this.gridContainerElement.querySelector(`.${PRELOAD_CONTAINER_PREFIX}`);\n if (this._preloadComponent && containerElement) {\n const preloadComp = this.angularUtilService.createAngularComponentAppendToDom(\n this._preloadComponent,\n containerElement,\n {},\n { sanitizer: this._grid.sanitizeHtmlString }\n );\n this._preloadCompRef = preloadComp.componentRef;\n }\n }\n\n /** Render (or re-render) the View Component (Row Detail) */\n renderViewModel(item: any): CreatedView | undefined {\n const containerElement = this.gridContainerElement.querySelector(\n `.${ROW_DETAIL_CONTAINER_PREFIX}${item[this.datasetIdPropName]}`\n );\n if (this._viewComponent && containerElement) {\n // render row detail\n const componentOutput = this.angularUtilService.createAngularComponentAppendToDom(\n this._viewComponent,\n containerElement,\n {\n model: item,\n addon: this,\n grid: this._grid,\n dataView: this.dataView,\n parent: this.rowDetailViewOptions?.parent,\n },\n {\n sanitizer: this._grid.sanitizeHtmlString,\n }\n );\n\n if (componentOutput?.componentRef) {\n const viewObj = this._views.find((obj) => obj.id === item[this.datasetIdPropName]);\n if (viewObj) {\n viewObj.componentRef = componentOutput.componentRef;\n viewObj.rendered = true;\n }\n return viewObj;\n }\n }\n return undefined;\n }\n\n // --\n // protected functions\n // ------------------\n\n protected disposeViewByItem(item: any, removeFromArray = false): void {\n const foundViewIndex = this._views.findIndex((view: CreatedView) => view.id === item[this.datasetIdPropName]);\n if (foundViewIndex >= 0) {\n this.disposeView(this._views[foundViewIndex]);\n if (removeFromArray) {\n this._views.splice(foundViewIndex, 1);\n }\n }\n }\n\n protected disposeView(expandedView: CreatedView): CreatedView | void {\n expandedView.rendered = false;\n const compRef = expandedView?.componentRef;\n if (compRef) {\n this.appRef.detachView(compRef.hostView);\n if (typeof compRef?.destroy === 'function') {\n compRef.destroy();\n }\n return expandedView;\n }\n }\n\n /**\n * notify the onAsyncResponse with the \"args.item\" (required property)\n * the plugin will then use item to populate the row detail panel with the \"postTemplate\"\n * @param item\n */\n protected notifyTemplate(item: any) {\n this.onAsyncResponse.notify({ item, itemDetail: item }, new SlickEventData(), this);\n }\n\n /**\n * On Processing, we will notify the plugin with the new item detail once backend server call completes\n * @param item\n */\n protected async onProcessing(item: any) {\n if (item && typeof this._userProcessFn === 'function') {\n let awaitedItemDetail: any;\n const userProcessFn = this._userProcessFn(item);\n\n // wait for the \"userProcessFn\", once resolved we will save it into the \"collection\"\n const response: any | any[] = await userProcessFn;\n\n if (this.datasetIdPropName in response) {\n awaitedItemDetail = response; // from Promise\n } else if ((response && response instanceof Observable) || response instanceof Promise) {\n awaitedItemDetail = await castObservableToPromise(this.rxjs as RxJsFacade, response); // from Angular-http-client\n }\n\n if (!awaitedItemDetail || !(this.datasetIdPropName in awaitedItemDetail)) {\n throw new Error(\n '[Angular-Slickgrid] could not process the Row Detail, you must make sure that your \"process\" callback ' +\n `returns an item object that has an \"${this.datasetIdPropName}\" property`\n );\n }\n\n // notify the plugin with the new item details\n this.notifyTemplate(awaitedItemDetail || {});\n }\n }\n\n /**\n * Just before the row get expanded or collapsed we will do the following\n * First determine if the row is expanding or collapsing,\n * if it's expanding we will add it to our View Components reference array if we don't already have it\n * or if it's collapsing we will remove it from our View Components reference array\n */\n protected handleOnBeforeRowDetailToggle(_e: SlickEventData<OnBeforeRowDetailToggleArgs>, args: { grid: SlickGrid; item: any }) {\n // expanding\n if (args?.item?.__collapsed) {\n // expanding row detail\n const viewInfo: CreatedView = {\n id: args.item[this.datasetIdPropName],\n dataContext: args.item,\n rendered: false,\n };\n addToArrayWhenNotExists(this._views, viewInfo, this.datasetIdPropName);\n } else {\n // collapsing, so dispose of the View/Component\n this.disposeViewByItem(args.item, true);\n }\n }\n\n /** When Row comes back to Viewport Range, we need to redraw the View */\n protected handleOnRowBackToViewportRange(_e: SlickEventData<OnRowBackToViewportRangeArgs>, args: OnRowBackToViewportRangeArgs) {\n const viewModel = this._views.find((x) => x.id === args.rowId);\n if (viewModel && !viewModel.rendered) {\n this.redrawViewComponent(viewModel);\n }\n }\n}\n","import {\n type Column,\n DelimiterType,\n EventNamingStyle,\n FileType,\n Filters,\n type GridOption as GridOptionUniversal,\n OperatorType,\n type TreeDataOption,\n} from '@slickgrid-universal/common';\nimport type { GridOption, RowDetailView } from './models/index';\n\n/** Global Grid Options Defaults */\nexport const GlobalGridOptions: Partial<GridOption> = {\n alwaysShowVerticalScroll: true,\n autoEdit: false,\n asyncEditorLoading: false,\n autoFitColumnsOnFirstLoad: true,\n autoResize: {\n applyResizeToContainer: true,\n autoHeight: true,\n autoHeightRecalcRow: 100,\n calculateAvailableSizeBy: 'window',\n bottomPadding: 20,\n minHeight: 250,\n minWidth: 300,\n rightPadding: 0,\n },\n cellHighlightCssClass: 'slick-cell-modified',\n checkboxSelector: {\n cssClass: 'slick-cell-checkboxsel',\n },\n columnPicker: {\n hideForceFitButton: false,\n hideSyncResizeButton: true,\n headerColumnValueExtractor: pickerHeaderColumnValueExtractor,\n },\n cellMenu: {\n autoAdjustDrop: true,\n autoAlignSide: true,\n hideCloseButton: true,\n hideCommandSection: false,\n hideOptionSection: false,\n },\n contextMenu: {\n autoAdjustDrop: true,\n autoAlignSide: true,\n hideCloseButton: true,\n hideClearAllGrouping: false,\n hideCollapseAllGroups: false,\n hideCommandSection: false,\n hideCopyCellValueCommand: false,\n hideExpandAllGroups: false,\n hideExportCsvCommand: false,\n hideExportExcelCommand: false,\n hideExportTextDelimitedCommand: true,\n hideMenuOnScroll: true,\n hideOptionSection: false,\n iconCollapseAllGroupsCommand: 'mdi mdi-arrow-collapse',\n iconExpandAllGroupsCommand: 'mdi mdi-arrow-expand',\n iconClearGroupingCommand: 'mdi mdi-close',\n iconCopyCellValueCommand: 'mdi mdi-content-copy',\n iconExportCsvCommand: 'mdi mdi-download',\n iconExportExcelCommand: 'mdi mdi-file-excel-outline',\n iconExportTextDelimitedCommand: 'mdi mdi-download',\n showBulletWhenIconMissing: true,\n subItemChevronClass: 'mdi mdi-chevron-down mdi-rotate-270',\n },\n customFooterOptions: {\n dateFormat: 'YYYY-MM-DD, hh:mm a',\n hideRowSelectionCount: false,\n hideTotalItemCount: false,\n hideLastUpdateTimestamp: true,\n footerHeight: 25,\n leftContainerClass: 'col-xs-12 col-sm-5',\n rightContainerClass: 'col-xs-6 col-sm-7',\n metricSeparator: '|',\n metricTexts: {\n items: 'items',\n itemsKey: 'ITEMS',\n itemsSelected: 'items selected',\n itemsSelectedKey: 'ITEMS_SELECTED',\n of: 'of',\n ofKey: 'OF',\n },\n },\n dataView: {\n // when enabled, this will preserve the row selection even after filtering/sorting/grouping\n syncGridSelection: {\n preserveHidden: false,\n preserveHiddenOnSelectionChange: true,\n },\n syncGridSelectionWithBackendService: false, // but disable it when using backend services\n },\n datasetIdPropertyName: 'id',\n defaultFilter: Filters.input,\n defaultBackendServiceFilterTypingDebounce: 500,\n defaultColumnSortFieldId: 'id',\n defaultFilterPlaceholder: '🔎︎',\n defaultFilterRangeOperator: OperatorType.rangeInclusive,\n editable: false,\n enableAutoResize: true,\n enableAutoSizeColumns: true,\n enableCellNavigation: false,\n enableColumnPicker: true,\n enableColumnReorder: true,\n enableColumnResizeOnDoubleClick: true,\n enableContextMenu: true,\n enableExcelExport: false,\n enableTextExport: false, // CSV/Text with Tab Delimited\n enableFilterTrimWhiteSpace: false, // do we want to trim white spaces on all Filters?\n enableGridMenu: true,\n enableHeaderMenu: true,\n enableEmptyDataWarningMessage: true,\n emptyDataWarning: {\n className: 'slick-empty-data-warning',\n message: 'No data to display.',\n messageKey: 'EMPTY_DATA_WARNING_MESSAGE',\n hideFrozenLeftWarning: false,\n hideFrozenRightWarning: false,\n leftViewportMarginLeft: '40%',\n rightViewportMarginLeft: '40%',\n frozenLeftViewportMarginLeft: '0px',\n frozenRightViewportMarginLeft: '40%',\n },\n enableMouseHoverHighlightRow: true,\n enableSorting: true,\n enableTextSelectionOnCells: true,\n eventNamingStyle: EventNamingStyle.camelCase,\n explicitInitialization: true,\n excelExportOptions: {\n addGroupIndentation: true,\n exportWithFormatter: false,\n filename: 'export',\n format: FileType.xlsx,\n groupingColumnHeaderTitle: 'Group By',\n groupCollapsedSymbol: '⮞',\n groupExpandedSymbol: '⮟',\n groupingAggregatorRowText: '',\n sanitizeDataExport: false,\n },\n textExportOptions: {\n delimiter: DelimiterType.comma,\n exportWithFormatter: false,\n filename: 'export',\n format: FileType.csv,\n groupingColumnHeaderTitle: 'Group By',\n groupingAggregatorRowText: '',\n sanitizeDataExport: false,\n useUtf8WithBom: true,\n },\n filterTypingDebounce: 0,\n forceFitColumns: false,\n frozenHeaderWidthCalcDifferential: 0,\n gridMenu: {\n dropSide: 'left',\n commandLabels: {\n clearAllFiltersCommandKey: 'CLEAR_ALL_FILTERS',\n clearAllSortingCommandKey: 'CLEAR_ALL_SORTING',\n clearFrozenColumnsCommandKey: 'CLEAR_PINNING',\n exportCsvCommandKey: 'EXPORT_TO_CSV',\n exportExcelCommandKey: 'EXPORT_TO_EXCEL',\n exportTextDelimitedCommandKey: 'EXPORT_TO_TAB_DELIMITED',\n refreshDatasetCommandKey: 'REFRESH_DATASET',\n toggleDarkModeCommandKey: 'TOGGLE_DARK_MODE',\n toggleFilterCommandKey: 'TOGGLE_FILTER_ROW',\n togglePreHeaderCommandKey: 'TOGGLE_PRE_HEADER_ROW',\n },\n hideClearAllFiltersCommand: false,\n hideClearAllSortingCommand: false,\n hideClearFrozenColumnsCommand: true, // opt-in command\n hideExportCsvCommand: false,\n hideExportExcelCommand: false,\n hideExportTextDelimitedCommand: true,\n hideForceFitButton: false,\n hideRefreshDatasetCommand: false,\n hideSyncResizeButton: true,\n hideToggleDarkModeCommand: true,\n hideToggleFilterCommand: false,\n hideTogglePreHeaderCommand: false,\n iconCssClass: 'mdi mdi-menu',\n iconClearAllFiltersCommand: 'mdi mdi-filter-remove-outline',\n iconClearAllSortingCommand: 'mdi mdi-sort-variant-off',\n iconClearFrozenColumnsCommand: 'mdi mdi-pin-off-outline',\n iconExportCsvCommand: 'mdi mdi-download',\n iconExportExcelCommand: 'mdi mdi-file-excel-outline',\n iconExportTextDelimitedCommand: 'mdi mdi-download',\n iconRefreshDatasetCommand: 'mdi mdi-sync',\n iconToggleDarkModeCommand: 'mdi mdi-brightness-4',\n iconToggleFilterCommand: 'mdi mdi-flip-vertical',\n iconTogglePreHeaderCommand: 'mdi mdi-flip-vertical',\n menuWidth: 16,\n resizeOnShowHeaderRow: true,\n subItemChevronClass: 'mdi mdi-chevron-down mdi-rotate-270',\n headerColumnValueExtractor: pickerHeaderColumnValueExtractor,\n },\n headerMenu: {\n autoAlign: true,\n autoAlignOffset: 12,\n minWidth: 140,\n iconClearFilterCommand: 'mdi mdi-filter-remove-outline',\n iconClearSortCommand: 'mdi mdi-sort-variant-off',\n iconFreezeColumns: 'mdi mdi-pin-outline',\n iconSortAscCommand: 'mdi mdi-sort-ascending',\n iconSortDescCommand: 'mdi mdi-sort-descending',\n iconColumnHideCommand: 'mdi mdi-close',\n iconColumnResizeByContentCommand: 'mdi mdi-arrow-expand-horizontal',\n hideColumnResizeByContentCommand: false,\n hideColumnHideCommand: false,\n hideClearFilterCommand: false,\n hideClearSortCommand: false,\n hideFreezeColumnsCommand: true, // opt-in command\n hideSortCommands: false,\n subItemChevronClass: 'mdi mdi-chevron-down mdi-rotate-270',\n },\n headerRowHeight: 35,\n multiColumnSort: true,\n numberedMultiColumnSort: true,\n tristateMultiColumnSort: false,\n sortColNumberInSeparateSpan: true,\n suppressActiveCellChangeOnEdit: false,\n pagination: {\n pageSizes: [10, 15, 20, 25, 30, 40, 50, 75, 100],\n pageSize: 25,\n totalItems: 0,\n },\n // technically speaking the Row Detail requires the process & viewComponent but we'll ignore it just to set certain options\n rowDetailView: {\n collapseAllOnSort: true,\n cssClass: 'detail-view-toggle',\n panelRows: 1,\n keyPrefix: '__',\n useRowClick: false,\n saveDetailViewOnScroll: false,\n } as RowDetailView,\n rowHeight: 35,\n topPanelHeight: 35,\n preHeaderPanelWidth: '100%', // mostly useful for Draggable Grouping dropzone to take full width\n translationNamespaceSeparator: ':',\n resetFilterSearchValueAfterOnBeforeCancellation: true,\n resizeByContentOnlyOnFirstLoad: true,\n resizeByContentOptions: {\n alwaysRecalculateColumnWidth: false,\n cellCharWidthInPx: 7.8,\n cellPaddingWidthInPx: 14,\n defaultRatioForStringType: 0.88,\n formatterPaddingWidthInPx: 0,\n maxItemToInspectCellContentWidth: 1000,\n maxItemToInspectSingleColumnWidthByContent: 5000,\n widthToRemoveFromExceededWidthReadjustment: 50,\n },\n treeDataOptions: {\n exportIndentMarginLeft: 5,\n exportIndentationLeadingChar: '͏͏͏͏͏͏͏͏͏·',\n } as unknown as TreeDataOption,\n};\n\n/**\n * Value Extractor for both ColumnPicker & GridMenu Picker\n * when using Column Header Grouping, we'll prefix the column group title\n * else we'll simply return the column name title\n */\nfunction pickerHeaderColumnValueExtractor(column: Column, gridOptions?: GridOptionUniversal) {\n let colName = column?.columnPickerLabel ?? column?.name ?? '';\n if (colName instanceof HTMLElement || colName instanceof DocumentFragment) {\n colName = colName.textContent || '';\n }\n const headerGroup = column?.columnGroup || '';\n const columnGroupSeparator = gridOptions?.columnGroupSeparator ?? ' - ';\n if (headerGroup) {\n return headerGroup + columnGroupSeparator + colName;\n }\n return colName;\n}\n","import type { GridOption } from './models/gridOption.interface';\nimport { GlobalGridOptions } from './global-grid-options';\n\nexport class SlickgridConfig {\n options: Partial<GridOption>;\n\n constructor() {\n this.options = GlobalGridOptions;\n }\n}\n","import type { Locale } from '@slickgrid-universal/common';\n\nexport class Constants {\n // English Locale texts when using only 1 Locale instead of I18N\n static readonly locales: Locale = {\n TEXT_ALL_SELECTED: 'All Selected',\n TEXT_ALL_X_RECORDS_SELECTED: 'All {{x}} records selected',\n TEXT_APPLY_MASS_UPDATE: 'Apply Mass Update',\n TEXT_APPLY_TO_SELECTION: 'Update Selection',\n TEXT_CANCEL: 'Cancel',\n TEXT_CLEAR_ALL_FILTERS: 'Clear all Filters',\n TEXT_CLEAR_ALL_GROUPING: 'Clear all Grouping',\n TEXT_CLEAR_ALL_SORTING: 'Clear all Sorting',\n TEXT_CLEAR_PINNING: 'Unfreeze Columns/Rows',\n TEXT_CLONE: 'Clone',\n TEXT_COLLAPSE_ALL_GROUPS: 'Collapse all Groups',\n TEXT_CONTAINS: 'Contains',\n TEXT_COLUMNS: 'Columns',\n TEXT_COLUMN_RESIZE_BY_CONTENT: 'Resize by Content',\n TEXT_COMMANDS: 'Commands',\n TEXT_COPY: 'Copy',\n TEXT_EQUALS: 'Equals',\n TEXT_EQUAL_TO: 'Equal to',\n TEXT_ENDS_WITH: 'Ends With',\n TEXT_ERROR_EDITABLE_GRID_REQUIRED: 'Your grid must be editable in order to use the Composite Editor Modal.',\n TEXT_ERROR_ENABLE_CELL_NAVIGATION_REQUIRED:\n 'Composite Editor requires the flag \"enableCellNavigation\" to be set to True in your Grid Options.',\n TEXT_ERROR_NO_CHANGES_DETECTED: 'Sorry we could not detect any changes.',\n TEXT_ERROR_NO_EDITOR_FOUND: 'We could not find any Editor in your Column Definition.',\n TEXT_ERROR_NO_RECORD_FOUND: 'No records selected for edit or clone operation.',\n TEXT_ERROR_ROW_NOT_EDITABLE: 'Current row is not editable.',\n TEXT_ERROR_ROW_SELECTION_REQUIRED: 'You must select some rows before trying to apply new value(s).',\n TEXT_EXPAND_ALL_GROUPS: 'Expand all Groups',\n TEXT_EXPORT_TO_CSV: 'Export in CSV format',\n TEXT_EXPORT_TO_TEXT_FORMAT: 'Export in Text format (Tab delimited)',\n TEXT_EXPORT_TO_EXCEL: 'Export to Excel',\n TEXT_EXPORT_TO_TAB_DELIMITED: 'Export in Text format (Tab delimited)',\n TEXT_FORCE_FIT_COLUMNS: 'Force fit columns',\n TEXT_FREEZE_COLUMNS: 'Freeze Columns',\n TEXT_GREATER_THAN: 'Greater than',\n TEXT_GREATER_THAN_OR_EQUAL_TO: 'Greater than or equal to',\n TEXT_GROUP_BY: 'Group By',\n TEXT_HIDE_COLUMN: 'Hide Column',\n TEXT_ITEMS: 'items',\n TEXT_ITEMS_PER_PAGE: 'items per page',\n TEXT_ITEMS_SELECTED: 'items selected',\n TEXT_OF: 'of',\n TEXT_OK: 'OK',\n TEXT_LAST_UPDATE: 'Last Update',\n TEXT_LESS_THAN: 'Less than',\n TEXT_LESS_THAN_OR_EQUAL_TO: 'Less than or equal to',\n TEXT_NO_ELEMENTS_FOUND: 'Aucun élément trouvé',\n TEXT_NOT_CONTAINS: 'Not contains',\n TEXT_NOT_EQUAL_TO: 'Not equal to',\n TEXT_PAGE: 'Page',\n TEXT_REFRESH_DATASET: 'Refresh Dataset',\n TEXT_REMOVE_FILTER: 'Remove Filter',\n TEXT_REMOVE_SORT: 'Remove Sort',\n TEXT_SAVE: 'Save',\n TEXT_SELECT_ALL: 'Select All',\n TEXT_SYNCHRONOUS_RESIZE: 'Synchronous resize',\n TEXT_SORT_ASCENDING: 'Sort Ascending',\n TEXT_SORT_DESCENDING: 'Sort Descending',\n TEXT_STARTS_WITH: 'Starts With',\n TEXT_TOGGLE_DARK_MODE: 'Toggle Dark Mode',\n TEXT_TOGGLE_FILTER_ROW: 'Toggle Filter Row',\n TEXT_TOGGLE_PRE_HEADER_ROW: 'Toggle Pre-Header Row',\n TEXT_X_OF_Y_SELECTED: '# of % selected',\n TEXT_X_OF_Y_MASS_SELECTED: '{{x}} of {{y}} selected',\n };\n\n static readonly treeDataProperties = {\n CHILDREN_PROP: 'children',\n COLLAPSED_PROP: '__collapsed',\n HAS_CHILDREN_PROP: '__hasChildren',\n TREE_LEVEL_PROP: '__treeLevel',\n PARENT_PROP: '__parentId',\n };\n\n // some Validation default texts\n static readonly VALIDATION_REQUIRED_FIELD = 'Field is required';\n static readonly VALIDATION_EDITOR_VALID_NUMBER = 'Please enter a valid number';\n static readonly VALIDATION_EDITOR_VALID_INTEGER = 'Please enter a valid integer number';\n static readonly VALIDATION_EDITOR_INTEGER_BETWEEN = 'Please enter a valid integer number between {{minValue}} and {{maxValue}}';\n static readonly VALIDATION_EDITOR_INTEGER_MAX = 'Please enter a valid integer number that is lower than {{maxValue}}';\n static readonly VALIDATION_EDITOR_INTEGER_MAX_INCLUSIVE =\n 'Please enter a valid integer number that is lower than or equal to {{maxValue}}';\n static readonly VALIDATION_EDITOR_INTEGER_MIN = 'Please enter a valid integer number that is greater than {{minValue}}';\n static readonly VALIDATION_EDITOR_INTEGER_MIN_INCLUSIVE =\n 'Please enter a valid integer number that is greater than or equal to {{minValue}}';\n static readonly VALIDATION_EDITOR_NUMBER_BETWEEN = 'Please enter a valid number between {{minValue}} and {{maxValue}}';\n static readonly VALIDATION_EDITOR_NUMBER_MAX = 'Please enter a valid number that is lower than {{maxValue}}';\n static readonly VALIDATION_EDITOR_NUMBER_MAX_INCLUSIVE =\n 'Please enter a valid number that is lower than or equal to {{maxValue}}';\n static readonly VALIDATION_EDITOR_NUMBER_MIN = 'Please enter a valid number that is greater than {{minValue}}';\n static readonly VALIDATION_EDITOR_NUMBER_MIN_INCLUSIVE =\n 'Please enter a valid number that is greater than or equal to {{minValue}}';\n static readonly VALIDATION_EDITOR_DECIMAL_BETWEEN = 'Please enter a valid number with a maximum of {{maxDecimal}} decimals';\n static readonly VALIDATION_EDITOR_TEXT_LENGTH_BETWEEN =\n 'Please make sure your text length is between {{minLength}} and {{maxLength}} characters';\n static readonly VALIDATION_EDITOR_TEXT_MAX_LENGTH = 'Please make sure your text is less than {{maxLength}} characters';\n static readonly VALIDATION_EDITOR_TEXT_MAX_LENGTH_INCLUSIVE =\n 'Please make sure your text is less than or equal to {{maxLength}} characters';\n static readonly VALIDATION_EDITOR_TEXT_MIN_LENGTH = 'Please make sure your text is more than {{minLength}} character(s)';\n static readonly VALIDATION_EDITOR_TEXT_MIN_LENGTH_INCLUSIVE =\n 'Please make sure your text is at least {{minLength}} character(s)';\n}\n","import {\n AfterViewInit,\n ApplicationRef,\n ChangeDetectorRef,\n Component,\n ContentChild,\n ElementRef,\n EventEmitter,\n Inject,\n Input,\n OnDestroy,\n Optional,\n Output,\n TemplateRef,\n} from '@angular/core';\nimport {\n AutocompleterEditor,\n BackendService,\n BackendServiceApi,\n BackendServiceOption,\n BasePaginationComponent,\n Column,\n DataViewOption,\n EventSubscription,\n ExternalResource,\n isColumnDateType,\n ItemMetadata,\n Locale,\n Metrics,\n Pagination,\n PaginationMetadata,\n RxJsFacade,\n SelectEditor,\n SlickDataView,\n SlickEventHandler,\n SlickGrid,\n} from '@slickgrid-universal/common';\nimport {\n ExtensionName,\n ExtensionUtility,\n SlickGroupItemMetadataProvider,\n\n // services\n BackendUtilityService,\n CollectionService,\n EventNamingStyle,\n ExtensionService,\n FilterFactory,\n FilterService,\n GridEventService,\n GridService,\n GridStateService,\n HeaderGroupingService,\n PaginationService,\n ResizerService,\n SharedService,\n SlickgridConfig,\n SortService,\n TreeDataService,\n\n // utilities\n autoAddEditorFormatterToColumnsWithEditor,\n emptyElement,\n GridStateType,\n unsubscribeAll,\n} from '@slickgrid-universal/common';\nimport { EventPubSubService } from '@slickgrid-universal/event-pub-sub';\nimport { SlickEmptyWarningComponent } from '@slickgrid-universal/empty-warning-component';\nimport { SlickFooterComponent } from '@slickgrid-universal/custom-footer-component';\nimport { SlickPaginationComponent } from '@slickgrid-universal/pagination-component';\nimport { RxJsResource } from '@slickgrid-universal/rxjs-observable';\nimport { extend } from '@slickgrid-universal/utils';\nimport { TranslateService } from '@ngx-translate/core';\nimport { dequal } from 'dequal/lite';\nimport { Observable } from 'rxjs';\n\nimport { Constants } from '../constants';\nimport type { AngularGridInstance, ExternalTestingDependencies, GridOption } from './../models/index';\nimport { GlobalGridOptions } from './../global-grid-options';\nimport { TranslaterService } from '../services/translater.service';\n\n// Services\nimport { AngularUtilService } from '../services/angularUtil.service';\nimport { SlickRowDetailView } from '../extensions/slickRowDetailView';\nimport { ContainerService } from '../services/container.service';\n\nconst WARN_NO_PREPARSE_DATE_SIZE = 10000; // data size to warn user when pre-parse isn't enabled\n\n@Component({\n selector: 'angular-slickgrid',\n templateUrl: './angular-slickgrid.component.html',\n providers: [\n // make everything transient (non-singleton)\n AngularUtilService,\n TranslaterService,\n ],\n})\nexport class AngularSlickgridComponent<TData = any> implements AfterViewInit, OnDestroy {\n protected _dataset?: TData[] | null;\n protected _columnDefinitions!: Column[];\n protected _currentDatasetLength = 0;\n protected _darkMode = false;\n protected _eventHandler: SlickEventHandler = new SlickEventHandler();\n protected _eventPubSubService!: EventPubSubService;\n protected _angularGridInstances: AngularGridInstance | undefined;\n protected _hideHeaderRowAfterPageLoad = false;\n protected _isAutosizeColsCalled = false;\n protected _isGridInitialized = false;\n protected _isDatasetInitialized = false;\n protected _isDatasetHierarchicalInitialized = false;\n protected _isPaginationInitialized = false;\n protected _isLocalGrid = true;\n protected _paginationOptions: Pagination | undefined;\n protected _registeredResources: ExternalResource[] = [];\n protected _scrollEndCalled = false;\n dataView!: SlickDataView;\n slickGrid!: SlickGrid;\n groupingDefinition: any = {};\n groupItemMetadataProvider?: SlickGroupItemMetadataProvider;\n backendServiceApi?: BackendServiceApi;\n locales!: Locale;\n metrics?: Metrics;\n showPagination = false;\n serviceList: any[] = [];\n totalItems = 0;\n paginationData?: {\n gridOptions: GridOption;\n paginationService: PaginationService;\n };\n subscriptions: EventSubscription[] = [];\n\n // components / plugins\n slickEmptyWarning?: SlickEmptyWarningComponent;\n slickFooter?: SlickFooterComponent;\n slickPagination?: BasePaginationComponent;\n paginationComponent: BasePaginationComponent | undefined;\n slickRowDetailView?: SlickRowDetailView;\n\n // services\n backendUtilityService!: BackendUtilityService;\n collectionService: CollectionService;\n extensionService: ExtensionService;\n extensionUtility: ExtensionUtility;\n filterFactory!: FilterFactory;\n filterService: FilterService;\n gridEventService: GridEventService;\n gridService: GridService;\n gridStateService: GridStateService;\n headerGroupingService: HeaderGroupingService;\n paginationService: PaginationService;\n resizerService!: ResizerService;\n rxjs?: RxJsFacade;\n sharedService: SharedService;\n sortService: SortService;\n treeDataService: TreeDataService;\n\n @Input() customDataView: any;\n @Input() gridId = '';\n @Input() gridOptions: GridOption = {};\n\n @Input()\n get paginationOptions(): Pagination | undefined {\n return this._paginationOptions;\n }\n set paginationOptions(newPaginationOptions: Pagination | undefined) {\n if (newPaginationOptions && this._paginationOptions) {\n this._paginationOptions = { ...this.gridOptions.pagination, ...this._paginationOptions, ...newPaginationOptions };\n } else {\n this._paginationOptions = newPaginationOptions;\n }\n this.gridOptions.pagination = this._paginationOptions ?? this.gridOptions.pagination;\n this.paginationService.updateTotalItems(this.gridOptions.pagination?.totalItems ?? 0, true);\n }\n\n @Input()\n get columnDefinitions(): Column[] {\n return this._columnDefinitions;\n }\n set columnDefinitions(columnDefinitions: Column[]) {\n this._columnDefinitions = columnDefinitions;\n if (this._isGridInitialized) {\n this.updateColumnDefinitionsList(columnDefinitions);\n }\n if (columnDefinitions.length > 0) {\n this.copyColumnWidthsReference(columnDefinitions);\n }\n }\n\n // make the columnDefinitions a 2-way binding so that plugin adding cols\n // are synched on user's side as well (RowMove, RowDetail, RowSelections)\n @Output() columnDefinitionsChange = new EventEmitter(true);\n\n @Input()\n get dataset(): any[] {\n return (this.customDataView ? this.slickGrid?.getData?.() : this.dataView?.getItems()) || [];\n }\n set dataset(newDataset: any[]) {\n const prevDatasetLn = this._currentDatasetLength;\n const isDatasetEqual = dequal(newDataset, this._dataset || []);\n let data = newDataset;\n\n // when Tree Data is enabled and we don't yet have the hierarchical dataset filled, we can force a convert+sort of the array\n if (\n this.slickGrid &&\n this.gridOptions?.enableTreeData &&\n Array.isArray(newDataset) &&\n (newDataset.length > 0 || newDataset.length !== prevDatasetLn || !isDatasetEqual)\n ) {\n this._isDatasetHierarchicalInitialized = false;\n data = this.sortTreeDataset(newDataset, !isDatasetEqual); // if dataset changed, then force a refresh anyway\n }\n this._dataset = data;\n this.refreshGridData(data || []);\n this._currentDatasetLength = (newDataset || []).length;\n\n // expand/autofit columns on first page load\n // we can assume that if the prevDataset was empty then we are on first load\n if (this.slickGrid && this.gridOptions?.autoFitColumnsOnFirstLoad && prevDatasetLn === 0 && !this._isAutosizeColsCalled) {\n this.slickGrid.autosizeColumns();\n this._isAutosizeColsCalled = true;\n }\n this.suggestDateParsingWhenHelpful();\n }\n\n @Input()\n get datasetHierarchical(): any[] | undefined {\n return this.sharedService.hierarchicalDataset;\n }\n set datasetHierarchical(newHierarchicalDataset: any[] | undefined) {\n const isDatasetEqual = dequal(newHierarchicalDataset, this.sharedService?.hierarchicalDataset ?? []);\n const prevFlatDatasetLn = this._currentDatasetLength;\n this.sharedService.hierarchicalDataset = newHierarchicalDataset;\n\n if (newHierarchicalDataset && this.columnDefinitions && this.filterService?.clearFilters) {\n this.filterService.clearFilters();\n }\n\n // when a hierarchical dataset is set afterward, we can reset the flat dataset and call a tree data sort that will overwrite the flat dataset\n if (newHierarchicalDataset && this.slickGrid && this.sortService?.processTreeDataInitialSort) {\n this.sortService.processTreeDataInitialSort();\n\n // we also need to reset/refresh the Tree Data filters because if we inserted new item(s) then it might not show up without doing this refresh\n // however we need to queue our process until the flat dataset is ready, so we can queue a microtask to execute the DataView refresh only after everything is ready\n queueMicrotask(() => {\n const flatDatasetLn = this.dataView.getItemCount();\n if (flatDatasetLn > 0 && (flatDatasetLn !== prevFlatDatasetLn || !isDatasetEqual)) {\n this.filterService.refreshTreeDataFilters();\n }\n });\n this._isDatasetHierarchicalInitialized = true;\n }\n }\n\n get elementRef(): ElementRef {\n return this.elm;\n }\n\n get backendService(): BackendService | undefined {\n return this.gridOptio