@swimlane/ngx-datatable
Version:
ngx-datatable is an Angular table grid component for presenting large and complex data.
1 lines • 357 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/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/body-cell.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/events.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/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-row.directive.ts","../../../../projects/swimlane/ngx-datatable/src/lib/utils/selection.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/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/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/ngx-datatable.config.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})\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})\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, Row } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-group-header'\n})\nexport class DatatableGroupHeaderDirective<TRow extends Row = 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>> | undefined {\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 | undefined): ValueGetter {\n // TODO requires better typing which will also involve adjust TableColum. So postponing it.\n if (prop == null) {\n return emptyStringGetter;\n }\n\n if (typeof prop === 'number') {\n return numericIndexGetter as ValueGetter;\n } else {\n // deep or simple\n if (prop.indexOf('.') !== -1) {\n return deepValueGetter as ValueGetter;\n } else {\n return shallowValueGetter as ValueGetter;\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';\nimport { Row } from '../types/public.types';\n\nexport type OptionalValueGetter = ((row: any) => any) | undefined;\nexport function optionalGetterForProp(prop: TableColumnProp | undefined): OptionalValueGetter {\n return prop ? row => getterForProp(prop)(row, prop) : undefined;\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 extends Row>(\n rows: (TRow | undefined)[],\n from?: OptionalValueGetter,\n to?: OptionalValueGetter\n): (TRow | undefined)[] {\n if (from && to) {\n const treeRows = rows.filter(row => !!row).map(row => new TreeNode(row));\n const uniqIDs = new Map(treeRows.map(node => [to(node.row), node]));\n\n const rootNodes = treeRows.reduce((root, node) => {\n const fromValue = from(node.row);\n const parent = uniqIDs.get(fromValue);\n if (parent) {\n node.row.level = parent.row.level! + 1; // TODO: should be reflected by type, that level is defined\n node.parent = parent;\n parent.children.push(node);\n } else {\n node.row.level = 0;\n root.push(node);\n }\n return root;\n }, [] as TreeNode<TRow>[]);\n\n return rootNodes.flatMap(child => child.flatten());\n } else {\n return rows;\n }\n}\n\nclass TreeNode<TRow extends Row> {\n public row: TRow;\n public parent?: TreeNode<TRow>;\n public children: TreeNode<TRow>[];\n\n constructor(row: TRow) {\n this.row = row;\n this.children = [];\n }\n\n flatten(): TRow[] {\n if (this.row.treeStatus === 'expanded') {\n return [this.row, ...this.children.flatMap(child => child.flatten())];\n } else {\n return [this.row];\n }\n }\n}\n","import { Directive } from '@angular/core';\nimport { HeaderCellContext } from '../../types/public.types';\n\n@Directive({\n selector: '[ngx-datatable-header-template]'\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})\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})\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})\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, Row } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-column'\n})\nexport class DataTableColumnDirective<TRow extends Row> 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?: (\n valueA: any,\n valueB: any,\n rowA: TRow,\n rowB: TRow,\n sortDir: 'desc' | 'asc'\n ) => number;\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>> | undefined {\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> | undefined {\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> | undefined {\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> | undefined {\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})\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 { DetailToggleEvents, Row, RowDetailContext } from '../../types/public.types';\n\n@Directive({\n selector: 'ngx-datatable-row-detail'\n})\nexport class DatatableRowDetailDirective<TRow extends Row = 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>> | undefined {\n return this._templateInput || this._templateQuery;\n }\n\n /**\n * Row detail row visbility was toggled.\n */\n @Output() toggle = new EventEmitter<DetailToggleEvents<TRow>>();\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, 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})\nexport class DatatableFooterDirective {\n @Input('template')\n _templateInput?: TemplateRef<FooterContext>;\n\n @ContentChild(DataTableFooterTemplateDirective, { read: TemplateRef })\n _templateQuery?: TemplateRef<FooterContext>;\n\n get template(): TemplateRef<FooterContext> | undefined {\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})\nexport class ScrollerComponent implements OnInit, OnDestroy {\n private renderer = inject(Renderer2);\n\n @Input() scrollbarV?: boolean;\n @Input() scrollbarH?: boolean;\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(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 {\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 {\n ColumnGroupWidth,\n PinnedColumns,\n TableColumnGroup,\n TableColumnInternal\n} from '../types/internal.types';\n\n/**\n * Returns the columns by pin.\n */\nexport function columnsByPin(cols: TableColumnInternal[]) {\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(\n groups: TableColumnGroup,\n all: TableColumnInternal[]\n): 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\n */\nexport function columnTotalWidth(columns?: TableColumnInternal[]) {\n return columns?.reduce((total, column) => total + column.width, 0) ?? 0;\n}\n\nexport function columnsByPinArr(val: TableColumnInternal[]): 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 indexOffset,\n rowCount,\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 = indexOffset + i;\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';\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 SortEvent {\n column: TableColumn;\n prevValue: SortDirection | undefined;\n newValue: SortDirection | undefined;\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: Event;\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' | undefined;\n sortFn: () => void;\n allRowsSelected?: boolean;\n selectFn: () => void;\n}\n\nexport interface GroupContext<TRow extends Row = any> {\n group: Group<TRow>;\n expanded: boolean;\n rowIndex: number;\n}\n\nexport interface CellContext<TRow extends Row = 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 rowInGroupIndex?: number;\n treeStatus?: TreeStatus;\n disabled?: 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 extends Row = any> {\n row: TRow;\n expanded: boolean;\n rowIndex: number;\n disabled?: boolean;\n}\n\n/**\n * Consumer provided rows should extend this interface\n * to get access to implicit row properties which are set by the datatable if required.\n */\nexport interface Row {\n [key: TableColumnProp]: any;\n treeStatus?: TreeStatus;\n level?: number;\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 /** @deprecated Use {@link pageSize} instead. */\n limit: number | undefined;\n offset: number;\n sorts: SortPropDir[];\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 interface DetailToggleEvent<TRow> {\n type: 'row';\n value: TRow;\n}\n\nexport interface AllDetailToggleEvent {\n type: 'all';\n value: boolean;\n}\n\nexport type DetailToggleEvents<TRow> = DetailToggleEvent<TRow> | AllDetailToggleEvent;\n\nexport enum SelectionType {\n single = 'single',\n multi = 'multi',\n multiClick = 'multiClick',\n cell = 'cell',\n checkbox = 'checkbox'\n}\n\nexport interface SelectEvent<TRow> {\n selected: TRow[];\n}\n\nexport interface ContextMenuEventBody<TRow> {\n event: MouseEvent;\n type: ContextmenuType.body;\n content: RowOrGroup<TRow>;\n}\n\nexport interface ContextMenuEvenHeader {\n event: MouseEvent;\n type: ContextmenuType.header;\n content: TableColumn;\n}\n\nexport type ContextMenuEvent<TRow> = ContextMenuEventBody<TRow> | ContextMenuEvenHeader;\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 {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DoCheck,\n ElementRef,\n EventEmitter,\n HostBinding,\n HostListener,\n inject,\n Input,\n Output\n} from '@angular/core';\n\nimport { Keys } from '../../utils/keys';\nimport {\n ActivateEvent,\n CellContext,\n Row,\n RowOrGroup,\n SortDirection,\n SortPropDir,\n TreeStatus\n} from '../../types/public.types';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { CellActiveEvent, RowIndex, TableColumnInternal } from '../../types/internal.types';\n\n@Component({\n selector: 'datatable-body-cell',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\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]=\"disabled\"\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 [ngTemplateOutlet]=\"column.cellTemplate\" [ngTemplateOutletContext]=\"cellContext\">\n </ng-template>\n }\n </div>\n `,\n styleUrl: './body-cell.component.scss',\n imports: [NgTemplateOutlet]\n})\nexport class DataTableBodyCellComponent<TRow extends Row = any> implements DoCheck {\n private cd = inject(ChangeDetectorRef);\n\n @Input() displayCheck?: (row: TRow, column: TableColumnInternal, value: any) => boolean;\n\n @Input() set disabled(value: boolean | undefined) {\n this.cellContext.disabled = value;\n this._disabled = value;\n }\n\n get disabled(): boolean | undefined {\n return this._disabled;\n }\n\n @Input() set group(group: TRow[] | undefined) {\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 | undefined) {\n this._isSelected = val;\n this.cellContext.isSelected = val;\n this.cd.markForCheck();\n }\n\n get isSelected(): boolean | undefined {\n return this._isSelected;\n }\n\n @Input() set expanded(val: boolean | undefined) {\n this._expanded = val;\n this.cellContext.expanded = val;\n this.cd.markForCheck();\n }\n\n get expanded(): boolean | undefined {\n return this._expanded;\n }\n\n @Input() set rowIndex(val: RowIndex) {\n this._rowIndex = val;\n this.cellContext.rowIndex = val?.index;\n this.cellContext.rowInGroupIndex = val?.indexInGroup;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get rowIndex(): RowIndex {\n return this._rowIndex;\n }\n\n @Input() set column(column: TableColumnInternal) {\n this._column = column;\n this.cellContext.column = column;\n this.checkValueUpdates();\n this.cd.markForCheck();\n }\n\n get column(): TableColumnInternal {\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 | undefined) {\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 | undefined {\n return this._treeStatus;\n }\n\n @Output() activate = new EventEmitter<CellActiveEvent<TRow>>();\n\n @Output() treeAction: EventEmitter<any> = new EventEmitter();\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._disabled) {\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._disabled) {\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 | undefined {\n return this.column.minWidth;\n }\n\n @HostBinding('style.maxWidth.px')\n get maxWidth(): number | undefined {\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!: TableColumnInternal;\n private _row!: TRow;\n private _group?: TRow[];\n private _rowHeight!: number;\n private _rowIndex!: RowIndex;\n private _expanded?: boolean;\n private _element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n private _treeStatus?: TreeStatus;\n private _disabled?: boolean;\n\n constructor() {\n this.cellContext = {\n onCheckboxChangeFn: (event: Event) => 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?.index,\n rowInGroupIndex: this.rowIndex?.indexInGroup,\n treeStatus: this.treeStatus,\n disabled: this._disabled,\n onTreeAction: () => this.onTreeAction()\n };\n }\n\n ngDoCheck(): void {\n this.checkValueUpdates();\n }\n\n checkValueUpdates(): void {\n let value = '';\n\n if (!this.row || !this.column || this.column.prop == undefined) {\n value = '';\n } else {\n const val = this.column.$$valueGetter(this.row, this.column.prop);\n const userPipe = this.column.pipe;\n\n if (userPipe) {\n value = userPipe.transform(val);\n } else if (value !== undefined) {\n value = val;\n }\n }\n\n if (this.value !== value) {\n this.value = value;\n this.cellContext.value = value;\n this.cellContext.disabled = this._disabled;\n this.sanitizedValue = value !== null && value !== undefined ? this.stripHtml(value) : value;\n this.cd.markForCheck();\n }\n }\n\n @HostListener('focus')\n onFocus(): void {\n this.isFocused = true;\n }\n\n @HostListener('blur')\n onBlur(): void {\n this.isFocused = false;\n }\n\n @HostListener('click', ['$event'])\n onClick(event: MouseEvent): void {\n this.activate.emit({\n type: 'click',\n event,\n row: this.row,\n group: this.group,\n rowHeight: this.rowHeight,\n column: this.column,\n value: this.value,\n cellElement: this._element\n });\n }\n\n @HostListener('dblclick', ['$event'])\n onDblClick(event: MouseEvent): void {\n this.activate.emit({\n type: 'dblclick',\n event,\n row: this.row,\n group: this.group,\n rowHeight: this.rowHeight,\n column: this.column,\n value: this.value,\n cellElement: this._element\n });\n }\n\n @HostListener('keydown', ['$event'])\n onKeyDown(event: KeyboardEvent): void {\n const key = event.key;\n const isTargetCell = event.target === this._element;\n\n const isAction =\n key === Keys.return ||\n key === Keys.down ||\n key === Keys.up ||\n key === Keys.left ||\n key === Keys.right;\n\n if (isAction && isTargetCell) {\n event.preventDefault();\n event.stopPropagation();\n\n this.activate.emit({\n type: 'keydown',\n event,\n row: this.row,\n group: this.group,\n rowHeight: this.rowHeight,\n column: this.column,\n value: this.value,\n cellElement: this._element\n });\n }\n }\n\n onCheckboxChange(event: Event): void {\n this.activate.emit({\n type: 'checkbox',\n event,\n row: this.row,\n group: this.group,\n rowHeight: this.rowHeight,\n column: this.column,\n value: this.value,\n cellElement: this._element,\n treeStatus: 'collapsed'\n });\n }\n\n calcSortDir(sorts: SortPropDir[]): SortDirection | undefined {\n if (!sorts) {\n return undefined;\n }\n\n const sort = sorts.find(s => s.prop === this.column.prop);\n\n return sort?.dir as SortDirection;\n }\n\n stripHtml(html: string): string {\n if (!html.replace) {\n return html;\n }\n return html.replace(/<\\/?[^>]+(>|$)/g, '');\n }\n\n onTreeAction() {\n this.treeAction.emit(this.row);\n }\n\n calcLeftMargin(column: TableColumnInternal, row: RowOrGroup<TRow>): number {\n const levelIndent = column.treeLevelIndent != null ? column.treeLevelIndent : 50;\n return column.isTreeColumn ? (row as TRow).level! * levelIndent : 0;\n }\n}\n","import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n DoCheck,\n ElementRef,\n EventEmitter,\n HostBinding,\n HostListener,\n inject,\n Input,\n KeyValueDiffer,\n KeyValueDiffers,\n OnChanges,\n Output,\n SimpleChanges\n} from '@angular/core';\n\nimport { columnGroupWidths, columnsByPin, columnsByPinArr } from '../../utils/column';\nimport { Keys } from '../../utils/keys';\nimport { ActivateEvent, Row, RowOrGroup, TreeStatus } from '../../types/public.types';\nimport {\n CellActiveEvent,\n ColumnGroupWidth,\n PinnedColumns,\n RowIndex,\n TableColumnInternal\n} from '../../types/internal.types';\nimport { DataTableBodyCellComponent } from './body-cell.component';\n\n@Component({\n selector: 'datatable-body-row',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @for (colGroup of _columnsByPin; track colGroup.type) { @if (colGroup.columns.length) {\n <div\n class=\"datatable-row-{{ colGroup.type }} datatable-row-group\"\n [style.width.px]=\"_columnGroupWidths[colGroup.type]\"\n [class.row-disabled]=\"disabled\"\n >\n @for (column of colGroup.columns; track column.$$id; let ii = $index) {\n <datatable-body-cell\n role=\"cell\"\n tabindex=\"-1\"\n [row]=\"row\"\n [group]=\"group\"\n [expanded]=\"expanded\"\n [isSelected]=\"isSelected\"\n [rowIndex]=\"rowIndex\"\n [column]=\"column\"\n [rowHeight]=\"rowHeight\"\n [displayCheck]=\"displayCheck\"\n [disabled]=\"disabled\"\n [treeStatus]=\"treeStatus\"\n (activate)=\"onActivate($event, ii)\"\n (treeAction)=\"onTreeAction()\"\n >\n </datatable-body-cell>\n }\n </div>\n } }\n `,\n styleUrl: './body-row.component.scss',\n imports: [DataTableBodyCellComponent]\n})\nexport class DataTableBodyRowComponent<TRow extends Row = any> implements DoCheck, OnChanges {\n private cd = inject(ChangeDetectorRef);\n\n @Input() set columns(val: TableColumnInternal[]) {\n this._columns = val;\n this.recalculateColumns(val);\n }\n\n get columns(): TableColumnInternal[] {\n return this._columns;\n }\n\n @Input() set innerWidth(val: number) {\n if (this._columns) {\n const colByPin = columnsByPin(this._columns);\n this._columnGroupWidths = columnGroupWidths(colByPin, this._columns);\n }\n\n this._innerWidth = val;\n this.recalculateColumns();\n }\n\n get innerWidth(): number {\n return this._innerWidth;\n }\n\n @Input() expanded?: boolean;\n @Input() rowClass?: (row: TRow) => string | Record<string, boolean>;\n @Input() row!: TRow;\n @Input() group?: TRow[];\n @Input() isSelected?: boolean;\n @Input() rowIndex!: RowIndex;\n @Input() displayCheck?: (row: TRow, column: TableColumnInternal, value?: any) => boolean;\n @Input() treeStatus?: TreeStatus = 'collapsed';\n @Input() verticalScrollVisible = false;\n\n @Input() disabled?: boolean;\n\n @HostBinding('class')\n get cssClass() {\n let cls = 'datatable-body-row';\n if (this.isSelected) {\n cls += ' active';\n }\n if (this.innerRowIndex % 2 !== 0) {\n cls += ' datatable-row-odd';\n }\n if (this.innerRowIndex % 2 === 0) {\n cls += ' datatable-row-even';\n }\n if (this.disabled) {\n cls += ' row-disabled';\n }\n\n if (this.rowClass) {\n const res = this.rowClass(this.row);\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 return cls;\n }\n\n @HostBinding('style.height.px')\n @Input()\