angular-slickgrid
Version: 
Slickgrid components made available in Angular
1 lines • 190 kB
Source Map (JSON)
{"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