@swimlane/ngx-datatable
Version:
ngx-datatable is an Angular table grid component for presenting large and complex data.
1 lines • 377 kB
Source Map (JSON)
{"version":3,"file":"swimlane-ngx-datatable.mjs","sources":["../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/tree.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/camel-case.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/id.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/services/column-changes.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column-ghost-cell.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/column.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/keys.ts","../../../../projects/swimlane/ngx-datatable/src/lib/types/public.types.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.html","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row-def.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/table-token.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/selection.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/sort.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/math.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html","../../../../projects/swimlane/ngx-datatable/src/lib/directives/disable-row.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts","../../../../projects/swimlane/ngx-datatable/src/public-api.ts","../../../../projects/swimlane/ngx-datatable/src/swimlane-ngx-datatable.ts"],"sourcesContent":["import { Directive } from '@angular/core';\nimport { FooterContext } from '../../types/public.types';\n\n@Directive({\n selector: '[ngx-datatable-footer-template]',\n standalone: true\n})\nexport class DataTableFooterTemplateDirective {\n static ngTemplateContextGuard(\n directive: DataTableFooterTemplateDirective,\n context: unknown\n ): context is FooterContext {\n return true;\n }\n}\n","import { Directive } from '@angular/core';\nimport { GroupContext } from '../../types/public.types';\n\n@Directive({\n selector: '[ngx-datatable-group-header-template]',\n standalone: true\n})\nexport class DatatableGroupHeaderTemplateDirective {\n static ngTemplateContextGuard(\n directive: DatatableGroupHeaderTemplateDirective,\n context: unknown\n ): context is GroupContext {\n return true;\n }\n}\n","import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core';\nimport { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive';\nimport { Group, GroupContext, GroupToggleEvents } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-group-header',\n standalone: true\n})\nexport class DatatableGroupHeaderDirective<TRow = any> {\n /**\n * Row height is required when virtual scroll is enabled.\n */\n @Input() rowHeight: number | ((group?: Group<TRow>, index?: number) => number) = 0;\n\n /**\n * Show checkbox at group header to select all rows of the group.\n */\n @Input() checkboxable = false;\n\n @Input('template')\n _templateInput: TemplateRef<GroupContext<TRow>>;\n\n @ContentChild(DatatableGroupHeaderTemplateDirective, { read: TemplateRef, static: true })\n _templateQuery: TemplateRef<GroupContext<TRow>>;\n\n get template(): TemplateRef<GroupContext<TRow>> {\n return this._templateInput || this._templateQuery;\n }\n\n /**\n * Track toggling of group visibility\n */\n @Output() toggle: EventEmitter<GroupToggleEvents<TRow>> = new EventEmitter();\n\n /**\n * Toggle the expansion of a group\n */\n toggleExpandGroup(group: Group<TRow>): void {\n this.toggle.emit({\n type: 'group',\n value: group\n });\n }\n\n /**\n * Expand all groups\n */\n expandAllGroups(): void {\n this.toggle.emit({\n type: 'all',\n value: true\n });\n }\n\n /**\n * Collapse all groups\n */\n collapseAllGroups(): void {\n this.toggle.emit({\n type: 'all',\n value: false\n });\n }\n}\n","import { TableColumnProp } from '../types/table-column.type';\n\n// maybe rename this file to prop-getters.ts\n\nexport type ValueGetter = (obj: any, prop: TableColumnProp) => any;\n\n/**\n * Always returns the empty string ''\n */\nexport function emptyStringGetter(): string {\n return '';\n}\n\n/**\n * Returns the appropriate getter function for this kind of prop.\n * If prop == null, returns the emptyStringGetter.\n */\nexport function getterForProp(prop: TableColumnProp): ValueGetter {\n if (prop == null) {\n return emptyStringGetter;\n }\n\n if (typeof prop === 'number') {\n return numericIndexGetter;\n } else {\n // deep or simple\n if (prop.indexOf('.') !== -1) {\n return deepValueGetter;\n } else {\n return shallowValueGetter;\n }\n }\n}\n\n/**\n * Returns the value at this numeric index.\n * @param row array of values\n * @param index numeric index\n * @returns any or '' if invalid index\n */\nexport function numericIndexGetter(row: any[], index: number): any {\n if (row == null) {\n return '';\n }\n // mimic behavior of deepValueGetter\n if (!row || index == null) {\n return row;\n }\n\n const value = row[index];\n if (value == null) {\n return '';\n }\n return value;\n}\n\n/**\n * Returns the value of a field.\n * (more efficient than deepValueGetter)\n * @param obj object containing the field\n * @param fieldName field name string\n */\nexport function shallowValueGetter(obj: any, fieldName: string): any {\n if (obj == null) {\n return '';\n }\n if (!obj || !fieldName) {\n return obj;\n }\n\n const value = obj[fieldName];\n if (value == null) {\n return '';\n }\n return value;\n}\n\n/**\n * Returns a deep object given a string. zoo['animal.type']\n */\nexport function deepValueGetter(obj: any, path: string): any {\n if (obj == null) {\n return '';\n }\n if (!obj || !path) {\n return obj;\n }\n\n // check if path matches a root-level field\n // { \"a.b.c\": 123 }\n let current = obj[path];\n if (current !== undefined) {\n return current;\n }\n\n current = obj;\n const split = path.split('.');\n\n if (split.length) {\n for (let i = 0; i < split.length; i++) {\n current = current[split[i]];\n\n // if found undefined, return empty string\n if (current === undefined || current === null) {\n return '';\n }\n }\n }\n\n return current;\n}\n","import { getterForProp } from './column-prop-getters';\nimport { TableColumnProp } from '../types/table-column.type';\n\nexport type OptionalValueGetter = (row: any) => any | undefined;\nexport function optionalGetterForProp(prop: TableColumnProp): OptionalValueGetter {\n return prop && (row => getterForProp(prop)(row, prop));\n}\n\n/**\n * This functions rearrange items by their parents\n * Also sets the level value to each of the items\n *\n * Note: Expecting each item has a property called parentId\n * Note: This algorithm will fail if a list has two or more items with same ID\n * NOTE: This algorithm will fail if there is a deadlock of relationship\n *\n * For example,\n *\n * Input\n *\n * id -> parent\n * 1 -> 0\n * 2 -> 0\n * 3 -> 1\n * 4 -> 1\n * 5 -> 2\n * 7 -> 8\n * 6 -> 3\n *\n *\n * Output\n * id -> level\n * 1 -> 0\n * --3 -> 1\n * ----6 -> 2\n * --4 -> 1\n * 2 -> 0\n * --5 -> 1\n * 7 -> 8\n *\n *\n * @param rows\n *\n */\nexport function groupRowsByParents<TRow>(\n rows: TRow[],\n from?: OptionalValueGetter,\n to?: OptionalValueGetter\n): TRow[] {\n if (from && to) {\n const nodeById = {};\n const l = rows.length;\n let node: TreeNode | null = null;\n\n nodeById[0] = new TreeNode(); // that's the root node\n\n const uniqIDs = rows.reduce((arr, item) => {\n const toValue = to(item);\n if (arr.indexOf(toValue) === -1) {\n arr.push(toValue);\n }\n return arr;\n }, []);\n\n for (let i = 0; i < l; i++) {\n // make TreeNode objects for each item\n nodeById[to(rows[i])] = new TreeNode(rows[i]);\n }\n\n for (let i = 0; i < l; i++) {\n // link all TreeNode objects\n node = nodeById[to(rows[i])];\n let parent = 0;\n const fromValue = from(node.row);\n if (!!fromValue && uniqIDs.indexOf(fromValue) > -1) {\n parent = fromValue;\n }\n node.parent = nodeById[parent];\n node.row['level'] = node.parent.row['level'] + 1;\n node.parent.children.push(node);\n }\n\n let resolvedRows: any[] = [];\n nodeById[0].flatten(function () {\n resolvedRows = [...resolvedRows, this.row];\n }, true);\n\n return resolvedRows;\n } else {\n return rows;\n }\n}\n\nclass TreeNode {\n public row: any;\n public parent: any;\n public children: any[];\n\n constructor(row: any | null = null) {\n if (!row) {\n row = {\n level: -1,\n treeStatus: 'expanded'\n };\n }\n this.row = row;\n this.parent = null;\n this.children = [];\n }\n\n flatten(f: any, recursive: boolean) {\n if (this.row['treeStatus'] === 'expanded') {\n for (let i = 0, l = this.children.length; i < l; i++) {\n const child = this.children[i];\n f.apply(child, Array.prototype.slice.call(arguments, 2));\n if (recursive) {\n child.flatten.apply(child, arguments);\n }\n }\n }\n }\n}\n","/**\n * Converts strings from something to camel case\n * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase\n */\nexport function camelCase(str: string): string {\n // Replace special characters with a space\n str = str.replace(/[^a-zA-Z0-9 ]/g, ' ');\n // put a space before an uppercase letter\n str = str.replace(/([a-z](?=[A-Z]))/g, '$1 ');\n\n // Lower case first character and some other stuff\n str = str\n .replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '')\n .trim()\n .toLowerCase();\n\n // uppercase characters preceded by a space or number\n str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function (a, b, c) {\n return b.trim() + c.toUpperCase();\n });\n\n return str;\n}\n\n/**\n * Converts strings from camel case to words\n * http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text\n */\nexport function deCamelCase(str: string): string {\n return str.replace(/([A-Z])/g, match => ` ${match}`).replace(/^./, match => match.toUpperCase());\n}\n","/**\n * Creates a unique object id.\n * http://stackoverflow.com/questions/6248666/how-to-generate-short-uid-like-ax4j9z-in-js\n */\nexport function id() {\n return ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4);\n}\n","import { camelCase, deCamelCase } from './camel-case';\nimport { id } from './id';\nimport { getterForProp } from './column-prop-getters';\nimport { TableColumn } from '../types/table-column.type';\nimport { DataTableColumnDirective } from '../components/columns/column.directive';\n\n/**\n * Sets the column defaults\n */\nexport function setColumnDefaults(columns: TableColumn[], defaultColumnWidth = 150) {\n if (!columns) {\n return;\n }\n\n // Only one column should hold the tree view\n // Thus if multiple columns are provided with\n // isTreeColumn as true we take only the first one\n let treeColumnFound: boolean = false;\n\n for (const column of columns) {\n if (!column.$$id) {\n column.$$id = id();\n }\n\n // prop can be numeric; zero is valid not a missing prop\n // translate name => prop\n if (isNullOrUndefined(column.prop) && column.name) {\n column.prop = camelCase(column.name);\n }\n\n if (!column.$$valueGetter) {\n column.$$valueGetter = getterForProp(column.prop);\n }\n\n // format props if no name passed\n if (!isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) {\n column.name = deCamelCase(String(column.prop));\n }\n\n if (isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) {\n column.name = ''; // Fixes IE and Edge displaying `null`\n }\n\n if (!('resizeable' in column)) {\n column.resizeable = true;\n }\n\n if (!('sortable' in column)) {\n column.sortable = true;\n }\n\n if (!('draggable' in column)) {\n column.draggable = true;\n }\n\n if (!('canAutoResize' in column)) {\n column.canAutoResize = true;\n }\n\n if (!('width' in column)) {\n column.width = defaultColumnWidth;\n }\n\n if (!('isTreeColumn' in column)) {\n column.isTreeColumn = false;\n } else {\n if (column.isTreeColumn && !treeColumnFound) {\n // If the first column with isTreeColumn is true found\n // we mark that treeCoulmn is found\n treeColumnFound = true;\n } else {\n // After that isTreeColumn property for any other column\n // will be set as false\n column.isTreeColumn = false;\n }\n }\n }\n}\n\nexport function isNullOrUndefined<T>(value: T | null | undefined): value is null | undefined {\n return value === null || value === undefined;\n}\n\n/**\n * Translates templates definitions to objects\n */\nexport function translateTemplates<TRow>(\n templates: DataTableColumnDirective<TRow>[]\n): TableColumn[] {\n const result: TableColumn[] = [];\n for (const temp of templates) {\n const col: TableColumn = {};\n\n const props = Object.getOwnPropertyNames(temp);\n for (const prop of props) {\n col[prop] = temp[prop];\n }\n\n if (temp.headerTemplate) {\n col.headerTemplate = temp.headerTemplate;\n }\n\n if (temp.cellTemplate) {\n col.cellTemplate = temp.cellTemplate;\n }\n\n if (temp.ghostCellTemplate) {\n col.ghostCellTemplate = temp.ghostCellTemplate;\n }\n\n if (temp.summaryFunc) {\n col.summaryFunc = temp.summaryFunc;\n }\n\n if (temp.summaryTemplate) {\n col.summaryTemplate = temp.summaryTemplate;\n }\n\n result.push(col);\n }\n\n return result;\n}\n","import { Directive } from '@angular/core';\nimport { HeaderCellContext } from '../../types/public.types';\n\n@Directive({\n selector: '[ngx-datatable-header-template]',\n standalone: true\n})\nexport class DataTableColumnHeaderDirective {\n static ngTemplateContextGuard(\n directive: DataTableColumnHeaderDirective,\n context: unknown\n ): context is HeaderCellContext {\n return true;\n }\n}\n","import { Directive, inject, TemplateRef } from '@angular/core';\nimport { CellContext } from '../../types/public.types';\n\n@Directive({\n selector: '[ngx-datatable-cell-template]',\n standalone: true\n})\nexport class DataTableColumnCellDirective {\n template = inject<TemplateRef<CellContext>>(TemplateRef);\n\n static ngTemplateContextGuard(dir: DataTableColumnCellDirective, ctx: any): ctx is CellContext {\n return true;\n }\n}\n","import { Directive, inject, TemplateRef } from '@angular/core';\n\n@Directive({\n selector: '[ngx-datatable-tree-toggle]',\n standalone: true\n})\nexport class DataTableColumnCellTreeToggle {\n template = inject<TemplateRef<any>>(TemplateRef);\n}\n","import { Injectable } from '@angular/core';\n\nimport { Observable, Subject } from 'rxjs';\n\n/**\n * service to make DatatableComponent aware of changes to\n * input bindings of DataTableColumnDirective\n */\n@Injectable()\nexport class ColumnChangesService {\n private columnInputChanges = new Subject<void>();\n\n get columnInputChanges$(): Observable<void> {\n return this.columnInputChanges.asObservable();\n }\n\n onInputChange(): void {\n this.columnInputChanges.next(undefined);\n }\n}\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ngx-datatable-ghost-cell-template]',\n standalone: true\n})\nexport class DataTableColumnGhostCellDirective {\n static ngTemplateContextGuard(\n directive: DataTableColumnGhostCellDirective,\n context: unknown\n ): context is void {\n return true;\n }\n}\n","import {\n booleanAttribute,\n ContentChild,\n Directive,\n inject,\n Input,\n numberAttribute,\n OnChanges,\n PipeTransform,\n TemplateRef\n} from '@angular/core';\nimport { DataTableColumnHeaderDirective } from './column-header.directive';\nimport { DataTableColumnCellDirective } from './column-cell.directive';\nimport { DataTableColumnCellTreeToggle } from './tree.directive';\nimport { ColumnChangesService } from '../../services/column-changes.service';\nimport { TableColumn, TableColumnProp } from '../../types/table-column.type';\nimport { DataTableColumnGhostCellDirective } from './column-ghost-cell.directive';\nimport { CellContext, HeaderCellContext } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-column',\n standalone: true\n})\nexport class DataTableColumnDirective<TRow> implements TableColumn, OnChanges {\n private columnChangesService = inject(ColumnChangesService);\n @Input() name: string;\n @Input() prop: TableColumnProp;\n @Input({ transform: booleanAttribute }) bindAsUnsafeHtml?: boolean;\n @Input({ transform: booleanAttribute }) frozenLeft: boolean;\n @Input({ transform: booleanAttribute }) frozenRight: boolean;\n @Input({ transform: numberAttribute }) flexGrow: number;\n @Input({ transform: booleanAttribute }) resizeable: boolean;\n @Input() comparator: any;\n @Input() pipe: PipeTransform;\n @Input({ transform: booleanAttribute }) sortable: boolean;\n @Input({ transform: booleanAttribute }) draggable: boolean;\n @Input({ transform: booleanAttribute }) canAutoResize: boolean;\n @Input({ transform: numberAttribute }) minWidth: number;\n @Input({ transform: numberAttribute }) width: number;\n @Input({ transform: numberAttribute }) maxWidth: number;\n @Input({ transform: booleanAttribute }) checkboxable: boolean;\n @Input({ transform: booleanAttribute }) headerCheckboxable: boolean;\n @Input() headerClass:\n | string\n | ((data: { column: TableColumn }) => string | Record<string, boolean>);\n @Input() cellClass?:\n | string\n | ((data: {\n row: TRow;\n group?: TRow[];\n column: TableColumn<TRow>;\n value: any;\n rowHeight: number;\n }) => string | Record<string, boolean>);\n @Input({ transform: booleanAttribute }) isTreeColumn: boolean;\n @Input() treeLevelIndent: number;\n @Input() summaryFunc: (cells: any[]) => any;\n @Input() summaryTemplate: TemplateRef<any>;\n\n @Input('cellTemplate')\n _cellTemplateInput: TemplateRef<CellContext<TRow>>;\n\n @ContentChild(DataTableColumnCellDirective, { read: TemplateRef, static: true })\n _cellTemplateQuery: TemplateRef<CellContext<TRow>>;\n\n get cellTemplate(): TemplateRef<CellContext<TRow>> {\n return this._cellTemplateInput || this._cellTemplateQuery;\n }\n\n @Input('headerTemplate')\n _headerTemplateInput: TemplateRef<HeaderCellContext>;\n\n @ContentChild(DataTableColumnHeaderDirective, { read: TemplateRef, static: true })\n _headerTemplateQuery: TemplateRef<HeaderCellContext>;\n\n get headerTemplate(): TemplateRef<HeaderCellContext> {\n return this._headerTemplateInput || this._headerTemplateQuery;\n }\n\n @Input('treeToggleTemplate')\n _treeToggleTemplateInput: TemplateRef<any>;\n\n @ContentChild(DataTableColumnCellTreeToggle, { read: TemplateRef, static: true })\n _treeToggleTemplateQuery: TemplateRef<any>;\n\n get treeToggleTemplate(): TemplateRef<any> {\n return this._treeToggleTemplateInput || this._treeToggleTemplateQuery;\n }\n\n @Input('ghostCellTemplate')\n _ghostCellTemplateInput: TemplateRef<void>;\n\n @ContentChild(DataTableColumnGhostCellDirective, { read: TemplateRef, static: true })\n _ghostCellTemplateQuery: TemplateRef<void>;\n\n get ghostCellTemplate(): TemplateRef<void> {\n return this._ghostCellTemplateInput || this._ghostCellTemplateQuery;\n }\n\n private isFirstChange = true;\n\n ngOnChanges() {\n if (this.isFirstChange) {\n this.isFirstChange = false;\n } else {\n this.columnChangesService.onInputChange();\n }\n }\n}\n","import { Directive } from '@angular/core';\nimport { RowDetailContext } from '../../types/public.types';\n\n@Directive({\n selector: '[ngx-datatable-row-detail-template]',\n standalone: true\n})\nexport class DatatableRowDetailTemplateDirective {\n static ngTemplateContextGuard(\n directive: DatatableRowDetailTemplateDirective,\n context: unknown\n ): context is RowDetailContext {\n return true;\n }\n}\n","import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core';\nimport { DatatableRowDetailTemplateDirective } from './row-detail-template.directive';\nimport { RowDetailContext } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-row-detail',\n standalone: true\n})\nexport class DatatableRowDetailDirective<TRow = any> {\n /**\n * The detail row height is required especially\n * when virtual scroll is enabled.\n */\n @Input() rowHeight: number | ((row?: TRow, index?: number) => number) = 0;\n\n @Input('template')\n _templateInput: TemplateRef<RowDetailContext<TRow>>;\n\n @ContentChild(DatatableRowDetailTemplateDirective, { read: TemplateRef, static: true })\n _templateQuery: TemplateRef<RowDetailContext<TRow>>;\n\n get template(): TemplateRef<RowDetailContext<TRow>> {\n return this._templateInput || this._templateQuery;\n }\n\n /**\n * Row detail row visbility was toggled.\n */\n @Output() toggle: EventEmitter<any> = new EventEmitter();\n\n /**\n * Toggle the expansion of the row\n */\n toggleExpandRow(row: TRow): void {\n this.toggle.emit({\n type: 'row',\n value: row\n });\n }\n\n /**\n * API method to expand all the rows.\n */\n expandAllRows(): void {\n this.toggle.emit({\n type: 'all',\n value: true\n });\n }\n\n /**\n * API method to collapse all the rows.\n */\n collapseAllRows(): void {\n this.toggle.emit({\n type: 'all',\n value: false\n });\n }\n}\n","import { ContentChild, Directive, Input, numberAttribute, TemplateRef } from '@angular/core';\nimport { DataTableFooterTemplateDirective } from './footer-template.directive';\nimport { FooterContext } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-footer',\n standalone: true\n})\nexport class DatatableFooterDirective {\n @Input({ transform: numberAttribute }) footerHeight: number;\n @Input() totalMessage: string;\n @Input() selectedMessage: string | boolean;\n @Input() pagerLeftArrowIcon: string;\n @Input() pagerRightArrowIcon: string;\n @Input() pagerPreviousIcon: string;\n @Input() pagerNextIcon: string;\n\n @Input('template')\n _templateInput: TemplateRef<FooterContext>;\n\n @ContentChild(DataTableFooterTemplateDirective, { read: TemplateRef })\n _templateQuery: TemplateRef<FooterContext>;\n\n get template(): TemplateRef<FooterContext> {\n return this._templateInput || this._templateQuery;\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n EventEmitter,\n HostBinding,\n inject,\n Input,\n OnDestroy,\n OnInit,\n Output,\n Renderer2\n} from '@angular/core';\n\n@Component({\n selector: 'datatable-scroller',\n template: ` <ng-content></ng-content> `,\n host: {\n class: 'datatable-scroll'\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true\n})\nexport class ScrollerComponent implements OnInit, OnDestroy {\n private renderer = inject(Renderer2);\n\n @Input() scrollbarV = false;\n @Input() scrollbarH = false;\n\n @HostBinding('style.height.px')\n @Input()\n scrollHeight: number;\n\n @HostBinding('style.width.px')\n @Input()\n scrollWidth: number;\n\n @Output() scroll: EventEmitter<any> = new EventEmitter();\n\n scrollYPos = 0;\n scrollXPos = 0;\n prevScrollYPos = 0;\n prevScrollXPos = 0;\n element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n parentElement: HTMLElement;\n\n private _scrollEventListener: any = null;\n\n ngOnInit(): void {\n // manual bind so we don't always listen\n if (this.scrollbarV || this.scrollbarH) {\n const renderer = this.renderer;\n this.parentElement = renderer.parentNode(renderer.parentNode(this.element));\n this._scrollEventListener = this.onScrolled.bind(this);\n this.parentElement.addEventListener('scroll', this._scrollEventListener);\n }\n }\n\n ngOnDestroy(): void {\n if (this._scrollEventListener) {\n this.parentElement.removeEventListener('scroll', this._scrollEventListener);\n this._scrollEventListener = null;\n }\n }\n\n setOffset(offsetY: number): void {\n if (this.parentElement) {\n this.parentElement.scrollTop = offsetY;\n }\n }\n\n onScrolled(event: MouseEvent): void {\n const dom: Element = <Element>event.currentTarget;\n requestAnimationFrame(() => {\n this.scrollYPos = dom.scrollTop;\n this.scrollXPos = dom.scrollLeft;\n this.updateOffset();\n });\n }\n\n updateOffset(): void {\n let direction: string;\n if (this.scrollYPos < this.prevScrollYPos) {\n direction = 'down';\n } else if (this.scrollYPos > this.prevScrollYPos) {\n direction = 'up';\n }\n\n this.scroll.emit({\n direction,\n scrollYPos: this.scrollYPos,\n scrollXPos: this.scrollXPos\n });\n\n this.prevScrollYPos = this.scrollYPos;\n this.prevScrollXPos = this.scrollXPos;\n }\n}\n","import { TableColumn, TableColumnGroup } from '../types/table-column.type';\nimport { ColumnGroupWidth, PinnedColumns } from '../types/internal.types';\n\n/**\n * Returns the columns by pin.\n */\nexport function columnsByPin(cols: TableColumn[]) {\n const ret: TableColumnGroup = {\n left: [],\n center: [],\n right: []\n };\n\n if (cols) {\n for (const col of cols) {\n if (col.frozenLeft) {\n ret.left.push(col);\n } else if (col.frozenRight) {\n ret.right.push(col);\n } else {\n ret.center.push(col);\n }\n }\n }\n\n return ret;\n}\n\n/**\n * Returns the widths of all group sets of a column\n */\nexport function columnGroupWidths(groups: TableColumnGroup, all: TableColumn[]): ColumnGroupWidth {\n return {\n left: columnTotalWidth(groups.left),\n center: columnTotalWidth(groups.center),\n right: columnTotalWidth(groups.right),\n total: Math.floor(columnTotalWidth(all))\n };\n}\n\n/**\n * Calculates the total width of all columns and their groups\n */\nexport function columnTotalWidth(columns: TableColumn[], prop?: string) {\n let totalWidth = 0;\n\n if (columns) {\n for (const c of columns) {\n const has = prop && c[prop];\n const width = has ? c[prop] : c.width;\n totalWidth = totalWidth + parseFloat(width);\n }\n }\n\n return totalWidth;\n}\n\n/**\n * Calculates the total width of all columns and their groups\n */\nexport function columnsTotalWidth(columns: TableColumn[], prop?: keyof TableColumn) {\n let totalWidth = 0;\n\n for (const column of columns) {\n const has = prop && column[prop];\n totalWidth = totalWidth + (has ? column[prop] : column.width);\n }\n\n return totalWidth;\n}\n\nexport function columnsByPinArr(val: TableColumn[]): PinnedColumns[] {\n const colsByPin = columnsByPin(val);\n return [\n { type: 'left' as const, columns: colsByPin.left },\n { type: 'center' as const, columns: colsByPin.center },\n { type: 'right' as const, columns: colsByPin.right }\n ];\n}\n","/**\n * This object contains the cache of the various row heights that are present inside\n * the data table. Its based on Fenwick tree data structure that helps with\n * querying sums that have time complexity of log n.\n *\n * Fenwick Tree Credits: http://petr-mitrichev.blogspot.com/2013/05/fenwick-tree-range-updates.html\n * https://github.com/mikolalysenko/fenwick-tree\n *\n */\nexport class RowHeightCache {\n /**\n * Tree Array stores the cumulative information of the row heights to perform efficient\n * range queries and updates. Currently the tree is initialized to the base row\n * height instead of the detail row height.\n */\n private treeArray: number[] = [];\n\n /**\n * Clear the Tree array.\n */\n clearCache(): void {\n this.treeArray = [];\n }\n\n /**\n * Initialize the Fenwick tree with row Heights.\n *\n * @param rows The array of rows which contain the expanded status.\n * @param rowHeight The row height.\n * @param detailRowHeight The detail row height.\n */\n initCache(details: any): void {\n const {\n rows,\n rowHeight,\n detailRowHeight,\n externalVirtual,\n rowCount,\n rowIndexes,\n rowExpansions\n } = details;\n const isFn = typeof rowHeight === 'function';\n const isDetailFn = typeof detailRowHeight === 'function';\n\n if (!isFn && isNaN(rowHeight)) {\n throw new Error(`Row Height cache initialization failed. Please ensure that 'rowHeight' is a\n valid number or function value: (${rowHeight}) when 'scrollbarV' is enabled.`);\n }\n\n // Add this additional guard in case detailRowHeight is set to 'auto' as it wont work.\n if (!isDetailFn && isNaN(detailRowHeight)) {\n throw new Error(`Row Height cache initialization failed. Please ensure that 'detailRowHeight' is a\n valid number or function value: (${detailRowHeight}) when 'scrollbarV' is enabled.`);\n }\n\n const n = externalVirtual ? rowCount : rows.length;\n this.treeArray = new Array(n);\n\n for (let i = 0; i < n; ++i) {\n this.treeArray[i] = 0;\n }\n\n for (let i = 0; i < n; ++i) {\n const row = rows[i];\n let currentRowHeight = rowHeight;\n if (isFn) {\n currentRowHeight = rowHeight(row);\n }\n\n // Add the detail row height to the already expanded rows.\n // This is useful for the table that goes through a filter or sort.\n const expanded = rowExpansions.has(row);\n if (row && expanded) {\n if (isDetailFn) {\n const index = rowIndexes.get(row);\n currentRowHeight += detailRowHeight(row, index);\n } else {\n currentRowHeight += detailRowHeight;\n }\n }\n\n this.update(i, currentRowHeight);\n }\n }\n\n /**\n * Given the ScrollY position i.e. sum, provide the rowIndex\n * that is present in the current view port. Below handles edge cases.\n */\n getRowIndex(scrollY: number): number {\n if (scrollY === 0) {\n return 0;\n }\n return this.calcRowIndex(scrollY);\n }\n\n /**\n * When a row is expanded or rowHeight is changed, update the height. This can\n * be utilized in future when Angular Data table supports dynamic row heights.\n */\n update(atRowIndex: number, byRowHeight: number): void {\n if (!this.treeArray.length) {\n throw new Error(`Update at index ${atRowIndex} with value ${byRowHeight} failed:\n Row Height cache not initialized.`);\n }\n\n const n = this.treeArray.length;\n atRowIndex |= 0;\n\n while (atRowIndex < n) {\n this.treeArray[atRowIndex] += byRowHeight;\n atRowIndex |= atRowIndex + 1;\n }\n }\n\n /**\n * Range Sum query from 1 to the rowIndex\n */\n query(atIndex: number): number {\n if (!this.treeArray.length) {\n throw new Error(`query at index ${atIndex} failed: Fenwick tree array not initialized.`);\n }\n\n let sum = 0;\n atIndex |= 0;\n\n while (atIndex >= 0) {\n sum += this.treeArray[atIndex];\n atIndex = (atIndex & (atIndex + 1)) - 1;\n }\n\n return sum;\n }\n\n /**\n * Find the total height between 2 row indexes\n */\n queryBetween(atIndexA: number, atIndexB: number): number {\n return this.query(atIndexB) - this.query(atIndexA - 1);\n }\n\n /**\n * Given the ScrollY position i.e. sum, provide the rowIndex\n * that is present in the current view port.\n */\n private calcRowIndex(sum: number): number {\n if (!this.treeArray.length) {\n return 0;\n }\n\n let pos = -1;\n const dataLength = this.treeArray.length;\n\n // Get the highest bit for the block size.\n const highestBit = Math.pow(2, dataLength.toString(2).length - 1);\n\n for (let blockSize = highestBit; blockSize !== 0; blockSize >>= 1) {\n const nextPos = pos + blockSize;\n if (nextPos < dataLength && sum >= this.treeArray[nextPos]) {\n sum -= this.treeArray[nextPos];\n pos = nextPos;\n }\n }\n\n return pos + 1;\n }\n}\n","export enum Keys {\n up = 'ArrowUp',\n down = 'ArrowDown',\n return = 'Enter',\n escape = 'Escape',\n left = 'ArrowLeft',\n right = 'ArrowRight'\n}\n","import { TableColumn, TableColumnProp } from './table-column.type';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nexport interface SortPropDir {\n dir: SortDirection | 'desc' | 'asc';\n prop: TableColumnProp;\n}\n\nexport enum SortDirection {\n asc = 'asc',\n desc = 'desc'\n}\n\nexport interface InnerSortEvent {\n column: TableColumn;\n prevValue: SortDirection;\n newValue: SortDirection;\n}\n\nexport interface SortEvent extends InnerSortEvent {\n sorts: SortPropDir[];\n}\n\nexport enum SortType {\n single = 'single',\n multi = 'multi'\n}\n\nexport enum ColumnMode {\n standard = 'standard',\n flex = 'flex',\n force = 'force'\n}\n\nexport type TreeStatus = 'collapsed' | 'expanded' | 'loading' | 'disabled';\n\nexport interface ActivateEvent<TRow> {\n type: 'checkbox' | 'click' | 'dblclick' | 'keydown' | 'mouseenter';\n event: MouseEvent | KeyboardEvent;\n row: TRow;\n group?: TRow[];\n rowHeight?: number;\n column?: TableColumn;\n value?: any;\n cellElement?: HTMLElement;\n treeStatus?: TreeStatus;\n cellIndex?: number;\n rowElement?: HTMLElement;\n}\n\nexport interface HeaderCellContext {\n column: TableColumn;\n sortDir: SortDirection | 'asc' | 'desc';\n sortFn: () => void;\n allRowsSelected: boolean;\n selectFn: () => void;\n}\n\nexport interface GroupContext<TRow = any> {\n group: Group<TRow>;\n expanded: boolean;\n rowIndex: number;\n}\n\nexport interface CellContext<TRow = any> {\n onCheckboxChangeFn: (event: Event) => void;\n activateFn: (event: ActivateEvent<TRow>) => void;\n row: TRow;\n group: TRow[];\n value: any;\n column: TableColumn;\n rowHeight: number;\n isSelected: boolean;\n rowIndex: number;\n treeStatus: TreeStatus;\n disable$: BehaviorSubject<boolean>;\n onTreeAction: () => void;\n expanded?: boolean;\n}\n\nexport interface FooterContext {\n rowCount: number;\n pageSize: number;\n selectedCount: number;\n curPage: number;\n offset: number;\n}\n\nexport enum ContextmenuType {\n header = 'header',\n body = 'body'\n}\n\n/** A Group row */\nexport interface Group<TRow> {\n /** The value by which to rows are grouped. */\n key: TRow[keyof TRow];\n /** All rows that are part of the group. */\n value: TRow[];\n}\n\n/** Type for either a row or a group */\nexport type RowOrGroup<TRow> = TRow | Group<TRow>;\n\nexport interface RowDetailContext<TRow = any> {\n row: TRow;\n expanded: boolean;\n rowIndex: number;\n disableRow$?: Observable<boolean>;\n}\n\nexport interface ReorderEvent {\n column: TableColumn;\n prevValue: number;\n newValue: number;\n}\n\nexport interface PageEvent {\n count: number;\n pageSize: number;\n limit: number;\n offset: number;\n}\n\nexport interface PagerPageEvent {\n page: number;\n}\n\nexport interface ColumnResizeEvent {\n column: TableColumn;\n prevValue: number;\n newValue: number;\n}\n\nexport interface ScrollEvent {\n offsetY: number;\n offsetX: number;\n}\n\nexport interface GroupToggleEvent<TRow> {\n type: 'group';\n value: Group<TRow>;\n}\n\nexport interface AllGroupsToggleEvent {\n type: 'all';\n value: boolean;\n}\n\nexport type GroupToggleEvents<TRow> = GroupToggleEvent<TRow> | AllGroupsToggleEvent;\n\nexport enum SelectionType {\n single = 'single',\n multi = 'multi',\n multiClick = 'multiClick',\n cell = 'cell',\n checkbox = 'checkbox'\n}\n\nexport type DragEventType =\n | 'drag'\n | 'dragend'\n | 'dragenter'\n | 'dragleave'\n | 'dragover'\n | 'dragstart'\n | 'drop';\n\nexport interface DragEventData {\n event: DragEvent;\n srcElement: HTMLElement;\n targetElement?: HTMLElement;\n eventType: DragEventType;\n dragRow: any;\n dropRow?: any;\n}\n","import { ChangeDetectionStrategy, Component, Input, numberAttribute } from '@angular/core';\nimport { TableColumn } from '../../../types/table-column.type';\nimport { NgTemplateOutlet } from '@angular/common';\n\n@Component({\n selector: `ghost-loader`,\n templateUrl: `./ghost-loader.component.html`,\n styleUrls: [`./ghost-loader.component.scss`],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgTemplateOutlet]\n})\nexport class DataTableGhostLoaderComponent {\n @Input() columns: TableColumn[];\n @Input({ transform: numberAttribute }) pageSize: number;\n @Input() rowHeight: number | 'auto' | ((row?: any) => number);\n @Input({ transform: numberAttribute }) ghostBodyHeight: number;\n}\n","<div [style.height]=\"ghostBodyHeight + 'px'\" class=\"ghost-loader ghost-cell-container\">\n @for (item of [].constructor(pageSize); track item) {\n <div [style.height]=\"rowHeight + 'px'\" class=\"ghost-element\">\n @for (col of columns; track col) {\n @if (!col.ghostCellTemplate) {\n <div class=\"line ghost-cell-strip\" [style.width]=\"col?.width + 'px'\"> </div>\n } @else {\n <ng-template [ngTemplateOutlet]=\"col.ghostCellTemplate\"> </ng-template>\n }\n }\n </div>\n }\n</div>\n","import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DoCheck,\n ElementRef,\n EventEmitter,\n HostBinding,\n HostListener,\n inject,\n Input,\n OnDestroy,\n Output,\n PipeTransform,\n ViewChild,\n ViewContainerRef\n} from '@angular/core';\n\nimport { TableColumn } from '../../types/table-column.type';\nimport { Keys } from '../../utils/keys';\nimport { BehaviorSubject } from 'rxjs';\nimport {\n ActivateEvent,\n CellContext,\n RowOrGroup,\n SortDirection,\n SortPropDir,\n TreeStatus\n} from '../../types/public.types';\nimport { DataTableGhostLoaderComponent } from './ghost-loader/ghost-loader.component';\nimport { AsyncPipe, NgTemplateOutlet } from '@angular/common';\n\n@Component({\n selector: 'datatable-body-cell',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (row) {\n <div class=\"datatable-body-cell-label\" [style.margin-left.px]=\"calcLeftMargin(column, row)\">\n @if (column.checkboxable && (!displayCheck || displayCheck(row, column, value))) {\n <label class=\"datatable-checkbox\">\n <input\n type=\"checkbox\"\n [disabled]=\"disable$ | async\"\n [checked]=\"isSelected\"\n (click)=\"onCheckboxChange($event)\"\n />\n </label>\n } @if (column.isTreeColumn) { @if (!column.treeToggleTemplate) {\n <button\n class=\"datatable-tree-button\"\n [disabled]=\"treeStatus === 'disabled'\"\n (click)=\"onTreeAction()\"\n [attr.aria-label]=\"treeStatus\"\n >\n <span>\n @if (treeStatus === 'loading') {\n <i class=\"icon datatable-icon-collapse\"></i>\n } @if (treeStatus === 'collapsed') {\n <i class=\"icon datatable-icon-up\"></i>\n } @if (treeStatus === 'expanded' || treeStatus === 'disabled') {\n <i class=\"icon datatable-icon-down\"></i>\n }\n </span>\n </button>\n } @else {\n <ng-template\n [ngTemplateOutlet]=\"column.treeToggleTemplate\"\n [ngTemplateOutletContext]=\"{ cellContext: cellContext }\"\n >\n </ng-template>\n } } @if (!column.cellTemplate) { @if (column.bindAsUnsafeHtml) {\n <span [title]=\"sanitizedValue\" [innerHTML]=\"value\"> </span>\n } @else {\n <span [title]=\"sanitizedValue\">{{ value }}</span>\n } } @else {\n <ng-template\n #cellTemplate\n [ngTemplateOutlet]=\"column.cellTemplate\"\n [ngTemplateOutletContext]=\"cellContext\"\n >\n </ng-template>\n }\n </div>\n } @else { @if (ghostLoadingIndicator) {\n <ghost-loader [columns]=\"[column]\" [pageSize]=\"1\"></ghost-loader>\n } }\n `,\n imports: [NgTemplateOutlet, DataTableGhostLoaderComponent, AsyncPipe]\n})\nexport class DataTableBodyCellComponent<TRow extends { level?: number } = any>\n implements DoCheck, OnDestroy\n{\n private cd = inject(ChangeDetectorRef);\n\n @Input() displayCheck: (row: RowOrGroup<TRow>, column: TableColumn, value: any) => boolean;\n\n _disable$: BehaviorSubject<boolean>;\n @Input() set disable$(val: BehaviorSubject<boolean>) {\n this._disable$ = val;\n this.cellContext.disable$ = val;\n }\n get disable$() {\n return this._disable$;\n }\n\n @Input() set group(group: TRow[]) {\n this._group = group;\n this.cellContext.group = group;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get group() {\n return this._group;\n }\n\n @Input() set rowHeight(val: number) {\n this._rowHeight = val;\n this.cellContext.rowHeight = val;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get rowHeight() {\n return this._rowHeight;\n }\n\n @Input() set isSelected(val: boolean) {\n this._isSelected = val;\n this.cellContext.isSelected = val;\n this.cd.markForCheck();\n }\n\n get isSelected(): boolean {\n return this._isSelected;\n }\n\n @Input() set expanded(val: boolean) {\n this._expanded = val;\n this.cellContext.expanded = val;\n this.cd.markForCheck();\n }\n\n get expanded(): boolean {\n return this._expanded;\n }\n\n @Input() set rowIndex(val: number) {\n this._rowIndex = val;\n this.cellContext.rowIndex = val;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get rowIndex(): number {\n return this._rowIndex;\n }\n\n @Input() set column(column: TableColumn) {\n this._column = column;\n this.cellContext.column = column;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get column(): TableColumn {\n return this._column;\n }\n\n @Input() set row(row: TRow) {\n this._row = row;\n this.cellContext.row = row;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get row(): TRow {\n return this._row;\n }\n\n @Input() set sorts(val: SortPropDir[]) {\n this._sorts = val;\n this.sortDir = this.calcSortDir(val);\n }\n\n get sorts(): SortPropDir[] {\n return this._sorts;\n }\n\n @Input() set treeStatus(status: TreeStatus) {\n if (\n status !== 'collapsed' &&\n status !== 'expanded' &&\n status !== 'loading' &&\n status !== 'disabled'\n ) {\n this._treeStatus = 'collapsed';\n } else {\n this._treeStatus = status;\n }\n this.cellContext.treeStatus = this._treeStatus;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get treeStatus(): TreeStatus {\n return this._treeStatus;\n }\n\n @Input() ghostLoadingIndicator = false;\n\n @Output() activate: EventEmitter<ActivateEvent<TRow>> = new EventEmitter();\n\n @Output() treeAction: EventEmitter<any> = new EventEmitter();\n\n @ViewChild('cellTemplate', { read: ViewContainerRef, static: true })\n cellTemplate: ViewContainerRef;\n\n @ViewChild('ghostLoaderTemplate', { read: ViewContainerRef, static: true })\n ghostLoaderTemplate: ViewContainerRef;\n\n @HostBinding('class')\n get columnCssClasses(): string {\n let cls = 'datatable-body-cell';\n if (this.column.cellClass) {\n if (typeof this.column.cellClass === 'string') {\n cls += ' ' + this.column.cellClass;\n } else if (typeof this.column.cellClass === 'function') {\n const res = this.column.cellClass({\n row: this.row,\n group: this.group,\n column: this.column,\n value: this.value,\n rowHeight: this.rowHeight\n });\n\n if (typeof res === 'string') {\n cls += ' ' + res;\n } else if (typeof res === 'object') {\n const keys = Object.keys(res);\n for (const k of keys) {\n if (res[k] === true) {\n cls += ` ${k}`;\n }\n }\n }\n }\n }\n if (!this.sortDir) {\n cls += ' sort-active';\n }\n if (this.isFocused && !this.disable$?.value) {\n cls += ' active';\n }\n if (this.sortDir === SortDirection.asc) {\n cls += ' sort-asc';\n }\n if (this.sortDir === SortDirection.desc) {\n cls += ' sort-desc';\n }\n if (this.disable$?.value) {\n cls += ' row-disabled';\n }\n\n return cls;\n }\n\n @HostBinding('style.width.px')\n get width(): number {\n return this.column.width;\n }\n\n @HostBinding('style.minWidth.px')\n get minWidth(): number {\n return this.column.minWidth;\n }\n\n @HostBinding('style.maxWidth.px')\n get maxWidth(): number {\n return this.column.maxWidth;\n }\n\n @HostBinding('style.height')\n get height(): string | number {\n const height = this.rowHeight;\n if (isNaN(height)) {\n return height;\n }\n return height + 'px';\n }\n\n sanitizedValue: string;\n value: any;\n sortDir: SortDirection;\n isFocused = false;\n\n cellContext: CellContext<TRow>;\n\n private _isSelected: boolean;\n private _sorts: SortPropDir[];\n private _column: TableColumn;\n private _row: TRow;\n private _group: TRow[];\n private _rowHeight: number;\n private _rowIndex: number;\n private _expanded: boolean;\n private _element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private _treeStatus: TreeStatus;\n\n constructor() {\n this.cellContext = {\n onCheckboxChangeFn: (event: MouseEvent | KeyboardEvent) => this.onCheckboxChange(event),\n activateFn: (event: ActivateEvent<TRow>) => this.activate.emit(event),\n row: this.row,\n group: this.group,\n value: this.value,\n column: this.column,\n rowHeight: this.rowHeight,\n isSelected: this.isSelected,\n rowIndex: this.rowIndex,\n treeStatus: this.treeStatus,\n disable$: this.disable$,\n onTreeAction: () => this.onTreeAction()\n };\n }\n\n ngDoCheck(): void {\n this.checkValueUpdates();\n }\n\n ngOnDestroy(): void {\n if (this.cellTemplate) {\n this.cellTemplate.clear();\n }\n if (this.ghostLoaderTemplate) {\n this.ghostLoaderTemplate.clear();\n }\n }\n\n checkValueUpdates(): void {\n let value = '';\n\n if (!this.row || !this.column) {\n value = '';\n } else {\n const val = this.column.$$valueGetter(this.row, this.column.prop);\n const userPipe: PipeTransform = this.column.pipe;\n\n if (userPipe) {\n value = userPipe.transform(val);\n }