diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 80848532e..000000000 --- a/.browserslistrc +++ /dev/null @@ -1,12 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -> 0.5% -last 2 versions -Firefox ESR -not dead -not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 5a76414ed..ec5bba0c2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,12 +30,7 @@ module.exports = { rules: { '@angular-eslint/no-attribute-decorator': 'error', '@angular-eslint/no-forward-ref': 'error', - '@angular-eslint/no-host-metadata-property': [ - 'error', - { - 'allowStatic': true - } - ], + '@angular-eslint/no-host-metadata-property': 'off', 'brace-style': 'off', 'no-bitwise': 'off', 'comma-dangle': 'off', diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index 6537d72aa..e104e032e 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -12,13 +12,13 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: 20.17.0 + node-version: 22.16.0 - name: Enable Corepack run: corepack enable - name: Install Yarn - run: corepack prepare yarn@4.5.1 --activate + run: corepack prepare yarn@4.9.2 --activate - name: Install dependencies run: yarn install --immutable diff --git a/docs/api/table/inputs.md b/docs/api/table/inputs.md index 40605615c..751c8d4ef 100644 --- a/docs/api/table/inputs.md +++ b/docs/api/table/inputs.md @@ -62,7 +62,23 @@ Static messages in the table you can override for localization. totalMessage: 'total', // Footer selected message - selectedMessage: 'selected' + selectedMessage: 'selected', + + // Pager screen reader message for the first page button + ariaFirstPageMessage: 'go to first page', + + // Pager screen reader message for the previous page button + ariaPreviousPageMessage: 'go to previous page', + + // Pager screen reader message for the n-th page button. + // It will be rendered as: `{{ariaPageNMessage}} {{n}}`. + ariaPageNMessage: 'page', + + // Pager screen reader message for the next page button + ariaNextPageMessage: 'go to next page', + + // Pager screen reader message for the last page button + ariaLastPageMessage: 'go to last page' } ``` diff --git a/docs/api/table/outputs.md b/docs/api/table/outputs.md index a7ff5ada4..d6335c32e 100644 --- a/docs/api/table/outputs.md +++ b/docs/api/table/outputs.md @@ -18,17 +18,6 @@ A cell or row was focused via keyboard or mouse click. } ``` -### `detailToggle` - -Row detail row was toggled. - -``` -{ - rows - currentIndex -} -``` - ### `page` The table was paged either triggered by the pager or the body scroll. diff --git a/package.json b/package.json index a4fac4fec..4f633e306 100644 --- a/package.json +++ b/package.json @@ -31,31 +31,31 @@ }, "private": true, "dependencies": { - "@angular/animations": "^19.1.4", - "@angular/cdk": "^18.2.14", - "@angular/common": "^19.1.4", - "@angular/compiler": "^19.1.4", - "@angular/core": "^19.1.4", - "@angular/forms": "^19.1.4", - "@angular/platform-browser": "^19.1.4", - "@angular/platform-browser-dynamic": "^19.1.4", - "@angular/router": "^19.1.4", + "@angular/animations": "^20.0.5", + "@angular/cdk": "^20.0.4", + "@angular/common": "^20.0.5", + "@angular/compiler": "^20.0.5", + "@angular/core": "^20.0.5", + "@angular/forms": "^20.0.5", + "@angular/platform-browser": "^20.0.5", + "@angular/platform-browser-dynamic": "^20.0.5", + "@angular/router": "^20.0.5", "rxjs": "~7.8.1", "tslib": "~2.4.0", "zone.js": "~0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^19.1.4", - "@angular-devkit/core": "^19.1.4", - "@angular-devkit/schematics": "^19.1.4", - "@angular-eslint/builder": "^18.0.0", - "@angular-eslint/eslint-plugin": "^18.0.0", - "@angular-eslint/eslint-plugin-template": "^18.0.0", - "@angular-eslint/schematics": "^18.0.0", - "@angular-eslint/template-parser": "^18.0.0", - "@angular/cli": "^19.1.6", - "@angular/compiler-cli": "^19.1.4", - "@angular/language-service": "^19.1.4", + "@angular-devkit/build-angular": "^20.0.4", + "@angular-devkit/core": "^20.0.4", + "@angular-devkit/schematics": "^20.0.4", + "@angular-eslint/builder": "^20.1.1", + "@angular-eslint/eslint-plugin": "^20.1.1", + "@angular-eslint/eslint-plugin-template": "^20.1.1", + "@angular-eslint/schematics": "^20.1.1", + "@angular-eslint/template-parser": "^20.1.1", + "@angular/cli": "^20.0.4", + "@angular/compiler-cli": "^20.0.5", + "@angular/language-service": "^20.0.5", "@swimlane/eslint-config": "^2.0.0", "@swimlane/prettier-config-swimlane": "^3.0.2", "@types/jasmine": "4.3.0", @@ -87,7 +87,7 @@ "karma-coverage-istanbul-reporter": "^3.0.3", "karma-jasmine": "5.1.0", "karma-jasmine-html-reporter": "2.0.0", - "ng-packagr": "^19.0.0", + "ng-packagr": "^20.0.1", "npm-run-all": "^4.1.5", "prettier": "2.7.1", "pretty-quick": "3.1.3", @@ -95,7 +95,7 @@ "sass": "^1.77.6", "scss-bundle": "^3.1.1", "ts-node": "10.9.1", - "typescript": "5.7.3" + "typescript": "5.8.3" }, "husky": { "hooks": { @@ -103,8 +103,8 @@ } }, "volta": { - "node": "20.14.0", - "yarn": "4.5.1" + "node": "22.16.0", + "yarn": "4.9.2" }, - "packageManager": "yarn@4.5.1" + "packageManager": "yarn@4.9.2" } diff --git a/projects/swimlane/ngx-datatable/package.json b/projects/swimlane/ngx-datatable/package.json index f877c8cc8..2d4e2c6f2 100644 --- a/projects/swimlane/ngx-datatable/package.json +++ b/projects/swimlane/ngx-datatable/package.json @@ -3,9 +3,9 @@ "version": "21.1.0", "description": "ngx-datatable is an Angular table grid component for presenting large and complex data.", "peerDependencies": { - "@angular/common": "17.x || 18.x || 19.x", - "@angular/core": "17.x || 18.x || 19.x", - "@angular/platform-browser": "17.x || 18.x || 19.x", + "@angular/common": "18.x || 19.x || 20.x", + "@angular/core": "18.x || 19.x || 20.x", + "@angular/platform-browser": "18.x || 19.x || 20.x", "rxjs": "7.x" }, "dependencies": { diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.scss new file mode 100644 index 000000000..8634aa322 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.scss @@ -0,0 +1,11 @@ +@use '../shared'; + +:host { + @include shared.cell-styles(); +} + +:host-context(ngx-datatable.fixed-row) :host { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.spec.ts index 3215620f4..63dc7b3f8 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.spec.ts @@ -1,43 +1,24 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { numericIndexGetter } from '../../utils/column-prop-getters'; -import { setColumnDefaults } from '../../utils/column-helper'; -import { TableColumn } from '../../types/table-column.type'; +import { toInternalColumn } from '../../utils/column-helper'; import { DataTableBodyCellComponent } from './body-cell.component'; describe('DataTableBodyCellComponent', () => { let fixture: ComponentFixture; let component: DataTableBodyCellComponent; - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DataTableBodyCellComponent] - }); - }); - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableBodyCellComponent); - component = fixture.componentInstance; - }); + fixture = TestBed.createComponent(DataTableBodyCellComponent); + component = fixture.componentInstance; })); - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - }); - describe('prop tests', () => { // verify there wasn't a mistake where the falsey 0 value // resulted in a code path for missing column prop it('should get value from zero-index prop', () => { component.row = ['Hello']; - const columns: TableColumn[] = [{ name: 'First Column', prop: 0 }]; - // users should never set columns on DataTableBodyCellComponent directly - // setColumnDefaults will be run on columns before they are set on BodyCellComponent - setColumnDefaults(columns); + const columns = toInternalColumn([{ name: 'First Column', prop: 0 }]); expect(columns[0].$$valueGetter).toBe(numericIndexGetter); component.column = columns[0]; diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts index 6936306f2..edfac2fb6 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-cell.component.ts @@ -9,38 +9,32 @@ import { HostListener, inject, Input, - OnDestroy, - Output, - PipeTransform, - ViewChild, - ViewContainerRef + Output } from '@angular/core'; -import { TableColumn } from '../../types/table-column.type'; import { Keys } from '../../utils/keys'; -import { BehaviorSubject } from 'rxjs'; import { ActivateEvent, CellContext, + Row, RowOrGroup, SortDirection, SortPropDir, TreeStatus } from '../../types/public.types'; -import { DataTableGhostLoaderComponent } from './ghost-loader/ghost-loader.component'; -import { AsyncPipe, NgTemplateOutlet } from '@angular/common'; +import { NgTemplateOutlet } from '@angular/common'; +import { CellActiveEvent, RowIndex, TableColumnInternal } from '../../types/internal.types'; @Component({ selector: 'datatable-body-cell', changeDetection: ChangeDetectionStrategy.OnPush, template: ` - @if (row) {
@if (column.checkboxable && (!displayCheck || displayCheck(row, column, value))) {
- } @else { @if (ghostLoadingIndicator) { - - } } `, - imports: [NgTemplateOutlet, DataTableGhostLoaderComponent, AsyncPipe] + styleUrl: './body-cell.component.scss', + imports: [NgTemplateOutlet] }) -export class DataTableBodyCellComponent - implements DoCheck, OnDestroy -{ +export class DataTableBodyCellComponent implements DoCheck { private cd = inject(ChangeDetectorRef); - @Input() displayCheck: (row: RowOrGroup, column: TableColumn, value: any) => boolean; + @Input() displayCheck?: (row: TRow, column: TableColumnInternal, value: any) => boolean; - _disable$: BehaviorSubject; - @Input() set disable$(val: BehaviorSubject) { - this._disable$ = val; - this.cellContext.disable$ = val; + @Input() set disabled(value: boolean | undefined) { + this.cellContext.disabled = value; + this._disabled = value; } - get disable$() { - return this._disable$; + + get disabled(): boolean | undefined { + return this._disabled; } - @Input() set group(group: TRow[]) { + @Input() set group(group: TRow[] | undefined) { this._group = group; this.cellContext.group = group; this.checkValueUpdates(); @@ -125,45 +111,46 @@ export class DataTableBodyCellComponent return this._rowHeight; } - @Input() set isSelected(val: boolean) { + @Input() set isSelected(val: boolean | undefined) { this._isSelected = val; this.cellContext.isSelected = val; this.cd.markForCheck(); } - get isSelected(): boolean { + get isSelected(): boolean | undefined { return this._isSelected; } - @Input() set expanded(val: boolean) { + @Input() set expanded(val: boolean | undefined) { this._expanded = val; this.cellContext.expanded = val; this.cd.markForCheck(); } - get expanded(): boolean { + get expanded(): boolean | undefined { return this._expanded; } - @Input() set rowIndex(val: number) { + @Input() set rowIndex(val: RowIndex) { this._rowIndex = val; - this.cellContext.rowIndex = val; + this.cellContext.rowIndex = val?.index; + this.cellContext.rowInGroupIndex = val?.indexInGroup; this.checkValueUpdates(); this.cd.markForCheck(); } - get rowIndex(): number { + get rowIndex(): RowIndex { return this._rowIndex; } - @Input() set column(column: TableColumn) { + @Input() set column(column: TableColumnInternal) { this._column = column; this.cellContext.column = column; this.checkValueUpdates(); this.cd.markForCheck(); } - get column(): TableColumn { + get column(): TableColumnInternal { return this._column; } @@ -187,7 +174,7 @@ export class DataTableBodyCellComponent return this._sorts; } - @Input() set treeStatus(status: TreeStatus) { + @Input() set treeStatus(status: TreeStatus | undefined) { if ( status !== 'collapsed' && status !== 'expanded' && @@ -203,22 +190,14 @@ export class DataTableBodyCellComponent this.cd.markForCheck(); } - get treeStatus(): TreeStatus { + get treeStatus(): TreeStatus | undefined { return this._treeStatus; } - @Input() ghostLoadingIndicator = false; - - @Output() activate: EventEmitter> = new EventEmitter(); + @Output() activate = new EventEmitter>(); @Output() treeAction: EventEmitter = new EventEmitter(); - @ViewChild('cellTemplate', { read: ViewContainerRef, static: true }) - cellTemplate: ViewContainerRef; - - @ViewChild('ghostLoaderTemplate', { read: ViewContainerRef, static: true }) - ghostLoaderTemplate: ViewContainerRef; - @HostBinding('class') get columnCssClasses(): string { let cls = 'datatable-body-cell'; @@ -249,7 +228,7 @@ export class DataTableBodyCellComponent if (!this.sortDir) { cls += ' sort-active'; } - if (this.isFocused && !this.disable$?.value) { + if (this.isFocused && !this._disabled) { cls += ' active'; } if (this.sortDir === SortDirection.asc) { @@ -258,7 +237,7 @@ export class DataTableBodyCellComponent if (this.sortDir === SortDirection.desc) { cls += ' sort-desc'; } - if (this.disable$?.value) { + if (this._disabled) { cls += ' row-disabled'; } @@ -271,12 +250,12 @@ export class DataTableBodyCellComponent } @HostBinding('style.minWidth.px') - get minWidth(): number { + get minWidth(): number | undefined { return this.column.minWidth; } @HostBinding('style.maxWidth.px') - get maxWidth(): number { + get maxWidth(): number | undefined { return this.column.maxWidth; } @@ -289,27 +268,28 @@ export class DataTableBodyCellComponent return height + 'px'; } - sanitizedValue: string; + sanitizedValue!: string; value: any; - sortDir: SortDirection; + sortDir?: SortDirection; isFocused = false; cellContext: CellContext; - private _isSelected: boolean; - private _sorts: SortPropDir[]; - private _column: TableColumn; - private _row: TRow; - private _group: TRow[]; - private _rowHeight: number; - private _rowIndex: number; - private _expanded: boolean; + private _isSelected?: boolean; + private _sorts!: SortPropDir[]; + private _column!: TableColumnInternal; + private _row!: TRow; + private _group?: TRow[]; + private _rowHeight!: number; + private _rowIndex!: RowIndex; + private _expanded?: boolean; private _element = inject>(ElementRef).nativeElement; - private _treeStatus: TreeStatus; + private _treeStatus?: TreeStatus; + private _disabled?: boolean; constructor() { this.cellContext = { - onCheckboxChangeFn: (event: MouseEvent | KeyboardEvent) => this.onCheckboxChange(event), + onCheckboxChangeFn: (event: Event) => this.onCheckboxChange(event), activateFn: (event: ActivateEvent) => this.activate.emit(event), row: this.row, group: this.group, @@ -317,9 +297,10 @@ export class DataTableBodyCellComponent column: this.column, rowHeight: this.rowHeight, isSelected: this.isSelected, - rowIndex: this.rowIndex, + rowIndex: this.rowIndex?.index, + rowInGroupIndex: this.rowIndex?.indexInGroup, treeStatus: this.treeStatus, - disable$: this.disable$, + disabled: this._disabled, onTreeAction: () => this.onTreeAction() }; } @@ -328,23 +309,14 @@ export class DataTableBodyCellComponent this.checkValueUpdates(); } - ngOnDestroy(): void { - if (this.cellTemplate) { - this.cellTemplate.clear(); - } - if (this.ghostLoaderTemplate) { - this.ghostLoaderTemplate.clear(); - } - } - checkValueUpdates(): void { let value = ''; - if (!this.row || !this.column) { + if (!this.row || !this.column || this.column.prop == undefined) { value = ''; } else { const val = this.column.$$valueGetter(this.row, this.column.prop); - const userPipe: PipeTransform = this.column.pipe; + const userPipe = this.column.pipe; if (userPipe) { value = userPipe.transform(val); @@ -356,7 +328,7 @@ export class DataTableBodyCellComponent if (this.value !== value) { this.value = value; this.cellContext.value = value; - this.cellContext.disable$ = this.disable$; + this.cellContext.disabled = this._disabled; this.sanitizedValue = value !== null && value !== undefined ? this.stripHtml(value) : value; this.cd.markForCheck(); } @@ -429,7 +401,7 @@ export class DataTableBodyCellComponent } } - onCheckboxChange(event: MouseEvent | KeyboardEvent): void { + onCheckboxChange(event: Event): void { this.activate.emit({ type: 'checkbox', event, @@ -443,16 +415,14 @@ export class DataTableBodyCellComponent }); } - calcSortDir(sorts: SortPropDir[]): SortDirection { + calcSortDir(sorts: SortPropDir[]): SortDirection | undefined { if (!sorts) { - return; + return undefined; } const sort = sorts.find(s => s.prop === this.column.prop); - if (sort) { - return sort.dir as SortDirection; - } + return sort?.dir as SortDirection; } stripHtml(html: string): string { @@ -466,8 +436,8 @@ export class DataTableBodyCellComponent this.treeAction.emit(this.row); } - calcLeftMargin(column: TableColumn, row: RowOrGroup): number { + calcLeftMargin(column: TableColumnInternal, row: RowOrGroup): number { const levelIndent = column.treeLevelIndent != null ? column.treeLevelIndent : 50; - return column.isTreeColumn ? (row as TRow).level * levelIndent : 0; + return column.isTreeColumn ? (row as TRow).level! * levelIndent : 0; } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts index ea6faf58d..790949b5a 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header-template.directive.ts @@ -2,8 +2,7 @@ import { Directive } from '@angular/core'; import { GroupContext } from '../../types/public.types'; @Directive({ - selector: '[ngx-datatable-group-header-template]', - standalone: true + selector: '[ngx-datatable-group-header-template]' }) export class DatatableGroupHeaderTemplateDirective { static ngTemplateContextGuard( diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.spec.ts deleted file mode 100644 index fa7366b45..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { Component } from '@angular/core'; -import { By } from '@angular/platform-browser'; - -import { DatatableGroupHeaderDirective } from './body-group-header.directive'; -import { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive'; - -@Component({ - selector: 'test-fixture-component', - template: ` - - - - - `, - imports: [DatatableGroupHeaderDirective, DatatableGroupHeaderTemplateDirective] -}) -class TestFixtureComponent {} - -describe('DatatableGroupHeaderDirective', () => { - let fixture: ComponentFixture; - let component: TestFixtureComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - DatatableGroupHeaderDirective, - DatatableGroupHeaderTemplateDirective, - TestFixtureComponent - ] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - let directive: DatatableGroupHeaderDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.directive(DatatableGroupHeaderDirective)) - .injector.get(DatatableGroupHeaderDirective); - }); - - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - - it('should have at least one DatatableGroupHeaderDirective directive', () => { - expect(directive).toBeTruthy(); - }); - }); - - describe('directive #1', () => { - let directive: DatatableGroupHeaderDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.css('#t1')) - .injector.get(DatatableGroupHeaderDirective); - }); - - it('should be found', () => { - expect(directive).toBeTruthy(); - }); - - it('should not have a template', () => { - fixture.detectChanges(); - expect(directive.template).toBeUndefined(); - }); - }); - - describe('directive #2', () => { - let directive: DatatableGroupHeaderDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.css('#t2')) - .injector.get(DatatableGroupHeaderDirective); - }); - - it('should be found', () => { - expect(directive).toBeTruthy(); - }); - - it('should have a template', () => { - fixture.detectChanges(); - expect(directive.template).toBeDefined(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts index 1fb6ce5e4..7bdfa376d 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-group-header.directive.ts @@ -1,12 +1,11 @@ import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; import { DatatableGroupHeaderTemplateDirective } from './body-group-header-template.directive'; -import { Group, GroupContext, GroupToggleEvents } from '../../types/public.types'; +import { Group, GroupContext, GroupToggleEvents, Row } from '../../types/public.types'; @Directive({ - selector: 'ngx-datatable-group-header', - standalone: true + selector: 'ngx-datatable-group-header' }) -export class DatatableGroupHeaderDirective { +export class DatatableGroupHeaderDirective { /** * Row height is required when virtual scroll is enabled. */ @@ -18,12 +17,12 @@ export class DatatableGroupHeaderDirective { @Input() checkboxable = false; @Input('template') - _templateInput: TemplateRef>; + _templateInput?: TemplateRef>; @ContentChild(DatatableGroupHeaderTemplateDirective, { read: TemplateRef, static: true }) - _templateQuery: TemplateRef>; + _templateQuery?: TemplateRef>; - get template(): TemplateRef> { + get template(): TemplateRef> | undefined { return this._templateInput || this._templateQuery; } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-def.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-def.component.ts index 5715e376a..1e294ffcc 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-def.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-def.component.ts @@ -10,6 +10,7 @@ import { ViewContainerRef } from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; +import { RowOrGroup } from '../../types/public.types'; /** * This component is passed as ng-template and rendered by BodyComponent. @@ -21,18 +22,21 @@ import { NgTemplateOutlet } from '@angular/common'; template: `@if (rowDef.rowDefInternal.rowTemplate) { }`, imports: [NgTemplateOutlet] }) export class DatatableRowDefComponent { rowDef = inject(RowDefToken); + rowContext = { + ...this.rowDef.rowDefInternal, + disabled: this.rowDef.rowDefInternalDisabled + }; } @Directive({ - selector: '[rowDef]', - standalone: true + selector: '[rowDef]' }) export class DatatableRowDefDirective { static ngTemplateContextGuard( @@ -47,13 +51,13 @@ export class DatatableRowDefDirective { * @internal To be used internally by ngx-datatable. */ @Directive({ - selector: '[rowDefInternal]', - standalone: true + selector: '[rowDefInternal]' }) export class DatatableRowDefInternalDirective implements OnInit { vc = inject(ViewContainerRef); - @Input() rowDefInternal?: RowDefContext; + @Input() rowDefInternal!: RowDefContext; + @Input() rowDefInternalDisabled?: boolean; ngOnInit(): void { this.vc.createEmbeddedView( @@ -78,6 +82,6 @@ const RowDefToken = new InjectionToken('RowDef type RowDefContext = { template: TemplateRef; rowTemplate: TemplateRef; - row: any; + row: RowOrGroup; index: number; }; diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.scss new file mode 100644 index 000000000..b238efb90 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.scss @@ -0,0 +1,8 @@ +:host { + display: flex; + flex-direction: column; +} + +.datatable-row-detail { + overflow-y: hidden; +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.spec.ts deleted file mode 100644 index ab61e3cf1..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { DataTableRowWrapperComponent } from './body-row-wrapper.component'; -import { DatatableComponentToken } from '../../utils/table-token'; - -describe('DataTableRowWrapperComponent', () => { - let fixture: ComponentFixture; - let component: DataTableRowWrapperComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DataTableRowWrapperComponent], - providers: [ - { - provide: DatatableComponentToken, - useValue: {} - } - ] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableRowWrapperComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts index d54db0a5e..efcb37a3e 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row-wrapper.component.ts @@ -3,7 +3,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - computed, DoCheck, ElementRef, EventEmitter, @@ -21,10 +20,9 @@ import { SimpleChanges, ViewChild } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; import { NgTemplateOutlet } from '@angular/common'; import { DatatableComponentToken } from '../../utils/table-token'; -import { Group, GroupContext, RowDetailContext, RowOrGroup } from '../../types/public.types'; +import { Group, GroupContext, Row, RowDetailContext, RowOrGroup } from '../../types/public.types'; import { DatatableGroupHeaderDirective } from './body-group-header.directive'; import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive'; @@ -32,28 +30,28 @@ import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive' selector: 'datatable-row-wrapper', changeDetection: ChangeDetectionStrategy.OnPush, template: ` - @if (groupHeader?.template) { + @if (isGroup(row) && groupHeader?.template) {
- @if (groupHeader.checkboxable) { + @if (groupHeader!.checkboxable) {
}
@@ -62,7 +60,7 @@ import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive' } @if (rowDetail?.template && expanded) {
- +
} @@ -70,55 +68,45 @@ import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive' host: { class: 'datatable-row-wrapper' }, + styleUrl: './body-row-wrapper.component.scss', imports: [NgTemplateOutlet] }) -export class DataTableRowWrapperComponent implements DoCheck, OnInit, OnChanges { +export class DataTableRowWrapperComponent + implements DoCheck, OnInit, OnChanges +{ @ViewChild('select') checkBoxInput!: ElementRef; - @Input() innerWidth: number; - @Input() rowDetail: DatatableRowDetailDirective; - @Input() groupHeader: DatatableGroupHeaderDirective; - @Input() offsetX: number; - @Input() detailRowHeight: number; - @Input() groupHeaderRowHeight: number; - @Input() row: RowOrGroup; - @Input() groupedRows: Group[]; - @Input() disableCheck: (row: RowOrGroup) => boolean; - @Input() selected: TRow[]; + @Input() innerWidth!: number; + @Input() rowDetail?: DatatableRowDetailDirective; + @Input() groupHeader?: DatatableGroupHeaderDirective; + @Input() offsetX!: number; + @Input() detailRowHeight!: number; + @Input() groupHeaderRowHeight!: number; + @Input() row!: RowOrGroup; + @Input() groupedRows?: Group[]; + @Input() selected!: TRow[]; + @Input() disabled?: boolean; @Output() rowContextmenu = new EventEmitter<{ event: MouseEvent; row: RowOrGroup; }>(false); - @Input() rowIndex?: number; + @Input() rowIndex!: number; selectedGroupRows = signal([]); @Input({ transform: booleanAttribute }) expanded = false; - groupContext?: GroupContext; - rowContext?: RowDetailContext; - disable$: BehaviorSubject; + context!: RowDetailContext | GroupContext; private rowDiffer: KeyValueDiffer, any> = inject(KeyValueDiffers) .find({}) .create(); private iterableDiffers = inject(IterableDiffers); - private selectedRowsDiffer: IterableDiffer; + private selectedRowsDiffer!: IterableDiffer; private tableComponent = inject(DatatableComponentToken); private cd = inject(ChangeDetectorRef); - protected group = computed(() => { - if (typeof this.row === 'object' && 'value' in this.row) { - return this.row; - } - }); - ngOnInit(): void { - if (this.disableCheck) { - const isRowDisabled = this.disableCheck(this.row); - this.disable$ = new BehaviorSubject(isRowDisabled); - this.rowContext.disableRow$ = this.disable$; - } this.selectedRowsDiffer = this.iterableDiffers.find(this.selected ?? []).create(); } @@ -126,45 +114,34 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit if (changes['row']) { // this component renders either a group header or a row. Never both. if (this.isGroup(this.row)) { - this.groupContext = { + this.context = { group: this.row, expanded: this.expanded, rowIndex: this.rowIndex }; } else { - this.rowContext = { + this.context = { row: this.row, expanded: this.expanded, rowIndex: this.rowIndex, - disableRow$: this.disable$ + disabled: this.disabled }; } } if (changes['rowIndex']) { - (this.rowContext ?? this.groupContext).rowIndex = this.rowIndex; + this.context.rowIndex = this.rowIndex; } if (changes['expanded']) { - (this.groupContext ?? this.rowContext)!.expanded = this.expanded; - if (this.rowContext) { - this.rowContext.expanded = this.expanded; - } + this.context.expanded = this.expanded; } } ngDoCheck(): void { - if (this.disableCheck) { - const isRowDisabled = this.disableCheck(this.row); - if (isRowDisabled !== this.disable$.value) { - this.disable$.next(isRowDisabled); - this.cd.markForCheck(); - } - } - if (this.rowDiffer.diff(this.row)) { - if (this.isGroup(this.row)) { - this.groupContext.group = this.row; + if ('group' in this.context) { + this.context.group = this.row as Group; } else { - this.rowContext.row = this.row; + this.context.row = this.row as TRow; } this.cd.markForCheck(); } @@ -172,12 +149,17 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit // on currently selected rows to check if it is modified // if any of the row of this group is not present in `selected` rows array // mark group header checkbox state as indeterminate - if (this.groupHeader?.checkboxable && this.selectedRowsDiffer.diff(this.selected)) { + if ( + this.isGroup(this.row) && + this.groupHeader?.checkboxable && + this.selectedRowsDiffer.diff(this.selected) + ) { + const thisRow = this.row; const selectedRows = this.selected.filter(row => - this.group()?.value.find(item => item === row) + thisRow.value.find((item: TRow) => item === row) ); if (this.checkBoxInput) { - if (selectedRows.length && selectedRows.length !== this.group()?.value.length) { + if (selectedRows.length && selectedRows.length !== this.row.value.length) { this.checkBoxInput.nativeElement.indeterminate = true; } else { this.checkBoxInput.nativeElement.indeterminate = false; @@ -192,14 +174,14 @@ export class DataTableRowWrapperComponent implements DoCheck, OnInit this.rowContextmenu.emit({ event: $event, row: this.row }); } - onCheckboxChange(groupSelected: boolean): void { + onCheckboxChange(groupSelected: boolean, group: Group): void { // First remove all rows of this group from `selected` this.selected = [ - ...this.selected.filter(row => !this.group().value.find(item => item === row)) + ...this.selected.filter(row => !group.value.find((item: TRow) => item === row)) ]; // If checkbox is checked then add all rows of this group in `selected` if (groupSelected) { - this.selected = [...this.selected, ...this.group().value]; + this.selected = [...this.selected, ...group.value]; } // Update `selected` of DatatableComponent with newly evaluated `selected` this.tableComponent.selected = [...this.selected]; diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.scss new file mode 100644 index 000000000..109e5fd83 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.scss @@ -0,0 +1,13 @@ +:host { + display: flex; + outline: none; + + :host-context(ngx-datatable.fixed-row) & { + white-space: nowrap; + } +} + +.datatable-row-group { + display: flex; + position: relative; +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.spec.ts index cab83486a..d6d4ecfa7 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.spec.ts @@ -1,28 +1,60 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DataTableBodyRowComponent } from './body-row.component'; -import { DataTableBodyCellComponent } from './body-cell.component'; +import { Component } from '@angular/core'; +import { ScrollbarHelper } from '../../services/scrollbar-helper.service'; +import { TableColumn } from '../../types/table-column.type'; +import { By } from '@angular/platform-browser'; +import { RowIndex } from '../../types/internal.types'; +import { toInternalColumn } from '../../utils/column-helper'; describe('DataTableBodyRowComponent', () => { - let fixture: ComponentFixture; - let component: DataTableBodyRowComponent; + @Component({ + template: ` `, + imports: [DataTableBodyRowComponent] + }) + class TestHostComponent { + rowIndex: RowIndex = { index: 0 }; + row: any = { prop: 'value' }; + columns: TableColumn[] = toInternalColumn([{ prop: 'prop' }]); + } + + let fixture: ComponentFixture; + let component: TestHostComponent; // provide our implementations or mocks to the dependency injector beforeEach(() => { TestBed.configureTestingModule({ - imports: [DataTableBodyCellComponent, DataTableBodyRowComponent] + imports: [TestHostComponent], + providers: [ScrollbarHelper] }); }); beforeEach(waitForAsync(() => { TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableBodyRowComponent); + fixture = TestBed.createComponent(TestHostComponent); component = fixture.componentInstance; }); })); - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); + it('should apply odd/event without groups', () => { + component.rowIndex = { index: 0 }; + fixture.detectChanges(); + const element = fixture.debugElement.query(By.directive(DataTableBodyRowComponent)) + .nativeElement as HTMLElement; + expect(element.classList).toContain('datatable-row-even'); + component.rowIndex = { index: 3 }; + fixture.detectChanges(); + expect(element.classList).toContain('datatable-row-odd'); + }); + + it('should apply event odd/even if row is grouped', () => { + component.rowIndex = { index: 1, indexInGroup: 0 }; + fixture.detectChanges(); + const element = fixture.debugElement.query(By.directive(DataTableBodyRowComponent)) + .nativeElement as HTMLElement; + expect(element.classList).toContain('datatable-row-even'); + component.rowIndex = { index: 666, indexInGroup: 3 }; + fixture.detectChanges(); + expect(element.classList).toContain('datatable-row-odd'); }); }); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts index b22d8d949..4b43e8973 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.component.ts @@ -18,22 +18,25 @@ import { import { columnGroupWidths, columnsByPin, columnsByPinArr } from '../../utils/column'; import { Keys } from '../../utils/keys'; -import { BehaviorSubject } from 'rxjs'; -import { ActivateEvent, RowOrGroup, TreeStatus } from '../../types/public.types'; -import { AsyncPipe } from '@angular/common'; -import { TableColumn } from '../../types/table-column.type'; -import { ColumnGroupWidth, PinnedColumns } from '../../types/internal.types'; +import { ActivateEvent, Row, RowOrGroup, TreeStatus } from '../../types/public.types'; +import { + CellActiveEvent, + ColumnGroupWidth, + PinnedColumns, + RowIndex, + TableColumnInternal +} from '../../types/internal.types'; import { DataTableBodyCellComponent } from './body-cell.component'; @Component({ selector: 'datatable-body-row', changeDetection: ChangeDetectionStrategy.OnPush, template: ` - @for (colGroup of _columnsByPin; track colGroup.type; let i = $index) { + @for (colGroup of _columnsByPin; track colGroup.type) { @if (colGroup.columns.length) {
@for (column of colGroup.columns; track column.$$id; let ii = $index) { }
- } + } } `, - imports: [DataTableBodyCellComponent, AsyncPipe] + styleUrl: './body-row.component.scss', + imports: [DataTableBodyCellComponent] }) -export class DataTableBodyRowComponent implements DoCheck, OnChanges { +export class DataTableBodyRowComponent implements DoCheck, OnChanges { private cd = inject(ChangeDetectorRef); - @Input() set columns(val: TableColumn[]) { + @Input() set columns(val: TableColumnInternal[]) { this._columns = val; this.recalculateColumns(val); } - get columns(): TableColumn[] { + get columns(): TableColumnInternal[] { return this._columns; } @@ -86,25 +89,17 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges return this._innerWidth; } - @Input() expanded: boolean; - @Input() rowClass?: (row: RowOrGroup) => string | Record; - @Input() row: TRow; - @Input() group: TRow[]; - @Input() isSelected: boolean; - @Input() rowIndex: number; - @Input() displayCheck: (row: TRow, column: TableColumn, value?: any) => boolean; + @Input() expanded?: boolean; + @Input() rowClass?: (row: TRow) => string | Record; + @Input() row!: TRow; + @Input() group?: TRow[]; + @Input() isSelected?: boolean; + @Input() rowIndex!: RowIndex; + @Input() displayCheck?: (row: TRow, column: TableColumnInternal, value?: any) => boolean; @Input() treeStatus?: TreeStatus = 'collapsed'; - @Input() ghostLoadingIndicator = false; @Input() verticalScrollVisible = false; - @Input() disable$: BehaviorSubject; - @Input() - set offsetX(val: number) { - this._offsetX = val; - } - get offsetX() { - return this._offsetX; - } + @Input() disabled?: boolean; @HostBinding('class') get cssClass() { @@ -112,13 +107,13 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges if (this.isSelected) { cls += ' active'; } - if (this.rowIndex % 2 !== 0) { + if (this.innerRowIndex % 2 !== 0) { cls += ' datatable-row-odd'; } - if (this.rowIndex % 2 === 0) { + if (this.innerRowIndex % 2 === 0) { cls += ' datatable-row-even'; } - if (this.disable$ && this.disable$.value) { + if (this.disabled) { cls += ' row-disabled'; } @@ -141,7 +136,7 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges @HostBinding('style.height.px') @Input() - rowHeight: number; + rowHeight!: number; @HostBinding('style.width.px') get columnsTotalWidths(): number { @@ -152,11 +147,10 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges @Output() treeAction: EventEmitter = new EventEmitter(); _element = inject>(ElementRef).nativeElement; - _columnGroupWidths: ColumnGroupWidth; - _columnsByPin: PinnedColumns[]; - _offsetX: number; - _columns: TableColumn[]; - _innerWidth: number; + _columnGroupWidths!: ColumnGroupWidth; + _columnsByPin!: PinnedColumns[]; + _columns!: TableColumnInternal[]; + _innerWidth!: number; private _rowDiffer: KeyValueDiffer, any> = inject(KeyValueDiffers) .find({}) @@ -174,10 +168,8 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges } } - onActivate(event: ActivateEvent, index: number): void { - event.cellIndex = index; - event.rowElement = this._element; - this.activate.emit(event); + onActivate(event: CellActiveEvent, index: number): void { + this.activate.emit({ ...event, rowElement: this._element, cellIndex: index }); } @HostListener('keydown', ['$event']) @@ -217,7 +209,7 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges }); } - recalculateColumns(val: TableColumn[] = this.columns): void { + recalculateColumns(val: TableColumnInternal[] = this.columns): void { this._columns = val; const colsByPin = columnsByPin(this._columns); this._columnsByPin = columnsByPinArr(this._columns); @@ -227,4 +219,9 @@ export class DataTableBodyRowComponent implements DoCheck, OnChanges onTreeAction() { this.treeAction.emit(); } + + /** Returns the row index, or if in a group, the index within a group. */ + private get innerRowIndex(): number { + return this.rowIndex?.indexInGroup ?? this.rowIndex?.index ?? 0; + } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.directive.ts new file mode 100644 index 000000000..a7bef986d --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body-row.directive.ts @@ -0,0 +1,20 @@ +import { Directive } from '@angular/core'; +import { Row } from '../../types/public.types'; + +@Directive({ + selector: '[ngx-datatable-body-row]' +}) +export class DatatableBodyRowDirective { + static ngTemplateContextGuard( + directive: DatatableBodyRowDirective, + context: unknown + ): context is { + row: TRow; + groupedRows: TRow[]; + index: number; + indexInGroup?: number; + disabled: boolean; + } { + return true; + } +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.scss new file mode 100644 index 000000000..5b47784e0 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.scss @@ -0,0 +1,27 @@ +:host { + position: relative; + z-index: 10; + display: block; + overflow: hidden; + + :host-context(ngx-datatable.scroll-horz) & { + overflow-x: auto; + -webkit-overflow-scrolling: touch; // Required for iOS optimization + } +} + +datatable-scroller { + display: block; + + :host-context(ngx-datatable.fixed-row) & { + white-space: nowrap; + } +} + +:host-context(ngx-datatable.scroll-vertical) :host { + overflow-y: auto; +} + +[hidden] { + display: none !important; +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.spec.ts index ab9debdf7..efa8e9c1d 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.spec.ts @@ -1,42 +1,27 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DataTableBodyComponent } from './body.component'; +import { ScrollbarHelper } from '../../services/scrollbar-helper.service'; +import { DatatableComponentToken } from '../../utils/table-token'; +import { By } from '@angular/platform-browser'; import { DataTableBodyRowComponent } from './body-row.component'; -import { DataTableRowWrapperComponent } from './body-row-wrapper.component'; -import { DataTableBodyCellComponent } from './body-cell.component'; -import { DataTableSelectionComponent } from './selection.component'; -import { DataTableSummaryRowComponent } from './summary/summary-row.component'; -import { ProgressBarComponent } from './progress-bar.component'; +import { toInternalColumn } from '../../utils/column-helper'; import { ScrollerComponent } from './scroller.component'; -import { ScrollbarHelper } from '../../services/scrollbar-helper.service'; +import { DataTableGhostLoaderComponent } from './ghost-loader/ghost-loader.component'; describe('DataTableBodyComponent', () => { let fixture: ComponentFixture; let component: DataTableBodyComponent; // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - DataTableBodyComponent, - DataTableBodyRowComponent, - DataTableRowWrapperComponent, - DataTableBodyCellComponent, - DataTableSelectionComponent, - DataTableSummaryRowComponent, - ProgressBarComponent, - ScrollerComponent - ], - providers: [ScrollbarHelper] - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DataTableBodyComponent], + providers: [ScrollbarHelper, { provide: DatatableComponentToken, useValue: {} }] + }).compileComponents(); + fixture = TestBed.createComponent(DataTableBodyComponent); + component = fixture.componentInstance; }); - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableBodyComponent); - component = fixture.componentInstance; - }); - })); - describe('fixture', () => { it('should have a component instance', () => { expect(component).toBeTruthy(); @@ -109,21 +94,80 @@ describe('DataTableBodyComponent', () => { component.updateIndexes(); expect(component.indexes()).toEqual(expectedIndexes); }); + + it('should render ghost rows based rowCount', () => { + component.trackByProp = 'num'; + component.rows = [{ num: 1 }, { num: 2 }, { num: 3 }, { num: 4 }]; + component.externalPaging = true; + component.scrollbarV = true; + component.virtualization = true; + component.rowHeight = 50; + component.ghostLoadingIndicator = true; + component.bodyHeight = 200; + component.pageSize = 5; + component.rowCount = 10; + component.offset = 0; + fixture.detectChanges(); + expect(component.indexes()).toEqual({ first: 0, last: 5 }); + fixture.debugElement + .query(By.directive(ScrollerComponent)) + .triggerEventHandler('scroll', { scrollYPos: 250, scrollXPos: 0 }); + fixture.detectChanges(); + expect(component.indexes()).toEqual({ first: 5, last: 10 }); + expect(fixture.debugElement.queryAll(By.directive(DataTableGhostLoaderComponent))).toHaveSize( + 5 + ); + }); }); - describe('Summary row', () => { - it('should not return custom styles for a bottom summary row if a scrollbar mode is off', () => { - const styles = component.bottomSummaryRowsStyles(); - expect(styles).toBeFalsy(); + describe('with disableCheck', () => { + beforeEach(() => { + component.columns = toInternalColumn([{ name: 'value', prop: 'value' }]); + component.disableRowCheck = (row: any) => row.disabled; }); - it('should return custom styles for a bottom summary row if a scrollbar mode is on', () => { - component.rowHeight = 50; - component.scrollbarV = true; - component.virtualization = true; - component.rows = [{ num: 1 }, { num: 2 }, { num: 3 }, { num: 4 }]; - const styles = component.bottomSummaryRowsStyles(); - expect(styles).toBeDefined(); + it('should disable rows', () => { + component.rows = [ + { value: '1', disabled: false }, + { value: '2', disabled: true } + ]; + component.rowCount = 2; + component.pageSize = 2; + component.offset = 0; + component.updateIndexes(); + fixture.detectChanges(); + let rows = fixture.debugElement.queryAll(By.directive(DataTableBodyRowComponent)); + expect(rows[0].classes['row-disabled']).toBeFalsy(); + expect(rows[1].classes['row-disabled']).toBeTrue(); + component.rows = [ + { value: '1', disabled: true }, + { value: '2', disabled: false } + ]; + fixture.detectChanges(); + rows = fixture.debugElement.queryAll(By.directive(DataTableBodyRowComponent)); + expect(rows[0].classes['row-disabled']).toBeTrue(); + expect(rows[1].classes['row-disabled']).toBeFalsy(); + }); + + it('should disable grouped rows', () => { + component.groupedRows = [ + { + key: 'g1', + value: [ + { value: '1', disabled: false }, + { value: '2', disabled: true } + ] + } + ]; + component.rows = ['dummy']; + component.rowCount = 2; + component.pageSize = 2; + component.offset = 0; + component.updateIndexes(); + fixture.detectChanges(); + const rows = fixture.debugElement.queryAll(By.directive(DataTableBodyRowComponent)); + expect(rows[0].classes['row-disabled']).toBeFalsy(); + expect(rows[1].classes['row-disabled']).toBeTrue(); }); }); }); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts index bfd027042..d30405594 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts @@ -18,27 +18,29 @@ import { import { ScrollerComponent } from './scroller.component'; import { columnGroupWidths, columnsByPin } from '../../utils/column'; import { RowHeightCache } from '../../utils/row-height-cache'; -import { NgStyle } from '@angular/common'; -import { TableColumn } from '../../types/table-column.type'; +import { NgTemplateOutlet } from '@angular/common'; import { DatatableGroupHeaderDirective } from './body-group-header.directive'; import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive'; import { DataTableBodyRowComponent } from './body-row.component'; -import { ColumnGroupWidth } from '../../types/internal.types'; +import { ColumnGroupWidth, TableColumnInternal } from '../../types/internal.types'; import { ActivateEvent, DragEventData, Group, + Row, RowOrGroup, ScrollEvent, - SelectionType, - TreeStatus + SelectEvent, + SelectionType } from '../../types/public.types'; import { DraggableDirective } from '../../directives/draggable.directive'; import { DatatableRowDefInternalDirective } from './body-row-def.component'; import { DataTableRowWrapperComponent } from './body-row-wrapper.component'; import { DataTableSummaryRowComponent } from './summary/summary-row.component'; -import { DataTableSelectionComponent } from './selection.component'; import { DataTableGhostLoaderComponent } from './ghost-loader/ghost-loader.component'; +import { DatatableBodyRowDirective } from './body-row.directive'; +import { selectRows, selectRowsBetween } from '../../utils/selection'; +import { Keys } from '../../utils/keys'; @Component({ selector: 'datatable-body', @@ -60,244 +62,208 @@ import { DataTableGhostLoaderComponent } from './ghost-loader/ghost-loader.compo > } - - @if (rows?.length) { - + @if (summaryRow && summaryPosition === 'top') { + + + } + - @if (summaryRow && summaryPosition === 'top') { - - - } + + + + +
@for (group of rowsToRender(); track rowTrackingFn(i, group); let i = $index) { - - @if (rowDefTemplate) { - - } @else { - @if (isRow(group)) { - - - } - } - - - @if (isRow(group)) { - - + @if (!group && ghostLoadingIndicator) { + + } @else if (group) { + @let disabled = isRow(group) && disableRowCheck && disableRowCheck(group); + + + + @if (rowDefTemplate) { + + } @else { + @if (isRow(group)) { + + } } - - - @if (isGroup(group)) { - - @for (row of group.value; track rowTrackingFn(i, row); let i = $index) { - - + + @if (isGroup(group)) { + + @for (row of group.value; track rowTrackingFn($index, row)) { + @let disabled = disableRowCheck && disableRowCheck(row); + + } } - } - - } - @if (summaryRow && summaryPosition === 'bottom') { - - + + } } - - } - @if (!rows?.length && !loadingIndicator && !ghostLoadingIndicator) { - + + @if (summaryRow && summaryPosition === 'bottom') { + - + } - + } + @if (!rows?.length && !loadingIndicator && !ghostLoadingIndicator) { + + + + } `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'datatable-body' }, + styleUrl: './body.component.scss', imports: [ DataTableGhostLoaderComponent, - DataTableSelectionComponent, ScrollerComponent, DataTableSummaryRowComponent, DataTableRowWrapperComponent, - NgStyle, DatatableRowDefInternalDirective, DataTableBodyRowComponent, - DraggableDirective + DraggableDirective, + NgTemplateOutlet, + DatatableBodyRowDirective ] }) -export class DataTableBodyComponent - implements OnInit, OnDestroy -{ +export class DataTableBodyComponent implements OnInit, OnDestroy { cd = inject(ChangeDetectorRef); @Input() rowDefTemplate?: TemplateRef; - @Input() scrollbarV: boolean; - @Input() scrollbarH: boolean; - @Input() loadingIndicator: boolean; - @Input() ghostLoadingIndicator: boolean; - @Input() externalPaging: boolean; - @Input() rowHeight: number | 'auto' | ((row?: any) => number); - @Input() offsetX: number; - @Input() selectionType: SelectionType; + @Input() scrollbarV?: boolean; + @Input() scrollbarH?: boolean; + @Input() loadingIndicator?: boolean; + @Input() ghostLoadingIndicator?: boolean; + @Input() externalPaging?: boolean; + @Input() rowHeight!: number | 'auto' | ((row?: any) => number); + @Input() offsetX!: number; + @Input() selectionType?: SelectionType; @Input() selected: any[] = []; - @Input() rowIdentity: any; - @Input() rowDetail: DatatableRowDetailDirective; - @Input() groupHeader: DatatableGroupHeaderDirective; - @Input() selectCheck: (value: TRow, index: number, array: TRow[]) => boolean; - @Input() displayCheck: (row: TRow, column: TableColumn, value?: any) => boolean; - @Input() trackByProp: string; - @Input() rowClass: (row: RowOrGroup) => string | Record; - @Input() groupedRows: Group[]; - @Input() groupExpansionDefault: boolean; - @Input() innerWidth: number; - @Input() groupRowsBy: keyof TRow; - @Input() virtualization: boolean; - @Input() summaryRow: boolean; - @Input() summaryPosition: string; - @Input() summaryHeight: number; - @Input() rowDraggable: boolean; - @Input() rowDragEvents: EventEmitter; - @Input() disableRowCheck: (row: TRow) => boolean; + @Input() rowIdentity!: (x: RowOrGroup) => unknown; + @Input() rowDetail?: DatatableRowDetailDirective; + @Input() groupHeader?: DatatableGroupHeaderDirective; + @Input() selectCheck?: (value: TRow, index: number, array: TRow[]) => boolean; + @Input() displayCheck?: (row: TRow, column: TableColumnInternal, value?: any) => boolean; + @Input() trackByProp?: string; + @Input() rowClass?: (row: TRow) => string | Record; + @Input() groupedRows?: Group[]; + @Input() groupExpansionDefault?: boolean; + @Input() innerWidth!: number; + @Input() groupRowsBy?: keyof TRow; + @Input() virtualization?: boolean; + @Input() summaryRow?: boolean; + @Input() summaryPosition!: string; + @Input() summaryHeight!: number; + @Input() rowDraggable?: boolean; + @Input() rowDragEvents!: EventEmitter; + @Input() disableRowCheck?: (row: TRow) => boolean | undefined; @Input() set pageSize(val: number) { if (val !== this._pageSize) { @@ -315,18 +281,18 @@ export class DataTableBodyComponent = new EventEmitter(); - @Output() page: EventEmitter = new EventEmitter(); - @Output() activate: EventEmitter> = new EventEmitter(); - @Output() select: EventEmitter<{ selected: TRow[] }> = new EventEmitter(); - @Output() detailToggle: EventEmitter = new EventEmitter(); + @Output() scroll = new EventEmitter(); + @Output() page = new EventEmitter(); + @Output() activate = new EventEmitter>(); + @Output() select = new EventEmitter>(); @Output() rowContextmenu = new EventEmitter<{ event: MouseEvent; row: RowOrGroup }>(false); @Output() treeAction: EventEmitter<{ row: TRow }> = new EventEmitter(); - @ViewChild(ScrollerComponent) scroller: ScrollerComponent; + @ViewChild(ScrollerComponent) scroller!: ScrollerComponent; /** * Returns if selection is enabled. @@ -427,22 +389,21 @@ export class DataTableBodyComponent({ first: 0, last: 0 }); - columnGroupWidths: ColumnGroupWidth; - rowTrackingFn: TrackByFunction>; + columnGroupWidths!: ColumnGroupWidth; + rowTrackingFn: TrackByFunction | undefined>; listener: any; - rowIndexes = new WeakMap(); rowExpansions: any[] = []; - _rows: TRow[]; - _bodyHeight: string; - _columns: TableColumn[]; - _rowCount: number; - _offset: number; - _pageSize: number; + _rows!: (TRow | undefined)[]; + _bodyHeight!: string; + _columns!: TableColumnInternal[]; + _rowCount!: number; + _offset!: number; + _pageSize!: number; _offsetEvent = -1; - private _draggedRow: RowOrGroup; - private _draggedRowElement: HTMLElement; + private _draggedRow?: RowOrGroup; + private _draggedRowElement?: HTMLElement; /** * Creates an instance of DataTableBodyComponent. @@ -453,11 +414,10 @@ export class DataTableBodyComponent[] { + updateRows(): (RowOrGroup | undefined)[] { const { first, last } = this.indexes(); - let rowIndex = first; - let idx = 0; - const temp: RowOrGroup[] = []; - // if grouprowsby has been specified treat row paging // parameters as group paging parameters ie if limit 10 has been // specified treat it as 10 groups rather than 10 rows - if (this.groupedRows) { - while (rowIndex < last && rowIndex < this.groupedRows.length) { - // Add the groups into this page - const group = this.groupedRows[rowIndex]; - this.rowIndexes.set(group, rowIndex); - - if (group.value) { - // add indexes for each group item - group.value.forEach((g: any, i: number) => { - const _idx = `${rowIndex}-${i}`; - this.rowIndexes.set(g, _idx); - }); - } - temp[idx] = group; - idx++; - - // Group index in this context - rowIndex++; - } - } else { - while (rowIndex < last && rowIndex < this.rowCount) { - const row = this.rows[rowIndex]; - - if (row) { - // add indexes for each row - this.rowIndexes.set(row, rowIndex); - temp[idx] = row; - } else if (this.ghostLoadingIndicator && this.virtualization) { - temp[idx] = undefined; - } + const rows = this.groupedRows + ? this.groupedRows.slice(first, Math.min(last, this.groupedRows.length)) + : this.rows.slice(first, Math.min(last, this.rowCount)); - idx++; - rowIndex++; - } - } - return temp; + rows.length = last - first; + return rows; } /** @@ -695,75 +621,18 @@ export class DataTableBodyComponent row0 - * transform: translate3d(0px, 30px, 0px); -> row1 - * transform: translate3d(0px, 130px, 0px); -> row2 - * - * Row heights have to be calculated based on the row heights cache as we wont - * be able to determine which row is of what height before hand. In the above - * case the positionY of the translate3d for row2 would be the sum of all the - * heights of the rows before it (i.e. row0 and row1). - * - * @returns the CSS3 style to be applied - */ - rowsStyles = computed(() => { - const rowsStyles: NgStyle['ngStyle'][] = []; - this.rowsToRender().forEach((rows, index) => { - const styles: NgStyle['ngStyle'] = {}; - - // only add styles for the group if there is a group - if (this.groupedRows) { - styles.width = this.columnGroupWidths.total; - } - - if (this.scrollbarV && this.virtualization) { - let idx = 0; - - if (Array.isArray(rows)) { - // Get the latest row rowindex in a group - const row = rows[rows.length - 1]; - idx = row ? this.getRowIndex(row) : 0; - } else { - if (rows) { - idx = this.getRowIndex(rows); - } else { - // When ghost cells are enabled use index to get the position of them - idx = this.indexes().first + index; - } - } - - // const pos = idx * rowHeight; - // The position of this row would be the sum of all row heights - // until the previous row position. - styles.transform = `translateY(${this.rowHeightsCache().query(idx - 1)}px)`; - } - rowsStyles.push(styles); - }); - return rowsStyles; - }); - - /** - * Calculate bottom summary row offset for scrollbar mode. - * For more information about cache and offset calculation - * see description for `rowsStyles` signal - * - * @returns the CSS3 style to be applied + * Calculates the offset of the rendered rows. + * As virtual rows are not shown, we have to move all rendered rows + * by the total size of previous non-rendered rows. + * If each row has a size of 10px and the first 10 rows are not rendered due to scroll, + * then we have a renderOffset of 100px. */ - bottomSummaryRowsStyles = computed(() => { - if (!this.scrollbarV || !this.rows || !this.rows.length || !this.rowsToRender()) { - return null; + renderOffset = computed(() => { + if (this.scrollbarV && this.virtualization) { + return `translateY(${this.rowHeightsCache().query(this.indexes().first - 1)}px)`; + } else { + return ''; } - - const pos = this.rowHeightsCache().query(this.rows.length - 1); - return { - transform: `translateY(${pos}px)`, - position: 'absolute' - }; }); /** @@ -814,11 +683,11 @@ export class DataTableBodyComponent(); if (this.rowDetail) { for (const row of this.rows) { - if (this.getRowExpanded(row)) { + if (row && this.getRowExpanded(row)) { rowExpansions.add(row); } } @@ -829,31 +698,14 @@ export class DataTableBodyComponent -1; - // If the detailRowHeight is auto --> only in case of non-virtualized scroll - if (this.scrollbarV && this.virtualization) { - const detailRowHeight = this.getDetailRowHeight(row) * (expanded ? -1 : 1); - // const idx = this.rowIndexes.get(row) || 0; - const idx = this.getRowIndex(row); - this.rowHeightsCache().update(idx, detailRowHeight); - } - // Update the toggled row and update thive nevere heights in the cache. if (expanded) { this.rowExpansions.splice(rowExpandedIdx, 1); @@ -881,10 +723,10 @@ export class DataTableBodyComponent only in case of non-virtualized scroll + if (this.scrollbarV && this.virtualization) { + this.refreshRowHeightCache(); + } } /** @@ -894,8 +736,6 @@ export class DataTableBodyComponent): boolean { if (this.rowExpansions.length === 0 && this.groupExpansionDefault) { - for (const group of this.groupedRows) { + for (const group of this.groupedRows!) { this.rowExpansions.push(group); } } @@ -948,13 +782,6 @@ export class DataTableBodyComponent): number { - return this.rowIndexes.get(row) || 0; - } - onTreeAction(row: TRow) { this.treeAction.emit({ row }); } @@ -963,7 +790,7 @@ export class DataTableBodyComponent !!rowItem); + } else if ( + (event as KeyboardEvent).ctrlKey || + (event as KeyboardEvent).metaKey || + multiClick || + chkbox + ) { + selected = selectRows([...this.selected], row, this.getRowSelectedIdx.bind(this)); + } else { + selected = selectRows([], row, this.getRowSelectedIdx.bind(this)); + } + } else { + selected = selectRows([], row, this.getRowSelectedIdx.bind(this)); + } + + if (typeof this.selectCheck === 'function') { + selected = selected.filter(this.selectCheck.bind(this)); + } + + if (typeof this.disableRowCheck === 'function') { + selected = selected.filter(rowData => !this.disableRowCheck!(rowData)); + } + + this.selected.splice(0, this.selected.length); + this.selected.push(...selected); + + this.prevIndex = index; + + this.select.emit({ + selected + }); + } + + onActivate(model: ActivateEvent, index: number): void { + const { type, event, row } = model; + const chkbox = this.selectionType === SelectionType.checkbox; + const select = + (!chkbox && (type === 'click' || type === 'dblclick')) || (chkbox && type === 'checkbox'); + + if (select) { + this.selectRow(event, index, row); + } else if (type === 'keydown') { + if ((event as KeyboardEvent).key === Keys.return) { + this.selectRow(event, index, row); + } else if ( + (event as KeyboardEvent).key === 'a' && + ((event as KeyboardEvent).ctrlKey || (event as KeyboardEvent).metaKey) + ) { + this.selectRow(event, 0, row); // The row property is ignored in this case. So we can pass anything. + } else { + this.onKeyboardFocus(model); + } + } + this.activate.emit(model); + } + + onKeyboardFocus(model: ActivateEvent): void { + const { key } = model.event as KeyboardEvent; + const shouldFocus = + key === Keys.up || key === Keys.down || key === Keys.right || key === Keys.left; + + if (shouldFocus) { + const isCellSelection = this.selectionType === SelectionType.cell; + if (typeof this.disableRowCheck === 'function') { + const isRowDisabled = this.disableRowCheck(model.row); + if (isRowDisabled) { + return; + } + } + if (!model.cellElement || !isCellSelection) { + this.focusRow(model.rowElement, key); + } else if (isCellSelection && model.cellIndex !== undefined) { + this.focusCell(model.cellElement, model.rowElement, key, model.cellIndex); + } + } + } + + focusRow(rowElement: HTMLElement, key: Keys): void { + const nextRowElement = this.getPrevNextRow(rowElement, key); + if (nextRowElement) { + nextRowElement.focus(); + } + } + + getPrevNextRow(rowElement: HTMLElement, key: Keys): any { + const parentElement = rowElement.parentElement; + + if (parentElement) { + let focusElement: Element | null = null; + if (key === Keys.up) { + focusElement = parentElement.previousElementSibling; + } else if (key === Keys.down) { + focusElement = parentElement.nextElementSibling; + } + + if (focusElement && focusElement.children.length) { + return focusElement.children[0]; + } + } + } + + focusCell(cellElement: HTMLElement, rowElement: HTMLElement, key: Keys, cellIndex: number): void { + let nextCellElement: Element | null = null; + + if (key === Keys.left) { + nextCellElement = cellElement.previousElementSibling; + } else if (key === Keys.right) { + nextCellElement = cellElement.nextElementSibling; + } else if (key === Keys.up || key === Keys.down) { + const nextRowElement = this.getPrevNextRow(rowElement, key); + if (nextRowElement) { + const children = nextRowElement.getElementsByClassName('datatable-body-cell'); + if (children.length) { + nextCellElement = children[cellIndex]; + } + } + } + + if ( + nextCellElement && + 'focus' in nextCellElement && + typeof nextCellElement.focus === 'function' + ) { + nextCellElement.focus(); + } + } + + getRowSelected(row: TRow): boolean { + return this.getRowSelectedIdx(row, this.selected) > -1; + } + + getRowSelectedIdx(row: TRow, selected: any[]): number { + if (!selected || !selected.length) { + return -1; + } + + const rowId = this.rowIdentity(row); + return selected.findIndex(r => { + const id = this.rowIdentity(r); + return id === rowId; + }); + } + protected isGroup(row: RowOrGroup[]): row is Group[]; protected isGroup(row: RowOrGroup): row is Group; @@ -1049,7 +1040,7 @@ export class DataTableBodyComponent): row is TRow { + protected isRow(row: RowOrGroup | undefined): row is TRow { return !this.groupedRows; } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.html b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.html index 55fb44a53..69e67622b 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.html +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.html @@ -1,13 +1,15 @@ -
+
@for (item of [].constructor(pageSize); track item) { -
- @for (col of columns; track col) { - @if (!col.ghostCellTemplate) { -
- } @else { - - } +
+ @for (col of columns; track col) { +
+ @if (!col.ghostCellTemplate) { +
+ } @else { + }
+ } +
}
diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.scss index ef7e44bb8..322125c33 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.scss +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.scss @@ -23,6 +23,7 @@ .ghost-element { display: flex; + align-items: center; } } @@ -30,9 +31,7 @@ position: sticky; top: 20px; - .ghost-loader { - .line { - margin: 0.9rem 1.2rem; - } + .ghost-cell { + padding-inline: 1.2rem; } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.spec.ts deleted file mode 100644 index 1e5ee2a3b..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { DataTableGhostLoaderComponent } from './ghost-loader.component'; - -describe('DataTableGhostLoaderComponent', () => { - let fixture: ComponentFixture; - let component: DataTableGhostLoaderComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DataTableGhostLoaderComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableGhostLoaderComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.ts index 2fdf556f5..2b00f4f66 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/ghost-loader/ghost-loader.component.ts @@ -1,6 +1,12 @@ -import { ChangeDetectionStrategy, Component, Input, numberAttribute } from '@angular/core'; -import { TableColumn } from '../../../types/table-column.type'; +import { + booleanAttribute, + ChangeDetectionStrategy, + Component, + Input, + numberAttribute +} from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; +import { TableColumnInternal } from '../../../types/internal.types'; @Component({ selector: `ghost-loader`, @@ -10,8 +16,9 @@ import { NgTemplateOutlet } from '@angular/common'; imports: [NgTemplateOutlet] }) export class DataTableGhostLoaderComponent { - @Input() columns: TableColumn[]; - @Input({ transform: numberAttribute }) pageSize: number; - @Input() rowHeight: number | 'auto' | ((row?: any) => number); - @Input({ transform: numberAttribute }) ghostBodyHeight: number; + @Input() columns!: TableColumnInternal[]; + @Input({ transform: numberAttribute }) pageSize!: number; + @Input() rowHeight!: number | 'auto' | ((row?: any) => number); + @Input({ transform: numberAttribute }) ghostBodyHeight?: number; + @Input({ transform: booleanAttribute }) cellMode = false; } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.spec.ts deleted file mode 100644 index 6297b138a..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { ProgressBarComponent } from './progress-bar.component'; - -describe('ProgressBarComponent', () => { - let fixture: ComponentFixture; - let component: ProgressBarComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ProgressBarComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(ProgressBarComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts index aae6915a9..82d8000cf 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/progress-bar.component.ts @@ -9,7 +9,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
`, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true + changeDetection: ChangeDetectionStrategy.OnPush }) export class ProgressBarComponent {} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.spec.ts deleted file mode 100644 index b637ba53f..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { ScrollerComponent } from './scroller.component'; - -describe('ScrollerComponent', () => { - let fixture: ComponentFixture; - let component: ScrollerComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ScrollerComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(ScrollerComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts index 8b244cf34..39f2c99b4 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/scroller.component.ts @@ -18,22 +18,21 @@ import { host: { class: 'datatable-scroll' }, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true + changeDetection: ChangeDetectionStrategy.OnPush }) export class ScrollerComponent implements OnInit, OnDestroy { private renderer = inject(Renderer2); - @Input() scrollbarV = false; - @Input() scrollbarH = false; + @Input() scrollbarV?: boolean; + @Input() scrollbarH?: boolean; @HostBinding('style.height.px') @Input() - scrollHeight: number; + scrollHeight?: number; @HostBinding('style.width.px') @Input() - scrollWidth: number; + scrollWidth?: number; @Output() scroll: EventEmitter = new EventEmitter(); @@ -42,7 +41,7 @@ export class ScrollerComponent implements OnInit, OnDestroy { prevScrollYPos = 0; prevScrollXPos = 0; element = inject>(ElementRef).nativeElement; - parentElement: HTMLElement; + parentElement?: HTMLElement; private _scrollEventListener: any = null; @@ -50,15 +49,15 @@ export class ScrollerComponent implements OnInit, OnDestroy { // manual bind so we don't always listen if (this.scrollbarV || this.scrollbarH) { const renderer = this.renderer; - this.parentElement = renderer.parentNode(renderer.parentNode(this.element)); + this.parentElement = renderer.parentNode(this.element); this._scrollEventListener = this.onScrolled.bind(this); - this.parentElement.addEventListener('scroll', this._scrollEventListener); + this.parentElement?.addEventListener('scroll', this._scrollEventListener); } } ngOnDestroy(): void { if (this._scrollEventListener) { - this.parentElement.removeEventListener('scroll', this._scrollEventListener); + this.parentElement?.removeEventListener('scroll', this._scrollEventListener); this._scrollEventListener = null; } } @@ -82,7 +81,7 @@ export class ScrollerComponent implements OnInit, OnDestroy { let direction: string; if (this.scrollYPos < this.prevScrollYPos) { direction = 'down'; - } else if (this.scrollYPos > this.prevScrollYPos) { + } else { direction = 'up'; } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.spec.ts deleted file mode 100644 index c3173d79a..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { DataTableSelectionComponent } from './selection.component'; - -describe('DataTableSelectionComponent', () => { - let fixture: ComponentFixture; - let component: DataTableSelectionComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DataTableSelectionComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableSelectionComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.ts deleted file mode 100644 index 23a6719bf..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/selection.component.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { selectRows, selectRowsBetween } from '../../utils/selection'; -import { Keys } from '../../utils/keys'; -import { ActivateEvent, SelectionType } from '../../types/public.types'; - -@Component({ - selector: 'datatable-selection', - template: ` `, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true -}) -export class DataTableSelectionComponent { - @Input() rows: TRow[]; - @Input() selected: TRow[]; - @Input() selectEnabled: boolean; - @Input() selectionType: SelectionType; - @Input() rowIdentity: any; - @Input() selectCheck: (value: TRow, index: number, array: TRow[]) => boolean; - @Input() disableCheck: (row: TRow) => boolean; - - @Output() activate: EventEmitter> = new EventEmitter(); - @Output() select: EventEmitter<{ selected: TRow[] }> = new EventEmitter(); - - prevIndex: number; - - selectRow(event: KeyboardEvent | MouseEvent, index: number, row: TRow): void { - if (!this.selectEnabled) { - return; - } - - const chkbox = this.selectionType === SelectionType.checkbox; - const multi = this.selectionType === SelectionType.multi; - const multiClick = this.selectionType === SelectionType.multiClick; - let selected: TRow[] = []; - - if (multi || chkbox || multiClick) { - if (event.shiftKey) { - selected = selectRowsBetween([], this.rows, index, this.prevIndex); - } else if ((event as KeyboardEvent).key === 'a' && (event.ctrlKey || event.metaKey)) { - // select all rows except dummy rows which are added for ghostloader in case of virtual scroll - selected = this.rows.filter(rowItem => !!rowItem); - } else if (event.ctrlKey || event.metaKey || multiClick || chkbox) { - selected = selectRows([...this.selected], row, this.getRowSelectedIdx.bind(this)); - } else { - selected = selectRows([], row, this.getRowSelectedIdx.bind(this)); - } - } else { - selected = selectRows([], row, this.getRowSelectedIdx.bind(this)); - } - - if (typeof this.selectCheck === 'function') { - selected = selected.filter(this.selectCheck.bind(this)); - } - - if (typeof this.disableCheck === 'function') { - selected = selected.filter(rowData => !this.disableCheck(rowData)); - } - - this.selected.splice(0, this.selected.length); - this.selected.push(...selected); - - this.prevIndex = index; - - this.select.emit({ - selected - }); - } - - onActivate(model: ActivateEvent, index: number): void { - const { type, event, row } = model; - const chkbox = this.selectionType === SelectionType.checkbox; - const select = - (!chkbox && (type === 'click' || type === 'dblclick')) || (chkbox && type === 'checkbox'); - - if (select) { - this.selectRow(event, index, row); - } else if (type === 'keydown') { - if ((event as KeyboardEvent).key === Keys.return) { - this.selectRow(event, index, row); - } else if ((event as KeyboardEvent).key === 'a' && (event.ctrlKey || event.metaKey)) { - this.selectRow(event, 0, this.rows[this.rows.length - 1]); - } else { - this.onKeyboardFocus(model); - } - } - this.activate.emit(model); - } - - onKeyboardFocus(model: ActivateEvent): void { - const { key } = model.event as KeyboardEvent; - const shouldFocus = - key === Keys.up || key === Keys.down || key === Keys.right || key === Keys.left; - - if (shouldFocus) { - const isCellSelection = this.selectionType === SelectionType.cell; - if (typeof this.disableCheck === 'function') { - const isRowDisabled = this.disableCheck(model.row); - if (isRowDisabled) { - return; - } - } - if (!model.cellElement || !isCellSelection) { - this.focusRow(model.rowElement, key); - } else if (isCellSelection) { - this.focusCell(model.cellElement, model.rowElement, key, model.cellIndex); - } - } - } - - focusRow(rowElement: HTMLElement, key: Keys): void { - const nextRowElement = this.getPrevNextRow(rowElement, key); - if (nextRowElement) { - nextRowElement.focus(); - } - } - - getPrevNextRow(rowElement: HTMLElement, key: Keys): any { - const parentElement = rowElement.parentElement; - - if (parentElement) { - let focusElement: Element; - if (key === Keys.up) { - focusElement = parentElement.previousElementSibling; - } else if (key === Keys.down) { - focusElement = parentElement.nextElementSibling; - } - - if (focusElement && focusElement.children.length) { - return focusElement.children[0]; - } - } - } - - focusCell(cellElement: HTMLElement, rowElement: HTMLElement, key: Keys, cellIndex: number): void { - let nextCellElement: Element; - - if (key === Keys.left) { - nextCellElement = cellElement.previousElementSibling; - } else if (key === Keys.right) { - nextCellElement = cellElement.nextElementSibling; - } else if (key === Keys.up || key === Keys.down) { - const nextRowElement = this.getPrevNextRow(rowElement, key); - if (nextRowElement) { - const children = nextRowElement.getElementsByClassName('datatable-body-cell'); - if (children.length) { - nextCellElement = children[cellIndex]; - } - } - } - - if ( - nextCellElement && - 'focus' in nextCellElement && - typeof nextCellElement.focus === 'function' - ) { - nextCellElement.focus(); - } - } - - getRowSelected(row: TRow): boolean { - return this.getRowSelectedIdx(row, this.selected) > -1; - } - - getRowSelectedIdx(row: TRow, selected: any[]): number { - if (!selected || !selected.length) { - return -1; - } - - const rowId = this.rowIdentity(row); - return selected.findIndex(r => { - const id = this.rowIdentity(r); - return id === rowId; - }); - } -} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.spec.ts index 5e03902bd..aaf378c69 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.spec.ts @@ -4,9 +4,10 @@ import { By } from '@angular/platform-browser'; import { DataTableBodyRowComponent } from '../body-row.component'; import { DataTableBodyCellComponent } from '../body-cell.component'; -import { DataTableSummaryRowComponent, ISummaryColumn } from './summary-row.component'; +import { DataTableSummaryRowComponent } from './summary-row.component'; import { ScrollbarHelper } from '../../../services/scrollbar-helper.service'; -import { setColumnDefaults } from '../../../utils/column-helper'; +import { toInternalColumn } from '../../../utils/column-helper'; +import { TableColumnInternal } from '../../../types/internal.types'; describe('DataTableSummaryRowComponent', () => { let fixture: ComponentFixture; @@ -14,15 +15,14 @@ describe('DataTableSummaryRowComponent', () => { let element: DebugElement; let rows: any[]; - let columns: ISummaryColumn[]; + let columns: TableColumnInternal[]; beforeEach(() => { rows = [ { col1: 10, col2: 20 }, { col1: 1, col2: 30 } ]; - columns = [{ prop: 'col1' }, { prop: 'col2' }]; - setColumnDefaults(columns); + columns = toInternalColumn([{ prop: 'col1' }, { prop: 'col2' }]); }); beforeEach(waitForAsync(() => { @@ -56,6 +56,7 @@ describe('DataTableSummaryRowComponent', () => { describe('Visibility', () => { it('should not be visible when there are no columns', () => { + component.columns = []; component.rows = rows; triggerChange(); expect(element.query(By.css('datatable-body-row'))).toBeNull(); @@ -63,6 +64,7 @@ describe('DataTableSummaryRowComponent', () => { it('should not be visible when there are no rows', () => { component.columns = columns; + component.rows = []; triggerChange(); expect(element.query(By.css('datatable-body-row'))).toBeNull(); }); @@ -114,7 +116,7 @@ describe('DataTableSummaryRowComponent', () => { triggerChange(); - expect(component.summaryRow.col1).toEqual(null); + expect(component.summaryRow.col1).toEqual(undefined); }); it('should use provided summary function', () => { diff --git a/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts index 18c4af36b..a7f3a3a3a 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/body/summary/summary-row.component.ts @@ -1,14 +1,6 @@ -import { Component, Input, OnChanges, PipeTransform, TemplateRef } from '@angular/core'; -import { TableColumn, TableColumnProp } from '../../../types/table-column.type'; +import { Component, Input, OnChanges } from '@angular/core'; import { DataTableBodyRowComponent } from '../body-row.component'; - -export interface ISummaryColumn { - summaryFunc?: (cells: any[]) => any; - summaryTemplate?: TemplateRef; - - prop?: TableColumnProp; - pipe?: PipeTransform; -} +import { TableColumnInternal } from '../../../types/internal.types'; function defaultSumFunc(cells: any[]): any { const cellsWithValues = cells.filter(cell => !!cell); @@ -24,7 +16,7 @@ function defaultSumFunc(cells: any[]): any { } function noopSumFunc(cells: any[]): void { - return null; + return; } @Component({ @@ -34,11 +26,10 @@ function noopSumFunc(cells: any[]): void { } @@ -49,18 +40,17 @@ function noopSumFunc(cells: any[]): void { imports: [DataTableBodyRowComponent] }) export class DataTableSummaryRowComponent implements OnChanges { - @Input() rows: any[]; - @Input() columns: TableColumn[]; + @Input() rows!: any[]; + @Input() columns!: TableColumnInternal[]; - @Input() rowHeight: number; - @Input() offsetX: number; - @Input() innerWidth: number; + @Input() rowHeight!: number; + @Input() innerWidth!: number; - _internalColumns: ISummaryColumn[]; + _internalColumns!: TableColumnInternal[]; summaryRow: any = {}; ngOnChanges() { - if (!this.columns || !this.rows) { + if (!this.columns.length || !this.rows.length) { return; } this.updateInternalColumns(); @@ -78,18 +68,18 @@ export class DataTableSummaryRowComponent implements OnChanges { this.summaryRow = {}; this.columns - .filter(col => !col.summaryTemplate) + .filter(col => !col.summaryTemplate && col.prop) .forEach(col => { - const cellsFromSingleColumn = this.rows.map(row => row[col.prop]); + const cellsFromSingleColumn = this.rows.map(row => row[col.prop!]); const sumFunc = this.getSummaryFunction(col); - this.summaryRow[col.prop] = col.pipe + this.summaryRow[col.prop!] = col.pipe ? col.pipe.transform(sumFunc(cellsFromSingleColumn)) : sumFunc(cellsFromSingleColumn); }); } - private getSummaryFunction(column: ISummaryColumn): (a: any[]) => any { + private getSummaryFunction(column: TableColumnInternal): (a: any[]) => any { if (column.summaryFunc === undefined) { return defaultSumFunc; } else if (column.summaryFunc === null) { diff --git a/projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts index 6244a0edd..53f35ffac 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/columns/column-cell.directive.ts @@ -2,8 +2,7 @@ import { Directive, inject, TemplateRef } from '@angular/core'; import { CellContext } from '../../types/public.types'; @Directive({ - selector: '[ngx-datatable-cell-template]', - standalone: true + selector: '[ngx-datatable-cell-template]' }) export class DataTableColumnCellDirective { template = inject>(TemplateRef); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/columns/column-ghost-cell.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/columns/column-ghost-cell.directive.ts index effd6644c..2ab2d6aa0 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/columns/column-ghost-cell.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/columns/column-ghost-cell.directive.ts @@ -1,8 +1,7 @@ import { Directive } from '@angular/core'; @Directive({ - selector: '[ngx-datatable-ghost-cell-template]', - standalone: true + selector: '[ngx-datatable-ghost-cell-template]' }) export class DataTableColumnGhostCellDirective { static ngTemplateContextGuard( diff --git a/projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts index 3f1721e1f..b1b443b44 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/columns/column-header.directive.ts @@ -2,8 +2,7 @@ import { Directive } from '@angular/core'; import { HeaderCellContext } from '../../types/public.types'; @Directive({ - selector: '[ngx-datatable-header-template]', - standalone: true + selector: '[ngx-datatable-header-template]' }) export class DataTableColumnHeaderDirective { static ngTemplateContextGuard( diff --git a/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.spec.ts index a3a6880dd..78cb439ca 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.spec.ts @@ -4,6 +4,8 @@ import { By } from '@angular/platform-browser'; import { ColumnChangesService } from '../../services/column-changes.service'; import { DataTableColumnDirective } from './column.directive'; +import { Row } from '../../types/public.types'; +import Spy = jasmine.Spy; @Component({ selector: 'test-fixture-component', @@ -14,40 +16,24 @@ import { DataTableColumnDirective } from './column.directive'; `, - imports: [DataTableColumnDirective] + imports: [DataTableColumnDirective], + providers: [ColumnChangesService] // usually provided by the table.component }) class TestFixtureComponent { - columnName: string; + columnName?: string; } describe('DataTableColumnDirective', () => { let fixture: ComponentFixture; let component: TestFixtureComponent; - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DataTableColumnDirective, TestFixtureComponent], - providers: [ - { - provide: ColumnChangesService, - useValue: { - onInputChange: jasmine.createSpy('onInputChange') - } - } - ] - }); - }); - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - }); + fixture = TestBed.createComponent(TestFixtureComponent); + component = fixture.componentInstance; })); describe('fixture', () => { - let directive: DataTableColumnDirective; + let directive: DataTableColumnDirective; beforeEach(() => { directive = fixture.debugElement @@ -65,16 +51,12 @@ describe('DataTableColumnDirective', () => { }); describe('directive #1', () => { - let directive: DataTableColumnDirective; + let directive: DataTableColumnDirective; beforeEach(() => { directive = fixture.debugElement.query(By.css('#t1')).injector.get(DataTableColumnDirective); }); - it('should be found', () => { - expect(directive).toBeTruthy(); - }); - it('should have undefined inputs by default', () => { fixture.detectChanges(); expect(directive.name).toBeUndefined(); @@ -96,33 +78,28 @@ describe('DataTableColumnDirective', () => { }); describe('directive #2', () => { - let directive: DataTableColumnDirective; + let columnInputChange: Spy<() => void>; beforeEach(() => { - directive = fixture.debugElement.query(By.css('#t2')).injector.get(DataTableColumnDirective); + columnInputChange = spyOn(ColumnChangesService.prototype, 'onInputChange'); }); - - it('should be found', () => { - expect(directive).toBeTruthy(); - }); - it('should not notify of changes if its the first change', () => { component.columnName = 'Column A'; fixture.detectChanges(); - expect(TestBed.inject(ColumnChangesService).onInputChange).not.toHaveBeenCalled(); + expect(columnInputChange).not.toHaveBeenCalled(); }); it('notifies of subsequent changes', () => { component.columnName = 'Column A'; fixture.detectChanges(); - expect(TestBed.inject(ColumnChangesService).onInputChange).not.toHaveBeenCalled(); + expect(columnInputChange).not.toHaveBeenCalled(); component.columnName = 'Column B'; fixture.detectChanges(); - expect(TestBed.inject(ColumnChangesService).onInputChange).toHaveBeenCalled(); + expect(columnInputChange).toHaveBeenCalled(); }); }); }); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts index 193e7cce1..d180a88d3 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/columns/column.directive.ts @@ -15,32 +15,37 @@ import { DataTableColumnCellTreeToggle } from './tree.directive'; import { ColumnChangesService } from '../../services/column-changes.service'; import { TableColumn, TableColumnProp } from '../../types/table-column.type'; import { DataTableColumnGhostCellDirective } from './column-ghost-cell.directive'; -import { CellContext, HeaderCellContext } from '../../types/public.types'; +import { CellContext, HeaderCellContext, Row } from '../../types/public.types'; @Directive({ - selector: 'ngx-datatable-column', - standalone: true + selector: 'ngx-datatable-column' }) -export class DataTableColumnDirective implements TableColumn, OnChanges { +export class DataTableColumnDirective implements TableColumn, OnChanges { private columnChangesService = inject(ColumnChangesService); - @Input() name: string; - @Input() prop: TableColumnProp; + @Input() name?: string; + @Input() prop?: TableColumnProp; @Input({ transform: booleanAttribute }) bindAsUnsafeHtml?: boolean; - @Input({ transform: booleanAttribute }) frozenLeft: boolean; - @Input({ transform: booleanAttribute }) frozenRight: boolean; - @Input({ transform: numberAttribute }) flexGrow: number; - @Input({ transform: booleanAttribute }) resizeable: boolean; - @Input() comparator: any; - @Input() pipe: PipeTransform; - @Input({ transform: booleanAttribute }) sortable: boolean; - @Input({ transform: booleanAttribute }) draggable: boolean; - @Input({ transform: booleanAttribute }) canAutoResize: boolean; - @Input({ transform: numberAttribute }) minWidth: number; - @Input({ transform: numberAttribute }) width: number; - @Input({ transform: numberAttribute }) maxWidth: number; - @Input({ transform: booleanAttribute }) checkboxable: boolean; - @Input({ transform: booleanAttribute }) headerCheckboxable: boolean; - @Input() headerClass: + @Input({ transform: booleanAttribute }) frozenLeft?: boolean; + @Input({ transform: booleanAttribute }) frozenRight?: boolean; + @Input({ transform: numberAttribute }) flexGrow?: number; + @Input({ transform: booleanAttribute }) resizeable?: boolean; + @Input() comparator?: ( + valueA: any, + valueB: any, + rowA: TRow, + rowB: TRow, + sortDir: 'desc' | 'asc' + ) => number; + @Input() pipe?: PipeTransform; + @Input({ transform: booleanAttribute }) sortable?: boolean; + @Input({ transform: booleanAttribute }) draggable?: boolean; + @Input({ transform: booleanAttribute }) canAutoResize?: boolean; + @Input({ transform: numberAttribute }) minWidth?: number; + @Input({ transform: numberAttribute }) width?: number; + @Input({ transform: numberAttribute }) maxWidth?: number; + @Input({ transform: booleanAttribute }) checkboxable?: boolean; + @Input({ transform: booleanAttribute }) headerCheckboxable?: boolean; + @Input() headerClass?: | string | ((data: { column: TableColumn }) => string | Record); @Input() cellClass?: @@ -52,48 +57,48 @@ export class DataTableColumnDirective implements TableColumn, OnChanges { value: any; rowHeight: number; }) => string | Record); - @Input({ transform: booleanAttribute }) isTreeColumn: boolean; - @Input() treeLevelIndent: number; - @Input() summaryFunc: (cells: any[]) => any; - @Input() summaryTemplate: TemplateRef; + @Input({ transform: booleanAttribute }) isTreeColumn?: boolean; + @Input() treeLevelIndent?: number; + @Input() summaryFunc?: (cells: any[]) => any; + @Input() summaryTemplate?: TemplateRef; @Input('cellTemplate') - _cellTemplateInput: TemplateRef>; + _cellTemplateInput?: TemplateRef>; @ContentChild(DataTableColumnCellDirective, { read: TemplateRef, static: true }) - _cellTemplateQuery: TemplateRef>; + _cellTemplateQuery?: TemplateRef>; - get cellTemplate(): TemplateRef> { + get cellTemplate(): TemplateRef> | undefined { return this._cellTemplateInput || this._cellTemplateQuery; } @Input('headerTemplate') - _headerTemplateInput: TemplateRef; + _headerTemplateInput?: TemplateRef; @ContentChild(DataTableColumnHeaderDirective, { read: TemplateRef, static: true }) - _headerTemplateQuery: TemplateRef; + _headerTemplateQuery?: TemplateRef; - get headerTemplate(): TemplateRef { + get headerTemplate(): TemplateRef | undefined { return this._headerTemplateInput || this._headerTemplateQuery; } @Input('treeToggleTemplate') - _treeToggleTemplateInput: TemplateRef; + _treeToggleTemplateInput?: TemplateRef; @ContentChild(DataTableColumnCellTreeToggle, { read: TemplateRef, static: true }) - _treeToggleTemplateQuery: TemplateRef; + _treeToggleTemplateQuery?: TemplateRef; - get treeToggleTemplate(): TemplateRef { + get treeToggleTemplate(): TemplateRef | undefined { return this._treeToggleTemplateInput || this._treeToggleTemplateQuery; } @Input('ghostCellTemplate') - _ghostCellTemplateInput: TemplateRef; + _ghostCellTemplateInput?: TemplateRef; @ContentChild(DataTableColumnGhostCellDirective, { read: TemplateRef, static: true }) - _ghostCellTemplateQuery: TemplateRef; + _ghostCellTemplateQuery?: TemplateRef; - get ghostCellTemplate(): TemplateRef { + get ghostCellTemplate(): TemplateRef | undefined { return this._ghostCellTemplateInput || this._ghostCellTemplateQuery; } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts index 3062f774a..911e76f0c 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/columns/tree.directive.ts @@ -1,8 +1,7 @@ import { Directive, inject, TemplateRef } from '@angular/core'; @Directive({ - selector: '[ngx-datatable-tree-toggle]', - standalone: true + selector: '[ngx-datatable-tree-toggle]' }) export class DataTableColumnCellTreeToggle { template = inject>(TemplateRef); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html index d51309697..c22078c2e 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html +++ b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.html @@ -1,33 +1,33 @@
@if (headerHeight) { - - + + } -
+
+
+
@if (footerHeight) { - - + + }
diff --git a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.scss index 77a575223..74f943397 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.scss +++ b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.scss @@ -1,325 +1,7 @@ -.ngx-datatable { +:host { display: block; overflow: hidden; justify-content: center; position: relative; transform: translate3d(0, 0, 0); - - [hidden] { - display: none !important; - } - - *, - *:before, - *:after { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - } - - /** - * Vertical Scrolling Adjustments - */ - &.scroll-vertical { - .datatable-body { - overflow-y: auto; - } - &.virtualized { - .datatable-body { - .datatable-row-wrapper { - position: absolute; - } - } - } - } - - /** - * Horizontal Scrolling Adjustments - */ - &.scroll-horz { - .datatable-body { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - } - - /** - * Fixed Header Height Adjustments - */ - &.fixed-header { - .datatable-header { - .datatable-header-inner { - white-space: nowrap; - .datatable-header-cell { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - } - } - - /** - * Fixed row height adjustments - */ - &.fixed-row { - .datatable-scroll { - white-space: nowrap; - - .datatable-body-row { - white-space: nowrap; - - .datatable-body-cell { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - - .datatable-body-group-cell { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - } - } - } - - /** - * Shared Styles - */ - .datatable-body-row, - .datatable-row-center, - .datatable-header-inner { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - - flex-direction: row; - -webkit-flex-flow: row; - -moz-flex-flow: row; - -ms-flex-flow: row; - -o-flex-flow: row; - flex-flow: row; - } - - .datatable-body-cell, - .datatable-header-cell { - overflow-x: hidden; - vertical-align: top; - display: inline-block; - line-height: 1.625; - - &:focus { - outline: none; - } - } - - .datatable-row-left, - .datatable-row-right, - .datatable-group-header { - z-index: 9; - position: sticky !important; - } - - .datatable-row-left, - .datatable-group-header { - left: 0; - } - - .datatable-row-right { - right: 0; - } - - .datatable-row-center, - .datatable-row-group { - position: relative; - } - - /** - * Header Styles - */ - .datatable-header { - display: block; - overflow: hidden; - - .datatable-header-inner { - align-items: stretch; - -webkit-align-items: stretch; - } - - .datatable-header-cell { - position: relative; - display: inline-block; - - &.sortable { - .datatable-header-cell-wrapper { - cursor: pointer; - } - } - - &.longpress .datatable-header-cell-wrapper { - cursor: move; - } - - .sort-btn { - line-height: 100%; - vertical-align: middle; - display: inline-block; - cursor: pointer; - } - - .resize-handle, - .resize-handle--not-resizable { - display: inline-block; - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: 5px; - padding: 0 4px; - visibility: hidden; - } - - .resize-handle { - cursor: ew-resize; - } - - &.resizeable:hover { - .resize-handle { - visibility: visible; - } - } - - &:hover { - .resize-handle--not-resizable { - visibility: visible; - } - } - - .targetMarker { - position: absolute; - top: 0; - bottom: 0; - - &.dragFromLeft { - right: 0; - } - - &.dragFromRight { - left: 0; - } - } - - .datatable-header-cell-template-wrap { - height: inherit; - } - } - } - - /** - * Body Styles - */ - .datatable-body { - position: relative; - z-index: 10; - display: block; - overflow: hidden; - - .datatable-scroll { - display: inline-block; - } - - .datatable-row-detail { - overflow-y: hidden; - } - - .datatable-row-wrapper { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -webkit-flex-direction: column; - -moz-box-orient: vertical; - -moz-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - } - - .datatable-body-row { - outline: none; - - > div { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - } - } - } - - /** - * Footer Styles - */ - .datatable-footer { - display: block; - width: 100%; - overflow: auto; - - .datatable-footer-inner { - display: flex; - align-items: center; - width: 100%; - } - - .selected-count { - .page-count { - flex: 1 1 40%; - } - .datatable-pager { - flex: 1 1 60%; - } - } - - .page-count { - flex: 1 1 20%; - } - - .datatable-pager { - flex: 1 1 80%; - text-align: right; - - .pager, - .pager li { - padding: 0; - margin: 0; - display: inline-block; - list-style: none; - } - - .pager { - li, - li a { - outline: none; - } - - li { - a { - cursor: pointer; - display: inline-block; - } - - &.disabled a { - cursor: not-allowed; - } - } - } - } - } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.spec.ts index 78e649921..15d54146b 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.spec.ts @@ -1,21 +1,33 @@ import { Component } from '@angular/core'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; - -import { ColumnChangesService } from '../services/column-changes.service'; -import { NgxDatatableModule } from '../ngx-datatable.module'; import { DatatableComponent } from './datatable.component'; import { DataTableBodyRowComponent } from './body/body-row.component'; import { DataTableBodyCellComponent } from './body/body-cell.component'; import { DataTableColumnDirective } from './columns/column.directive'; import { DataTableColumnCellDirective } from './columns/column-cell.directive'; import { DataTableColumnHeaderDirective } from './columns/column-header.directive'; - -let fixture: ComponentFixture; -let component: any; +import { SortPropDir } from '../types/public.types'; +import { TableColumn } from '../types/table-column.type'; describe('DatatableComponent', () => { - beforeEach(waitForAsync(() => setupTest(TestFixtureComponent))); + let fixture: ComponentFixture; + let component: TestFixtureComponent; + + @Component({ + template: ` `, + imports: [DatatableComponent] + }) + class TestFixtureComponent { + columns: TableColumn[] = []; + rows: Record[] = []; + sorts: any[] = []; + } + + beforeEach(() => { + fixture = TestBed.createComponent(TestFixtureComponent); + component = fixture.componentInstance; + }); it('should sort date values', () => { const initialRows = [ @@ -35,20 +47,20 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `birthDate` ascending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('1978', 'Ascending'); - expect(textContent({ row: 2, column: 1 })).toContain('1980', 'Ascending'); - expect(textContent({ row: 3, column: 1 })).toContain('1995', 'Ascending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('1978'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('1980'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('1995'); // sort by `birthDate` descending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('1995', 'Descending'); - expect(textContent({ row: 2, column: 1 })).toContain('1980', 'Descending'); - expect(textContent({ row: 3, column: 1 })).toContain('1978', 'Descending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('1995'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('1980'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('1978'); }); it('should sort number values', () => { @@ -65,20 +77,20 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `id` ascending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('5', 'Ascending'); - expect(textContent({ row: 2, column: 1 })).toContain('12', 'Ascending'); - expect(textContent({ row: 3, column: 1 })).toContain('20', 'Ascending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('5'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('12'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('20'); // sort by `id` descending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('20', 'Descending'); - expect(textContent({ row: 2, column: 1 })).toContain('12', 'Descending'); - expect(textContent({ row: 3, column: 1 })).toContain('5', 'Descending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('20'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('12'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('5'); }); it('should sort string values', () => { @@ -99,20 +111,20 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `product` ascending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('Bikes', 'Ascending'); - expect(textContent({ row: 2, column: 1 })).toContain('Computers', 'Ascending'); - expect(textContent({ row: 3, column: 1 })).toContain('Smartphones', 'Ascending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('Bikes'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('Computers'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('Smartphones'); // sort by `product` descending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('Smartphones', 'Descending'); - expect(textContent({ row: 2, column: 1 })).toContain('Computers', 'Descending'); - expect(textContent({ row: 3, column: 1 })).toContain('Bikes', 'Descending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('Smartphones'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('Computers'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('Bikes'); }); it('should sort with a custom comparator', () => { @@ -132,20 +144,20 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `product` ascending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('Cars', 'Ascending'); - expect(textContent({ row: 2, column: 1 })).toContain('Bikes', 'Ascending'); - expect(textContent({ row: 3, column: 1 })).toContain('Smartphones', 'Ascending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('Cars'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('Bikes'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('Smartphones'); // sort by `product` descending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('Smartphones', 'Descending'); - expect(textContent({ row: 2, column: 1 })).toContain('Bikes', 'Descending'); - expect(textContent({ row: 3, column: 1 })).toContain('Cars', 'Descending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('Smartphones'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('Bikes'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('Cars'); }); it('should sort using a stable sorting algorithm', () => { @@ -220,26 +232,26 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `name` ascending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); // sort by `state` descending - sortBy({ column: 2 }); + sortBy({ column: 2 }, fixture); fixture.detectChanges(); - sortBy({ column: 2 }); + sortBy({ column: 2 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('amet'); - expect(textContent({ row: 2, column: 1 })).toContain('dolor'); - expect(textContent({ row: 3, column: 1 })).toContain('ipsum'); - expect(textContent({ row: 4, column: 1 })).toContain('lorem'); - expect(textContent({ row: 5, column: 1 })).toContain('maecennas'); - expect(textContent({ row: 6, column: 1 })).toContain('sed'); - expect(textContent({ row: 7, column: 1 })).toContain('foo'); - expect(textContent({ row: 8, column: 1 })).toContain('bar'); - expect(textContent({ row: 9, column: 1 })).toContain('cat'); - expect(textContent({ row: 10, column: 1 })).toContain('sit'); - expect(textContent({ row: 11, column: 1 })).toContain('man'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('amet'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('dolor'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('ipsum'); + expect(textContent({ row: 4, column: 1 }, fixture)).toContain('lorem'); + expect(textContent({ row: 5, column: 1 }, fixture)).toContain('maecennas'); + expect(textContent({ row: 6, column: 1 }, fixture)).toContain('sed'); + expect(textContent({ row: 7, column: 1 }, fixture)).toContain('foo'); + expect(textContent({ row: 8, column: 1 }, fixture)).toContain('bar'); + expect(textContent({ row: 9, column: 1 }, fixture)).toContain('cat'); + expect(textContent({ row: 10, column: 1 }, fixture)).toContain('sit'); + expect(textContent({ row: 11, column: 1 }, fixture)).toContain('man'); }); it('should sort correctly after push events', () => { @@ -275,13 +287,13 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `state` descending - sortBy({ column: 2 }); + sortBy({ column: 2 }, fixture); fixture.detectChanges(); - sortBy({ column: 2 }); + sortBy({ column: 2 }, fixture); fixture.detectChanges(); // sort by `name` ascending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); // mimic new `rows` data pushed to component @@ -289,22 +301,22 @@ describe('DatatableComponent', () => { fixture.detectChanges(); // sort by `state` descending - sortBy({ column: 2 }); + sortBy({ column: 2 }, fixture); fixture.detectChanges(); - sortBy({ column: 2 }); + sortBy({ column: 2 }, fixture); fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('amet'); - expect(textContent({ row: 2, column: 1 })).toContain('dolor'); - expect(textContent({ row: 3, column: 1 })).toContain('ipsum'); - expect(textContent({ row: 4, column: 1 })).toContain('lorem'); - expect(textContent({ row: 5, column: 1 })).toContain('maecennas'); - expect(textContent({ row: 6, column: 1 })).toContain('sed'); - expect(textContent({ row: 7, column: 1 })).toContain('foo'); - expect(textContent({ row: 8, column: 1 })).toContain('bar'); - expect(textContent({ row: 9, column: 1 })).toContain('cat'); - expect(textContent({ row: 10, column: 1 })).toContain('sit'); - expect(textContent({ row: 11, column: 1 })).toContain('man'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('amet'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('dolor'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('ipsum'); + expect(textContent({ row: 4, column: 1 }, fixture)).toContain('lorem'); + expect(textContent({ row: 5, column: 1 }, fixture)).toContain('maecennas'); + expect(textContent({ row: 6, column: 1 }, fixture)).toContain('sed'); + expect(textContent({ row: 7, column: 1 }, fixture)).toContain('foo'); + expect(textContent({ row: 8, column: 1 }, fixture)).toContain('bar'); + expect(textContent({ row: 9, column: 1 }, fixture)).toContain('cat'); + expect(textContent({ row: 10, column: 1 }, fixture)).toContain('sit'); + expect(textContent({ row: 11, column: 1 }, fixture)).toContain('man'); }); it('should set offset to 0 when sorting by a column', () => { @@ -326,9 +338,9 @@ describe('DatatableComponent', () => { datatableComponent.offset = 1; // sort by `id` descending - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); - sortBy({ column: 1 }); + sortBy({ column: 1 }, fixture); fixture.detectChanges(); expect(datatableComponent.offset).toBe(0); @@ -344,50 +356,85 @@ describe('DatatableComponent', () => { component.columns = columns; fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('Hello'); - expect(textContent({ row: 1, column: 2 })).toContain('123'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('Hello'); + expect(textContent({ row: 1, column: 2 }, fixture)).toContain('123'); }); }); describe('DatatableComponent With Custom Templates', () => { - beforeEach(waitForAsync(() => setupTest(TestFixtureComponentWithCustomTemplates))); + @Component({ + template: ` + + + + {{ column.name }} + + + {{ row.id }} + + + + + {{ column.name }} + + + {{ row[column.prop!] }} + + + + `, + imports: [ + DatatableComponent, + DataTableColumnDirective, + DataTableColumnCellDirective, + DataTableColumnHeaderDirective + ] + }) + // eslint-disable-next-line @angular-eslint/component-class-suffix + class TestFixtureComponentWithCustomTemplates { + rows: Record[] = []; + sorts: SortPropDir[] = []; + columnTwoProp?: string; + } + + let fixture: ComponentFixture; + let component: TestFixtureComponentWithCustomTemplates; + + beforeEach(() => { + fixture = TestBed.createComponent(TestFixtureComponentWithCustomTemplates); + component = fixture.componentRef.instance; + }); it('should sort when the table is initially rendered if `sorts` are provided', () => { - const initialRows = [{ id: 5 }, { id: 20 }, { id: 12 }]; - - const sorts = [ + component.rows = [{ id: 5 }, { id: 20 }, { id: 12 }]; + component.sorts = [ { prop: 'id', - dir: 'asc' + dir: 'asc' as const } ]; - - component.rows = initialRows; - component.sorts = sorts; fixture.detectChanges(); - expect(textContent({ row: 1, column: 1 })).toContain('5', 'Ascending'); - expect(textContent({ row: 2, column: 1 })).toContain('12', 'Ascending'); - expect(textContent({ row: 3, column: 1 })).toContain('20', 'Ascending'); + expect(textContent({ row: 1, column: 1 }, fixture)).toContain('5'); + expect(textContent({ row: 2, column: 1 }, fixture)).toContain('12'); + expect(textContent({ row: 3, column: 1 }, fixture)).toContain('20'); }); it('should reflect changes to input bindings of `ngx-datatable-column`', () => { - const initialRows = [ + /** + * initially display `user` column as the second column in the table + */ + component.rows = [ { id: 5, user: 'Sam', age: 35 }, { id: 20, user: 'Bob', age: 50 }, { id: 12, user: 'Joe', age: 60 } ]; - - /** - * initially display `user` column as the second column in the table - */ - component.rows = initialRows; component.columnTwoProp = 'user'; fixture.detectChanges(); - expect(textContent({ row: 1, column: 2 })).toContain('Sam', 'Displays user'); - expect(textContent({ row: 2, column: 2 })).toContain('Bob', 'Displays user'); - expect(textContent({ row: 3, column: 2 })).toContain('Joe', 'Displays user'); + expect(textContent({ row: 1, column: 2 }, fixture)).toContain('Sam'); + expect(textContent({ row: 2, column: 2 }, fixture)).toContain('Bob'); + expect(textContent({ row: 3, column: 2 }, fixture)).toContain('Joe'); /** * switch to displaying `age` column as the second column in the table @@ -395,73 +442,16 @@ describe('DatatableComponent With Custom Templates', () => { component.columnTwoProp = 'age'; fixture.detectChanges(); - expect(textContent({ row: 1, column: 2 })).toContain('35', 'Displays age'); - expect(textContent({ row: 2, column: 2 })).toContain('50', 'Displays age'); - expect(textContent({ row: 3, column: 2 })).toContain('60', 'Displays age'); + expect(textContent({ row: 1, column: 2 }, fixture)).toContain('35'); + expect(textContent({ row: 2, column: 2 }, fixture)).toContain('50'); + expect(textContent({ row: 3, column: 2 }, fixture)).toContain('60'); }); }); -@Component({ - template: ` `, - imports: [DatatableComponent] -}) -class TestFixtureComponent { - columns: any[] = []; - rows: any[] = []; - sorts: any[] = []; -} - -@Component({ - template: ` - - - - {{ column.name }} - - - {{ row.id }} - - - - - {{ column.name }} - - - {{ row[column.prop] }} - - - - `, - imports: [ - DatatableComponent, - DataTableColumnDirective, - DataTableColumnCellDirective, - DataTableColumnHeaderDirective - ] -}) -// eslint-disable-next-line @angular-eslint/component-class-suffix -class TestFixtureComponentWithCustomTemplates { - rows: any[] = []; - sorts: any[] = []; - columnTwoProp: string; -} - -function setupTest(componentClass) { - return TestBed.configureTestingModule({ - imports: [NgxDatatableModule, componentClass], - providers: [ColumnChangesService] - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(componentClass); - component = fixture.componentInstance; - }); -} - /** * mimics the act of a user clicking a column to sort it */ -function sortBy({ column }: { column: number }) { +function sortBy({ column }: { column: number }, fixture: ComponentFixture) { const columnIndex = column - 1; const headerCellDe = fixture.debugElement.queryAll(By.css('datatable-header-cell'))[columnIndex]; const de = headerCellDe.query(By.css('span:last-child')); @@ -472,7 +462,10 @@ function sortBy({ column }: { column: number }) { * test helper function to return text content of a cell within the * body of the ngx-datatable component */ -function textContent({ row, column }: { row: number; column: number }) { +function textContent( + { row, column }: { row: number; column: number }, + fixture: ComponentFixture +) { const [rowIndex, columnIndex] = [row - 1, column - 1]; const bodyRowDe = fixture.debugElement.queryAll(By.directive(DataTableBodyRowComponent))[ rowIndex diff --git a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts index 0e77d9708..7d4e49cfb 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/datatable.component.ts @@ -23,17 +23,14 @@ import { QueryList, signal, TemplateRef, - ViewChild, - ViewEncapsulation + ViewChild } from '@angular/core'; import { DatatableGroupHeaderDirective } from './body/body-group-header.directive'; -import { BehaviorSubject, Subscription } from 'rxjs'; -import { INgxDatatableConfig } from '../ngx-datatable.module'; +import { Subscription } from 'rxjs'; import { groupRowsByParents, optionalGetterForProp } from '../utils/tree'; import { TableColumn } from '../types/table-column.type'; -import { setColumnDefaults, translateTemplates } from '../utils/column-helper'; import { DataTableColumnDirective } from './columns/column.directive'; import { DatatableRowDetailDirective } from './row-detail/row-detail.directive'; import { DatatableFooterDirective } from './footer/footer.directive'; @@ -50,30 +47,38 @@ import { ActivateEvent, ColumnMode, ColumnResizeEvent, + ContextMenuEvent, ContextmenuType, DragEventData, Group, PageEvent, PagerPageEvent, ReorderEvent, + Row, RowOrGroup, ScrollEvent, + SelectEvent, SelectionType, SortEvent, SortPropDir, SortType, TreeStatus } from '../types/public.types'; -import { AsyncPipe } from '@angular/common'; import { DataTableFooterComponent } from './footer/footer.component'; import { VisibilityDirective } from '../directives/visibility.directive'; import { ProgressBarComponent } from './body/progress-bar.component'; +import { toInternalColumn } from '../utils/column-helper'; +import { + ColumnResizeEventInternal, + ReorderEventInternal, + TableColumnInternal +} from '../types/internal.types'; +import { NGX_DATATABLE_CONFIG, NgxDatatableConfig } from '../ngx-datatable.config'; @Component({ selector: 'ngx-datatable', templateUrl: './datatable.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None, styleUrls: ['./datatable.component.scss'], host: { class: 'ngx-datatable' @@ -90,28 +95,30 @@ import { ProgressBarComponent } from './body/progress-bar.component'; DataTableHeaderComponent, DataTableBodyComponent, DataTableFooterComponent, - AsyncPipe, ProgressBarComponent ] }) -export class DatatableComponent +export class DatatableComponent implements OnInit, DoCheck, AfterViewInit, AfterContentInit, OnDestroy { private scrollbarHelper = inject(ScrollbarHelper); private cd = inject(ChangeDetectorRef); private columnChangesService = inject(ColumnChangesService); - private configuration = inject('configuration' as any, { optional: true }); + private configuration = + inject(NGX_DATATABLE_CONFIG, { optional: true }) ?? + // This is the old injection token for backward compatibility. + inject('configuration' as any, { optional: true }); /** * Template for the target marker of drag target columns. */ - @Input() targetMarkerTemplate: TemplateRef; + @Input() targetMarkerTemplate?: TemplateRef; /** * Rows that are displayed in the table. */ - @Input() set rows(val: TRow[] | null | undefined) { - this._rows = val; + @Input() set rows(val: (TRow | undefined)[] | null | undefined) { + this._rows = val ?? []; // This will ensure that datatable detects changes on doing like this rows = [...rows]; this.rowDiffer.diff([] as any); if (val) { @@ -122,17 +129,17 @@ export class DatatableComponent /** * Gets the rows. */ - get rows(): TRow[] { + get rows(): (TRow | undefined)[] { return this._rows; } /** * This attribute allows the user to set the name of the column to group the data with */ - @Input() set groupRowsBy(val: keyof TRow) { + @Input() set groupRowsBy(val: keyof TRow | undefined) { if (val) { this._groupRowsBy = val; - if (this._rows && this._groupRowsBy) { + if (this._groupRowsBy) { // creates a new array with the data grouped this.groupedRows = this.groupArrayBy(this._rows, this._groupRowsBy); } @@ -158,15 +165,14 @@ export class DatatableComponent * ]} * ] */ - @Input() groupedRows: Group[]; + @Input() groupedRows?: Group[]; /** * Columns to be displayed. */ @Input() set columns(val: TableColumn[]) { if (val) { - this._internalColumns = [...val]; - setColumnDefaults(this._internalColumns, this._defaultColumnWidth); + this._internalColumns = toInternalColumn(val, this._defaultColumnWidth); this.recalculateColumns(); } @@ -209,7 +215,7 @@ export class DatatableComponent * The row height; which is necessary * to calculate the height for the lazy rendering. */ - @Input() rowHeight: number | 'auto' | ((row?: TRow) => number) = 30; + @Input() rowHeight: number | 'auto' | ((row: TRow) => number) = 30; /** * Type of column width distribution formula. @@ -302,7 +308,7 @@ export class DatatableComponent this._ghostLoadingIndicator = val; if (val && this.scrollbarV && !this.externalPaging) { // in case where we don't have predefined total page length - this.rows = [...(this.rows ?? []), undefined]; // undefined row will render ghost cell row at the end of the page + this.rows = [...this.rows, undefined]; // undefined row will render ghost cell row at the end of the page } } get ghostLoadingIndicator(): boolean { @@ -321,7 +327,7 @@ export class DatatableComponent * For no selection pass a `falsey`. * Default value: `undefined` */ - @Input() selectionType: SelectionType; + @Input() selectionType?: SelectionType; /** * Enable/Disable ability to re-order columns @@ -349,41 +355,33 @@ export class DatatableComponent /** * Css class overrides */ - @Input() cssClasses: Partial = { - sortAscending: 'datatable-icon-up', - sortDescending: 'datatable-icon-down', - sortUnset: 'datatable-icon-sort-unset', - pagerLeftArrow: 'datatable-icon-left', - pagerRightArrow: 'datatable-icon-right', - pagerPrevious: 'datatable-icon-prev', - pagerNext: 'datatable-icon-skip' - }; + @Input() cssClasses: Partial['cssClasses']> = {}; /** * Message overrides for localization * - * emptyMessage [default] = 'No data to display' - * totalMessage [default] = 'total' - * selectedMessage [default] = 'selected' - */ - @Input() messages: Partial = { - // Message to show when array is presented - // but contains no values - emptyMessage: 'No data to display', - - // Footer total message - totalMessage: 'total', - - // Footer selected message - selectedMessage: 'selected' - }; + * @defaultValue + * ``` + * { + * emptyMessage: 'No data to display', + * totalMessage: 'total', + * selectedMessage: 'selected', + * ariaFirstPageMessage: 'go to first page', + * ariaPreviousPageMessage: 'go to previous page', + * ariaPageNMessage: 'page', + * ariaNextPageMessage: 'go to next page', + * ariaLastPageMessage: 'go to last page' + * } + * ``` + */ + @Input() messages: Partial['messages']> = {}; /** * A function which is called with the row and should return either: * - a string: `"class-1 class-2` * - a Record: `{ 'class-1': true, 'class-2': false }` */ - @Input() rowClass: (row: Group | TRow) => string | Record; + @Input() rowClass?: (row: TRow) => string | Record; /** * A boolean/function you can use to check whether you want @@ -393,7 +391,7 @@ export class DatatableComponent * return selection !== 'Ethel Price'; * } */ - @Input() selectCheck: (value: TRow, index: number, array: TRow[]) => boolean; + @Input() selectCheck?: (value: TRow, index: number, array: TRow[]) => boolean; /** * A function you can use to check whether you want @@ -403,7 +401,7 @@ export class DatatableComponent * return row.name !== 'Ethel Price'; * } */ - @Input() displayCheck: (row: TRow, column: TableColumn, value?: any) => boolean; + @Input() displayCheck?: (row: TRow, column: TableColumn, value?: any) => boolean; /** * A boolean you can use to set the detault behaviour of rows and groups @@ -416,7 +414,7 @@ export class DatatableComponent * Property to which you can use for custom tracking of rows. * Example: 'name' */ - @Input() trackByProp: string; + @Input() trackByProp?: string; /** * Property to which you can use for determining select all @@ -432,12 +430,12 @@ export class DatatableComponent /** * Tree from relation */ - @Input() treeFromRelation: string; + @Input() treeFromRelation?: string; /** * Tree to relation */ - @Input() treeToRelation: string; + @Input() treeToRelation?: string; /** * A flag for switching summary row on / off @@ -462,7 +460,7 @@ export class DatatableComponent * return row.name !== 'Ethel Price'; * } */ - @Input() disableRowCheck: (row: TRow) => boolean; + @Input() disableRowCheck?: (row: TRow) => boolean; /** * A flag to enable drag behavior of native HTML5 drag and drop API on rows. @@ -490,7 +488,7 @@ export class DatatableComponent /** * A cell or row was selected. */ - @Output() select: EventEmitter<{ selected: TRow[] }> = new EventEmitter(); + @Output() select = new EventEmitter>(); /** * Column sort was invoked. @@ -517,11 +515,7 @@ export class DatatableComponent * type indicates whether the header or the body was clicked. * content contains either the column or the row that was clicked. */ - @Output() tableContextmenu = new EventEmitter<{ - event: MouseEvent; - type: ContextmenuType; - content: TableColumn | RowOrGroup; - }>(false); + @Output() tableContextmenu = new EventEmitter>(false); /** * A row was expanded ot collapsed for tree @@ -639,36 +633,36 @@ export class DatatableComponent * Row Detail templates gathered from the ContentChild */ @ContentChild(DatatableRowDetailDirective) - rowDetail: DatatableRowDetailDirective; + rowDetail?: DatatableRowDetailDirective; /** * Group Header templates gathered from the ContentChild */ @ContentChild(DatatableGroupHeaderDirective) - groupHeader: DatatableGroupHeaderDirective; + groupHeader?: DatatableGroupHeaderDirective; /** * Footer template gathered from the ContentChild */ @ContentChild(DatatableFooterDirective) - footer: DatatableFooterDirective; + footer?: DatatableFooterDirective; /** * Reference to the body component for manually * invoking functions on the body. */ @ViewChild(DataTableBodyComponent) - bodyComponent: DataTableBodyComponent; + bodyComponent!: DataTableBodyComponent; /** * Reference to the header component for manually * invoking functions on the header. */ @ViewChild(DataTableHeaderComponent) - headerComponent: DataTableHeaderComponent; + headerComponent!: DataTableHeaderComponent; @ViewChild(DataTableBodyComponent, { read: ElementRef }) - private bodyElement: ElementRef; + private bodyElement!: ElementRef; @ContentChild(DatatableRowDefDirective, { read: TemplateRef }) @@ -696,15 +690,15 @@ export class DatatableComponent bodyHeight: number; rowCount = 0; - _offsetX = new BehaviorSubject(0); + _offsetX = 0; _limit: number | undefined; _count = 0; _offset = 0; - _rows: TRow[] | null | undefined; - _groupRowsBy: keyof TRow; - _internalRows: TRow[]; - _internalColumns: TableColumn[]; - _columns: TableColumn[]; + _rows: (TRow | undefined)[] = []; + _groupRowsBy?: keyof TRow; + _internalRows: (TRow | undefined)[] = []; + _internalColumns!: TableColumnInternal[]; + _columns!: TableColumn[]; _subscriptions: Subscription[] = []; _ghostLoadingIndicator = false; _defaultColumnWidth?: number; @@ -762,7 +756,8 @@ export class DatatableComponent count: this.count, pageSize: this.pageSize, limit: this.limit, - offset: 0 + offset: 0, + sorts: this.sorts }); } }); @@ -786,7 +781,7 @@ export class DatatableComponent * * (`fn(x) === fn(y)` instead of `x === y`) */ - @Input() rowIdentity: (x: TRow | Group) => unknown = x => { + @Input() rowIdentity: (x: RowOrGroup) => unknown = x => { if (this._groupRowsBy) { // each group in groupedRows are stored as {key, value: [rows]}, // where key is the groupRowsBy index @@ -801,10 +796,8 @@ export class DatatableComponent */ translateColumns(val: QueryList>) { if (val) { - const arr = val.toArray(); - if (arr.length) { - this._internalColumns = translateTemplates(arr); - setColumnDefaults(this._internalColumns, this._defaultColumnWidth); + if (val.length) { + this._internalColumns = toInternalColumn(val, this._defaultColumnWidth); this.recalculateColumns(); if (!this.externalSorting && this.rows?.length) { this.sortInternalRows(); @@ -820,17 +813,23 @@ export class DatatableComponent * @param originalArray the original array passed via parameter * @param groupBy the key of the column to group the data by */ - groupArrayBy(originalArray: TRow[], groupBy: keyof TRow) { + groupArrayBy(originalArray: (TRow | undefined)[], groupBy: keyof TRow) { // create a map to hold groups with their corresponding results const map = new Map(); let i = 0; originalArray.forEach(item => { + if (!item) { + // skip undefined items + return; + } + const key = item[groupBy]; - if (!map.has(key)) { + const value = map.get(key); + if (!value) { map.set(key, [item]); } else { - map.get(key).push(item); + value.push(item); } i++; }); @@ -861,7 +860,7 @@ export class DatatableComponent optionalGetterForProp(this.treeToRelation) ); - if (this._rows && this._groupRowsBy) { + if (this._groupRowsBy) { // If a column has been specified in _groupRowsBy create a new array with the data grouped by that row this.groupedRows = this.groupArrayBy(this._rows, this._groupRowsBy); } @@ -909,8 +908,8 @@ export class DatatableComponent * distribution mode and scrollbar offsets. */ recalculateColumns( - columns: TableColumn[] = this._internalColumns, - forceIdx: number = -1, + columns: TableColumnInternal[] = this._internalColumns, + forceIdx = -1, allowBleed: boolean = this.scrollbarH ): TableColumn[] | undefined { let width = this._innerWidth; @@ -999,7 +998,8 @@ export class DatatableComponent count: this.count, pageSize: this.pageSize, limit: this.limit, - offset: this.offset + offset: this.offset, + sorts: this.sorts }); } } @@ -1008,9 +1008,8 @@ export class DatatableComponent * The body triggered a scroll event. */ onBodyScroll(event: ScrollEvent): void { - this._offsetX.next(event.offsetX); + this._offsetX = event.offsetX; this.scroll.emit(event); - this.cd.detectChanges(); } /** @@ -1024,7 +1023,8 @@ export class DatatableComponent count: this.count, pageSize: this.pageSize, limit: this.limit, - offset: this.offset + offset: this.offset, + sorts: this.sorts }); if (this.selectAllRowsOnPage) { @@ -1066,10 +1066,6 @@ export class DatatableComponent */ calcRowCount(): number { if (!this.externalPaging) { - if (!this.rows) { - return 0; - } - if (this.groupedRows) { return this.groupedRows.length; } else if (this.treeFromRelation != null && this.treeToRelation != null) { @@ -1099,28 +1095,18 @@ export class DatatableComponent /** * The header triggered a column resize event. */ - onColumnResize({ column, newValue, prevValue }: ColumnResizeEvent): void { + onColumnResize({ column, newValue, prevValue }: ColumnResizeEventInternal): void { /* Safari/iOS 10.2 workaround */ if (column === undefined) { return; } - let idx: number; - const cols = this._internalColumns.map((c, i) => { - c = { ...c }; - - if (c.$$id === column.$$id) { - idx = i; - c.width = newValue; - - // set this so we can force the column - // width distribution to be to this value - c.$$oldWidth = newValue; - } - - return c; - }); - + const idx = this._internalColumns.indexOf(column); + const cols = this._internalColumns.map(col => ({ ...col })); + cols[idx].width = newValue; + // set this so we can force the column + // width distribution to be to this value + cols[idx].$$oldWidth = newValue; this.recalculateColumns(cols, idx); this._internalColumns = cols; @@ -1131,7 +1117,7 @@ export class DatatableComponent }); } - onColumnResizing({ column, newValue }: ColumnResizeEvent): void { + onColumnResizing({ column, newValue }: ColumnResizeEventInternal): void { if (column === undefined) { return; } @@ -1144,7 +1130,8 @@ export class DatatableComponent /** * The header triggered a column re-order event. */ - onColumnReorder({ column, newValue, prevValue }: ReorderEvent): void { + onColumnReorder(event: ReorderEventInternal): void { + const { column, newValue, prevValue } = event; const cols = this._internalColumns.map(c => ({ ...c })); if (this.swapColumns) { @@ -1169,11 +1156,7 @@ export class DatatableComponent this._internalColumns = cols; - this.reorder.emit({ - column, - newValue, - prevValue - }); + this.reorder.emit(event); } /** @@ -1212,7 +1195,8 @@ export class DatatableComponent count: this.count, pageSize: this.pageSize, limit: this.limit, - offset: this.offset + offset: this.offset, + sorts: this.sorts }); this.sort.emit(event); } @@ -1232,14 +1216,16 @@ export class DatatableComponent // do the opposite here if (!allSelected) { - this.selected.push(...this._internalRows.slice(first, last)); + this.selected.push(...this._internalRows.slice(first, last).filter(row => !!row)); } } else { - let relevantRows; + let relevantRows: TRow[]; if (this.disableRowCheck) { - relevantRows = this.rows.filter(row => !this.disableRowCheck(row)); + relevantRows = this.rows.filter( + (row => row && !this.disableRowCheck!(row)) as (row: TRow | undefined) => row is TRow + ); } else { - relevantRows = this.rows; + relevantRows = this.rows.filter(row => !!row); } // before we splice, chk if we currently have all selected const allSelected = this.selected.length === relevantRows.length; @@ -1259,7 +1245,7 @@ export class DatatableComponent /** * A row was selected from body */ - onBodySelect(event: { selected: TRow[] }): void { + onBodySelect(event: SelectEvent): void { this.select.emit(event); } @@ -1270,7 +1256,7 @@ export class DatatableComponent const row = event.row; // TODO: For duplicated items this will not work const rowIndex = this._rows.findIndex( - r => r[this.treeToRelation] === event.row[this.treeToRelation] + r => r && r[this.treeToRelation!] === event.row[this.treeToRelation!] ); this.treeAction.emit({ row, rowIndex }); } @@ -1310,7 +1296,7 @@ export class DatatableComponent const sortOnGroupHeader = this.sorts?.find( sortColumns => sortColumns.prop === this._groupRowsBy ); - this.groupedRows = this.groupArrayBy(this._rows, this._groupRowsBy); + this.groupedRows = this.groupArrayBy(this._rows, this._groupRowsBy!); this.groupedRows = sortGroupedRows( this.groupedRows, this._internalColumns, diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts index 3e77a6015..a29253d60 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer-template.directive.ts @@ -2,8 +2,7 @@ import { Directive } from '@angular/core'; import { FooterContext } from '../../types/public.types'; @Directive({ - selector: '[ngx-datatable-footer-template]', - standalone: true + selector: '[ngx-datatable-footer-template]' }) export class DataTableFooterTemplateDirective { static ngTemplateContextGuard( diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.scss new file mode 100644 index 000000000..d8e8492d7 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.scss @@ -0,0 +1,21 @@ +:host { + display: block; + width: 100%; + overflow: auto; +} + +.datatable-footer-inner { + display: flex; + align-items: center; + width: 100%; +} + +.page-count { + flex: 1 1 20%; +} + +.selected-count { + .page-count { + flex: 1 1 40%; + } +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.spec.ts index 6a272abe1..8bcb76117 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.spec.ts @@ -2,7 +2,6 @@ import { Component, DebugElement, TemplateRef, ViewChild } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { addMatchers } from '../../test'; import { DataTablePagerComponent } from './pager.component'; import { DataTableFooterComponent } from './footer.component'; @@ -11,9 +10,12 @@ let component: TestFixtureComponent; let page: Page; describe('DataTableFooterComponent', () => { - beforeAll(addMatchers); - - beforeEach(waitForAsync(setupTest)); + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(TestFixtureComponent); + component = fixture.componentInstance; + page = new Page(); + page.detectChangesAndRunQueries(); + })); describe('div.datatable-footer-inner', () => { it(`should have a height`, () => { @@ -28,14 +30,14 @@ describe('DataTableFooterComponent', () => { component.selectedCount = 1; page.detectChangesAndRunQueries(); - expect(page.datatableFooterInner.nativeElement).toHaveCssClass('selected-count'); + expect(page.datatableFooterInner.nativeElement).toHaveClass('selected-count'); }); it('should not have `.selected-count` class if selectedMessage is not set', () => { component.selectedMessage = undefined; page.detectChangesAndRunQueries(); - expect(page.datatableFooterInner.nativeElement).not.toHaveCssClass('selected-count'); + expect(page.datatableFooterInner.nativeElement).not.toHaveClass('selected-count'); }); }); @@ -156,19 +158,13 @@ describe('DataTableFooterComponent', () => { component.pageSize = 5; page.detectChangesAndRunQueries(); - expect(page.datatablePager.nativeElement.hidden).toBe( - false, - 'DataTablePagerComponent should be hidden' - ); + expect(page.datatablePager).toBeTruthy(); component.rowCount = 1; component.pageSize = 2; page.detectChangesAndRunQueries(); - expect(page.datatablePager.nativeElement.hidden).toBe( - true, - 'DataTablePagerComponent should not be hidden' - ); + expect(page.datatablePager).toBeFalsy(); }); }); @@ -198,11 +194,11 @@ describe('DataTableFooterComponent', () => { page.detectChangesAndRunQueries(); const listItems = page.templateList.queryAll(By.css('li')); - expect(listItems[0].nativeElement).toHaveText('rowCount 12'); - expect(listItems[1].nativeElement).toHaveText('pageSize 1'); - expect(listItems[2].nativeElement).toHaveText('selectedCount 4'); - expect(listItems[3].nativeElement).toHaveText('curPage 1'); - expect(listItems[4].nativeElement).toHaveText('offset 0'); + expect(listItems[0].nativeElement.innerHTML).toContain('rowCount 12'); + expect(listItems[1].nativeElement.innerHTML).toContain('pageSize 1'); + expect(listItems[2].nativeElement.innerHTML).toContain('selectedCount 4'); + expect(listItems[3].nativeElement.innerHTML).toContain('curPage 1'); + expect(listItems[4].nativeElement.innerHTML).toContain('offset 0'); }); }); }); @@ -226,7 +222,7 @@ describe('DataTableFooterComponent', () => { [selectedCount]="selectedCount" [selectedMessage]="selectedMessage" [pagerNextIcon]="pagerNextIcon" - (page)="onPageEvent($event)" + (page)="onPageEvent()" > @@ -250,18 +246,18 @@ describe('DataTableFooterComponent', () => { imports: [DataTableFooterComponent] }) class TestFixtureComponent { - footerHeight: number; + footerHeight = 0; rowCount = 100; pageSize = 1; offset = 0; - pagerLeftArrowIcon: string; - pagerRightArrowIcon: string; - pagerPreviousIcon: string; - pagerNextIcon: string; - totalMessage: string; - footerTemplate: { template: TemplateRef }; - selectedCount: number; - selectedMessage: string; + pagerLeftArrowIcon = ''; + pagerRightArrowIcon = ''; + pagerPreviousIcon = ''; + pagerNextIcon = ''; + totalMessage = ''; + footerTemplate?: { template: TemplateRef }; + selectedCount = 0; + selectedMessage?: string; /** * establishes a reference to a test template that can @@ -269,36 +265,23 @@ class TestFixtureComponent { * in these unit tests */ @ViewChild('testTemplate', { read: TemplateRef, static: true }) - testTemplate: TemplateRef; + testTemplate!: TemplateRef; onPageEvent() { return; } } -function setupTest() { - return TestBed.configureTestingModule({ - imports: [TestFixtureComponent] - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - page = new Page(); - page.detectChangesAndRunQueries(); - }); -} - /** * a Page is a collection of references to DebugElements. it * makes for cleaner testing */ class Page { - datatableFooter: DebugElement; - datatableFooterInner: DebugElement; - templateList: DebugElement; - pageCount: DebugElement; - datatablePager: DebugElement; + datatableFooter!: DebugElement; + datatableFooterInner!: DebugElement; + templateList!: DebugElement; + pageCount!: DebugElement; + datatablePager!: DebugElement; detectChangesAndRunQueries() { fixture.detectChanges(); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts index d9ccb0fb6..8add706fd 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.component.ts @@ -11,9 +11,9 @@ import { NgClass, NgTemplateOutlet } from '@angular/common'; [ngClass]="{ 'selected-count': selectedMessage }" [style.height.px]="footerHeight" > - @if (footerTemplate) { + @if (footerTemplate?.template) { - } + } }
`, host: { class: 'datatable-footer' }, + styleUrl: './footer.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, NgTemplateOutlet, DataTablePagerComponent] }) export class DataTableFooterComponent { - @Input() footerHeight: number; - @Input() rowCount: number; - @Input() pageSize: number; - @Input() offset: number; - @Input() pagerLeftArrowIcon: string; - @Input() pagerRightArrowIcon: string; - @Input() pagerPreviousIcon: string; - @Input() pagerNextIcon: string; - @Input() totalMessage: string; - @Input() footerTemplate: DatatableFooterDirective; + @Input() footerHeight!: number; + @Input() rowCount!: number; + @Input() pageSize!: number; + @Input() offset!: number; + @Input() pagerLeftArrowIcon?: string; + @Input() pagerRightArrowIcon?: string; + @Input() pagerPreviousIcon?: string; + @Input() pagerNextIcon?: string; + @Input() totalMessage!: string; + @Input() footerTemplate?: DatatableFooterDirective; - @Input() selectedCount: number = 0; - @Input() selectedMessage: string | boolean; + @Input() selectedCount = 0; + @Input() selectedMessage?: string | boolean; @Output() page: EventEmitter = new EventEmitter(); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts index e6fed6bea..448dd757f 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/footer.directive.ts @@ -1,27 +1,18 @@ -import { ContentChild, Directive, Input, numberAttribute, TemplateRef } from '@angular/core'; +import { ContentChild, Directive, Input, TemplateRef } from '@angular/core'; import { DataTableFooterTemplateDirective } from './footer-template.directive'; import { FooterContext } from '../../types/public.types'; @Directive({ - selector: 'ngx-datatable-footer', - standalone: true + selector: 'ngx-datatable-footer' }) export class DatatableFooterDirective { - @Input({ transform: numberAttribute }) footerHeight: number; - @Input() totalMessage: string; - @Input() selectedMessage: string | boolean; - @Input() pagerLeftArrowIcon: string; - @Input() pagerRightArrowIcon: string; - @Input() pagerPreviousIcon: string; - @Input() pagerNextIcon: string; - @Input('template') - _templateInput: TemplateRef; + _templateInput?: TemplateRef; @ContentChild(DataTableFooterTemplateDirective, { read: TemplateRef }) - _templateQuery: TemplateRef; + _templateQuery?: TemplateRef; - get template(): TemplateRef { + get template(): TemplateRef | undefined { return this._templateInput || this._templateQuery; } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.scss new file mode 100644 index 000000000..19bbc9761 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.scss @@ -0,0 +1,36 @@ +:host-context(datatable-footer .selected-count) { + .datatable-pager { + flex: 1 1 60%; + } +} + +:host { + flex: 1 1 80%; + text-align: right; +} + +.pager, +.pager li { + padding: 0; + margin: 0; + display: inline-block; + list-style: none; +} + +.pager { + li, + li a { + outline: none; + } + + li { + a { + cursor: pointer; + display: inline-block; + } + + &.disabled a { + cursor: not-allowed; + } + } +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.spec.ts index 395682a9e..48f5c8b31 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.spec.ts @@ -1,21 +1,15 @@ -import { TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DataTablePagerComponent } from './pager.component'; +import { By } from '@angular/platform-browser'; +import { ChangeDetectorRef, DebugElement } from '@angular/core'; +import type { DatatableComponent } from '../datatable.component'; describe('DataTablePagerComponent', () => { - let fixture; + let fixture: ComponentFixture; let pager: DataTablePagerComponent; - // provide our implementations or mocks to the dependency injector - beforeEach(() => - TestBed.configureTestingModule({ - imports: [DataTablePagerComponent] - }) - ); - beforeEach(waitForAsync(() => { - TestBed.compileComponents(); - fixture = TestBed.createComponent(DataTablePagerComponent); pager = fixture.componentInstance; })); @@ -251,4 +245,86 @@ describe('DataTablePagerComponent', () => { expect(pages.length).toEqual(0); }); }); + + describe('localisation', () => { + let firstButton: DebugElement; + let previousButton: DebugElement; + let nextButton: DebugElement; + let lastButton: DebugElement; + let pageButtons: { button: DebugElement; page: number }[]; + beforeEach(() => { + pager.size = 10; + pager.count = 100; + fixture.detectChanges(); + [firstButton, previousButton, nextButton, lastButton] = fixture.debugElement + .queryAll(By.css('a[role=button]')) + .filter(it => !it.parent!.classes['pages']); + pageButtons = fixture.debugElement + .queryAll(By.css('li.pages')) + .map((button, index) => ({ button, page: index + 1 })); + }); + + function ariaLabel(element: DebugElement): string | null { + return element?.attributes['aria-label'] ?? null; + } + + describe('has default values without messages from table', () => { + it('first button', () => { + expect(ariaLabel(firstButton)).toEqual('go to first page'); + }); + + it('previous button', () => { + expect(ariaLabel(previousButton)).toEqual('go to previous page'); + }); + + it('next button', () => { + expect(ariaLabel(nextButton)).toEqual('go to next page'); + }); + + it('last button', () => { + expect(ariaLabel(lastButton)).toEqual('go to last page'); + }); + + it('page buttons', () => { + for (const { button, page } of pageButtons) { + expect(ariaLabel(button)).withContext(`${page} button`).toEqual(`page ${page}`); + } + }); + }); + + describe('takes messages-overrides from table', () => { + function setMessages(messages: DatatableComponent['messages']) { + (pager as any).dataTable = { messages }; + // do a change detection on the real changeDetectionRef + fixture.componentRef.injector.get(ChangeDetectorRef).detectChanges(); + } + + it('first button', () => { + setMessages({ ariaFirstPageMessage: 'link: first page' }); + expect(ariaLabel(firstButton)).toEqual('link: first page'); + }); + + it('previous button', () => { + setMessages({ ariaPreviousPageMessage: 'link: previous page' }); + expect(ariaLabel(previousButton)).toEqual('link: previous page'); + }); + + it('next button', () => { + setMessages({ ariaNextPageMessage: 'link: next page' }); + expect(ariaLabel(nextButton)).toEqual('link: next page'); + }); + + it('last button', () => { + setMessages({ ariaLastPageMessage: 'link: last page' }); + expect(ariaLabel(lastButton)).toEqual('link: last page'); + }); + + it('page buttons', () => { + setMessages({ ariaPageNMessage: 'link: page' }); + for (const { button, page } of pageButtons) { + expect(ariaLabel(button)).withContext(`${page} button`).toEqual(`link: page ${page}`); + } + }); + }); + }); }); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts index 5c83b200e..1004f13a3 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/footer/pager.component.ts @@ -1,41 +1,64 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + inject, + Input, + Output +} from '@angular/core'; import { PagerPageEvent } from '../../types/public.types'; import { Page } from '../../types/internal.types'; +import { DatatableComponent } from '../datatable.component'; @Component({ selector: 'datatable-pager', template: ` @@ -43,14 +66,20 @@ import { Page } from '../../types/internal.types'; host: { class: 'datatable-pager' }, - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true + styleUrl: './pager.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush }) export class DataTablePagerComponent { - @Input() pagerLeftArrowIcon: string; - @Input() pagerRightArrowIcon: string; - @Input() pagerPreviousIcon: string; - @Input() pagerNextIcon: string; + private dataTable = inject(DatatableComponent, { optional: true }); + + get messages(): DatatableComponent['messages'] { + return this.dataTable?.messages ?? {}; + } + + @Input() pagerLeftArrowIcon?: string; + @Input() pagerRightArrowIcon?: string; + @Input() pagerPreviousIcon?: string; + @Input() pagerNextIcon?: string; @Input() set size(val: number) { @@ -92,7 +121,7 @@ export class DataTablePagerComponent { _count = 0; _page = 1; _size = 0; - pages: Page[]; + pages!: Page[]; canPrevious(): boolean { return this.page > 1; diff --git a/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.scss new file mode 100644 index 000000000..0b7a0909c --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.scss @@ -0,0 +1,90 @@ +@use '../shared'; + +:host { + @include shared.cell-styles(); + + position: relative; + display: inline-block; + + :host-context(ngx-datatable.fixed-header) & { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &.sortable { + .datatable-header-cell-wrapper { + cursor: pointer; + } + } + + &.longpress .datatable-header-cell-wrapper { + cursor: move; + } +} + +.datatable-header-cell-template-wrap { + height: inherit; +} + +.sort-btn { + line-height: 100%; + vertical-align: middle; + display: inline-block; + cursor: pointer; +} + +.resize-handle, +.resize-handle--not-resizable { + display: inline-block; + position: absolute; + right: 0; + top: 0; + bottom: 0; + width: 5px; + padding: 0 4px; + visibility: hidden; +} + +.resize-handle { + cursor: ew-resize; + + :host(.resizeable:hover) & { + visibility: visible; + } +} +:host { + @media (hover: none) { + touch-action: none; + + .resize-handle { + visibility: visible; + } + + .datatable-header-cell-label.draggable { + user-select: none; + } + } +} + +.resize-handle--not-resizable { + :host(:hover) { + visibility: visible; + } +} + +:host::ng-deep { + .targetMarker { + position: absolute; + top: 0; + bottom: 0; + + &.dragFromLeft { + right: 0; + } + + &.dragFromRight { + left: 0; + } + } +} diff --git a/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.spec.ts index 7aa4fd10b..b73538916 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.spec.ts @@ -1,27 +1,46 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DataTableHeaderCellComponent } from './header-cell.component'; +import { TableColumnInternal } from '../../types/internal.types'; describe('DataTableHeaderCellComponent', () => { let fixture: ComponentFixture; let component: DataTableHeaderCellComponent; - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DataTableHeaderCellComponent] - }); - }); - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(DataTableHeaderCellComponent); - component = fixture.componentInstance; - }); + fixture = TestBed.createComponent(DataTableHeaderCellComponent); + component = fixture.componentInstance; })); - describe('fixture', () => { - it('should have a component instance', () => { - expect(component).toBeTruthy(); + it('should emit new width on resize', () => { + fixture.componentRef.setInput('column', { + name: 'test', + resizeable: true + }); + spyOn(component.resizing, 'emit'); + fixture.detectChanges(); + const initialWidth = fixture.nativeElement.clientWidth; + const event = new MouseEvent('mousedown'); + fixture.nativeElement.querySelector('.resize-handle').dispatchEvent(event); + const mouseMoveEvent = new MouseEvent('mousemove', { screenX: 100 }); + document.dispatchEvent(mouseMoveEvent); + const mouseUpEvent = new MouseEvent('mouseup'); + document.dispatchEvent(mouseUpEvent); + const newWidth = 100 + initialWidth; + expect(component.resizing.emit).toHaveBeenCalledWith({ + width: newWidth, + column: { name: 'test', resizeable: true } as TableColumnInternal + }); + }); + + it('should emit sort event', () => { + fixture.componentRef.setInput('column', { + prop: 'test', + sortable: true }); + spyOn(component.sort, 'emit'); + fixture.detectChanges(); + const event = new MouseEvent('click'); + fixture.nativeElement.querySelector('.datatable-header-cell-label').dispatchEvent(event); + expect(component.sort.emit).toHaveBeenCalled(); }); }); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts index 74e1bc4f2..ddc69c06a 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/header/header-cell.component.ts @@ -2,26 +2,29 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + ElementRef, EventEmitter, HostBinding, HostListener, inject, Input, + OnDestroy, OnInit, Output, TemplateRef } from '@angular/core'; -import { TableColumn } from '../../types/table-column.type'; import { nextSortDir } from '../../utils/sort'; import { HeaderCellContext, - InnerSortEvent, SelectionType, SortDirection, SortPropDir, SortType } from '../../types/public.types'; import { NgTemplateOutlet } from '@angular/common'; +import { InnerSortEvent, TableColumnInternal } from '../../types/internal.types'; +import { fromEvent, Subscription, takeUntil } from 'rxjs'; +import { getPositionFromEvent } from '../../utils/events'; @Component({ selector: 'datatable-header-cell', @@ -29,7 +32,7 @@ import { NgTemplateOutlet } from '@angular/common';
@if (isTarget) { @@ -52,27 +55,36 @@ import { NgTemplateOutlet } from '@angular/common'; }
+ @if (column.resizeable) { + + } `, host: { - class: 'datatable-header-cell' + 'class': 'datatable-header-cell', + '[attr.resizeable]': 'column.resizeable' }, + styleUrl: './header-cell.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet] }) -export class DataTableHeaderCellComponent implements OnInit { +export class DataTableHeaderCellComponent implements OnInit, OnDestroy { private cd = inject(ChangeDetectorRef); - @Input() sortType: SortType; - @Input() sortAscendingIcon: string; - @Input() sortDescendingIcon: string; - @Input() sortUnsetIcon: string; + @Input() sortType!: SortType; + @Input() sortAscendingIcon?: string; + @Input() sortDescendingIcon?: string; + @Input() sortUnsetIcon?: string; - @Input() isTarget: boolean; - @Input() targetMarkerTemplate: TemplateRef; + @Input() isTarget?: boolean; + @Input() targetMarkerTemplate?: TemplateRef; @Input() targetMarkerContext: any; @Input() enableClearingSortState = false; - _allRowsSelected: boolean; + _allRowsSelected?: boolean; @Input() set allRowsSelected(value) { this._allRowsSelected = value; @@ -82,21 +94,21 @@ export class DataTableHeaderCellComponent implements OnInit { return this._allRowsSelected; } - @Input() selectionType: SelectionType; + @Input() selectionType?: SelectionType; - @Input() set column(column: TableColumn) { + @Input() set column(column: TableColumnInternal) { this._column = column; this.cellContext.column = column; this.cd.markForCheck(); } - get column(): TableColumn { + get column(): TableColumnInternal { return this._column; } @HostBinding('style.height.px') @Input() - headerHeight: number; + headerHeight!: number; @Input() set sorts(val: SortPropDir[]) { this._sorts = val; @@ -110,9 +122,14 @@ export class DataTableHeaderCellComponent implements OnInit { return this._sorts; } - @Output() sort: EventEmitter = new EventEmitter(); - @Output() select: EventEmitter = new EventEmitter(); - @Output() columnContextmenu = new EventEmitter<{ event: MouseEvent; column: TableColumn }>(false); + @Output() sort = new EventEmitter(); + @Output() select = new EventEmitter(); + @Output() columnContextmenu = new EventEmitter<{ + event: MouseEvent; + column: TableColumnInternal; + }>(false); + @Output() resize = new EventEmitter<{ width: number; column: TableColumnInternal }>(); + @Output() resizing = new EventEmitter<{ width: number; column: TableColumnInternal }>(); @HostBinding('class') get columnCssClasses(): string { @@ -154,18 +171,18 @@ export class DataTableHeaderCellComponent implements OnInit { } @HostBinding('attr.title') - get name(): string { + get name(): string | undefined { // guaranteed to have a value by setColumnDefaults() in column-helper.ts return this.column.headerTemplate === undefined ? this.column.name : undefined; } @HostBinding('style.minWidth.px') - get minWidth(): number { + get minWidth(): number | undefined { return this.column.minWidth; } @HostBinding('style.maxWidth.px') - get maxWidth(): number { + get maxWidth(): number | undefined { return this.column.maxWidth; } @@ -178,17 +195,19 @@ export class DataTableHeaderCellComponent implements OnInit { return this.column.sortable ? 0 : -1; } - get isCheckboxable(): boolean { + get isCheckboxable(): boolean | undefined { return this.column.headerCheckboxable; } - sortClass: string; - sortDir: SortDirection; + sortClass?: string; + sortDir?: SortDirection; cellContext: HeaderCellContext; - private _column: TableColumn; - private _sorts: SortPropDir[]; + private _column!: TableColumnInternal; + private _sorts!: SortPropDir[]; + private element = inject(ElementRef).nativeElement; + private subscription?: Subscription; constructor() { this.cellContext = { @@ -203,6 +222,9 @@ export class DataTableHeaderCellComponent implements OnInit { @HostListener('contextmenu', ['$event']) onContextmenu($event: MouseEvent): void { this.columnContextmenu.emit({ event: $event, column: this.column }); + if (this.column.draggable) { + $event.preventDefault(); + } } @HostListener('keydown.enter') @@ -218,6 +240,10 @@ export class DataTableHeaderCellComponent implements OnInit { } } + ngOnDestroy() { + this.destroySubscription(); + } + calcSortDir(sorts: SortPropDir[]): any { if (sorts && this.column) { const sort = sorts.find((s: any) => { @@ -250,16 +276,59 @@ export class DataTableHeaderCellComponent implements OnInit { }); } - calcSortClass(sortDir: SortDirection): string { + calcSortClass(sortDir: SortDirection | undefined): string | undefined { if (!this.cellContext.column.sortable) { - return; + return undefined; } if (sortDir === SortDirection.asc) { - return `sort-btn sort-asc ${this.sortAscendingIcon}`; + return `sort-btn sort-asc ${this.sortAscendingIcon ?? 'datatable-icon-up'}`; } else if (sortDir === SortDirection.desc) { - return `sort-btn sort-desc ${this.sortDescendingIcon}`; + return `sort-btn sort-desc ${this.sortDescendingIcon ?? 'datatable-icon-down'}`; } else { - return `sort-btn ${this.sortUnsetIcon}`; + return `sort-btn ${this.sortUnsetIcon ?? 'datatable-icon-sort-unset'}`; + } + } + + protected onMousedown(event: MouseEvent | TouchEvent): void { + const isMouse = event instanceof MouseEvent; + const initialWidth = this.element.clientWidth; + const { screenX } = getPositionFromEvent(event); + event.stopPropagation(); + + const mouseup = fromEvent(document, isMouse ? 'mouseup' : 'touchend'); + this.subscription = mouseup.subscribe(() => this.onMouseup()); + + const mouseMoveSub = fromEvent( + document, + isMouse ? 'mousemove' : 'touchmove' + ) + .pipe(takeUntil(mouseup)) + .subscribe((e: MouseEvent | TouchEvent) => this.move(e, initialWidth, screenX)); + + this.subscription.add(mouseMoveSub); + } + + private onMouseup(): void { + if (this.subscription && !this.subscription.closed) { + this.destroySubscription(); + this.resize.emit({ width: this.element.clientWidth, column: this.column }); + } + } + + private move( + event: MouseEvent | TouchEvent, + initialWidth: number, + mouseDownScreenX: number + ): void { + const movementX = getPositionFromEvent(event).screenX - mouseDownScreenX; + const newWidth = initialWidth + movementX; + this.resizing.emit({ width: newWidth, column: this.column }); + } + + private destroySubscription(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + this.subscription = undefined; } } } diff --git a/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.scss b/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.scss new file mode 100644 index 000000000..9e3c036f4 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.scss @@ -0,0 +1,20 @@ +@use '../shared'; + +:host { + display: block; + overflow: hidden; +} + +.datatable-header-inner { + display: flex; + + :host-context(ngx-datatable.fixed-header) & { + white-space: nowrap; + } +} + +.datatable-row-group { + display: flex; +} + +@include shared.pinned-columns(); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts b/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts index b5a0023ff..ab806803f 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/header/header.component.ts @@ -14,26 +14,26 @@ import { } from '@angular/core'; import { columnGroupWidths, columnsByPin, columnsByPinArr } from '../../utils/column'; import { - ColumnResizeEvent, - InnerSortEvent, - ReorderEvent, + Row, SelectionType, SortDirection, SortEvent, SortPropDir, SortType } from '../../types/public.types'; -import { NgStyle } from '@angular/common'; +import { NgClass, NgStyle } from '@angular/common'; import { ScrollbarHelper } from '../../services/scrollbar-helper.service'; -import { TableColumn } from '../../types/table-column.type'; import { - OrderableReorderEvent, + ColumnResizeEventInternal, + InnerSortEvent, PinnedColumns, + ReorderEventInternal, + SortableTableColumnInternal, + TableColumnInternal, TargetChangedEvent } from '../../types/internal.types'; import { DraggableDirective } from '../../directives/draggable.directive'; import { LongPressDirective } from '../../directives/long-press.directive'; -import { ResizeableDirective } from '../../directives/resizeable.directive'; import { DataTableHeaderCellComponent } from './header-cell.component'; import { OrderableDirective } from '../../directives/orderable.directive'; @@ -48,15 +48,17 @@ import { OrderableDirective } from '../../directives/orderable.directive'; [style.width.px]="_columnGroupWidths.total" class="datatable-header-inner" > - @for (colGroup of _columnsByPin; track colGroup.type) { -
+ @for (colGroup of _columnsByPin; track colGroup.type) { @if (colGroup.columns.length) { +
@for (column of colGroup.columns; track column.$$id) { }
- } + } }
`, host: { class: 'datatable-header' }, + styleUrl: './header.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ OrderableDirective, NgStyle, DataTableHeaderCellComponent, - ResizeableDirective, LongPressDirective, - DraggableDirective + DraggableDirective, + NgClass ] }) export class DataTableHeaderComponent implements OnDestroy, OnChanges { private cd = inject(ChangeDetectorRef); private scrollbarHelper = inject(ScrollbarHelper); - @Input() sortAscendingIcon: string; - @Input() sortDescendingIcon: string; - @Input() sortUnsetIcon: string; - @Input() scrollbarH: boolean; - @Input() dealsWithGroup: boolean; - @Input() targetMarkerTemplate: TemplateRef; + @Input() sortAscendingIcon?: string; + @Input() sortDescendingIcon?: string; + @Input() sortUnsetIcon?: string; + @Input() scrollbarH?: boolean; + @Input() dealsWithGroup?: boolean; + @Input() targetMarkerTemplate?: TemplateRef; @Input() enableClearingSortState = false; @Input() set innerWidth(val: number) { @@ -130,14 +133,14 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { return this._innerWidth; } - @Input() sorts: SortPropDir[]; - @Input() sortType: SortType; - @Input() allRowsSelected: boolean; - @Input() selectionType: SelectionType; - @Input() reorderable: boolean; + @Input() sorts!: SortPropDir[]; + @Input() sortType!: SortType; + @Input() allRowsSelected?: boolean; + @Input() selectionType?: SelectionType; + @Input() reorderable?: boolean; @Input() verticalScrollVisible = false; - dragEventTarget?: MouseEvent; + dragEventTarget?: MouseEvent | TouchEvent; @HostBinding('style.height') @Input() @@ -153,7 +156,7 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { return this._headerHeight; } - @Input() set columns(val: TableColumn[]) { + @Input() set columns(val: TableColumnInternal[]) { this._columns = val; const colsByPin = columnsByPin(val); @@ -177,21 +180,24 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { return this._offsetX; } - @Output() sort: EventEmitter = new EventEmitter(); - @Output() reorder: EventEmitter = new EventEmitter(); - @Output() resize: EventEmitter = new EventEmitter(); - @Output() resizing: EventEmitter = new EventEmitter(); - @Output() select: EventEmitter = new EventEmitter(); - @Output() columnContextmenu = new EventEmitter<{ event: MouseEvent; column: TableColumn }>(false); - - _columnsByPin: PinnedColumns[]; + @Output() sort = new EventEmitter(); + @Output() reorder = new EventEmitter(); + @Output() resize = new EventEmitter(); + @Output() resizing = new EventEmitter(); + @Output() select = new EventEmitter(); + @Output() columnContextmenu = new EventEmitter<{ + event: MouseEvent; + column: TableColumnInternal; + }>(false); + + _columnsByPin!: PinnedColumns[]; _columnGroupWidths: any = { total: 100 }; - _innerWidth: number; - _offsetX: number; - _columns: TableColumn[]; - _headerHeight: string; + _innerWidth!: number; + _offsetX!: number; + _columns!: TableColumnInternal[]; + _headerHeight!: string; _styleByGroup: { left: NgStyle['ngStyle']; center: NgStyle['ngStyle']; @@ -213,12 +219,18 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { this.destroyed = true; } - onLongPressStart({ event, model }: { event: MouseEvent; model: TableColumn }) { + onLongPressStart({ + event, + model + }: { + event: MouseEvent | TouchEvent; + model: TableColumnInternal; + }) { model.dragging = true; this.dragEventTarget = event; } - onLongPressEnd({ model }: { model: TableColumn }) { + onLongPressEnd({ model }: { model: TableColumnInternal }) { this.dragEventTarget = undefined; // delay resetting so sort can be @@ -245,18 +257,21 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { return '100%'; } - onColumnResized(width: number, column: TableColumn): void { + onColumnResized({ width, column }: { width: number; column: TableColumnInternal }): void { this.resize.emit(this.makeResizeEvent(width, column)); } - onColumnResizing(width: number, column: TableColumn): void { + onColumnResizing({ width, column }: { width: number; column: TableColumnInternal }): void { this.resizing.emit(this.makeResizeEvent(width, column)); } - private makeResizeEvent(width: number, column: TableColumn): ColumnResizeEvent { - if (width <= column.minWidth) { + private makeResizeEvent( + width: number, + column: TableColumnInternal + ): ColumnResizeEventInternal { + if (column.minWidth && width <= column.minWidth) { width = column.minWidth; - } else if (width >= column.maxWidth) { + } else if (column.maxWidth && width >= column.maxWidth) { width = column.maxWidth; } return { @@ -266,15 +281,11 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { }; } - onColumnReordered({ prevIndex, newIndex, model }: OrderableReorderEvent): void { - const column = this.getColumn(newIndex); + onColumnReordered(event: ReorderEventInternal): void { + const column = this.getColumn(event.newValue); column.isTarget = false; column.targetMarkerContext = undefined; - this.reorder.emit({ - column: model, - prevValue: prevIndex, - newValue: newIndex - }); + this.reorder.emit(event); } onTargetChanged({ prevIndex, newIndex, initialIndex }: TargetChangedEvent): void { @@ -325,9 +336,9 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { } calcNewSorts( - column: TableColumn, - prevValue: SortDirection, - newValue: SortDirection + column: SortableTableColumnInternal, + prevValue: SortDirection | undefined, + newValue: SortDirection | undefined ): SortPropDir[] { let idx = 0; diff --git a/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts index f8d3d1ade..2ebb934ce 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail-template.directive.ts @@ -2,8 +2,7 @@ import { Directive } from '@angular/core'; import { RowDetailContext } from '../../types/public.types'; @Directive({ - selector: '[ngx-datatable-row-detail-template]', - standalone: true + selector: '[ngx-datatable-row-detail-template]' }) export class DatatableRowDetailTemplateDirective { static ngTemplateContextGuard( diff --git a/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.spec.ts deleted file mode 100644 index fabbf8e16..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { Component } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { DatatableRowDetailDirective } from './row-detail.directive'; -import { DatatableRowDetailTemplateDirective } from './row-detail-template.directive'; - -@Component({ - selector: 'test-fixture-component', - template: ` - - - - - `, - imports: [DatatableRowDetailDirective, DatatableRowDetailTemplateDirective] -}) -class TestFixtureComponent {} - -describe('DatatableRowDetailDirective', () => { - let fixture: ComponentFixture; - let component: TestFixtureComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - DatatableRowDetailDirective, - DatatableRowDetailTemplateDirective, - TestFixtureComponent - ] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - let directive: DatatableRowDetailDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.directive(DatatableRowDetailDirective)) - .injector.get(DatatableRowDetailDirective); - }); - - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - - it('should have at least one DatatableRowDetailDirective directive', () => { - expect(directive).toBeTruthy(); - }); - }); - - describe('directive #1', () => { - let directive: DatatableRowDetailDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.css('#t1')) - .injector.get(DatatableRowDetailDirective); - }); - - it('should be found', () => { - expect(directive).toBeTruthy(); - }); - - it('should not have a template', () => { - fixture.detectChanges(); - expect(directive.template).toBeUndefined(); - }); - }); - - describe('directive #2', () => { - let directive: DatatableRowDetailDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.css('#t2')) - .injector.get(DatatableRowDetailDirective); - }); - - it('should be found', () => { - expect(directive).toBeTruthy(); - }); - - it('should have a template', () => { - fixture.detectChanges(); - expect(directive.template).toBeDefined(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts b/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts index bc02facbb..415ab5134 100644 --- a/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/components/row-detail/row-detail.directive.ts @@ -1,12 +1,11 @@ import { ContentChild, Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; import { DatatableRowDetailTemplateDirective } from './row-detail-template.directive'; -import { RowDetailContext } from '../../types/public.types'; +import { DetailToggleEvents, Row, RowDetailContext } from '../../types/public.types'; @Directive({ - selector: 'ngx-datatable-row-detail', - standalone: true + selector: 'ngx-datatable-row-detail' }) -export class DatatableRowDetailDirective { +export class DatatableRowDetailDirective { /** * The detail row height is required especially * when virtual scroll is enabled. @@ -14,19 +13,19 @@ export class DatatableRowDetailDirective { @Input() rowHeight: number | ((row?: TRow, index?: number) => number) = 0; @Input('template') - _templateInput: TemplateRef>; + _templateInput?: TemplateRef>; @ContentChild(DatatableRowDetailTemplateDirective, { read: TemplateRef, static: true }) - _templateQuery: TemplateRef>; + _templateQuery?: TemplateRef>; - get template(): TemplateRef> { + get template(): TemplateRef> | undefined { return this._templateInput || this._templateQuery; } /** * Row detail row visbility was toggled. */ - @Output() toggle: EventEmitter = new EventEmitter(); + @Output() toggle = new EventEmitter>(); /** * Toggle the expansion of the row diff --git a/projects/swimlane/ngx-datatable/src/lib/components/shared.scss b/projects/swimlane/ngx-datatable/src/lib/components/shared.scss new file mode 100644 index 000000000..649a86510 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/components/shared.scss @@ -0,0 +1,26 @@ +@mixin pinned-columns() { + .datatable-row-left, + .datatable-row-right { + position: sticky; + z-index: 9; + } + + .datatable-row-left { + left: 0; + } + + .datatable-row-right { + right: 0; + } +} + +@mixin cell-styles() { + overflow-x: hidden; + vertical-align: top; + display: inline-block; + line-height: 1.625; + + &:focus { + outline: none; + } +} diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/disable-row.directive.ts b/projects/swimlane/ngx-datatable/src/lib/directives/disable-row.directive.ts index 6c0f0f831..8e69ba2e2 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/disable-row.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/disable-row.directive.ts @@ -1,4 +1,4 @@ -import { booleanAttribute, Directive, ElementRef, inject, Input } from '@angular/core'; +import { booleanAttribute, Directive, effect, ElementRef, inject, input } from '@angular/core'; /** * Row Disable Directive @@ -12,30 +12,30 @@ import { booleanAttribute, Directive, ElementRef, inject, Input } from '@angular *
*/ @Directive({ - selector: '[disable-row]', - standalone: true + selector: '[disable-row]' }) export class DisableRowDirective { - private element = inject(ElementRef); - private _disabled = false; - @Input({ transform: booleanAttribute }) set disabled(val: boolean) { - this._disabled = val; - if (val) { - this.disableAllElements(); - } - } + private readonly elementRef = inject>(ElementRef); - get disabled() { - return this._disabled; + readonly disabled = input(false, { + transform: booleanAttribute + }); + + constructor() { + effect(() => { + if (this.disabled()) { + this.disableAllElements(); + } + }); } - disableAllElements() { - const el = this.element?.nativeElement; - if (!el) { + private disableAllElements() { + const hostElement = this.elementRef?.nativeElement; + if (!hostElement) { return; } - Array.from(el.querySelectorAll('*') as HTMLAllCollection).forEach(child => { - child?.setAttribute('disabled', ''); + Array.from(hostElement.querySelectorAll('*')).forEach(child => { + child.setAttribute('disabled', ''); }); } } diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.spec.ts index 63bf54f0b..649db97e0 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.spec.ts @@ -16,19 +16,10 @@ describe('DraggableDirective', () => { let component: TestFixtureComponent; let element: any; - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [DraggableDirective, TestFixtureComponent] - }); - }); - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - element = fixture.nativeElement; - }); + fixture = TestBed.createComponent(TestFixtureComponent); + component = fixture.componentInstance; + element = fixture.nativeElement; })); describe('fixture', () => { @@ -53,10 +44,8 @@ describe('DraggableDirective', () => { beforeEach(() => { element.classList.add('draggable'); - mouseDown = { - target: element, - preventDefault: () => {} - }; + mouseDown = new MouseEvent('mousedown'); + Object.defineProperty(mouseDown, 'target', { value: element }); }); // or else the document:mouseup event can fire again when resizing. diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts b/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts index 97f8dc263..9f54640d6 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/draggable.directive.ts @@ -12,8 +12,8 @@ import { } from '@angular/core'; import { fromEvent, Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; -import { TableColumn } from '../types/table-column.type'; -import { DraggableDragEvent } from '../types/internal.types'; +import { DraggableDragEvent, TableColumnInternal } from '../types/internal.types'; +import { getPositionFromEvent } from '../utils/events'; /** * Draggable Directive for Angular2 @@ -24,12 +24,11 @@ import { DraggableDragEvent } from '../types/internal.types'; * */ @Directive({ - selector: '[draggable]', - standalone: true + selector: '[draggable]' }) export class DraggableDirective implements OnDestroy, OnChanges { @Input() dragEventTarget: any; - @Input() dragModel: TableColumn; + @Input() dragModel!: TableColumnInternal; @Input({ transform: booleanAttribute }) dragX = true; @Input({ transform: booleanAttribute }) dragY = true; @@ -39,7 +38,7 @@ export class DraggableDirective implements OnDestroy, OnChanges { element = inject(ElementRef).nativeElement; isDragging = false; - subscription: Subscription; + subscription?: Subscription; ngOnChanges(changes: SimpleChanges): void { if ( @@ -55,7 +54,7 @@ export class DraggableDirective implements OnDestroy, OnChanges { this._destroySubscription(); } - onMouseup(event: MouseEvent): void { + onMouseup(event: MouseEvent | TouchEvent): void { if (!this.isDragging) { return; } @@ -73,7 +72,8 @@ export class DraggableDirective implements OnDestroy, OnChanges { } } - onMousedown(event: MouseEvent): void { + onMousedown(event: MouseEvent | TouchEvent): void { + const isMouse = event instanceof MouseEvent; // we only want to drag the inner header text const isDragElm = (event.target).classList.contains('draggable'); @@ -81,14 +81,20 @@ export class DraggableDirective implements OnDestroy, OnChanges { event.preventDefault(); this.isDragging = true; - const mouseDownPos = { x: event.clientX, y: event.clientY }; + const mouseDownPos = getPositionFromEvent(event); - const mouseup = fromEvent(document, 'mouseup'); - this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup(ev)); + const mouseup = fromEvent( + document, + isMouse ? 'mouseup' : 'touchend' + ); + this.subscription = mouseup.subscribe(ev => this.onMouseup(ev)); - const mouseMoveSub = fromEvent(document, 'mousemove') + const mouseMoveSub = fromEvent( + document, + isMouse ? 'mousemove' : 'touchmove' + ) .pipe(takeUntil(mouseup)) - .subscribe((ev: MouseEvent) => this.move(ev, mouseDownPos)); + .subscribe(ev => this.move(ev, mouseDownPos)); this.subscription.add(mouseMoveSub); @@ -100,13 +106,14 @@ export class DraggableDirective implements OnDestroy, OnChanges { } } - move(event: MouseEvent, mouseDownPos: { x: number; y: number }): void { + move(event: MouseEvent | TouchEvent, mouseDownPos: { clientX: number; clientY: number }): void { if (!this.isDragging) { return; } - const x = event.clientX - mouseDownPos.x; - const y = event.clientY - mouseDownPos.y; + const { clientX, clientY } = getPositionFromEvent(event); + const x = clientX - mouseDownPos.clientX; + const y = clientY - mouseDownPos.clientY; if (this.dragX) { this.element.style.left = `${x}px`; diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.spec.ts deleted file mode 100644 index 24781234f..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { Component } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { LongPressDirective } from './long-press.directive'; - -@Component({ - selector: 'test-fixture-component', - template: `
`, - imports: [LongPressDirective] -}) -class TestFixtureComponent {} - -describe('LongPressDirective', () => { - let fixture: ComponentFixture; - let component: TestFixtureComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [LongPressDirective, TestFixtureComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - let directive: LongPressDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.directive(LongPressDirective)) - .injector.get(LongPressDirective); - }); - - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - - it('should have LongPressDirective directive', () => { - expect(directive).toBeTruthy(); - }); - - it('should have isLongPress set to false', () => { - expect(directive.isLongPress).toBeFalsy(); - }); - - /* - describe('When the mouse is clicked for 500 ms', () => { - - it('isLongPress should returns true', fakeAsync(() => { - - directive.onMouseDown(new MouseEvent('mousedown')); - expect(directive.isLongPress).toBe(false); - - tick(500); - expect(directive.isLongPress).toBe(true); - - directive.isLongPressing = false; - tick(50); //clear last timer - })); - }); - */ - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts b/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts index 0b1674b98..1b1908056 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/long-press.directive.ts @@ -2,54 +2,46 @@ import { booleanAttribute, Directive, EventEmitter, - HostBinding, - HostListener, Input, numberAttribute, OnDestroy, - Output + Output, + signal } from '@angular/core'; import { fromEvent, Subscription } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { TableColumn } from '../types/table-column.type'; +import { TableColumnInternal } from '../types/internal.types'; @Directive({ selector: '[long-press]', - standalone: true + host: { + '(touchstart)': 'onMouseDown($event)', + '(mousedown)': 'onMouseDown($event)', + '[class.press]': 'pressing()', + '[class.longpress]': 'isLongPressing()' + } }) export class LongPressDirective implements OnDestroy { @Input({ transform: booleanAttribute }) pressEnabled = true; - @Input() pressModel: TableColumn; + @Input() pressModel!: TableColumnInternal; @Input({ transform: numberAttribute }) duration = 500; - @Output() longPressStart: EventEmitter<{ event: MouseEvent; model: TableColumn }> = - new EventEmitter(); - @Output() longPressing: EventEmitter<{ event: MouseEvent; model: TableColumn }> = - new EventEmitter(); - @Output() longPressEnd: EventEmitter<{ model: TableColumn }> = new EventEmitter(); + @Output() longPressStart = new EventEmitter<{ + event: MouseEvent | TouchEvent; + model: TableColumnInternal; + }>(); + @Output() longPressEnd = new EventEmitter<{ model: TableColumnInternal }>(); - pressing: boolean; - isLongPressing: boolean; + pressing = signal(false); + isLongPressing = signal(false); timeout: any; - mouseX: number = 0; - mouseY: number = 0; - subscription: Subscription; + subscription?: Subscription; - @HostBinding('class.press') - get press(): boolean { - return this.pressing; - } + onMouseDown(event: MouseEvent | TouchEvent): void { + const isMouse = event instanceof MouseEvent; - @HostBinding('class.longpress') - get isLongPress(): boolean { - return this.isLongPressing; - } - - @HostListener('mousedown', ['$event']) - onMouseDown(event: MouseEvent): void { // don't do right/middle clicks - if (event.which !== 1 || !this.pressEnabled) { + if (!this.pressEnabled || (isMouse && event.button !== 0)) { return; } @@ -59,61 +51,25 @@ export class LongPressDirective implements OnDestroy { return; } - this.mouseX = event.clientX; - this.mouseY = event.clientY; - - this.pressing = true; - this.isLongPressing = false; + this.pressing.set(true); + this.isLongPressing.set(false); - const mouseup = fromEvent(document, 'mouseup'); - this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup()); + const mouseup = fromEvent(document, isMouse ? 'mouseup' : 'touchend'); + this.subscription = mouseup.subscribe(() => this.endPress()); this.timeout = setTimeout(() => { - this.isLongPressing = true; + this.isLongPressing.set(true); this.longPressStart.emit({ event, model: this.pressModel }); - - this.subscription.add( - fromEvent(document, 'mousemove') - .pipe(takeUntil(mouseup)) - .subscribe((mouseEvent: MouseEvent) => this.onMouseMove(mouseEvent)) - ); - - this.loop(event); }, this.duration); - - this.loop(event); - } - - onMouseMove(event: MouseEvent): void { - if (this.pressing && !this.isLongPressing) { - const xThres = Math.abs(event.clientX - this.mouseX) > 10; - const yThres = Math.abs(event.clientY - this.mouseY) > 10; - - if (xThres || yThres) { - this.endPress(); - } - } - } - - loop(event: MouseEvent): void { - if (this.isLongPressing) { - this.timeout = setTimeout(() => { - this.longPressing.emit({ - event, - model: this.pressModel - }); - this.loop(event); - }, 50); - } } endPress(): void { clearTimeout(this.timeout); - this.isLongPressing = false; - this.pressing = false; + this.isLongPressing.set(false); + this.pressing.set(false); this._destroySubscription(); this.longPressEnd.emit({ @@ -121,10 +77,6 @@ export class LongPressDirective implements OnDestroy { }); } - onMouseup(): void { - this.endPress(); - } - ngOnDestroy(): void { clearTimeout(this.timeout); this._destroySubscription(); diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.spec.ts index bb3c1032b..1eb6e58aa 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.spec.ts @@ -4,7 +4,8 @@ import { By } from '@angular/platform-browser'; import { OrderableDirective } from './orderable.directive'; import { DraggableDirective } from './draggable.directive'; -import { id } from '../utils/id'; +import { TableColumnInternal } from '../types/internal.types'; +import { toInternalColumn } from '../utils/column-helper'; @Component({ selector: 'test-fixture-component', @@ -18,7 +19,7 @@ import { id } from '../utils/id'; imports: [OrderableDirective, DraggableDirective] }) class TestFixtureComponent { - draggables = []; + draggables: TableColumnInternal[] = []; @ViewChildren(DraggableDirective) draggableDirectives!: QueryList; } @@ -26,24 +27,15 @@ describe('OrderableDirective', () => { let fixture: ComponentFixture; let component: TestFixtureComponent; - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [OrderableDirective, TestFixtureComponent, DraggableDirective] - }); - }); - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - /* This is required in order to resolve the `ContentChildren`. - * If we don't go through at least on change detection cycle - * the `draggables` will be `undefined` and `ngOnDestroy` will - * fail. - */ - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(TestFixtureComponent); + component = fixture.componentInstance; + /* This is required in order to resolve the `ContentChildren`. + * If we don't go through at least on change detection cycle + * the `draggables` will be `undefined` and `ngOnDestroy` will + * fail. + */ + fixture.detectChanges(); })); describe('fixture', () => { @@ -81,14 +73,12 @@ describe('OrderableDirective', () => { }); } - function newDraggable() { - return { - $$id: id() - }; + function newDraggable(name: string): TableColumnInternal { + return toInternalColumn([{ name }])[0]; } beforeEach(() => { - component.draggables = [newDraggable(), newDraggable(), newDraggable()]; + component.draggables = [newDraggable('d1'), newDraggable('d2'), newDraggable('d3')]; fixture.detectChanges(); checkAllSubscriptionsForActiveObservers(); diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts b/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts index dff57dd58..789545313 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/orderable.directive.ts @@ -2,6 +2,7 @@ import { AfterContentInit, ContentChildren, Directive, + DOCUMENT, EventEmitter, inject, KeyValueChangeRecord, @@ -12,13 +13,14 @@ import { QueryList } from '@angular/core'; import { DraggableDirective } from './draggable.directive'; -import { DOCUMENT } from '@angular/common'; -import { TableColumn } from '../types/table-column.type'; + import { DraggableDragEvent, - OrderableReorderEvent, + ReorderEventInternal, + TableColumnInternal, TargetChangedEvent } from '../types/internal.types'; +import { getPositionFromEvent } from '../utils/events'; interface OrderPosition { left: number; @@ -28,21 +30,20 @@ interface OrderPosition { } @Directive({ - selector: '[orderable]', - standalone: true + selector: '[orderable]' }) export class OrderableDirective implements AfterContentInit, OnDestroy { private document = inject(DOCUMENT); - @Output() reorder: EventEmitter = new EventEmitter(); - @Output() targetChanged: EventEmitter = new EventEmitter(); + @Output() reorder = new EventEmitter(); + @Output() targetChanged = new EventEmitter(); @ContentChildren(DraggableDirective, { descendants: true }) - draggables: QueryList; + draggables!: QueryList; - positions: Record; + positions?: Record; differ: KeyValueDiffer = inject(KeyValueDiffers).find({}).create(); - lastDraggingIndex: number; + lastDraggingIndex?: number; ngAfterContentInit(): void { // HACK: Investigate Better Way @@ -104,13 +105,13 @@ export class OrderableDirective implements AfterContentInit, OnDestroy { } onDragging({ element, model, event }: DraggableDragEvent): void { - const prevPos = this.positions[model.$$id]; + const prevPos = this.positions![model.$$id]; const target = this.isTarget(model, event); if (target) { if (this.lastDraggingIndex !== target.i) { this.targetChanged.emit({ - prevIndex: this.lastDraggingIndex, + prevIndex: this.lastDraggingIndex!, newIndex: target.i, initialIndex: prevPos.index }); @@ -118,7 +119,7 @@ export class OrderableDirective implements AfterContentInit, OnDestroy { } } else if (this.lastDraggingIndex !== prevPos.index) { this.targetChanged.emit({ - prevIndex: this.lastDraggingIndex, + prevIndex: this.lastDraggingIndex!, initialIndex: prevPos.index }); this.lastDraggingIndex = prevPos.index; @@ -126,14 +127,14 @@ export class OrderableDirective implements AfterContentInit, OnDestroy { } onDragEnd({ element, model, event }: DraggableDragEvent): void { - const prevPos = this.positions[model.$$id]; + const prevPos = this.positions![model.$$id]; const target = this.isTarget(model, event); if (target) { this.reorder.emit({ - prevIndex: prevPos.index, - newIndex: target.i, - model + prevValue: prevPos.index, + newValue: target.i, + column: model }); } @@ -141,11 +142,10 @@ export class OrderableDirective implements AfterContentInit, OnDestroy { element.style.left = 'auto'; } - isTarget(model: TableColumn, event: MouseEvent) { + isTarget(model: TableColumnInternal, event: MouseEvent | TouchEvent) { let i = 0; - const x = event.x || event.clientX; - const y = event.y || event.clientY; - const targets = this.document.elementsFromPoint(x, y); + const { clientX, clientY } = getPositionFromEvent(event); + const targets = this.document.elementsFromPoint(clientX, clientY); for (const id in this.positions) { // current column position which throws event. @@ -163,10 +163,10 @@ export class OrderableDirective implements AfterContentInit, OnDestroy { } } - private createMapDiffs(): { [key: string]: DraggableDirective } { + private createMapDiffs(): Record { return this.draggables.toArray().reduce((acc, curr) => { acc[curr.dragModel.$$id] = curr; return acc; - }, {}); + }, {} as Record); } } diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.spec.ts deleted file mode 100644 index a2c19b4e8..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { Component } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { ResizeableDirective } from './resizeable.directive'; - -@Component({ - selector: 'test-fixture-component', - template: `
`, - imports: [ResizeableDirective] -}) -class TestFixtureComponent {} - -describe('ResizeableDirective', () => { - let fixture: ComponentFixture; - let component: TestFixtureComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ResizeableDirective, TestFixtureComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - let directive: ResizeableDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.directive(ResizeableDirective)) - .injector.get(ResizeableDirective); - }); - - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - - it('should have ResizeableDirective directive', () => { - expect(directive).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.ts b/projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.ts deleted file mode 100644 index 8f9f4148a..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/directives/resizeable.directive.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - AfterViewInit, - booleanAttribute, - Directive, - ElementRef, - EventEmitter, - HostBinding, - HostListener, - inject, - Input, - numberAttribute, - OnDestroy, - Output, - Renderer2 -} from '@angular/core'; -import { fromEvent, Subscription } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; - -@Directive({ - selector: '[resizeable]', - standalone: true -}) -export class ResizeableDirective implements OnDestroy, AfterViewInit { - private renderer = inject(Renderer2); - - @HostBinding('class.resizeable') @Input({ transform: booleanAttribute }) resizeEnabled = true; - @Input({ transform: numberAttribute }) minWidth: number; - @Input({ transform: numberAttribute }) maxWidth: number; - - @Output() resize: EventEmitter = new EventEmitter(); - @Output() resizing: EventEmitter = new EventEmitter(); - - element = inject(ElementRef).nativeElement; - subscription: Subscription; - private resizeHandle: HTMLElement; - - ngAfterViewInit(): void { - const renderer2 = this.renderer; - this.resizeHandle = renderer2.createElement('span'); - if (this.resizeEnabled) { - renderer2.addClass(this.resizeHandle, 'resize-handle'); - } else { - renderer2.addClass(this.resizeHandle, 'resize-handle--not-resizable'); - } - renderer2.appendChild(this.element, this.resizeHandle); - } - - ngOnDestroy(): void { - this._destroySubscription(); - if (this.renderer.destroyNode) { - this.renderer.destroyNode(this.resizeHandle); - } else if (this.resizeHandle) { - this.renderer.removeChild(this.renderer.parentNode(this.resizeHandle), this.resizeHandle); - } - } - - onMouseup(): void { - if (this.subscription && !this.subscription.closed) { - this._destroySubscription(); - this.resize.emit(this.element.clientWidth); - } - } - - @HostListener('mousedown', ['$event']) - onMousedown(event: MouseEvent): void { - const isHandle = (event.target as HTMLElement).classList.contains('resize-handle'); - const initialWidth = this.element.clientWidth; - const mouseDownScreenX = event.screenX; - - if (isHandle) { - event.stopPropagation(); - - const mouseup = fromEvent(document, 'mouseup'); - this.subscription = mouseup.subscribe((ev: MouseEvent) => this.onMouseup()); - - const mouseMoveSub = fromEvent(document, 'mousemove') - .pipe(takeUntil(mouseup)) - .subscribe((e: MouseEvent) => this.move(e, initialWidth, mouseDownScreenX)); - - this.subscription.add(mouseMoveSub); - } - } - - move(event: MouseEvent, initialWidth: number, mouseDownScreenX: number): void { - const movementX = event.screenX - mouseDownScreenX; - const newWidth = initialWidth + movementX; - this.resizing.emit(newWidth); - } - - private _destroySubscription() { - if (this.subscription) { - this.subscription.unsubscribe(); - this.subscription = undefined; - } - } -} diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.spec.ts b/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.spec.ts deleted file mode 100644 index 2ea475d9e..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { Component } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { VisibilityDirective } from './visibility.directive'; - -@Component({ - selector: 'test-fixture-component', - styles: [ - ` - div { - width: 1px; - height: 1px; - } - ` - ], - template: `
`, - imports: [VisibilityDirective] -}) -class TestFixtureComponent {} - -describe('VisibilityDirective', () => { - let fixture: ComponentFixture; - let component: TestFixtureComponent; - - // provide our implementations or mocks to the dependency injector - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [VisibilityDirective, TestFixtureComponent] - }); - }); - - beforeEach(waitForAsync(() => { - TestBed.compileComponents().then(() => { - fixture = TestBed.createComponent(TestFixtureComponent); - component = fixture.componentInstance; - }); - })); - - describe('fixture', () => { - let directive: VisibilityDirective; - - beforeEach(() => { - directive = fixture.debugElement - .query(By.directive(VisibilityDirective)) - .injector.get(VisibilityDirective); - }); - - it('should have a component instance', () => { - expect(component).toBeTruthy(); - }); - - it('should have VisibilityDirective directive', () => { - expect(directive).toBeTruthy(); - }); - }); -}); diff --git a/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts b/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts index 7b486f961..e1940f4d5 100644 --- a/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts +++ b/projects/swimlane/ngx-datatable/src/lib/directives/visibility.directive.ts @@ -22,8 +22,7 @@ import { * */ @Directive({ - selector: '[visibilityObserver]', - standalone: true + selector: '[visibilityObserver]' }) export class VisibilityDirective implements OnInit, OnDestroy { private element = inject(ElementRef); diff --git a/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.config.ts b/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.config.ts new file mode 100644 index 000000000..92d3960fb --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.config.ts @@ -0,0 +1,76 @@ +import { InjectionToken, Provider } from '@angular/core'; + +/** Interface for messages to override default table texts. */ +export interface NgxDatatableMessages { + /** Message to show when the array is present but empty */ + emptyMessage: string; + /** Footer total message */ + totalMessage: string; + /** Footer selected message */ + selectedMessage: string; + /** Pager screen reader message for the first page button */ + ariaFirstPageMessage: string; + /** + * Pager screen reader message for the n-th page button. + * It will be rendered as: `{{ariaPageNMessage}} {{n}}`. + */ + ariaPageNMessage: string; + /** Pager screen reader message for the previous page button */ + ariaPreviousPageMessage: string; + /** Pager screen reader message for the next page button */ + ariaNextPageMessage: string; + /** Pager screen reader message for the last page button */ + ariaLastPageMessage: string; +} + +/** CSS classes for icons that override the default table icons. */ +export interface NgxDatatableCssClasses { + sortAscending: string; + sortDescending: string; + sortUnset: string; + pagerLeftArrow: string; + pagerRightArrow: string; + pagerPrevious: string; + pagerNext: string; +} + +/** + * Interface definition for ngx-datatable global configuration + */ +// TODO those properties should all be required in the interface. Should be changed with signal migration. +export interface NgxDatatableConfig { + messages?: NgxDatatableMessages; + cssClasses?: NgxDatatableCssClasses; + headerHeight?: number; + footerHeight?: number; + rowHeight?: number; + defaultColumnWidth?: number; +} + +export const NGX_DATATABLE_CONFIG = new InjectionToken('ngx-datatable.config'); + +/** + * This makes all properties recursively optional. + * + * @internal + */ +export type AllPartial = { [K in keyof T]?: AllPartial }; + +/** + * Interface definition for INgxDatatableConfig global configuration. + * + * @deprecated Use {@link NgxDatatableConfig} instead. + */ +export type INgxDatatableConfig = NgxDatatableConfig; + +/** + * Provides a global configuration for ngx-datatable. + * + * @param overrides The overrides of the table configuration. + */ +export function providedNgxDatatableConfig(overrides: AllPartial): Provider { + return { + provide: NGX_DATATABLE_CONFIG, + useValue: overrides + }; +} diff --git a/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts b/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts index 4d22bbf5f..8292f6dae 100644 --- a/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts +++ b/projects/swimlane/ngx-datatable/src/lib/ngx-datatable.module.ts @@ -2,7 +2,6 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { DataTableFooterTemplateDirective } from './components/footer/footer-template.directive'; import { DatatableComponent } from './components/datatable.component'; import { DataTableColumnDirective } from './components/columns/column.directive'; -import { DataTablePagerComponent } from './components/footer/pager.component'; import { DatatableRowDetailDirective } from './components/row-detail/row-detail.directive'; import { DatatableGroupHeaderDirective } from './components/body/body-group-header.directive'; import { DatatableRowDetailTemplateDirective } from './components/row-detail/row-detail-template.directive'; @@ -17,13 +16,13 @@ import { DatatableRowDefComponent, DatatableRowDefDirective } from './components/body/body-row-def.component'; +import { AllPartial, NgxDatatableConfig, providedNgxDatatableConfig } from './ngx-datatable.config'; @NgModule({ imports: [ DataTableFooterTemplateDirective, DatatableComponent, DataTableColumnDirective, - DataTablePagerComponent, DatatableRowDetailDirective, DatatableGroupHeaderDirective, DatatableRowDetailTemplateDirective, @@ -49,7 +48,6 @@ import { DataTableColumnCellTreeToggle, DataTableFooterTemplateDirective, DatatableFooterDirective, - DataTablePagerComponent, DatatableGroupHeaderTemplateDirective, DisableRowDirective, DatatableRowDefComponent, @@ -61,34 +59,12 @@ export class NgxDatatableModule { * Configure global configuration via INgxDatatableConfig * @param configuration */ - static forRoot(configuration: INgxDatatableConfig): ModuleWithProviders { + static forRoot( + configuration: AllPartial + ): ModuleWithProviders { return { ngModule: NgxDatatableModule, - providers: [{ provide: 'configuration', useValue: configuration }] + providers: [providedNgxDatatableConfig(configuration)] }; } } - -/** - * Interface definition for INgxDatatableConfig global configuration - */ -export interface INgxDatatableConfig { - messages?: { - emptyMessage: string; // Message to show when array is presented, but contains no values - totalMessage: string; // Footer total message - selectedMessage: string; // Footer selected message - }; - cssClasses?: { - sortAscending: string; - sortDescending: string; - sortUnset: string; - pagerLeftArrow: string; - pagerRightArrow: string; - pagerPrevious: string; - pagerNext: string; - }; - headerHeight?: number; - footerHeight?: number; - rowHeight?: number; - defaultColumnWidth?: number; -} diff --git a/projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts b/projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts index 25446d1cf..87e805573 100644 --- a/projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts +++ b/projects/swimlane/ngx-datatable/src/lib/services/scrollbar-helper.service.ts @@ -1,5 +1,4 @@ -import { inject, Injectable } from '@angular/core'; -import { DOCUMENT } from '@angular/common'; +import { DOCUMENT, inject, Injectable } from '@angular/core'; /** * Gets the width of the scrollbar. Nesc for windows @@ -25,7 +24,7 @@ export class ScrollbarHelper { outer.appendChild(inner); const widthWithScroll = inner.offsetWidth; - outer.parentNode.removeChild(outer); + this.document.body.removeChild(outer); return widthNoScroll - widthWithScroll; } diff --git a/projects/swimlane/ngx-datatable/src/lib/test/index.ts b/projects/swimlane/ngx-datatable/src/lib/test/index.ts deleted file mode 100644 index f5dea98a0..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/test/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './jasmine-matchers'; diff --git a/projects/swimlane/ngx-datatable/src/lib/test/jasmine-matchers.d.ts b/projects/swimlane/ngx-datatable/src/lib/test/jasmine-matchers.d.ts deleted file mode 100644 index 906f1e1fd..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/test/jasmine-matchers.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -declare namespace jasmine { - interface Matchers { - toHaveText(actual: any, expectationFailOutput?: any): jasmine.CustomMatcher; - toHaveCssClass(expected: any, expectationFailOutput?: any): boolean; - } -} - -/* -Copyright 2017-2018 Google Inc. All Rights Reserved. -Use of this source code is governed by an MIT-style license that -can be found in the LICENSE file at http://angular.io/license -*/ diff --git a/projects/swimlane/ngx-datatable/src/lib/test/jasmine-matchers.ts b/projects/swimlane/ngx-datatable/src/lib/test/jasmine-matchers.ts deleted file mode 100644 index f27defb45..000000000 --- a/projects/swimlane/ngx-datatable/src/lib/test/jasmine-matchers.ts +++ /dev/null @@ -1,59 +0,0 @@ -/// - -function elementText(n: any): string { - if (n instanceof Array) { - return n.map(elementText).join(''); - } - - if (n.nodeType === Node.COMMENT_NODE) { - return ''; - } - - if (n.nodeType === Node.ELEMENT_NODE && n.hasChildNodes()) { - return elementText(Array.prototype.slice.call(n.childNodes)); - } - - if (n.nativeElement) { - n = n.nativeElement; - } - - return n.textContent; -} - -export const addMatchers = () => - jasmine.addMatchers({ - toHaveText: function toHaveText(): jasmine.CustomMatcher { - return { - compare( - actual: any, - expectedText: string, - expectationFailOutput?: any - ): jasmine.CustomMatcherResult { - const actualText = elementText(actual); - const pass = actualText.indexOf(expectedText) > -1; - const message = pass ? '' : composeMessage(); - return { pass, message }; - - function composeMessage() { - const a = actualText.length < 100 ? actualText : actualText.substr(0, 100) + '...'; - const efo = expectationFailOutput ? ` '${expectationFailOutput}'` : ''; - return `Expected element to have text content '${expectedText}' instead of '${a}'${efo}`; - } - } - }; - }, - toHaveCssClass: function (): jasmine.CustomMatcher { - return { compare: buildError(false), negativeCompare: buildError(true) }; - - function buildError(isNot: boolean) { - return function (actual: HTMLElement, className: string) { - return { - pass: actual.classList.contains(className) === !isNot, - message: `Expected ${actual.outerHTML} ${ - isNot ? 'not ' : '' - }to contain the CSS class "${className}"` - }; - }; - } - } - }); diff --git a/projects/swimlane/ngx-datatable/src/lib/themes/bootstrap.scss b/projects/swimlane/ngx-datatable/src/lib/themes/bootstrap.scss index 43212f1de..31061e369 100644 --- a/projects/swimlane/ngx-datatable/src/lib/themes/bootstrap.scss +++ b/projects/swimlane/ngx-datatable/src/lib/themes/bootstrap.scss @@ -17,6 +17,13 @@ $datatble-ghost-cell-animation-duration: 10s; .ngx-datatable.bootstrap { box-shadow: none; font-size: 13px; + + *, + *:before, + *:after { + box-sizing: border-box; + } + .datatable-header { height: unset !important; .datatable-header-cell { diff --git a/projects/swimlane/ngx-datatable/src/lib/themes/dark.scss b/projects/swimlane/ngx-datatable/src/lib/themes/dark.scss index d20ac9f42..5136c5f32 100644 --- a/projects/swimlane/ngx-datatable/src/lib/themes/dark.scss +++ b/projects/swimlane/ngx-datatable/src/lib/themes/dark.scss @@ -5,6 +5,12 @@ color: #fff; font-size: 13px; + *, + *:before, + *:after { + box-sizing: border-box; + } + .datatable-header { background: #181b24; color: #72809b; @@ -99,7 +105,6 @@ border-radius: 3px; margin: 0 3px; text-align: center; - vertical-align: top; text-decoration: none; vertical-align: bottom; color: #72809b; diff --git a/projects/swimlane/ngx-datatable/src/lib/themes/material.scss b/projects/swimlane/ngx-datatable/src/lib/themes/material.scss index 2bf123e03..fb7a15d8a 100644 --- a/projects/swimlane/ngx-datatable/src/lib/themes/material.scss +++ b/projects/swimlane/ngx-datatable/src/lib/themes/material.scss @@ -86,6 +86,12 @@ $datatble-ghost-cell-animation-duration: 10s; background: $ngx-datatable-background; box-shadow: $ngx-datatable-box-shadow; + *, + *:before, + *:after { + box-sizing: border-box; + } + &.striped { .datatable-row-odd { background: $ngx-datatable-row-odd-background; @@ -309,16 +315,6 @@ $datatble-ghost-cell-animation-duration: 10s; // background: #0829e0 // } } - .datatable-body-group-cell { - text-align: left; - padding: 0.9rem 1.2rem; - vertical-align: top; - border-top: 0; - color: $datatable-body-cell-color; - transition: width 0.3s ease; - font-size: 14px; - font-weight: 400; - } } .progress-linear { @@ -337,13 +333,11 @@ $datatble-ghost-cell-animation-duration: 10s; overflow: hidden; width: 100%; height: 5px; - -webkit-transform: translate(0, 0) scale(1, 1); transform: translate(0, 0) scale(1, 1); background-color: rgb(170, 209, 249); .bar { transition: all 0.2s linear; - -webkit-animation: query 0.8s infinite cubic-bezier(0.39, 0.575, 0.565, 1); animation: query 0.8s infinite cubic-bezier(0.39, 0.575, 0.565, 1); transition: -webkit-transform 0.2s linear; @@ -469,8 +463,6 @@ $datatble-ghost-cell-animation-duration: 10s; outline: none; &:before { - -webkit-transition: all 0.3s ease-in-out; - -moz-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out; content: ''; position: absolute; @@ -482,9 +474,6 @@ $datatble-ghost-cell-animation-duration: 10s; } &:checked:before { - -webkit-transform: rotate(-45deg); - -moz-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); transform: rotate(-45deg); height: 0.5rem; border-color: #009688; diff --git a/projects/swimlane/ngx-datatable/src/lib/types/internal.types.ts b/projects/swimlane/ngx-datatable/src/lib/types/internal.types.ts index 3a91005dd..b74a11289 100644 --- a/projects/swimlane/ngx-datatable/src/lib/types/internal.types.ts +++ b/projects/swimlane/ngx-datatable/src/lib/types/internal.types.ts @@ -1,10 +1,12 @@ -import { TableColumn } from './table-column.type'; +import { TableColumn, TableColumnProp } from './table-column.type'; +import { ValueGetter } from '../utils/column-prop-getters'; +import { Row, SortDirection, TreeStatus } from './public.types'; export type PinDirection = 'left' | 'center' | 'right'; export interface PinnedColumns { type: PinDirection; - columns: TableColumn[]; + columns: TableColumnInternal[]; } export interface ColumnGroupWidth { @@ -14,25 +16,98 @@ export interface ColumnGroupWidth { total: number; } -export interface OrderableReorderEvent { - prevIndex: number; - newIndex: number; - model: TableColumn; -} - export interface TargetChangedEvent { newIndex?: number; prevIndex: number; initialIndex: number; } +export interface ColumnResizeEventInternal { + column: TableColumnInternal; + prevValue: number; + newValue: number; +} + +export interface ReorderEventInternal { + column: TableColumnInternal; + prevValue: number; + newValue: number; +} + export interface Page { number: number; text: string; } export interface DraggableDragEvent { - event: MouseEvent; + event: MouseEvent | TouchEvent; element: HTMLElement; - model: TableColumn; + model: TableColumnInternal; +} + +export interface InnerSortEvent { + column: SortableTableColumnInternal; + prevValue: SortDirection | undefined; + newValue: SortDirection | undefined; +} + +export interface CellActiveEvent { + type: 'checkbox' | 'click' | 'dblclick' | 'keydown' | 'mouseenter'; + event: Event; + row: TRow; + group?: TRow[]; + rowHeight?: number; + column?: TableColumn; + value?: any; + cellElement?: HTMLElement; + treeStatus?: TreeStatus; +} + +export interface BaseTableColumnInternal extends TableColumn { + /** Internal unique id */ + $$id: string; + + /** Internal for column width distributions */ + $$oldWidth?: number; + + /** Internal for setColumnDefaults */ + $$valueGetter: ValueGetter; + + dragging?: boolean; + isTarget?: boolean; + targetMarkerContext?: any; + + // Those properties are never null on the internal type: + name: string; + width: number; +} + +export interface StandardTableColumnInternal + extends BaseTableColumnInternal { + sortable?: false; +} + +export interface SortableTableColumnInternal + extends BaseTableColumnInternal { + comparator: Exclude; + prop: TableColumnProp; + sortable: true; +} + +export type TableColumnInternal = + | StandardTableColumnInternal + | SortableTableColumnInternal; + +export interface TableColumnGroup { + left: TableColumnInternal[]; + center: TableColumnInternal[]; + right: TableColumnInternal[]; +} + +/** Represents the index of a row. */ +export interface RowIndex { + /** Index of the row. If the row is inside a group, it will hold the index the group. */ + index: number; + /** Index of a row inside a group. Only present if the row is inside a group. */ + indexInGroup?: number; } diff --git a/projects/swimlane/ngx-datatable/src/lib/types/public.types.ts b/projects/swimlane/ngx-datatable/src/lib/types/public.types.ts index 027992c63..6bac248bf 100644 --- a/projects/swimlane/ngx-datatable/src/lib/types/public.types.ts +++ b/projects/swimlane/ngx-datatable/src/lib/types/public.types.ts @@ -1,5 +1,4 @@ import { TableColumn, TableColumnProp } from './table-column.type'; -import { BehaviorSubject, Observable } from 'rxjs'; export interface SortPropDir { dir: SortDirection | 'desc' | 'asc'; @@ -11,13 +10,10 @@ export enum SortDirection { desc = 'desc' } -export interface InnerSortEvent { +export interface SortEvent { column: TableColumn; - prevValue: SortDirection; - newValue: SortDirection; -} - -export interface SortEvent extends InnerSortEvent { + prevValue: SortDirection | undefined; + newValue: SortDirection | undefined; sorts: SortPropDir[]; } @@ -36,7 +32,7 @@ export type TreeStatus = 'collapsed' | 'expanded' | 'loading' | 'disabled'; export interface ActivateEvent { type: 'checkbox' | 'click' | 'dblclick' | 'keydown' | 'mouseenter'; - event: MouseEvent | KeyboardEvent; + event: Event; row: TRow; group?: TRow[]; rowHeight?: number; @@ -45,35 +41,36 @@ export interface ActivateEvent { cellElement?: HTMLElement; treeStatus?: TreeStatus; cellIndex?: number; - rowElement?: HTMLElement; + rowElement: HTMLElement; } export interface HeaderCellContext { column: TableColumn; - sortDir: SortDirection | 'asc' | 'desc'; + sortDir: SortDirection | 'asc' | 'desc' | undefined; sortFn: () => void; - allRowsSelected: boolean; + allRowsSelected?: boolean; selectFn: () => void; } -export interface GroupContext { +export interface GroupContext { group: Group; expanded: boolean; rowIndex: number; } -export interface CellContext { +export interface CellContext { onCheckboxChangeFn: (event: Event) => void; activateFn: (event: ActivateEvent) => void; row: TRow; - group: TRow[]; + group?: TRow[]; value: any; column: TableColumn; rowHeight: number; - isSelected: boolean; + isSelected?: boolean; rowIndex: number; - treeStatus: TreeStatus; - disable$: BehaviorSubject; + rowInGroupIndex?: number; + treeStatus?: TreeStatus; + disabled?: boolean; onTreeAction: () => void; expanded?: boolean; } @@ -102,11 +99,21 @@ export interface Group { /** Type for either a row or a group */ export type RowOrGroup = TRow | Group; -export interface RowDetailContext { +export interface RowDetailContext { row: TRow; expanded: boolean; rowIndex: number; - disableRow$?: Observable; + disabled?: boolean; +} + +/** + * Consumer provided rows should extend this interface + * to get access to implicit row properties which are set by the datatable if required. + */ +export interface Row { + [key: TableColumnProp]: any; + treeStatus?: TreeStatus; + level?: number; } export interface ReorderEvent { @@ -118,8 +125,10 @@ export interface ReorderEvent { export interface PageEvent { count: number; pageSize: number; - limit: number; + /** @deprecated Use {@link pageSize} instead. */ + limit: number | undefined; offset: number; + sorts: SortPropDir[]; } export interface PagerPageEvent { @@ -149,6 +158,18 @@ export interface AllGroupsToggleEvent { export type GroupToggleEvents = GroupToggleEvent | AllGroupsToggleEvent; +export interface DetailToggleEvent { + type: 'row'; + value: TRow; +} + +export interface AllDetailToggleEvent { + type: 'all'; + value: boolean; +} + +export type DetailToggleEvents = DetailToggleEvent | AllDetailToggleEvent; + export enum SelectionType { single = 'single', multi = 'multi', @@ -157,6 +178,24 @@ export enum SelectionType { checkbox = 'checkbox' } +export interface SelectEvent { + selected: TRow[]; +} + +export interface ContextMenuEventBody { + event: MouseEvent; + type: ContextmenuType.body; + content: RowOrGroup; +} + +export interface ContextMenuEvenHeader { + event: MouseEvent; + type: ContextmenuType.header; + content: TableColumn; +} + +export type ContextMenuEvent = ContextMenuEventBody | ContextMenuEvenHeader; + export type DragEventType = | 'drag' | 'dragend' diff --git a/projects/swimlane/ngx-datatable/src/lib/types/table-column.type.ts b/projects/swimlane/ngx-datatable/src/lib/types/table-column.type.ts index c4d636a64..63bc4eb8e 100644 --- a/projects/swimlane/ngx-datatable/src/lib/types/table-column.type.ts +++ b/projects/swimlane/ngx-datatable/src/lib/types/table-column.type.ts @@ -1,6 +1,5 @@ import { PipeTransform, TemplateRef } from '@angular/core'; -import { ValueGetter } from '../utils/column-prop-getters'; -import { CellContext, HeaderCellContext } from './public.types'; +import { CellContext, HeaderCellContext, Row } from './public.types'; /** * Column property that indicates how to retrieve this column's @@ -12,22 +11,7 @@ export type TableColumnProp = string | number; /** * Column Type */ -export interface TableColumn { - /** - * Internal unique id - */ - $$id?: string; - - /** - * Internal for column width distributions - */ - $$oldWidth?: number; - - /** - * Internal for setColumnDefaults - */ - $$valueGetter?: ValueGetter; - +export interface TableColumn { /** * Determines if column is checkbox */ @@ -74,7 +58,13 @@ export interface TableColumn { /** * Custom sort comparator */ - comparator?: any; + comparator?: ( + valueA: any, + valueB: any, + rowA: TRow, + rowB: TRow, + sortDir: 'desc' | 'asc' + ) => number; /** * Custom pipe transforms @@ -91,15 +81,6 @@ export interface TableColumn { */ draggable?: boolean; - /** @internal */ - dragging?: boolean; - - /** @internal */ - isTarget?: boolean; - - /** @internal */ - targetMarkerContext?: any; - /** * Whether the column can automatically resize to fill space in the table. */ @@ -182,17 +163,15 @@ export interface TableColumn { /** * Summary function + * + * Null and undefined have different meanings: + * - undefined will use the default summary function + * - null will not compute a summary */ - summaryFunc?: (cells: any[]) => any; + summaryFunc?: ((cells: any[]) => any) | null; /** * Summary cell template ref */ summaryTemplate?: TemplateRef; } - -export interface TableColumnGroup { - left: TableColumn[]; - center: TableColumn[]; - right: TableColumn[]; -} diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts b/projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts index a7388fb23..62638efa4 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/column-helper.ts @@ -2,122 +2,45 @@ import { camelCase, deCamelCase } from './camel-case'; import { id } from './id'; import { getterForProp } from './column-prop-getters'; import { TableColumn } from '../types/table-column.type'; +import { QueryList } from '@angular/core'; import { DataTableColumnDirective } from '../components/columns/column.directive'; - -/** - * Sets the column defaults - */ -export function setColumnDefaults(columns: TableColumn[], defaultColumnWidth = 150) { - if (!columns) { - return; - } - - // Only one column should hold the tree view - // Thus if multiple columns are provided with - // isTreeColumn as true we take only the first one - let treeColumnFound: boolean = false; - - for (const column of columns) { - if (!column.$$id) { - column.$$id = id(); - } - - // prop can be numeric; zero is valid not a missing prop - // translate name => prop - if (isNullOrUndefined(column.prop) && column.name) { - column.prop = camelCase(column.name); - } - - if (!column.$$valueGetter) { - column.$$valueGetter = getterForProp(column.prop); - } - - // format props if no name passed - if (!isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) { - column.name = deCamelCase(String(column.prop)); - } - - if (isNullOrUndefined(column.prop) && isNullOrUndefined(column.name)) { - column.name = ''; // Fixes IE and Edge displaying `null` - } - - if (!('resizeable' in column)) { - column.resizeable = true; - } - - if (!('sortable' in column)) { - column.sortable = true; - } - - if (!('draggable' in column)) { - column.draggable = true; - } - - if (!('canAutoResize' in column)) { - column.canAutoResize = true; - } - - if (!('width' in column)) { - column.width = defaultColumnWidth; - } - - if (!('isTreeColumn' in column)) { - column.isTreeColumn = false; - } else { - if (column.isTreeColumn && !treeColumnFound) { - // If the first column with isTreeColumn is true found - // we mark that treeCoulmn is found - treeColumnFound = true; - } else { - // After that isTreeColumn property for any other column - // will be set as false - column.isTreeColumn = false; - } - } - } -} - -export function isNullOrUndefined(value: T | null | undefined): value is null | undefined { - return value === null || value === undefined; -} - -/** - * Translates templates definitions to objects - */ -export function translateTemplates( - templates: DataTableColumnDirective[] -): TableColumn[] { - const result: TableColumn[] = []; - for (const temp of templates) { - const col: TableColumn = {}; - - const props = Object.getOwnPropertyNames(temp); - for (const prop of props) { - col[prop] = temp[prop]; - } - - if (temp.headerTemplate) { - col.headerTemplate = temp.headerTemplate; - } - - if (temp.cellTemplate) { - col.cellTemplate = temp.cellTemplate; - } - - if (temp.ghostCellTemplate) { - col.ghostCellTemplate = temp.ghostCellTemplate; - } - - if (temp.summaryFunc) { - col.summaryFunc = temp.summaryFunc; - } - - if (temp.summaryTemplate) { - col.summaryTemplate = temp.summaryTemplate; - } - - result.push(col); - } - - return result; +import { TableColumnInternal } from '../types/internal.types'; +import { Row } from '../types/public.types'; +import { orderByComparator } from './sort'; + +export function toInternalColumn( + columns: TableColumn[] | QueryList>, + defaultColumnWidth = 150 +): TableColumnInternal[] { + let hasTreeColumn = false; + // TS fails to infer the type here. + return (columns as TableColumn[]).map(column => { + const prop = column.prop ?? (column.name ? camelCase(column.name) : undefined); + // Only one column should hold the tree view, + // Thus if multiple columns are provided with + // isTreeColumn as true, we take only the first one + const isTreeColumn = !!column.isTreeColumn && !hasTreeColumn; + hasTreeColumn = hasTreeColumn || isTreeColumn; + // TODO: add check if prop or name is provided if sorting is enabled. + + return { + ...column, + $$id: id(), + $$valueGetter: getterForProp(prop), + prop, + name: column.name ?? (prop ? deCamelCase(String(prop)) : ''), + resizeable: column.resizeable ?? true, + sortable: column.sortable ?? true, + comparator: column.comparator ?? orderByComparator, + draggable: column.draggable ?? true, + canAutoResize: column.canAutoResize ?? true, + width: column.width ?? defaultColumnWidth, + isTreeColumn, + // in case of the directive, those are getters, so call them explicitly. + headerTemplate: column.headerTemplate, + cellTemplate: column.cellTemplate, + summaryTemplate: column.summaryTemplate, + ghostCellTemplate: column.ghostCellTemplate + } as TableColumnInternal; // TS cannot cast here + }); } diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts b/projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts index ed08a08f8..4f1fbf804 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/column-prop-getters.ts @@ -15,19 +15,20 @@ export function emptyStringGetter(): string { * Returns the appropriate getter function for this kind of prop. * If prop == null, returns the emptyStringGetter. */ -export function getterForProp(prop: TableColumnProp): ValueGetter { +export function getterForProp(prop: TableColumnProp | undefined): ValueGetter { + // TODO requires better typing which will also involve adjust TableColum. So postponing it. if (prop == null) { return emptyStringGetter; } if (typeof prop === 'number') { - return numericIndexGetter; + return numericIndexGetter as ValueGetter; } else { // deep or simple if (prop.indexOf('.') !== -1) { - return deepValueGetter; + return deepValueGetter as ValueGetter; } else { - return shallowValueGetter; + return shallowValueGetter as ValueGetter; } } } diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/column.ts b/projects/swimlane/ngx-datatable/src/lib/utils/column.ts index d6ccb581c..ec0d2d799 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/column.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/column.ts @@ -1,10 +1,14 @@ -import { TableColumn, TableColumnGroup } from '../types/table-column.type'; -import { ColumnGroupWidth, PinnedColumns } from '../types/internal.types'; +import { + ColumnGroupWidth, + PinnedColumns, + TableColumnGroup, + TableColumnInternal +} from '../types/internal.types'; /** * Returns the columns by pin. */ -export function columnsByPin(cols: TableColumn[]) { +export function columnsByPin(cols: TableColumnInternal[]) { const ret: TableColumnGroup = { left: [], center: [], @@ -29,7 +33,10 @@ export function columnsByPin(cols: TableColumn[]) { /** * Returns the widths of all group sets of a column */ -export function columnGroupWidths(groups: TableColumnGroup, all: TableColumn[]): ColumnGroupWidth { +export function columnGroupWidths( + groups: TableColumnGroup, + all: TableColumnInternal[] +): ColumnGroupWidth { return { left: columnTotalWidth(groups.left), center: columnTotalWidth(groups.center), @@ -39,37 +46,13 @@ export function columnGroupWidths(groups: TableColumnGroup, all: TableColumn[]): } /** - * Calculates the total width of all columns and their groups + * Calculates the total width of all columns */ -export function columnTotalWidth(columns: TableColumn[], prop?: string) { - let totalWidth = 0; - - if (columns) { - for (const c of columns) { - const has = prop && c[prop]; - const width = has ? c[prop] : c.width; - totalWidth = totalWidth + parseFloat(width); - } - } - - return totalWidth; -} - -/** - * Calculates the total width of all columns and their groups - */ -export function columnsTotalWidth(columns: TableColumn[], prop?: keyof TableColumn) { - let totalWidth = 0; - - for (const column of columns) { - const has = prop && column[prop]; - totalWidth = totalWidth + (has ? column[prop] : column.width); - } - - return totalWidth; +export function columnTotalWidth(columns?: TableColumnInternal[]) { + return columns?.reduce((total, column) => total + column.width, 0) ?? 0; } -export function columnsByPinArr(val: TableColumn[]): PinnedColumns[] { +export function columnsByPinArr(val: TableColumnInternal[]): PinnedColumns[] { const colsByPin = columnsByPin(val); return [ { type: 'left' as const, columns: colsByPin.left }, diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/events.ts b/projects/swimlane/ngx-datatable/src/lib/utils/events.ts new file mode 100644 index 000000000..484e76e26 --- /dev/null +++ b/projects/swimlane/ngx-datatable/src/lib/utils/events.ts @@ -0,0 +1,14 @@ +/** + * Extracts the position (x, y coordinates) from a MouseEvent or TouchEvent. + * + * @param {MouseEvent | TouchEvent} event - The event object from which to extract the position. Can be either a MouseEvent or a TouchEvent. + * @return {{ x: number, y: number }} An object containing the x and y coordinates of the event relative to the viewport. + */ +export function getPositionFromEvent(event: MouseEvent | TouchEvent): { + clientX: number; + clientY: number; + screenX: number; + screenY: number; +} { + return event instanceof MouseEvent ? (event as MouseEvent) : (event.changedTouches[0] as Touch); +} diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/math.spec.ts b/projects/swimlane/ngx-datatable/src/lib/utils/math.spec.ts index c82bf4369..ff5ab873c 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/math.spec.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/math.spec.ts @@ -1,4 +1,6 @@ import { adjustColumnWidths, forceFillColumnWidths } from './math'; +import { toInternalColumn } from './column-helper'; +import { TableColumnInternal } from '../types/internal.types'; describe('Math function', () => { describe('forceFillColumnWidths', () => { @@ -10,7 +12,7 @@ describe('Math function', () => { { prop: 'email', width: 250, canAutoResize: true } ]; - forceFillColumnWidths(columns, 750, 1, true); // Column 2 expanded from 250 to 400 + forceFillColumnWidths(columns as TableColumnInternal[], 750, 1, true); // Column 2 expanded from 250 to 400 expect(columns[0].width).toBe(250); // Not changed expect(columns[1].width).toBe(400); @@ -26,7 +28,7 @@ describe('Math function', () => { { prop: 'email', width: 250, canAutoResize: true } ]; - forceFillColumnWidths(columns, 750, 1, true); // Column 2 contracted from 250 to 180 + forceFillColumnWidths(columns as TableColumnInternal[], 750, 1, true); // Column 2 contracted from 250 to 180 expect(columns[0].width).toBe(250); // Not changed expect(columns[1].width).toBe(180); @@ -38,7 +40,7 @@ describe('Math function', () => { describe('adjustColumnWidths', () => { describe('flex mode', () => { it('should not go over/under compared to given max width', () => { - const cols = [ + const cols = toInternalColumn([ { prop: 'id1', width: 287, @@ -79,7 +81,7 @@ describe('Math function', () => { flexGrow: 1, canAutoResize: true } - ]; + ]); const givenTableWidth = 1180; @@ -90,7 +92,7 @@ describe('Math function', () => { }); it('should overflow if the total of given min widths is bigger than given max width', () => { - const cols = [ + const cols = toInternalColumn([ { prop: 'id1', width: 100, @@ -107,7 +109,7 @@ describe('Math function', () => { flexGrow: 1, canAutoResize: true } - ]; + ]); const maxWidth = 199; adjustColumnWidths(cols, maxWidth); @@ -117,7 +119,7 @@ describe('Math function', () => { }); it('should respect min widths', () => { - const cols = [ + const cols = toInternalColumn([ { prop: 'id1', width: 0, @@ -134,12 +136,12 @@ describe('Math function', () => { flexGrow: 1, canAutoResize: true } - ]; + ]); adjustColumnWidths(cols, 40); for (const col of cols) { - expect(col.width - col.minWidth).toBeGreaterThanOrEqual(0); + expect(col.width - col.minWidth!).toBeGreaterThanOrEqual(0); } }); }); diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/math.ts b/projects/swimlane/ngx-datatable/src/lib/utils/math.ts index 6acfabe89..6e28c02bc 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/math.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/math.ts @@ -1,5 +1,6 @@ -import { TableColumn, TableColumnGroup } from '../types/table-column.type'; -import { columnsByPin, columnsTotalWidth } from './column'; +import { TableColumn, TableColumnProp } from '../types/table-column.type'; +import { columnsByPin, columnTotalWidth } from './column'; +import { TableColumnGroup, TableColumnInternal } from '../types/internal.types'; /** * Calculates the Total Flex Grow @@ -18,8 +19,8 @@ export function getTotalFlexGrow(columns: TableColumn[]) { * Adjusts the column widths. * Inspired by: https://github.com/facebook/fixed-data-table/blob/master/src/FixedDataTableWidthHelper.js */ -export function adjustColumnWidths(allColumns: TableColumn[], expectedWidth: number) { - const columnsWidth = columnsTotalWidth(allColumns); +export function adjustColumnWidths(allColumns: TableColumnInternal[], expectedWidth: number) { + const columnsWidth = columnTotalWidth(allColumns); const totalFlexGrow = getTotalFlexGrow(allColumns); const colsByGroup = columnsByPin(allColumns); @@ -33,24 +34,20 @@ export function adjustColumnWidths(allColumns: TableColumn[], expectedWidth: num */ function scaleColumns(colsByGroup: TableColumnGroup, maxWidth: number, totalFlexGrow: number) { // calculate total width and flexgrow points for columns that can be resized - for (const attr in colsByGroup) { - if (attr in colsByGroup) { - for (const column of colsByGroup[attr]) { - if (column.$$oldWidth) { - // when manually resized, switch off auto-resize - column.canAutoResize = false; - } - if (!column.canAutoResize) { - maxWidth -= column.width; - totalFlexGrow -= column.flexGrow ? column.flexGrow : 0; - } else { - column.width = 0; - } - } + for (const column of Object.values(colsByGroup).flat()) { + if (column.$$oldWidth) { + // when manually resized, switch off auto-resize + column.canAutoResize = false; + } + if (!column.canAutoResize) { + maxWidth -= column.width; + totalFlexGrow -= column.flexGrow ? column.flexGrow : 0; + } else { + column.width = 0; } } - const hasMinWidth = {}; + const hasMinWidth: Record = {}; let remainingWidth = maxWidth; // resize columns until no width is left to be distributed @@ -58,27 +55,23 @@ function scaleColumns(colsByGroup: TableColumnGroup, maxWidth: number, totalFlex const widthPerFlexPoint = remainingWidth / totalFlexGrow; remainingWidth = 0; - for (const attr in colsByGroup) { - if (attr in colsByGroup) { - for (const column of colsByGroup[attr]) { - // if the column can be resize and it hasn't reached its minimum width yet - if (column.canAutoResize && !hasMinWidth[column.prop]) { - const newWidth = column.width + column.flexGrow * widthPerFlexPoint; - if (column.minWidth !== undefined && newWidth < column.minWidth) { - remainingWidth += newWidth - column.minWidth; - column.width = column.minWidth; - hasMinWidth[column.prop] = true; - } else { - column.width = newWidth; - } - } + for (const column of Object.values(colsByGroup).flat()) { + // if the column can be resize and it hasn't reached its minimum width yet + if (column.canAutoResize && !hasMinWidth[column.prop]) { + const newWidth = column.width + column.flexGrow * widthPerFlexPoint; + if (column.minWidth !== undefined && newWidth < column.minWidth) { + remainingWidth += newWidth - column.minWidth; + column.width = column.minWidth; + hasMinWidth[column.prop] = true; + } else { + column.width = newWidth; } } } } while (remainingWidth !== 0); // Adjust for any remaining offset in computed widths vs maxWidth - const columns: TableColumn[] = Object.values(colsByGroup).reduce( + const columns: TableColumnInternal[] = Object.values(colsByGroup).reduce( (acc, col) => acc.concat(col), [] ); @@ -122,7 +115,7 @@ function scaleColumns(colsByGroup: TableColumnGroup, maxWidth: number, totalFlex * the width should use the original width; not the newly proportioned widths. */ export function forceFillColumnWidths( - allColumns: TableColumn[], + allColumns: TableColumnInternal[], expectedWidth: number, startIdx: number, allowBleed: boolean, diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts b/projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts index 41f047cd2..0e214221d 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/row-height-cache.ts @@ -35,8 +35,8 @@ export class RowHeightCache { rowHeight, detailRowHeight, externalVirtual, + indexOffset, rowCount, - rowIndexes, rowExpansions } = details; const isFn = typeof rowHeight === 'function'; @@ -72,7 +72,7 @@ export class RowHeightCache { const expanded = rowExpansions.has(row); if (row && expanded) { if (isDetailFn) { - const index = rowIndexes.get(row); + const index = indexOffset + i; currentRowHeight += detailRowHeight(row, index); } else { currentRowHeight += detailRowHeight; diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/selection.ts b/projects/swimlane/ngx-datatable/src/lib/utils/selection.ts index 0adbf46f9..3ac2fa269 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/selection.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/selection.ts @@ -10,7 +10,12 @@ export function selectRows(selected: TRow[], row: TRow, comparefn: any) { return selected; } -export function selectRowsBetween(selected: TRow[], rows: TRow[], index: number, prevIndex: number): TRow[] { +export function selectRowsBetween( + selected: TRow[], + rows: (TRow | undefined)[], + index: number, + prevIndex: number +): TRow[] { const reverse = index < prevIndex; for (let i = 0; i < rows.length; i++) { @@ -34,7 +39,7 @@ export function selectRowsBetween(selected: TRow[], rows: TRow[], index: n if ((reverse && lesser) || (!reverse && greater)) { // if in the positive range to be added to `selected`, and // not already in the selected array, add it - if (i >= range.start && i <= range.end) { + if (i >= range.start && i <= range.end && row) { selected.push(row); } } diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/sort.ts b/projects/swimlane/ngx-datatable/src/lib/utils/sort.ts index 2f52dcb42..9ee575e59 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/sort.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/sort.ts @@ -1,13 +1,14 @@ import { getterForProp } from './column-prop-getters'; import { Group, SortDirection, SortPropDir, SortType } from '../types/public.types'; -import { TableColumn } from '../types/table-column.type'; +import { TableColumnProp } from '../types/table-column.type'; +import { SortableTableColumnInternal, TableColumnInternal } from '../types/internal.types'; /** * Gets the next sort direction */ export function nextSortDir( sortType: SortType, - current: SortDirection | 'desc' | 'asc' + current: SortDirection | 'desc' | 'asc' | undefined ): SortDirection | undefined { if (sortType === SortType.single) { if (current === SortDirection.asc) { @@ -75,7 +76,11 @@ export function orderByComparator(a: any, b: any): number { * creates a shallow copy of the `rows` input and returns the sorted copy. this function * does not sort the `rows` argument in place */ -export function sortRows(rows: TRow[], columns: TableColumn[], dirs: SortPropDir[]): TRow[] { +export function sortRows( + rows: TRow[], + columns: TableColumnInternal[], + dirs: SortPropDir[] +): TRow[] { if (!rows) { return []; } @@ -83,20 +88,13 @@ export function sortRows(rows: TRow[], columns: TableColumn[], dirs: SortP return [...rows]; } - /** - * record the row ordering of results from prior sort operations (if applicable) - * this is necessary to guarantee stable sorting behavior - */ - const rowToIndexMap = new Map(); - rows.forEach((row, index) => rowToIndexMap.set(row, index)); - const temp = [...rows]; const cols = columns.reduce((obj, col) => { - if (col.comparator && typeof col.comparator === 'function') { + if (col.sortable) { obj[col.prop] = col.comparator; } return obj; - }, {}); + }, {} as Record); // cache valueGetter and compareFn so that they // do not need to be looked-up in the sort function body @@ -106,11 +104,11 @@ export function sortRows(rows: TRow[], columns: TableColumn[], dirs: SortP prop, dir: dir.dir, valueGetter: getterForProp(prop), - compareFn: cols[prop] || orderByComparator + compareFn: cols[prop] }; }); - return temp.sort(function (rowA: any, rowB: any) { + return temp.sort((rowA: TRow, rowB: TRow) => { for (const cachedDir of cachedDirs) { // Get property and valuegetters for column to be sorted const { prop, valueGetter } = cachedDir; @@ -136,22 +134,15 @@ export function sortRows(rows: TRow[], columns: TableColumn[], dirs: SortP } } - if (!(rowToIndexMap.has(rowA) && rowToIndexMap.has(rowB))) { - return 0; - } - - /** - * all else being equal, preserve original order of the rows (stable sort) - */ - return rowToIndexMap.get(rowA) < rowToIndexMap.get(rowB) ? -1 : 1; + return 0; }); } export function sortGroupedRows( groupedRows: Group[], - columns: TableColumn[], + columns: TableColumnInternal[], dirs: SortPropDir[], - sortOnGroupHeader: SortPropDir + sortOnGroupHeader: SortPropDir | undefined ): Group[] { if (sortOnGroupHeader) { groupedRows = sortRows(groupedRows, columns, [ diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts b/projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts index 82eb00042..31fc923a1 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/throttle.ts @@ -59,7 +59,7 @@ export function throttleable(duration: number, options?: any) { value: throttle(descriptor.value, duration, options) }); - return this[key]; + return target[key]; } }; }; diff --git a/projects/swimlane/ngx-datatable/src/lib/utils/tree.ts b/projects/swimlane/ngx-datatable/src/lib/utils/tree.ts index 8b02d7716..b81ba9668 100644 --- a/projects/swimlane/ngx-datatable/src/lib/utils/tree.ts +++ b/projects/swimlane/ngx-datatable/src/lib/utils/tree.ts @@ -1,9 +1,10 @@ import { getterForProp } from './column-prop-getters'; import { TableColumnProp } from '../types/table-column.type'; +import { Row } from '../types/public.types'; -export type OptionalValueGetter = (row: any) => any | undefined; -export function optionalGetterForProp(prop: TableColumnProp): OptionalValueGetter { - return prop && (row => getterForProp(prop)(row, prop)); +export type OptionalValueGetter = ((row: any) => any) | undefined; +export function optionalGetterForProp(prop: TableColumnProp | undefined): OptionalValueGetter { + return prop ? row => getterForProp(prop)(row, prop) : undefined; } /** @@ -42,81 +43,50 @@ export function optionalGetterForProp(prop: TableColumnProp): OptionalValueGette * @param rows * */ -export function groupRowsByParents( - rows: TRow[], +export function groupRowsByParents( + rows: (TRow | undefined)[], from?: OptionalValueGetter, to?: OptionalValueGetter -): TRow[] { +): (TRow | undefined)[] { if (from && to) { - const nodeById = {}; - const l = rows.length; - let node: TreeNode | null = null; + const treeRows = rows.filter(row => !!row).map(row => new TreeNode(row)); + const uniqIDs = new Map(treeRows.map(node => [to(node.row), node])); - nodeById[0] = new TreeNode(); // that's the root node - - const uniqIDs = rows.reduce((arr, item) => { - const toValue = to(item); - if (arr.indexOf(toValue) === -1) { - arr.push(toValue); - } - return arr; - }, []); - - for (let i = 0; i < l; i++) { - // make TreeNode objects for each item - nodeById[to(rows[i])] = new TreeNode(rows[i]); - } - - for (let i = 0; i < l; i++) { - // link all TreeNode objects - node = nodeById[to(rows[i])]; - let parent = 0; + const rootNodes = treeRows.reduce((root, node) => { const fromValue = from(node.row); - if (!!fromValue && uniqIDs.indexOf(fromValue) > -1) { - parent = fromValue; + const parent = uniqIDs.get(fromValue); + if (parent) { + node.row.level = parent.row.level! + 1; // TODO: should be reflected by type, that level is defined + node.parent = parent; + parent.children.push(node); + } else { + node.row.level = 0; + root.push(node); } - node.parent = nodeById[parent]; - node.row['level'] = node.parent.row['level'] + 1; - node.parent.children.push(node); - } - - let resolvedRows: any[] = []; - nodeById[0].flatten(function () { - resolvedRows = [...resolvedRows, this.row]; - }, true); + return root; + }, [] as TreeNode[]); - return resolvedRows; + return rootNodes.flatMap(child => child.flatten()); } else { return rows; } } -class TreeNode { - public row: any; - public parent: any; - public children: any[]; +class TreeNode { + public row: TRow; + public parent?: TreeNode; + public children: TreeNode[]; - constructor(row: any | null = null) { - if (!row) { - row = { - level: -1, - treeStatus: 'expanded' - }; - } + constructor(row: TRow) { this.row = row; - this.parent = null; this.children = []; } - flatten(f: any, recursive: boolean) { - if (this.row['treeStatus'] === 'expanded') { - for (let i = 0, l = this.children.length; i < l; i++) { - const child = this.children[i]; - f.apply(child, Array.prototype.slice.call(arguments, 2)); - if (recursive) { - child.flatten.apply(child, arguments); - } - } + flatten(): TRow[] { + if (this.row.treeStatus === 'expanded') { + return [this.row, ...this.children.flatMap(child => child.flatten())]; + } else { + return [this.row]; } } } diff --git a/projects/swimlane/ngx-datatable/src/public-api.ts b/projects/swimlane/ngx-datatable/src/public-api.ts index 876bb569f..115d4aa41 100644 --- a/projects/swimlane/ngx-datatable/src/public-api.ts +++ b/projects/swimlane/ngx-datatable/src/public-api.ts @@ -5,20 +5,8 @@ // components export * from './lib/ngx-datatable.module'; export * from './lib/components/datatable.component'; -export * from './lib/components/header/header.component'; -export * from './lib/components/header/header-cell.component'; -export * from './lib/components/body/body.component'; -export * from './lib/components/body/body-cell.component'; -export * from './lib/components/body/body-row.component'; -export * from './lib/components/body/progress-bar.component'; -export * from './lib/components/body/scroller.component'; -export * from './lib/components/body/body-row-wrapper.component'; -export * from './lib/components/body/selection.component'; export * from './lib/components/body/body-group-header.directive'; export * from './lib/components/body/body-group-header-template.directive'; -export * from './lib/components/body/summary/summary-row.component'; -export * from './lib/components/footer/footer.component'; -export * from './lib/components/footer/pager.component'; export * from './lib/components/footer/footer.directive'; export * from './lib/components/footer/footer-template.directive'; export * from './lib/components/columns/column.directive'; @@ -31,31 +19,15 @@ export * from './lib/components/row-detail/row-detail-template.directive'; export * from './lib/components/body/body-row-def.component'; // directives -export * from './lib/directives/draggable.directive'; -export * from './lib/directives/long-press.directive'; -export * from './lib/directives/orderable.directive'; -export * from './lib/directives/resizeable.directive'; -export * from './lib/directives/visibility.directive'; export * from './lib/directives/disable-row.directive'; -// services -export * from './lib/services/scrollbar-helper.service'; -export * from './lib/services/column-changes.service'; - // types export * from './lib/types/public.types'; export * from './lib/types/table-column.type'; -// utils -export * from './lib/utils/id'; -export * from './lib/utils/column'; -export * from './lib/utils/column-prop-getters'; -export * from './lib/utils/camel-case'; -export * from './lib/utils/keys'; -export * from './lib/utils/math'; -export * from './lib/utils/selection'; -export * from './lib/utils/throttle'; -export * from './lib/utils/sort'; -export * from './lib/utils/row-height-cache'; -export * from './lib/utils/column-helper'; -export * from './lib/utils/tree'; +export { + providedNgxDatatableConfig, + NgxDatatableConfig, + NgxDatatableMessages, + NgxDatatableCssClasses +} from './lib/ngx-datatable.config'; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 6ebed30df..9dfc70009 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,124 +1,268 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { BasicAutoComponent } from './basic/basic-auto.component'; -import { BasicFixedComponent } from './basic/basic-fixed.component'; -import { FullScreenComponent } from './basic/fullscreen.component'; -import { InlineEditComponent } from './basic/inline.component'; -import { VirtualScrollComponent } from './basic/virtual.component'; -import { ResponsiveComponent } from './basic/responsive.component'; -import { DynamicHeightComponent } from './basic/dynamic-height.component'; -import { PagingScrollingNoVirtualizationComponent } from './paging/paging-scrolling-novirtualization.component'; -import { ColumnFlexComponent } from './columns/column-flex.component'; -import { ColumnToggleComponent } from './columns/column-toggle.component'; -import { ColumnStandardComponent } from './columns/column-standard.component'; -import { ColumnForceComponent } from './columns/column-force.component'; -import { ColumnReorderComponent } from './columns/column-reorder.component'; -import { HorzVertScrollingComponent } from './basic/scrolling.component'; -import { MultipleTablesComponent } from './basic/multiple.component'; -import { RowDetailsComponent } from './basic/row-detail.component'; -import { LiveDataComponent } from './basic/live.component'; -import { RowCssComponent } from './basic/css.component'; -import { DragDropComponent } from './drag-drop/drag-drop.component'; -import { DisabledRowsComponent } from './basic/disabled-rows.component'; -import { FullScreenTreeComponent } from './tree/fullscreen.component'; -import { RowGroupingComponent } from './basic/row-grouping.component'; -import { ClientPagingComponent } from './paging/paging-client.component'; -import { ServerPagingComponent } from './paging/paging-server.component'; -import { ServerScrollingComponent } from './paging/scrolling-server.component'; -import { VirtualPagingComponent } from './paging/paging-virtual.component'; -import { ClientSortingComponent } from './sorting/sorting-client.component'; -import { DefaultSortingComponent } from './sorting/sorting-default.component'; -import { ServerSortingComponent } from './sorting/sorting-server.component'; -import { CellSelectionComponent } from './selection/selection-cell.component'; -import { SingleSelectionComponent } from './selection/selection-single.component'; -import { MultiSelectionComponent } from './selection/selection-multi.component'; -import { MultiClickSelectionComponent } from './selection/selection-multi-click.component'; -import { InlineTemplatesComponent } from './templates/template-dom.component'; -import { ColumnPinningComponent } from './columns/pinning.component'; -import { SummaryRowSimpleComponent } from './summary/summary-row-simple.component'; -import { SummaryRowCustomTemplateComponent } from './summary/summary-row-custom-template.component'; -import { SummaryRowServerPagingComponent } from './summary/summary-row-server-paging.component'; -import { SummaryRowInlineHtmlComponent } from './summary/summary-row-inline-html.component'; -import { ScrollingDynamicallyComponent } from './basic/scrolling-dynamically.component'; -import { FilterComponent } from './basic/filter.component'; -import { TabsDemoComponent } from './basic/tabs.component'; -import { RxDemoComponent } from './basic/rx.component'; -import { ContextMenuDemoComponent } from './basic/contextmenu.component'; -import { FooterDemoComponent } from './basic/footer.component'; -import { BasicEmptyComponent } from './basic/empty.component'; -import { DarkThemeComponent } from './basic/dark-theme.component'; -import { BootstrapThemeComponent } from './basic/bootstrap.component'; -import { ClientTreeComponent } from './tree/client-tree.component'; -import { SortingComparatorComponent } from './sorting/sorting-comparator.component'; -import { MultiDisableSelectionComponent } from './selection/selection-disabled.component'; -import { CheckboxSelectionComponent } from './selection/selection-chkbox.component'; -import { CustomCheckboxSelectionComponent } from './selection/selection-chkbox-template.component'; -import { MultiClickCheckboxSelectionComponent } from './selection/selection-multi-click-chkbox.component'; -import { TemplateRefTemplatesComponent } from './templates/template-obj.component'; +import { Routes } from '@angular/router'; -const routes: Routes = [ - { path: '', component: BasicAutoComponent }, - { path: 'basic-fixed', component: BasicFixedComponent }, - { path: 'full-screen', component: FullScreenComponent }, - { path: 'inline-edit', component: InlineEditComponent }, - { path: 'virtual-scroll', component: VirtualScrollComponent }, - { path: 'horz-vert-scrolling', component: HorzVertScrollingComponent }, - { path: 'scrolling-dynamically', component: ScrollingDynamicallyComponent }, - { path: 'multiple-tables', component: MultipleTablesComponent }, - { path: 'row-details', component: RowDetailsComponent }, - { path: 'responsive', component: ResponsiveComponent }, - { path: 'filter', component: FilterComponent }, - { path: 'hidden', component: TabsDemoComponent }, - { path: 'live', component: LiveDataComponent }, - { path: 'rx', component: RxDemoComponent }, - { path: 'contextmenu', component: ContextMenuDemoComponent }, - { path: 'css', component: RowCssComponent }, - { path: 'dynamic', component: DynamicHeightComponent }, - { path: 'footer', component: FooterDemoComponent }, - { path: 'empty', component: BasicEmptyComponent }, - { path: 'drag-drop', component: DragDropComponent }, - { path: 'disabled', component: DisabledRowsComponent }, - { path: 'dark', component: DarkThemeComponent, data: { dark: true } }, - { path: 'bootstrap', component: BootstrapThemeComponent }, - { path: 'fullscreen-tree', component: FullScreenTreeComponent }, - { path: 'client-tree', component: ClientTreeComponent }, - { path: 'row-grouping', component: RowGroupingComponent }, - { path: 'client-paging', component: ClientPagingComponent }, - { path: 'server-paging', component: ServerPagingComponent }, +export const routes: Routes = [ + { + path: '', + loadComponent: () => import('./basic/basic-auto.component').then(c => c.BasicAutoComponent) + }, + { + path: 'basic-fixed', + loadComponent: () => import('./basic/basic-fixed.component').then(c => c.BasicFixedComponent) + }, + { + path: 'full-screen', + loadComponent: () => import('./basic/fullscreen.component').then(c => c.FullScreenComponent) + }, + { + path: 'inline-edit', + loadComponent: () => import('./basic/inline.component').then(c => c.InlineEditComponent) + }, + { + path: 'virtual-scroll', + loadComponent: () => import('./basic/virtual.component').then(c => c.VirtualScrollComponent) + }, + { + path: 'horz-vert-scrolling', + loadComponent: () => + import('./basic/scrolling.component').then(c => c.HorzVertScrollingComponent) + }, + { + path: 'scrolling-dynamically', + loadComponent: () => + import('./basic/scrolling-dynamically.component').then(c => c.ScrollingDynamicallyComponent) + }, + { + path: 'multiple-tables', + loadComponent: () => import('./basic/multiple.component').then(c => c.MultipleTablesComponent) + }, + { + path: 'row-details', + loadComponent: () => import('./basic/row-detail.component').then(c => c.RowDetailsComponent) + }, + { + path: 'responsive', + loadComponent: () => import('./basic/responsive.component').then(c => c.ResponsiveComponent) + }, + { + path: 'filter', + loadComponent: () => import('./basic/filter.component').then(c => c.FilterComponent) + }, + { + path: 'hidden', + loadComponent: () => import('./basic/tabs.component').then(c => c.TabsDemoComponent) + }, + { + path: 'live', + loadComponent: () => import('./basic/live.component').then(c => c.LiveDataComponent) + }, + { path: 'rx', loadComponent: () => import('./basic/rx.component').then(c => c.RxDemoComponent) }, + { + path: 'contextmenu', + loadComponent: () => + import('./basic/contextmenu.component').then(c => c.ContextMenuDemoComponent) + }, + { + path: 'css', + loadComponent: () => import('./basic/css.component').then(c => c.RowCssComponent) + }, + { + path: 'dynamic', + loadComponent: () => + import('./basic/dynamic-height.component').then(c => c.DynamicHeightComponent) + }, + { + path: 'footer', + loadComponent: () => import('./basic/footer.component').then(c => c.FooterDemoComponent) + }, + { + path: 'empty', + loadComponent: () => import('./basic/empty.component').then(c => c.BasicEmptyComponent) + }, + { + path: 'drag-drop', + loadComponent: () => import('./drag-drop/drag-drop.component').then(c => c.DragDropComponent) + }, + { + path: 'disabled', + loadComponent: () => + import('./basic/disabled-rows.component').then(c => c.DisabledRowsComponent) + }, + { + path: 'dark', + loadComponent: () => import('./basic/dark-theme.component').then(c => c.DarkThemeComponent), + data: { dark: true } + }, + { + path: 'bootstrap', + loadComponent: () => import('./basic/bootstrap.component').then(c => c.BootstrapThemeComponent) + }, + { + path: 'fullscreen-tree', + loadComponent: () => import('./tree/fullscreen.component').then(c => c.FullScreenTreeComponent) + }, + { + path: 'client-tree', + loadComponent: () => import('./tree/client-tree.component').then(c => c.ClientTreeComponent) + }, + { + path: 'row-grouping', + loadComponent: () => import('./basic/row-grouping.component').then(c => c.RowGroupingComponent) + }, + { + path: 'client-paging', + loadComponent: () => + import('./paging/paging-client.component').then(c => c.ClientPagingComponent) + }, + { + path: 'server-paging', + loadComponent: () => + import('./paging/paging-server.component').then(c => c.ServerPagingComponent) + }, { path: 'paging-scrolling-novirtualization', - component: PagingScrollingNoVirtualizationComponent - }, - { path: 'server-scrolling', component: ServerScrollingComponent }, - { path: 'virtual-paging', component: VirtualPagingComponent }, - { path: 'client-sorting', component: ClientSortingComponent }, - { path: 'default-sorting', component: DefaultSortingComponent }, - { path: 'server-sorting', component: ServerSortingComponent }, - { path: 'comparator-sorting', component: SortingComparatorComponent }, - { path: 'cell-selection', component: CellSelectionComponent }, - { path: 'single-selection', component: SingleSelectionComponent }, - { path: 'multi-selection', component: MultiSelectionComponent }, - { path: 'multi-click-selection', component: MultiClickSelectionComponent }, - { path: 'multidisable-selection', component: MultiDisableSelectionComponent }, - { path: 'chkbox-selection', component: CheckboxSelectionComponent }, - { path: 'chkbox-selection-template', component: CustomCheckboxSelectionComponent }, - { path: 'multi-click-chkbox-selection', component: MultiClickCheckboxSelectionComponent }, - { path: 'templateref', component: TemplateRefTemplatesComponent }, - { path: 'inline', component: InlineTemplatesComponent }, - { path: 'flex', component: ColumnFlexComponent }, - { path: 'toggle', component: ColumnToggleComponent }, - { path: 'fixed', component: ColumnStandardComponent }, - { path: 'force', component: ColumnForceComponent }, - { path: 'pinning', component: ColumnPinningComponent }, - { path: 'reorder', component: ColumnReorderComponent }, - { path: 'simple-summary', component: SummaryRowSimpleComponent }, - { path: 'custom-template-summary', component: SummaryRowCustomTemplateComponent }, - { path: 'paging-summary', component: SummaryRowServerPagingComponent }, - { path: 'inline-html-summary', component: SummaryRowInlineHtmlComponent } + loadComponent: () => + import('./paging/paging-scrolling-novirtualization.component').then( + c => c.PagingScrollingNoVirtualizationComponent + ) + }, + { + path: 'server-scrolling', + loadComponent: () => + import('./paging/scrolling-server.component').then(c => c.ServerScrollingComponent) + }, + { + path: 'virtual-paging', + loadComponent: () => + import('./paging/paging-virtual.component').then(c => c.VirtualPagingComponent) + }, + { + path: 'client-sorting', + loadComponent: () => + import('./sorting/sorting-client.component').then(c => c.ClientSortingComponent) + }, + { + path: 'default-sorting', + loadComponent: () => + import('./sorting/sorting-default.component').then(c => c.DefaultSortingComponent) + }, + { + path: 'server-sorting', + loadComponent: () => + import('./sorting/sorting-server.component').then(c => c.ServerSortingComponent) + }, + { + path: 'comparator-sorting', + loadComponent: () => + import('./sorting/sorting-comparator.component').then(c => c.SortingComparatorComponent) + }, + { + path: 'cell-selection', + loadComponent: () => + import('./selection/selection-cell.component').then(c => c.CellSelectionComponent) + }, + { + path: 'single-selection', + loadComponent: () => + import('./selection/selection-single.component').then(c => c.SingleSelectionComponent) + }, + { + path: 'multi-selection', + loadComponent: () => + import('./selection/selection-multi.component').then(c => c.MultiSelectionComponent) + }, + { + path: 'multi-click-selection', + loadComponent: () => + import('./selection/selection-multi-click.component').then( + c => c.MultiClickSelectionComponent + ) + }, + { + path: 'multidisable-selection', + loadComponent: () => + import('./selection/selection-disabled.component').then(c => c.MultiDisableSelectionComponent) + }, + { + path: 'chkbox-selection', + loadComponent: () => + import('./selection/selection-chkbox.component').then(c => c.CheckboxSelectionComponent) + }, + { + path: 'chkbox-selection-template', + loadComponent: () => + import('./selection/selection-chkbox-template.component').then( + c => c.CustomCheckboxSelectionComponent + ) + }, + { + path: 'multi-click-chkbox-selection', + loadComponent: () => + import('./selection/selection-multi-click-chkbox.component').then( + c => c.MultiClickCheckboxSelectionComponent + ) + }, + { + path: 'templateref', + loadComponent: () => + import('./templates/template-obj.component').then(c => c.TemplateRefTemplatesComponent) + }, + { + path: 'inline', + loadComponent: () => + import('./templates/template-dom.component').then(c => c.InlineTemplatesComponent) + }, + { + path: 'flex', + loadComponent: () => import('./columns/column-flex.component').then(c => c.ColumnFlexComponent) + }, + { + path: 'toggle', + loadComponent: () => + import('./columns/column-toggle.component').then(c => c.ColumnToggleComponent) + }, + { + path: 'fixed', + loadComponent: () => + import('./columns/column-standard.component').then(c => c.ColumnStandardComponent) + }, + { + path: 'force', + loadComponent: () => + import('./columns/column-force.component').then(c => c.ColumnForceComponent) + }, + { + path: 'pinning', + loadComponent: () => import('./columns/pinning.component').then(c => c.ColumnPinningComponent) + }, + { + path: 'reorder', + loadComponent: () => + import('./columns/column-reorder.component').then(c => c.ColumnReorderComponent) + }, + { + path: 'simple-summary', + loadComponent: () => + import('./summary/summary-row-simple.component').then(c => c.SummaryRowSimpleComponent) + }, + { + path: 'custom-template-summary', + loadComponent: () => + import('./summary/summary-row-custom-template.component').then( + c => c.SummaryRowCustomTemplateComponent + ) + }, + { + path: 'paging-summary', + loadComponent: () => + import('./summary/summary-row-server-paging.component').then( + c => c.SummaryRowServerPagingComponent + ) + }, + { + path: 'inline-html-summary', + loadComponent: () => + import('./summary/summary-row-inline-html.component').then( + c => c.SummaryRowInlineHtmlComponent + ) + } ]; - -@NgModule({ - imports: [RouterModule.forRoot(routes, {})], - exports: [RouterModule] -}) -export class AppRoutingModule {} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 21b2a8eac..95e6f974f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,7 +1,7 @@ import { Component, signal, ViewEncapsulation } from '@angular/core'; import { HashLocationStrategy, Location, LocationStrategy } from '@angular/common'; import packageInfo from 'projects/swimlane/ngx-datatable/package.json'; -import { RouterOutlet } from '@angular/router'; +import { RouterLink, RouterOutlet } from '@angular/router'; @Component({ selector: 'app-root', @@ -20,7 +20,7 @@ import { RouterOutlet } from '@angular/router'; useClass: HashLocationStrategy } ], - standalone: false + imports: [RouterLink, RouterOutlet] }) export class AppComponent { version = packageInfo.version; diff --git a/src/app/app.module.ts b/src/app/app.module.ts deleted file mode 100644 index 77a382f89..000000000 --- a/src/app/app.module.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; - -import { NgxDatatableModule } from '../../projects/swimlane/ngx-datatable/src/public-api'; -import { AppComponent } from './app.component'; - -// -- Basic -import { BasicFixedComponent } from './basic/basic-fixed.component'; -import { BasicAutoComponent } from './basic/basic-auto.component'; -import { VirtualScrollComponent } from './basic/virtual.component'; -import { InlineEditComponent } from './basic/inline.component'; -import { HorzVertScrollingComponent } from './basic/scrolling.component'; -import { MultipleTablesComponent } from './basic/multiple.component'; -import { FullScreenComponent } from './basic/fullscreen.component'; -import { RowDetailsComponent } from './basic/row-detail.component'; -import { ResponsiveComponent } from './basic/responsive.component'; -import { FilterComponent } from './basic/filter.component'; -import { TabsDemoComponent } from './basic/tabs.component'; -import { LiveDataComponent } from './basic/live.component'; -import { RxDemoComponent } from './basic/rx.component'; -import { ContextMenuDemoComponent } from './basic/contextmenu.component'; -import { RowCssComponent } from './basic/css.component'; -import { DynamicHeightComponent } from './basic/dynamic-height.component'; -import { FooterDemoComponent } from './basic/footer.component'; -import { RowGroupingComponent } from './basic/row-grouping.component'; -import { BasicEmptyComponent } from './basic/empty.component'; -import { DisabledRowsComponent } from './basic/disabled-rows.component'; - -// -- Themes -import { BootstrapThemeComponent } from './basic/bootstrap.component'; -import { DarkThemeComponent } from './basic/dark-theme.component'; - -// -- Paging -import { ClientPagingComponent } from './paging/paging-client.component'; -import { ServerPagingComponent } from './paging/paging-server.component'; -import { ServerScrollingComponent } from './paging/scrolling-server.component'; -import { VirtualPagingComponent } from './paging/paging-virtual.component'; -import { PagingScrollingNoVirtualizationComponent } from './paging/paging-scrolling-novirtualization.component'; - -// -- Sorting -import { SortingComparatorComponent } from './sorting/sorting-comparator.component'; -import { DefaultSortingComponent } from './sorting/sorting-default.component'; -import { ServerSortingComponent } from './sorting/sorting-server.component'; -import { ClientSortingComponent } from './sorting/sorting-client.component'; - -// -- Templates -import { InlineTemplatesComponent } from './templates/template-dom.component'; -import { TemplateRefTemplatesComponent } from './templates/template-obj.component'; - -// -- Tree -import { FullScreenTreeComponent } from './tree/fullscreen.component'; -import { ClientTreeComponent } from './tree/client-tree.component'; - -// -- Selection -import { CellSelectionComponent } from './selection/selection-cell.component'; -import { MultiSelectionComponent } from './selection/selection-multi.component'; -import { SingleSelectionComponent } from './selection/selection-single.component'; -import { MultiDisableSelectionComponent } from './selection/selection-disabled.component'; -import { CheckboxSelectionComponent } from './selection/selection-chkbox.component'; -import { MultiClickSelectionComponent } from './selection/selection-multi-click.component'; -import { MultiClickCheckboxSelectionComponent } from './selection/selection-multi-click-chkbox.component'; -import { CustomCheckboxSelectionComponent } from './selection/selection-chkbox-template.component'; - -// -- Columns -import { ColumnToggleComponent } from './columns/column-toggle.component'; -import { ColumnStandardComponent } from './columns/column-standard.component'; -import { ColumnForceComponent } from './columns/column-force.component'; -import { ColumnFlexComponent } from './columns/column-flex.component'; -import { ColumnPinningComponent } from './columns/pinning.component'; -import { ColumnReorderComponent } from './columns/column-reorder.component'; - -// -- Summary row -import { SummaryRowSimpleComponent } from './summary/summary-row-simple.component'; -import { SummaryRowCustomTemplateComponent } from './summary/summary-row-custom-template.component'; -import { SummaryRowServerPagingComponent } from './summary/summary-row-server-paging.component'; -import { SummaryRowInlineHtmlComponent } from './summary/summary-row-inline-html.component'; -import { AppRoutingModule } from './app-routing.module'; -import { CommonModule } from '@angular/common'; -import { ScrollingDynamicallyComponent } from './basic/scrolling-dynamically.component'; -import { DragDropComponent } from './drag-drop/drag-drop.component'; - -import { DragDropModule } from '@angular/cdk/drag-drop'; -@NgModule({ - declarations: [ - AppComponent, - BasicAutoComponent, - BasicFixedComponent, - DragDropComponent, - FullScreenComponent, - FullScreenTreeComponent, - InlineEditComponent, - VirtualScrollComponent, - HorzVertScrollingComponent, - ScrollingDynamicallyComponent, - MultipleTablesComponent, - RowDetailsComponent, - ResponsiveComponent, - ClientPagingComponent, - ServerPagingComponent, - PagingScrollingNoVirtualizationComponent, - ServerScrollingComponent, - ClientSortingComponent, - DefaultSortingComponent, - ServerSortingComponent, - SortingComparatorComponent, - CellSelectionComponent, - MultiSelectionComponent, - MultiClickCheckboxSelectionComponent, - InlineTemplatesComponent, - TemplateRefTemplatesComponent, - ColumnFlexComponent, - ColumnToggleComponent, - ColumnStandardComponent, - ColumnForceComponent, - ColumnPinningComponent, - ColumnReorderComponent, - FilterComponent, - VirtualPagingComponent, - DarkThemeComponent, - TabsDemoComponent, - SingleSelectionComponent, - LiveDataComponent, - MultiDisableSelectionComponent, - RxDemoComponent, - ContextMenuDemoComponent, - CheckboxSelectionComponent, - CustomCheckboxSelectionComponent, - MultiClickSelectionComponent, - RowCssComponent, - DynamicHeightComponent, - FooterDemoComponent, - RowGroupingComponent, - BasicEmptyComponent, - BootstrapThemeComponent, - ClientTreeComponent, - SummaryRowSimpleComponent, - SummaryRowCustomTemplateComponent, - SummaryRowServerPagingComponent, - SummaryRowInlineHtmlComponent, - DisabledRowsComponent - ], - imports: [ - CommonModule, - BrowserModule, - AppRoutingModule, - DragDropModule, - NgxDatatableModule.forRoot({ - messages: { - emptyMessage: 'No data to display', // Message to show when array is presented, but contains no values - totalMessage: 'total', // Footer total message - selectedMessage: 'selected' // Footer selected message - } - }) - ], - bootstrap: [AppComponent] -}) -export class AppModule {} diff --git a/src/app/basic/basic-auto.component.ts b/src/app/basic/basic-auto.component.ts index 01867c247..8776590f4 100644 --- a/src/app/basic/basic-auto.component.ts +++ b/src/app/basic/basic-auto.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'basic-auto-demo', @@ -31,7 +36,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class BasicAutoComponent { rows: Employee[] = []; @@ -46,23 +51,14 @@ export class BasicAutoComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; setTimeout(() => { this.loadingIndicator = false; }, 1500); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/basic-fixed.component.ts b/src/app/basic/basic-fixed.component.ts index 558efd828..0cf60a7e2 100644 --- a/src/app/basic/basic-fixed.component.ts +++ b/src/app/basic/basic-fixed.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'basic-fixed-demo', @@ -29,7 +34,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class BasicFixedComponent { rows: Employee[] = []; @@ -37,20 +42,11 @@ export class BasicFixedComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/bootstrap.component.ts b/src/app/basic/bootstrap.component.ts index 7a6f41ac5..d57e5e91d 100644 --- a/src/app/basic/bootstrap.component.ts +++ b/src/app/basic/bootstrap.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'basic-bootstrap-theme-demo', @@ -34,7 +39,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class BootstrapThemeComponent { rows: Employee[] = []; @@ -49,8 +54,10 @@ export class BootstrapThemeComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; setTimeout(() => { this.loadingIndicator = false; @@ -58,17 +65,6 @@ export class BootstrapThemeComponent { }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - private summaryForGender(cells: string[]) { const males = cells.filter(cell => cell === 'male').length; const females = cells.filter(cell => cell === 'female').length; diff --git a/src/app/basic/contextmenu.component.ts b/src/app/basic/contextmenu.component.ts index 1c5a983ba..a5fabbfcc 100644 --- a/src/app/basic/contextmenu.component.ts +++ b/src/app/basic/contextmenu.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + ContextMenuEvent, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'contextmenu-demo', @@ -50,7 +56,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class ContextMenuDemoComponent { rows: Employee[] = []; @@ -63,13 +69,15 @@ export class ContextMenuDemoComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - onTableContextMenu(contextMenuEvent) { + onTableContextMenu(contextMenuEvent: ContextMenuEvent) { console.log(contextMenuEvent); this.rawEvent = contextMenuEvent.event; @@ -84,15 +92,4 @@ export class ContextMenuDemoComponent { contextMenuEvent.event.preventDefault(); contextMenuEvent.event.stopPropagation(); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/css.component.ts b/src/app/basic/css.component.ts index 24892883b..5736352d7 100644 --- a/src/app/basic/css.component.ts +++ b/src/app/basic/css.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'row-css-demo', @@ -36,7 +42,7 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class RowCssComponent { rows: FullEmployee[] = []; @@ -44,33 +50,23 @@ export class RowCssComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { - this.rows = data; + this.dataService.load('100k.json').subscribe(data => { + this.rows = data.splice(0, 50); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - const rows = JSON.parse(req.response); - cb(rows.splice(0, 50)); - }; - - req.send(); - } - - getRowClass(row) { + getRowClass(row: FullEmployee) { return { 'age-is-ten': row.age % 10 === 0 }; } - getCellClass({ row, column, value }): any { + getCellClass: TableColumn['cellClass'] = ({ row, column, value }) => { return { 'is-female': value === 'female' }; - } + }; } diff --git a/src/app/basic/dark-theme.component.ts b/src/app/basic/dark-theme.component.ts index c1fc6d892..c1713a900 100644 --- a/src/app/basic/dark-theme.component.ts +++ b/src/app/basic/dark-theme.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'basic-dark-theme-demo', @@ -33,7 +38,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class DarkThemeComponent { rows: Employee[] = []; @@ -48,8 +53,10 @@ export class DarkThemeComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; setTimeout(() => { this.loadingIndicator = false; @@ -57,17 +64,6 @@ export class DarkThemeComponent { }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - private summaryForGender(cells: string[]) { const males = cells.filter(cell => cell === 'male').length; const females = cells.filter(cell => cell === 'female').length; diff --git a/src/app/basic/disabled-rows.component.ts b/src/app/basic/disabled-rows.component.ts index ca7674b89..915c01d63 100644 --- a/src/app/basic/disabled-rows.component.ts +++ b/src/app/basic/disabled-rows.component.ts @@ -1,7 +1,14 @@ -import { Component } from '@angular/core'; -import { ColumnMode, SelectionType } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent, + DisableRowDirective, + SelectionType +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; -import { BehaviorSubject } from 'rxjs'; +import { DataService } from '../data.service'; @Component({ selector: 'disabled-rows-demo', @@ -34,7 +41,7 @@ import { BehaviorSubject } from 'rxjs'; let-value="value" let-rowIndex="rowIndex" let-row="row" - let-disable$="disable$" + let-disabled="disabled" ngx-datatable-cell-template > {{ value }} @@ -45,14 +52,14 @@ import { BehaviorSubject } from 'rxjs'; let-value="value" let-rowIndex="rowIndex" let-row="row" - let-disable$="disable$" + let-disabled="disabled" ngx-datatable-cell-template > +
+
- +
@@ -79,7 +86,12 @@ import { BehaviorSubject } from 'rxjs'; `, - standalone: false + imports: [ + DatatableComponent, + DataTableColumnDirective, + DataTableColumnCellDirective, + DisableRowDirective + ] }) export class DisabledRowsComponent { rows: (FullEmployee & { isDisabled?: boolean })[] = []; @@ -87,41 +99,33 @@ export class DisabledRowsComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - isRowDisabled(row: FullEmployee & { isDisabled: boolean }) { + isRowDisabled(row: FullEmployee & { isDisabled?: boolean }) { return !(!row.isDisabled && row.age < 40); } - disableRow(rowIndex: number, disableRow$: BehaviorSubject) { + disableRow(rowIndex: number) { this.rows[rowIndex].isDisabled = true; this.rows = [...this.rows]; - disableRow$.next(true); } - updateValue(event, cell, rowIndex: number, disableRow$: BehaviorSubject) { - this.rows[rowIndex][cell] = event.target.value; + updateValue(event: Event, cell: 'gender' | 'age', rowIndex: number) { + const target = event.target as HTMLInputElement; this.rows = [...this.rows]; - if (disableRow$ && cell === 'age' && this.rows[rowIndex][cell] > 40) { - disableRow$.next(true); + if (cell === 'age' && this.rows[rowIndex][cell] > 40) { + this.rows[rowIndex].isDisabled = true; + this.rows[rowIndex].age = target.valueAsNumber; } - if (disableRow$ && cell === 'gender' && this.rows[rowIndex][cell] === 'male') { - disableRow$.next(true); + if (cell === 'gender' && this.rows[rowIndex][cell] === 'male') { + this.rows[rowIndex].isDisabled = true; + this.rows[rowIndex].gender = target.value; } } } diff --git a/src/app/basic/dynamic-height.component.ts b/src/app/basic/dynamic-height.component.ts index 20a4c7921..b2d347a2e 100644 --- a/src/app/basic/dynamic-height.component.ts +++ b/src/app/basic/dynamic-height.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'dynamic-height-demo', @@ -32,39 +37,26 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class DynamicHeightComponent { - rows: FullEmployee[] = []; + rows: (FullEmployee & { height: number })[] = []; expanded = {}; timeout: any; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { - this.rows = data; + this.dataService.load('100k.json').subscribe(data => { + this.rows = data + .splice(0, 100) + .map(d => ({ ...d, height: Math.floor(Math.random() * 80) + 50 })); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - const rows = JSON.parse(req.response).splice(0, 100); - - for (const row of rows) { - row.height = Math.floor(Math.random() * 80) + 50; - } - - cb(rows); - }; - - req.send(); - } - - getRowHeight(row) { + getRowHeight(row: FullEmployee & { height: number }) { console.log('ROW', row); if (!row) { return 50; diff --git a/src/app/basic/empty.component.ts b/src/app/basic/empty.component.ts index d49541578..c5bf29d8d 100644 --- a/src/app/basic/empty.component.ts +++ b/src/app/basic/empty.component.ts @@ -1,5 +1,9 @@ import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; @Component({ selector: 'empty-demo', @@ -30,7 +34,7 @@ import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/pub `, - standalone: false + imports: [DatatableComponent] }) export class BasicEmptyComponent { columns: TableColumn[] = [ diff --git a/src/app/basic/filter.component.ts b/src/app/basic/filter.component.ts index 6f57416bf..d27f9009c 100644 --- a/src/app/basic/filter.component.ts +++ b/src/app/basic/filter.component.ts @@ -1,7 +1,11 @@ -import { Component, ViewChild } from '@angular/core'; -import { DatatableComponent } from 'projects/swimlane/ngx-datatable/src/lib/components/datatable.component'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, ViewChild } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'filter-demo', @@ -38,7 +42,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class FilterComponent { rows: Employee[] = []; @@ -46,12 +50,14 @@ export class FilterComponent { temp: Employee[] = []; columns: TableColumn[] = [{ prop: 'name' }, { name: 'Company' }, { name: 'Gender' }]; - @ViewChild(DatatableComponent) table: DatatableComponent; + @ViewChild(DatatableComponent) table!: DatatableComponent; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { // cache our list this.temp = [...data]; @@ -60,19 +66,8 @@ export class FilterComponent { }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - updateFilter(event) { - const val = event.target.value.toLowerCase(); + updateFilter(event: KeyboardEvent) { + const val = (event.target as HTMLInputElement).value.toLowerCase(); // filter our data and update the rows this.rows = this.temp.filter(function (d) { diff --git a/src/app/basic/footer.component.ts b/src/app/basic/footer.component.ts index 38169a39b..5308e4c24 100644 --- a/src/app/basic/footer.component.ts +++ b/src/app/basic/footer.component.ts @@ -1,6 +1,13 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + DatatableFooterDirective, + DataTableFooterTemplateDirective, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'footer-demo', @@ -48,7 +55,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DatatableFooterDirective, DataTableFooterTemplateDirective] }) export class FooterDemoComponent { rows: Employee[] = []; @@ -57,20 +64,11 @@ export class FooterDemoComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/fullscreen.component.ts b/src/app/basic/fullscreen.component.ts index 0c4e86959..b24093ba6 100644 --- a/src/app/basic/fullscreen.component.ts +++ b/src/app/basic/fullscreen.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'full-screen-demo', @@ -41,27 +46,18 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class FullScreenComponent { rows: FullEmployee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/inline.component.ts b/src/app/basic/inline.component.ts index f07053c82..d4086bcdf 100644 --- a/src/app/basic/inline.component.ts +++ b/src/app/basic/inline.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'inline-edit-demo', @@ -74,35 +80,26 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class InlineEditComponent { - editing = {}; + editing: Record = {}; rows: Employee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - updateValue(event, cell, rowIndex: number) { + updateValue(event: Event, cell: 'name' | 'gender', rowIndex: number) { console.log('inline editing rowIndex', rowIndex); this.editing[rowIndex + '-' + cell] = false; - this.rows[rowIndex][cell] = event.target.value; + this.rows[rowIndex][cell] = (event.target as HTMLInputElement).value; this.rows = [...this.rows]; console.log('UPDATED!', this.rows[rowIndex][cell]); } diff --git a/src/app/basic/live.component.ts b/src/app/basic/live.component.ts index e7e84c5de..0bb8abe7e 100644 --- a/src/app/basic/live.component.ts +++ b/src/app/basic/live.component.ts @@ -1,6 +1,11 @@ -import { Component, ViewChild } from '@angular/core'; -import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, ViewChild } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'live-data-demo', @@ -40,25 +45,27 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class LiveDataComponent { - @ViewChild('mydatatable') mydatatable: DatatableComponent; + @ViewChild('mydatatable') mydatatable!: DatatableComponent; count = 50; rows: (Employee & { updated: string })[] = []; active = true; temp: (Employee & { updated: string })[] = []; - cols = ['name', 'gender', 'company']; + cols = ['name', 'gender', 'company'] as const; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { - this.rows = data.map(d => { - d.updated = Date.now().toString(); - return d; - }); + this.dataService.load('company.json').subscribe(data => { + this.rows = data.map(d => ({ + ...d, + updated: Date.now().toString() + })); this.temp = [...this.rows]; }); @@ -109,15 +116,4 @@ export class LiveDataComponent { // this.mydatatable.update(); this.start(); } - - fetch(cb: any): void { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/multiple.component.ts b/src/app/basic/multiple.component.ts index 90851f82e..a3cabcfca 100644 --- a/src/app/basic/multiple.component.ts +++ b/src/app/basic/multiple.component.ts @@ -1,5 +1,9 @@ import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; @Component({ selector: 'multiple-tables-demo', @@ -38,7 +42,7 @@ import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/pub `, - standalone: false + imports: [DatatableComponent] }) export class MultipleTablesComponent { columns1: TableColumn[] = [{ prop: 'name' }, { name: 'Gender' }, { name: 'Company' }]; diff --git a/src/app/basic/responsive.component.ts b/src/app/basic/responsive.component.ts index 11c47bb7e..98b0c31b7 100644 --- a/src/app/basic/responsive.component.ts +++ b/src/app/basic/responsive.component.ts @@ -1,6 +1,17 @@ -import { Component, ViewChild, ViewEncapsulation } from '@angular/core'; -import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DataTableColumnHeaderDirective, + DatatableComponent, + DatatableRowDetailDirective, + DatatableRowDetailTemplateDirective, + DetailToggleEvents, + PageEvent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'responsive-demo', @@ -112,11 +123,19 @@ import { FullEmployee } from '../data.model'; columns will be hidden and will appear in the row detail view. `, + // eslint-disable-next-line @angular-eslint/use-component-view-encapsulation encapsulation: ViewEncapsulation.None, - standalone: false + imports: [ + DatatableComponent, + DatatableRowDetailDirective, + DatatableRowDetailTemplateDirective, + DataTableColumnDirective, + DataTableColumnCellDirective, + DataTableColumnHeaderDirective + ] }) export class ResponsiveComponent { - @ViewChild('myTable') table: DatatableComponent; + @ViewChild('myTable') table!: DatatableComponent; rows: FullEmployee[] = []; expanded: any = {}; @@ -124,36 +143,27 @@ export class ResponsiveComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - onPage(event) { + onPage(event: PageEvent) { clearTimeout(this.timeout); this.timeout = setTimeout(() => { console.log('paged!', event); }, 100); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - toggleExpandRow(row) { + toggleExpandRow(row: FullEmployee) { console.log('Toggled Expand Row!', row); - this.table.rowDetail.toggleExpandRow(row); + this.table.rowDetail!.toggleExpandRow(row); } - onDetailToggle(event) { + onDetailToggle(event: DetailToggleEvents) { console.log('Detail Toggled', event); } } diff --git a/src/app/basic/row-detail.component.ts b/src/app/basic/row-detail.component.ts index 9f07f29b1..d231b5be3 100644 --- a/src/app/basic/row-detail.component.ts +++ b/src/app/basic/row-detail.component.ts @@ -1,6 +1,16 @@ -import { Component, ViewChild, ViewEncapsulation } from '@angular/core'; -import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent, + DatatableRowDetailDirective, + DatatableRowDetailTemplateDirective, + DetailToggleEvents, + PageEvent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'row-details-demo', @@ -17,9 +27,9 @@ import { FullEmployee } from '../data.model'; - Expand All + Expand All | - Collapse All + Collapse All `, + // eslint-disable-next-line @angular-eslint/use-component-view-encapsulation encapsulation: ViewEncapsulation.None, - standalone: false + imports: [ + DatatableComponent, + DatatableRowDetailDirective, + DatatableRowDetailTemplateDirective, + DataTableColumnDirective, + DataTableColumnCellDirective + ] }) export class RowDetailsComponent { - @ViewChild('myTable') table: DatatableComponent; + @ViewChild('myTable') table!: DatatableComponent; rows: FullEmployee[] = []; expanded: any = {}; @@ -98,36 +115,27 @@ export class RowDetailsComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - onPage(event) { + onPage(event: PageEvent) { clearTimeout(this.timeout); this.timeout = setTimeout(() => { console.log('paged!', event); }, 100); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - toggleExpandRow(row) { + toggleExpandRow(row: FullEmployee) { console.log('Toggled Expand Row!', row); - this.table.rowDetail.toggleExpandRow(row); + this.table.rowDetail!.toggleExpandRow(row); } - onDetailToggle(event) { + onDetailToggle(event: DetailToggleEvents) { console.log('Detail Toggled', event); } } diff --git a/src/app/basic/row-grouping.component.ts b/src/app/basic/row-grouping.component.ts index db4204b23..80c78c5ff 100644 --- a/src/app/basic/row-grouping.component.ts +++ b/src/app/basic/row-grouping.component.ts @@ -1,12 +1,17 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, inject, ViewChild } from '@angular/core'; import { GroupedEmployee } from '../data.model'; import { ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, DatatableComponent, + DatatableGroupHeaderDirective, + DatatableGroupHeaderTemplateDirective, Group, GroupToggleEvents, SelectionType } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { DataService } from '../data.service'; @Component({ selector: 'row-grouping-demo', @@ -87,7 +92,8 @@ import { name="{{ rowIndex }}" value="0" class="expectedpayment" - (change)="checkGroup($event, row, rowIndex, group)" + [attr.aria-label]="'ex pay1' + rowIndex" + (change)="checkGroup($event, row, rowIndex, group!)" [checked]="row.exppayyes === 1" /> @@ -98,7 +104,8 @@ import { name="{{ rowIndex }}" value="1" class="expectedpayment2" - (change)="checkGroup($event, row, rowIndex, group)" + [attr.aria-label]="'ex pay2' + rowIndex" + (change)="checkGroup($event, row, rowIndex, group!)" [checked]="row.exppayno === 1" /> @@ -109,7 +116,8 @@ import { name="{{ rowIndex }}" value="2" class="expectedpayment3" - (change)="checkGroup($event, row, rowIndex, group)" + [attr.aria-label]="'ex pay3' + rowIndex" + (change)="checkGroup($event, row, rowIndex, group!)" [checked]="row.exppaypending === 1" /> @@ -145,10 +153,16 @@ import { `, - standalone: false + imports: [ + DatatableComponent, + DatatableGroupHeaderDirective, + DatatableGroupHeaderTemplateDirective, + DataTableColumnDirective, + DataTableColumnCellDirective + ] }) export class RowGroupingComponent { - @ViewChild('myTable') table: DatatableComponent; + @ViewChild('myTable') table!: DatatableComponent; editing: Record = {}; rows: GroupedEmployee[] = []; @@ -156,39 +170,31 @@ export class RowGroupingComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('forRowGrouping.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/forRowGrouping.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - checkGroup(event, row, rowIndex, group) { + checkGroup(event: Event, row: GroupedEmployee, rowIndex: number, group: GroupedEmployee[]) { let groupStatus = 'Pending'; let expectedPaymentDealtWith = true; + const target = event.target as HTMLInputElement; row.exppayyes = 0; row.exppayno = 0; row.exppaypending = 0; - if (event.target.checked) { - if (event.target.value === '0') { + if (target.checked) { + if (target.value === '0') { // expected payment yes selected row.exppayyes = 1; - } else if (event.target.value === '1') { + } else if (target.value === '1') { // expected payment yes selected row.exppayno = 1; - } else if (event.target.value === '2') { + } else if (target.value === '2') { // expected payment yes selected row.exppaypending = 1; } @@ -197,15 +203,15 @@ export class RowGroupingComponent { if (group.length === 2) { // There are only 2 lines in a group if ( - ['Calculated', 'Funder'].indexOf(group[0].source) > -1 && - ['Calculated', 'Funder'].indexOf(group[1].source) > -1 + ['Calculated', 'Funder'].indexOf(group[0].source!) > -1 && + ['Calculated', 'Funder'].indexOf(group[1].source!) > -1 ) { // Sources are funder and calculated if (group[0].startdate === group[1].startdate && group[0].enddate === group[1].enddate) { // Start dates and end dates match for (let index = 0; index < group.length; index++) { if (group[index].source !== row.source) { - if (event.target.value === '0') { + if (target.value === '0') { // expected payment yes selected group[index].exppayyes = 0; group[index].exppaypending = 0; @@ -280,16 +286,15 @@ export class RowGroupingComponent { group[0].groupstatus = groupStatus; } - updateValue(event, cell, rowIndex) { - const index = rowIndex.split('-')[1]; + updateValue(event: Event, cell: 'comment', rowIndex: number) { this.editing[rowIndex + '-' + cell] = false; - this.rows[index][cell] = event.target.value; + this.rows[rowIndex][cell] = (event.target as HTMLInputElement).value; this.rows = [...this.rows]; } toggleExpandGroup(group: Group) { console.log('Toggled Expand Group!', group); - this.table.groupHeader.toggleExpandGroup(group); + this.table.groupHeader!.toggleExpandGroup(group); } onDetailToggle(event: GroupToggleEvents) { diff --git a/src/app/basic/rx.component.ts b/src/app/basic/rx.component.ts index 1367046d8..47bcbe5b8 100644 --- a/src/app/basic/rx.component.ts +++ b/src/app/basic/rx.component.ts @@ -1,7 +1,13 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { Observable } from 'rxjs'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; +import { AsyncPipe } from '@angular/common'; @Component({ selector: 'rx-demo', @@ -30,7 +36,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, AsyncPipe] }) export class RxDemoComponent { rows: Observable; @@ -39,27 +45,9 @@ export class RxDemoComponent { ColumnMode = ColumnMode; - constructor() { - this.rows = Observable.create(subscriber => { - this.fetch(data => { - subscriber.next(data.splice(0, 15)); - subscriber.next(data.splice(15, 30)); - subscriber.complete(); - }); - }); - - // Rx.DOM.ajax({ url: '/products', responseType: 'json'}).subscribe() - // this.rows = Observable.from(rows); - } + private dataService = inject(DataService); - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); + constructor() { + this.rows = this.dataService.load('company.json'); } } diff --git a/src/app/basic/scrolling-dynamically.component.ts b/src/app/basic/scrolling-dynamically.component.ts index 06564a1eb..1bcd35961 100644 --- a/src/app/basic/scrolling-dynamically.component.ts +++ b/src/app/basic/scrolling-dynamically.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'scrolling-dynamically-demo', @@ -77,7 +83,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class ScrollingDynamicallyComponent { editing: Record = {}; @@ -85,27 +91,18 @@ export class ScrollingDynamicallyComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.slice(0, 5); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - updateValue(event, cell, rowIndex) { + updateValue(event: Event, cell: 'gender' | 'name', rowIndex: number) { console.log('inline editing rowIndex', rowIndex); this.editing[rowIndex + '-' + cell] = false; - this.rows[rowIndex][cell] = event.target.value; + this.rows[rowIndex][cell] = (event.target as HTMLInputElement | HTMLSelectElement).value; this.rows = [...this.rows]; console.log('UPDATED!', this.rows[rowIndex][cell]); } diff --git a/src/app/basic/scrolling.component.ts b/src/app/basic/scrolling.component.ts index a0288881b..5e02268bf 100644 --- a/src/app/basic/scrolling.component.ts +++ b/src/app/basic/scrolling.component.ts @@ -1,5 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; +import { + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; @Component({ selector: 'horz-vert-scrolling-demo', @@ -38,25 +43,16 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class HorzVertScrollingComponent { rows: FullEmployee[] = []; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/tabs.component.ts b/src/app/basic/tabs.component.ts index 3d29091bc..6808e8f5a 100644 --- a/src/app/basic/tabs.component.ts +++ b/src/app/basic/tabs.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'tabs-demo', @@ -67,7 +72,7 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class TabsDemoComponent { rows: FullEmployee[] = []; @@ -78,20 +83,11 @@ export class TabsDemoComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/basic/virtual.component.ts b/src/app/basic/virtual.component.ts index 729b1300a..497b066f9 100644 --- a/src/app/basic/virtual.component.ts +++ b/src/app/basic/virtual.component.ts @@ -1,6 +1,13 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent, + PageEvent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'virtual-scroll-demo', @@ -41,46 +48,31 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class VirtualScrollComponent { - rows: FullEmployee[]; + rows: (FullEmployee & { height: number })[] = []; expanded = {}; timeout: any; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { - this.rows = data; + this.dataService.load('100k.json').subscribe(data => { + this.rows = data.map(row => ({ ...row, height: Math.floor(Math.random() * 80) + 50 })); }); } - onPage(event) { + onPage(event: PageEvent) { clearTimeout(this.timeout); this.timeout = setTimeout(() => { console.log('paged!', event); }, 100); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - const rows = JSON.parse(req.response); - - for (const row of rows) { - row.height = Math.floor(Math.random() * 80) + 50; - } - - cb(rows); - }; - - req.send(); - } - - getRowHeight(row) { + getRowHeight(row: FullEmployee & { height: number }) { return row.height; } } diff --git a/src/app/columns/column-flex.component.ts b/src/app/columns/column-flex.component.ts index 98b9befb3..f065fecee 100644 --- a/src/app/columns/column-flex.component.ts +++ b/src/app/columns/column-flex.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'column-flex-demo', @@ -43,27 +49,18 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class ColumnFlexComponent { rows: Employee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/columns/column-force.component.ts b/src/app/columns/column-force.component.ts index c96f48cb1..003be6b04 100644 --- a/src/app/columns/column-force.component.ts +++ b/src/app/columns/column-force.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'column-force-demo', @@ -43,27 +49,18 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class ColumnForceComponent { rows: Employee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/columns/column-reorder.component.ts b/src/app/columns/column-reorder.component.ts index 417bc4044..d601a12b5 100644 --- a/src/app/columns/column-reorder.component.ts +++ b/src/app/columns/column-reorder.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; +import { NgClass } from '@angular/common'; @Component({ selector: 'column-reorder-demo', @@ -55,7 +61,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, NgClass] }) export class ColumnReorderComponent { rows: Employee[] = []; @@ -71,23 +77,14 @@ export class ColumnReorderComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; setTimeout(() => { this.loadingIndicator = false; }, 1500); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/columns/column-standard.component.ts b/src/app/columns/column-standard.component.ts index de5afa120..f030247f9 100644 --- a/src/app/columns/column-standard.component.ts +++ b/src/app/columns/column-standard.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'column-standard-demo', @@ -43,27 +49,18 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class ColumnStandardComponent { rows: Employee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/columns/column-toggle.component.ts b/src/app/columns/column-toggle.component.ts index 9ddbb4e40..e93c762d2 100644 --- a/src/app/columns/column-toggle.component.ts +++ b/src/app/columns/column-toggle.component.ts @@ -1,5 +1,10 @@ import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; @Component({ @@ -49,7 +54,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class ColumnToggleComponent { rows: Employee[] = [ diff --git a/src/app/columns/pinning.component.ts b/src/app/columns/pinning.component.ts index 7320c40c7..87593fd91 100644 --- a/src/app/columns/pinning.component.ts +++ b/src/app/columns/pinning.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'column-pinning-demo', @@ -36,27 +41,18 @@ import { FullEmployee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class ColumnPinningComponent { rows: FullEmployee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('100k.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/data.model.ts b/src/app/data.model.ts index 0cb122dad..3d71b1b9c 100644 --- a/src/app/data.model.ts +++ b/src/app/data.model.ts @@ -1,3 +1,5 @@ +import { TreeStatus } from '../../projects/swimlane/ngx-datatable/src/lib/types/public.types'; + export interface Employee { name: string; gender: string; @@ -5,6 +7,10 @@ export interface Employee { age?: number; } +export interface TreeEmployee extends Employee { + treeStatus: TreeStatus; +} + export interface FullEmployee { id: number; name: string; @@ -27,4 +33,7 @@ export interface GroupedEmployee { age: number; comment?: string; groupcomment?: string; + startdate?: string; + enddate?: string; + groupstatus?: string; } diff --git a/src/app/data.service.ts b/src/app/data.service.ts new file mode 100644 index 000000000..f3428644b --- /dev/null +++ b/src/app/data.service.ts @@ -0,0 +1,19 @@ +import { inject, Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { Employee, FullEmployee, GroupedEmployee, TreeEmployee } from './data.model'; + +@Injectable({ + providedIn: 'root' +}) +export class DataService { + private client = inject(HttpClient); + + load(data: 'forRowGrouping.json'): Observable; + load(data: 'company_tree.json'): Observable; + load(data: 'company.json'): Observable; + load(data: '100k.json'): Observable; + load(data: string): Observable { + return this.client.get(`assets/data/${data}`); + } +} diff --git a/src/app/drag-drop/drag-drop.component.ts b/src/app/drag-drop/drag-drop.component.ts index d791ced9b..0ce0ef42c 100644 --- a/src/app/drag-drop/drag-drop.component.ts +++ b/src/app/drag-drop/drag-drop.component.ts @@ -1,6 +1,13 @@ -import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + DatatableRowDefComponent, + DatatableRowDefDirective +} from 'projects/swimlane/ngx-datatable/src/public-api'; +import { DataService } from '../data.service'; +import { Employee } from '../data.model'; @Component({ selector: 'drag-drop-demo', @@ -36,10 +43,16 @@ import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; `, - standalone: false + imports: [ + DatatableComponent, + CdkDropList, + DatatableRowDefDirective, + DatatableRowDefComponent, + CdkDrag + ] }) export class DragDropComponent { - rows = []; + rows: Employee[] = []; loadingIndicator = true; reorderable = true; @@ -51,8 +64,10 @@ export class DragDropComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; setTimeout(() => { this.loadingIndicator = false; @@ -60,17 +75,6 @@ export class DragDropComponent { }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - drop(event: CdkDragDrop) { moveItemInArray(this.rows, event.previousIndex, event.currentIndex); this.rows = [...this.rows]; diff --git a/src/app/paging/paging-client.component.ts b/src/app/paging/paging-client.component.ts index b9d97d8aa..4b3d893e6 100644 --- a/src/app/paging/paging-client.component.ts +++ b/src/app/paging/paging-client.component.ts @@ -1,6 +1,7 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'client-paging-demo', @@ -30,27 +31,18 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class ClientPagingComponent { rows: Employee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/paging/paging-scrolling-novirtualization.component.ts b/src/app/paging/paging-scrolling-novirtualization.component.ts index 8f33e59ea..3ee95e6a3 100644 --- a/src/app/paging/paging-scrolling-novirtualization.component.ts +++ b/src/app/paging/paging-scrolling-novirtualization.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { MockServerResultsService } from './mock-server-results-service'; import { Page } from './model/page'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; @Component({ @@ -35,12 +35,12 @@ import { Employee } from '../data.model'; [offset]="page.pageNumber" [limit]="page.size" [ghostLoadingIndicator]="isLoading > 0" - (page)="setPage($event)" + (page)="setPage($event.offset)" > `, - standalone: false + imports: [DatatableComponent] }) export class PagingScrollingNoVirtualizationComponent implements OnInit { page: Page = { @@ -57,15 +57,15 @@ export class PagingScrollingNoVirtualizationComponent implements OnInit { constructor(private serverResultsService: MockServerResultsService) {} ngOnInit() { - this.setPage({ offset: 0 }); + this.setPage(0); } /** * Populate the table with new data based on the page number * @param page The page to select */ - setPage(pageInfo) { - this.page.pageNumber = pageInfo.offset; + setPage(page: number) { + this.page.pageNumber = page; this.isLoading++; this.serverResultsService.getResults(this.page).subscribe(pagedData => { this.isLoading--; diff --git a/src/app/paging/paging-server.component.ts b/src/app/paging/paging-server.component.ts index 1d4a18175..2c2249bcf 100644 --- a/src/app/paging/paging-server.component.ts +++ b/src/app/paging/paging-server.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { MockServerResultsService } from './mock-server-results-service'; import { Page } from './model/page'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; @Component({ @@ -32,12 +32,12 @@ import { Employee } from '../data.model'; [count]="page.totalElements" [offset]="page.pageNumber" [limit]="page.size" - (page)="setPage($event)" + (page)="setPage($event.offset)" > `, - standalone: false + imports: [DatatableComponent] }) export class ServerPagingComponent implements OnInit { page: Page = { @@ -53,15 +53,15 @@ export class ServerPagingComponent implements OnInit { constructor(private serverResultsService: MockServerResultsService) {} ngOnInit() { - this.setPage({ offset: 0 }); + this.setPage(0); } /** * Populate the table with new data based on the page number * @param page The page to select */ - setPage(pageInfo) { - this.page.pageNumber = pageInfo.offset; + setPage(page: number) { + this.page.pageNumber = page; this.serverResultsService.getResults(this.page).subscribe(pagedData => { this.page = pagedData.page; this.rows = pagedData.data; diff --git a/src/app/paging/paging-virtual.component.ts b/src/app/paging/paging-virtual.component.ts index e91519d74..b66a924e2 100644 --- a/src/app/paging/paging-virtual.component.ts +++ b/src/app/paging/paging-virtual.component.ts @@ -1,16 +1,13 @@ import { Component } from '@angular/core'; import { MockServerResultsService } from './mock-server-results-service'; import { Page } from './model/page'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { + ColumnMode, + DatatableComponent, + PageEvent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; -interface PageInfo { - offset: number; - pageSize: number; - limit: number; - count: number; -} - @Component({ selector: 'virtual-paging-demo', providers: [MockServerResultsService], @@ -53,12 +50,12 @@ interface PageInfo { `, styleUrls: ['./paging-virtual.component.scss'], - standalone: false + imports: [DatatableComponent] }) export class VirtualPagingComponent { - totalElements: number; + totalElements = 0; pageNumber: number; - rows: Employee[]; + rows?: Employee[]; cache: Record = {}; cachePageSize = 0; @@ -70,7 +67,7 @@ export class VirtualPagingComponent { this.pageNumber = 0; } - setPage(pageInfo: PageInfo) { + setPage(pageInfo: PageEvent) { // Current page number is determined by last call to setPage // This is the page the UI is currently displaying // The current page is based on the UI pagesize and scroll position diff --git a/src/app/paging/scrolling-server.component.ts b/src/app/paging/scrolling-server.component.ts index 9266474e9..f49a29ab9 100644 --- a/src/app/paging/scrolling-server.component.ts +++ b/src/app/paging/scrolling-server.component.ts @@ -3,12 +3,12 @@ import { Observable, of } from 'rxjs'; import { delay, map } from 'rxjs/operators'; import data from 'src/assets/data/company.json'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { ColumnMode, DatatableComponent } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; const companyData = data as any[]; -class PagedData { +interface PagedData { data: T[]; } @@ -56,7 +56,7 @@ export class MockServerResultsService { `, styleUrls: ['./scrolling-server.component.css'], - standalone: false + imports: [DatatableComponent] }) export class ServerScrollingComponent { readonly headerHeight = 50; @@ -64,7 +64,7 @@ export class ServerScrollingComponent { readonly pageLimit = 10; rows: Employee[] = []; - isLoading: boolean; + isLoading?: boolean; ColumnMode = ColumnMode; diff --git a/src/app/selection/selection-cell.component.ts b/src/app/selection/selection-cell.component.ts index 65868d68f..37aa991a2 100644 --- a/src/app/selection/selection-cell.component.ts +++ b/src/app/selection/selection-cell.component.ts @@ -1,10 +1,14 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { + ActivateEvent, ColumnMode, + DatatableComponent, + SelectEvent, SelectionType, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'cell-selection-demo', @@ -37,7 +41,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class CellSelectionComponent { rows: Employee[] = []; @@ -47,28 +51,19 @@ export class CellSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect(event) { + onSelect(event: SelectEvent) { console.log('Event: select', event, this.selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Event: activate', event); } } diff --git a/src/app/selection/selection-chkbox-template.component.ts b/src/app/selection/selection-chkbox-template.component.ts index 2fbcf8fd6..4a156dd89 100644 --- a/src/app/selection/selection-chkbox-template.component.ts +++ b/src/app/selection/selection-chkbox-template.component.ts @@ -1,6 +1,16 @@ -import { Component } from '@angular/core'; -import { ColumnMode, SelectionType } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ActivateEvent, + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DataTableColumnHeaderDirective, + DatatableComponent, + SelectEvent, + SelectionType +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'chkbox-selection-template-demo', @@ -68,21 +78,26 @@ import { Employee } from '../data.model';

- Selections ({{ selected?.length }}) + Selections ({{ selected.length }})

    @for (sel of selected; track sel) {
  • {{ sel.name }}
  • - } @if (!selected?.length) { + } @if (!selected.length) {
  • No Selections
  • }
`, - standalone: false + imports: [ + DatatableComponent, + DataTableColumnDirective, + DataTableColumnHeaderDirective, + DataTableColumnCellDirective + ] }) export class CustomCheckboxSelectionComponent { rows: Employee[] = []; @@ -91,31 +106,22 @@ export class CustomCheckboxSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); this.selected.splice(0, this.selected.length); this.selected.push(...selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } diff --git a/src/app/selection/selection-chkbox.component.ts b/src/app/selection/selection-chkbox.component.ts index 5d367ce7f..3ca452508 100644 --- a/src/app/selection/selection-chkbox.component.ts +++ b/src/app/selection/selection-chkbox.component.ts @@ -1,6 +1,14 @@ -import { Component } from '@angular/core'; -import { ColumnMode, SelectionType } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ActivateEvent, + ColumnMode, + DataTableColumnDirective, + DatatableComponent, + SelectEvent, + SelectionType +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'chkbox-selection-demo', @@ -57,21 +65,21 @@ import { Employee } from '../data.model';

- Selections ({{ selected?.length }}) + Selections ({{ selected.length }})

    @for (sel of selected; track sel) {
  • {{ sel.name }}
  • - } @if (!selected?.length) { + } @if (!selected.length) {
  • No Selections
  • }
`, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class CheckboxSelectionComponent { rows: Employee[] = []; @@ -80,31 +88,22 @@ export class CheckboxSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); this.selected.splice(0, this.selected.length); this.selected.push(...selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } @@ -120,7 +119,7 @@ export class CheckboxSelectionComponent { this.selected = []; } - displayCheck(row) { + displayCheck(row: Employee) { return row.name !== 'Ethel Price'; } } diff --git a/src/app/selection/selection-disabled.component.ts b/src/app/selection/selection-disabled.component.ts index cfee82f66..c1a101887 100644 --- a/src/app/selection/selection-disabled.component.ts +++ b/src/app/selection/selection-disabled.component.ts @@ -1,10 +1,14 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { + ActivateEvent, ColumnMode, + DatatableComponent, + SelectEvent, SelectionType, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'multidisable-selection-demo', @@ -54,7 +58,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class MultiDisableSelectionComponent { rows: Employee[] = []; @@ -66,35 +70,26 @@ export class MultiDisableSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); this.selected.splice(0, this.selected.length); this.selected.push(...selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } - checkSelectable(event) { + checkSelectable(event: Employee) { console.log('Checking if selectable', event); return event.name !== 'Ethel Price'; } diff --git a/src/app/selection/selection-multi-click-chkbox.component.ts b/src/app/selection/selection-multi-click-chkbox.component.ts index 8caaaf5de..6aa15ba31 100644 --- a/src/app/selection/selection-multi-click-chkbox.component.ts +++ b/src/app/selection/selection-multi-click-chkbox.component.ts @@ -1,6 +1,14 @@ -import { Component } from '@angular/core'; -import { ColumnMode, SelectionType } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ActivateEvent, + ColumnMode, + DataTableColumnDirective, + DatatableComponent, + SelectEvent, + SelectionType +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'multi-click-chkbox-selection-demo', @@ -58,21 +66,21 @@ import { Employee } from '../data.model';

- Selections ({{ selected?.length }}) + Selections ({{ selected.length }})

    @for (sel of selected; track sel) {
  • {{ sel.name }}
  • - } @if (!selected?.length) { + } @if (!selected.length) {
  • No Selections
  • }
`, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class MultiClickCheckboxSelectionComponent { rows: Employee[] = []; @@ -81,31 +89,22 @@ export class MultiClickCheckboxSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); this.selected.splice(0, this.selected.length); this.selected.push(...selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } @@ -121,7 +120,7 @@ export class MultiClickCheckboxSelectionComponent { this.selected = []; } - allowSelection(row) { + allowSelection(row: Employee) { return row.name !== 'Beryl Rice'; } } diff --git a/src/app/selection/selection-multi-click.component.ts b/src/app/selection/selection-multi-click.component.ts index 83b5d6953..de34b817f 100644 --- a/src/app/selection/selection-multi-click.component.ts +++ b/src/app/selection/selection-multi-click.component.ts @@ -1,10 +1,14 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { + ActivateEvent, ColumnMode, + DatatableComponent, + SelectEvent, SelectionType, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'multi-click-selection-demo', @@ -57,7 +61,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class MultiClickSelectionComponent { rows: Employee[] = []; @@ -69,31 +73,22 @@ export class MultiClickSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); this.selected.splice(0, this.selected.length); this.selected.push(...selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } } diff --git a/src/app/selection/selection-multi.component.ts b/src/app/selection/selection-multi.component.ts index bfe2dde5f..09fa9f768 100644 --- a/src/app/selection/selection-multi.component.ts +++ b/src/app/selection/selection-multi.component.ts @@ -1,10 +1,14 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { + ActivateEvent, ColumnMode, + DatatableComponent, + SelectEvent, SelectionType, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'multi-selection-demo', @@ -60,7 +64,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class MultiSelectionComponent { rows: Employee[] = []; @@ -72,31 +76,22 @@ export class MultiSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); this.selected.splice(0, this.selected.length); this.selected.push(...selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } } diff --git a/src/app/selection/selection-single.component.ts b/src/app/selection/selection-single.component.ts index a8d0d2942..9c5da1dc1 100644 --- a/src/app/selection/selection-single.component.ts +++ b/src/app/selection/selection-single.component.ts @@ -1,10 +1,14 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { + ActivateEvent, ColumnMode, + DatatableComponent, + SelectEvent, SelectionType, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'single-selection-demo', @@ -60,7 +64,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class SingleSelectionComponent { rows: Employee[] = []; @@ -72,29 +76,20 @@ export class SingleSelectionComponent { ColumnMode = ColumnMode; SelectionType = SelectionType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.selected = [data[2]]; this.rows = data; }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - - onSelect({ selected }) { + onSelect({ selected }: SelectEvent) { console.log('Select Event', selected, this.selected); } - onActivate(event) { + onActivate(event: ActivateEvent) { console.log('Activate Event', event); } } diff --git a/src/app/sorting/sorting-client.component.ts b/src/app/sorting/sorting-client.component.ts index 49180cc5d..74e2491b7 100644 --- a/src/app/sorting/sorting-client.component.ts +++ b/src/app/sorting/sorting-client.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode, SortType, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + SortType, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'client-sorting-demo', @@ -31,7 +37,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class ClientSortingComponent { rows: Employee[] = []; @@ -41,21 +47,11 @@ export class ClientSortingComponent { ColumnMode = ColumnMode; SortType = SortType; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - const data = JSON.parse(req.response); - cb(data); - }; - - req.send(); - } } diff --git a/src/app/sorting/sorting-comparator.component.ts b/src/app/sorting/sorting-comparator.component.ts index cad41022a..5180f807a 100644 --- a/src/app/sorting/sorting-comparator.component.ts +++ b/src/app/sorting/sorting-comparator.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'comparator-sorting-demo', @@ -29,7 +34,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class SortingComparatorComponent { rows: Employee[] = []; @@ -42,25 +47,15 @@ export class SortingComparatorComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { - this.rows = data; + this.dataService.load('company.json').subscribe(data => { + this.rows = data.splice(0, 20); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - const data = JSON.parse(req.response); - cb(data.splice(0, 20)); - }; - - req.send(); - } - - companyComparator(propA, propB) { + companyComparator(propA: string, propB: string) { console.log('Sorting Comparator', propA, propB); // Just a simple sort function comparisoins @@ -70,5 +65,7 @@ export class SortingComparatorComponent { if (propA.toLowerCase() > propB.toLowerCase()) { return 1; } + + return 0; } } diff --git a/src/app/sorting/sorting-default.component.ts b/src/app/sorting/sorting-default.component.ts index 2370d115f..f44d38ab6 100644 --- a/src/app/sorting/sorting-default.component.ts +++ b/src/app/sorting/sorting-default.component.ts @@ -1,6 +1,12 @@ -import { Component, OnInit } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, OnInit } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'default-sorting-demo', @@ -44,28 +50,18 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class DefaultSortingComponent implements OnInit { rows: Employee[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + ngOnInit() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data; }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - const data = JSON.parse(req.response); - cb(data); - }; - - req.send(); - } } diff --git a/src/app/sorting/sorting-server.component.ts b/src/app/sorting/sorting-server.component.ts index 8a72aadaa..ece7e005a 100644 --- a/src/app/sorting/sorting-server.component.ts +++ b/src/app/sorting/sorting-server.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + SortEvent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'server-sorting-demo', @@ -32,7 +38,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) export class ServerSortingComponent { loading = false; @@ -47,25 +53,15 @@ export class ServerSortingComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { - this.rows = data; + this.dataService.load('company.json').subscribe(data => { + this.rows = data.splice(0, 20); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - const data = JSON.parse(req.response); - cb(data.splice(0, 20)); - }; - - req.send(); - } - - onSort(event) { + onSort(event: SortEvent) { // event was triggered, start sort sequence console.log('Sort Event', event); this.loading = true; @@ -76,8 +72,11 @@ export class ServerSortingComponent { // your server would return the result for // you and you would just set the rows prop const sort = event.sorts[0]; + type sortProp = 'company' | 'name' | 'gender'; rows.sort( - (a, b) => a[sort.prop].localeCompare(b[sort.prop]) * (sort.dir === 'desc' ? -1 : 1) + (a, b) => + a[sort.prop as sortProp].localeCompare(b[sort.prop as sortProp]) * + (sort.dir === 'desc' ? -1 : 1) ); this.rows = rows; diff --git a/src/app/summary/summary-row-custom-template.component.ts b/src/app/summary/summary-row-custom-template.component.ts index 3712aa2e0..1216447b4 100644 --- a/src/app/summary/summary-row-custom-template.component.ts +++ b/src/app/summary/summary-row-custom-template.component.ts @@ -1,6 +1,11 @@ -import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'summary-row-custom-template-demo', @@ -39,19 +44,21 @@ import { Employee } from '../data.model'; `, styleUrls: ['./summary-row-custom-template.component.scss'], - standalone: false + imports: [DatatableComponent] }) export class SummaryRowCustomTemplateComponent implements OnInit { rows: Employee[] = []; - @ViewChild('nameSummaryCell') nameSummaryCell: TemplateRef; + @ViewChild('nameSummaryCell') nameSummaryCell!: TemplateRef; columns: TableColumn[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } @@ -68,17 +75,6 @@ export class SummaryRowCustomTemplateComponent implements OnInit { ]; } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - getNames(): string[] { return this.rows.map(row => row.name).map(fullName => fullName.split(' ')[1]); } diff --git a/src/app/summary/summary-row-inline-html.component.ts b/src/app/summary/summary-row-inline-html.component.ts index bde6c6707..271ebd9d5 100644 --- a/src/app/summary/summary-row-inline-html.component.ts +++ b/src/app/summary/summary-row-inline-html.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'summary-row-inline-html', @@ -44,7 +49,7 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent, DataTableColumnDirective] }) export class SummaryRowInlineHtmlComponent { rows: Employee[] = []; @@ -54,23 +59,14 @@ export class SummaryRowInlineHtmlComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - getNames(): string[] { return this.rows.map(row => row.name).map(fullName => fullName.split(' ')[1]); } diff --git a/src/app/summary/summary-row-server-paging.component.ts b/src/app/summary/summary-row-server-paging.component.ts index 4654102f2..a8e46be49 100644 --- a/src/app/summary/summary-row-server-paging.component.ts +++ b/src/app/summary/summary-row-server-paging.component.ts @@ -1,7 +1,11 @@ import { Component, OnInit } from '@angular/core'; import { MockServerResultsService } from '../paging/mock-server-results-service'; import { Page } from '../paging/model/page'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; @Component({ @@ -33,12 +37,12 @@ import { Employee } from '../data.model'; [count]="page.totalElements" [offset]="page.pageNumber" [limit]="page.size" - (page)="setPage($event)" + (page)="setPage($event.offset)" > `, - standalone: false + imports: [DatatableComponent] }) export class SummaryRowServerPagingComponent implements OnInit { page: Page = { @@ -49,7 +53,7 @@ export class SummaryRowServerPagingComponent implements OnInit { }; rows: Employee[] = []; - columns = [ + columns: TableColumn[] = [ // NOTE: cells for current page only ! { name: 'Name', summaryFunc: cells => `${cells.length} total` }, { name: 'Gender', summaryFunc: () => this.getGenderSummary() }, @@ -61,15 +65,15 @@ export class SummaryRowServerPagingComponent implements OnInit { constructor(private serverResultsService: MockServerResultsService) {} ngOnInit() { - this.setPage({ offset: 0 }); + this.setPage(0); } /** * Populate the table with new data based on the page number * @param page The page to select */ - setPage(pageInfo) { - this.page.pageNumber = pageInfo.offset; + setPage(page: number) { + this.page.pageNumber = page; this.serverResultsService.getResults(this.page).subscribe(pagedData => { this.page = pagedData.page; this.rows = pagedData.data; diff --git a/src/app/summary/summary-row-simple.component.ts b/src/app/summary/summary-row-simple.component.ts index 1cbf9125d..f9f5f1626 100644 --- a/src/app/summary/summary-row-simple.component.ts +++ b/src/app/summary/summary-row-simple.component.ts @@ -1,6 +1,11 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'summary-row-simple-demo', @@ -49,13 +54,13 @@ import { Employee } from '../data.model'; `, styleUrls: ['./summary-row-simple.component.scss'], - standalone: false + imports: [DatatableComponent] }) export class SummaryRowSimpleComponent { rows: Employee[] = []; columns: TableColumn[] = [ - { prop: 'name', summaryFunc: null }, + { prop: 'name' }, { name: 'Gender', summaryFunc: cells => this.summaryForGender(cells) }, { prop: 'age', summaryFunc: cells => this.avgAge(cells) } ]; @@ -65,23 +70,14 @@ export class SummaryRowSimpleComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - fetch(cb: (data: [Employee]) => void) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } - onPositionSelectChange($event: Event) { const target = $event.target as HTMLSelectElement; this.summaryPosition = target.value; diff --git a/src/app/templates/template-dom.component.ts b/src/app/templates/template-dom.component.ts index 4cdfe820e..2ed27fc88 100644 --- a/src/app/templates/template-dom.component.ts +++ b/src/app/templates/template-dom.component.ts @@ -1,6 +1,13 @@ -import { Component } from '@angular/core'; -import { ColumnMode } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DataTableColumnHeaderDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'inline-templates-demo', @@ -52,7 +59,12 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [ + DatatableComponent, + DataTableColumnDirective, + DataTableColumnHeaderDirective, + DataTableColumnCellDirective + ] }) export class InlineTemplatesComponent { rows: Employee[] = []; @@ -60,20 +72,11 @@ export class InlineTemplatesComponent { ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/templates/template-obj.component.ts b/src/app/templates/template-obj.component.ts index 4e090fef9..cdab20b02 100644 --- a/src/app/templates/template-obj.component.ts +++ b/src/app/templates/template-obj.component.ts @@ -1,6 +1,11 @@ -import { Component, TemplateRef, ViewChild } from '@angular/core'; -import { ColumnMode, TableColumn } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { Component, inject, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { + ColumnMode, + DatatableComponent, + TableColumn +} from 'projects/swimlane/ngx-datatable/src/public-api'; import { Employee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'template-ref-demo', @@ -41,19 +46,21 @@ import { Employee } from '../data.model'; `, - standalone: false + imports: [DatatableComponent] }) -export class TemplateRefTemplatesComponent { - @ViewChild('editTmpl', { static: true }) editTmpl: TemplateRef; - @ViewChild('hdrTpl', { static: true }) hdrTpl: TemplateRef; +export class TemplateRefTemplatesComponent implements OnInit { + @ViewChild('editTmpl', { static: true }) editTmpl!: TemplateRef; + @ViewChild('hdrTpl', { static: true }) hdrTpl!: TemplateRef; rows: Employee[] = []; columns: TableColumn[] = []; ColumnMode = ColumnMode; + private dataService = inject(DataService); + constructor() { - this.fetch(data => { + this.dataService.load('company.json').subscribe(data => { this.rows = data.splice(0, 5); }); } @@ -67,15 +74,4 @@ export class TemplateRefTemplatesComponent { } ]; } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company.json`); - - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); - } } diff --git a/src/app/tree/client-tree.component.ts b/src/app/tree/client-tree.component.ts index 2f7bae4bf..afcb0753c 100644 --- a/src/app/tree/client-tree.component.ts +++ b/src/app/tree/client-tree.component.ts @@ -1,6 +1,12 @@ -import { Component } from '@angular/core'; -import { ColumnMode, TreeStatus } from 'projects/swimlane/ngx-datatable/src/public-api'; -import { Employee } from '../data.model'; +import { Component, inject } from '@angular/core'; +import { + ColumnMode, + DataTableColumnCellDirective, + DataTableColumnDirective, + DatatableComponent +} from 'projects/swimlane/ngx-datatable/src/public-api'; +import { TreeEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'client-side-tree-demo', @@ -47,28 +53,17 @@ import { Employee } from '../data.model'; `, styles: ['.icon {height: 10px; width: 10px; }', '.disabled {opacity: 0.5; }'], - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellDirective] }) export class ClientTreeComponent { - rows: (Employee & { treeStatus: TreeStatus })[] = []; + rows: TreeEmployee[] = []; ColumnMode = ColumnMode; - constructor() { - this.fetch(data => { - this.rows = data; - }); - } - - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/company_tree.json`); + private dataService = inject(DataService); - req.onload = () => { - cb(JSON.parse(req.response)); - }; - - req.send(); + constructor() { + this.dataService.load('company_tree.json').subscribe(data => (this.rows = data)); } onTreeAction(event: any) { diff --git a/src/app/tree/fullscreen.component.ts b/src/app/tree/fullscreen.component.ts index c92d4cb31..b26be3808 100644 --- a/src/app/tree/fullscreen.component.ts +++ b/src/app/tree/fullscreen.component.ts @@ -1,6 +1,13 @@ import { ChangeDetectorRef, Component } from '@angular/core'; -import { ColumnMode, TreeStatus } from 'projects/swimlane/ngx-datatable/src/public-api'; -import { Employee } from '../data.model'; +import { + ColumnMode, + DataTableColumnCellTreeToggle, + DataTableColumnDirective, + DatatableComponent, + TreeStatus +} from 'projects/swimlane/ngx-datatable/src/public-api'; +import { FullEmployee } from '../data.model'; +import { DataService } from '../data.service'; @Component({ selector: 'full-screen-tree-demo', @@ -64,51 +71,37 @@ import { Employee } from '../data.model'; `, styles: ['.icon {height: 10px; width: 10px; }', '.disabled {opacity: 0.5; }'], - standalone: false + imports: [DatatableComponent, DataTableColumnDirective, DataTableColumnCellTreeToggle] }) export class FullScreenTreeComponent { - rows: (Employee & { treeStatus: TreeStatus })[] = []; + rows: (FullEmployee & { treeStatus: TreeStatus; parentId?: string })[] = []; lastIndex = 15; ColumnMode = ColumnMode; - constructor(private cd: ChangeDetectorRef) { - this.fetch(data => { + constructor(private cd: ChangeDetectorRef, private dataService: DataService) { + this.dataService.load('100k.json').subscribe(data => { data = data.slice(1, this.lastIndex); - this.rows = data.map(d => { - d.treeStatus = 'collapsed'; - d.parentId = null; - return d; - }); + this.rows = data.map(d => ({ + ...d, + treeStatus: 'collapsed' as const + })); }); } - fetch(cb) { - const req = new XMLHttpRequest(); - req.open('GET', `assets/data/100k.json`); - - req.onload = () => { - setTimeout(() => { - cb(JSON.parse(req.response)); - }, 500); - }; - - req.send(); - } - onTreeAction(event: any) { const row = event.row; if (row.treeStatus === 'collapsed') { row.treeStatus = 'loading'; - this.fetch(data => { - data = data.slice(this.lastIndex, this.lastIndex + 3).map(d => { - d.treeStatus = 'collapsed'; - d.parentId = row.id; - return d; - }); + this.dataService.load('100k.json').subscribe(data => { + const newData = data.slice(this.lastIndex, this.lastIndex + 3).map(d => ({ + ...d, + treeStatus: 'collapsed' as const, + parentId: row.id + })); this.lastIndex = this.lastIndex + 3; row.treeStatus = 'expanded'; - this.rows = [...this.rows, ...data]; + this.rows = [...this.rows, ...newData]; this.cd.detectChanges(); }); } else { diff --git a/src/assets/app.css b/src/assets/app.css index eae837e55..110f0e519 100644 --- a/src/assets/app.css +++ b/src/assets/app.css @@ -37,8 +37,6 @@ body { .selected-column { background: #fff; - -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); - -moz-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); } diff --git a/src/main.ts b/src/main.ts index fa4e0aef3..285a2e6b8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,32 @@ import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; +import { provideHttpClient } from '@angular/common/http'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { routes } from './app/app-routing.module'; +import { providedNgxDatatableConfig } from 'projects/swimlane/ngx-datatable/src/public-api'; +import { AppComponent } from './app/app.component'; +import { provideRouter, withHashLocation } from '@angular/router'; if (environment.production) { enableProdMode(); } -platformBrowserDynamic() - .bootstrapModule(AppModule) - .catch(err => console.error(err)); +bootstrapApplication(AppComponent, { + providers: [ + providedNgxDatatableConfig({ + messages: { + emptyMessage: 'No data to display', // Message to show when array is presented, but contains no values + totalMessage: 'total', // Footer total message + selectedMessage: 'selected', // Footer selected message + ariaFirstPageMessage: 'go to first page', // Pager screen reader message for the first page button + ariaPreviousPageMessage: 'go to previous page', // Pager screen reader message for the previous page button + ariaPageNMessage: 'page', // Pager screen reader message for the n-th page button + ariaNextPageMessage: 'go to next page', // Pager screen reader message for the next page button + ariaLastPageMessage: 'go to last page' // Pager screen reader message for the last page button + } + }), + provideRouter(routes, withHashLocation()), + provideHttpClient() + ] +}).catch(err => console.error(err)); diff --git a/tsconfig.json b/tsconfig.json index b690eaf3b..cfd7dbbec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,8 @@ "@swimlane/ngx-datatable": ["dist/swimlane/ngx-datatable"], "@swimlane/ngx-datatable/*": ["dist/swimlane/ngx-datatable/*"] }, - "useDefineForClassFields": false + "useDefineForClassFields": false, + "strictFunctionTypes": true }, "angularCompilerOptions": { "fullTemplateTypeCheck": true, diff --git a/yarn.lock b/yarn.lock index e9002f881..1fad1face 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,7 +5,7 @@ __metadata: version: 8 cacheKey: 10c0 -"@ampproject/remapping@npm:2.3.0, @ampproject/remapping@npm:^2.2.0": +"@ampproject/remapping@npm:2.3.0, @ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.3.0": version: 2.3.0 resolution: "@ampproject/remapping@npm:2.3.0" dependencies: @@ -15,28 +15,13 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/architect@npm:0.1902.10": - version: 0.1902.10 - resolution: "@angular-devkit/architect@npm:0.1902.10" +"@angular-devkit/architect@npm:0.2000.4, @angular-devkit/architect@npm:>= 0.2000.0 < 0.2100.0": + version: 0.2000.4 + resolution: "@angular-devkit/architect@npm:0.2000.4" dependencies: - "@angular-devkit/core": "npm:19.2.10" - rxjs: "npm:7.8.1" - checksum: 10c0/4ce187452618041674ea9b3533183a580a3c0c11c4bc25985801a82b6f02d55e4ab21e25a26f8394deced831accf586225bcd02ed40227f818f2b1636fe00f92 - languageName: node - linkType: hard - -"@angular-devkit/architect@npm:>= 0.1800.0 < 0.1900.0": - version: 0.1802.19 - resolution: "@angular-devkit/architect@npm:0.1802.19" - dependencies: - "@angular-devkit/core": "npm:18.2.19" - rxjs: "npm:7.8.1" - dependenciesMeta: - esbuild: - built: true - puppeteer: - built: true - checksum: 10c0/9a81e1e5357ddd6160eedf4ed40ca01b6092f377ef5969e67f0a4c577764a3a52b23011c8a85b5374dcf48ec23fe5cb8ca3577696476cd1838751e492259afcd + "@angular-devkit/core": "npm:20.0.4" + rxjs: "npm:7.8.2" + checksum: 10c0/ebca373f9c09309c13172c2cbf563b925518690cb516db63d1a4de075893516e25f0ad1c9a39628b239923669bb6e37c60c008d67a616609a43792e0932664fc languageName: node linkType: hard @@ -50,87 +35,93 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/build-angular@npm:^19.1.4": - version: 19.2.10 - resolution: "@angular-devkit/build-angular@npm:19.2.10" +"@angular-devkit/build-angular@npm:^20.0.4": + version: 20.0.4 + resolution: "@angular-devkit/build-angular@npm:20.0.4" dependencies: "@ampproject/remapping": "npm:2.3.0" - "@angular-devkit/architect": "npm:0.1902.10" - "@angular-devkit/build-webpack": "npm:0.1902.10" - "@angular-devkit/core": "npm:19.2.10" - "@angular/build": "npm:19.2.10" - "@babel/core": "npm:7.26.10" - "@babel/generator": "npm:7.26.10" - "@babel/helper-annotate-as-pure": "npm:7.25.9" + "@angular-devkit/architect": "npm:0.2000.4" + "@angular-devkit/build-webpack": "npm:0.2000.4" + "@angular-devkit/core": "npm:20.0.4" + "@angular/build": "npm:20.0.4" + "@babel/core": "npm:7.27.1" + "@babel/generator": "npm:7.27.1" + "@babel/helper-annotate-as-pure": "npm:7.27.1" "@babel/helper-split-export-declaration": "npm:7.24.7" - "@babel/plugin-transform-async-generator-functions": "npm:7.26.8" - "@babel/plugin-transform-async-to-generator": "npm:7.25.9" - "@babel/plugin-transform-runtime": "npm:7.26.10" - "@babel/preset-env": "npm:7.26.9" - "@babel/runtime": "npm:7.26.10" + "@babel/plugin-transform-async-generator-functions": "npm:7.27.1" + "@babel/plugin-transform-async-to-generator": "npm:7.27.1" + "@babel/plugin-transform-runtime": "npm:7.27.1" + "@babel/preset-env": "npm:7.27.2" + "@babel/runtime": "npm:7.27.1" "@discoveryjs/json-ext": "npm:0.6.3" - "@ngtools/webpack": "npm:19.2.10" - "@vitejs/plugin-basic-ssl": "npm:1.2.0" + "@ngtools/webpack": "npm:20.0.4" + "@vitejs/plugin-basic-ssl": "npm:2.0.0" ansi-colors: "npm:4.1.3" - autoprefixer: "npm:10.4.20" - babel-loader: "npm:9.2.1" + autoprefixer: "npm:10.4.21" + babel-loader: "npm:10.0.0" browserslist: "npm:^4.21.5" - copy-webpack-plugin: "npm:12.0.2" + copy-webpack-plugin: "npm:13.0.0" css-loader: "npm:7.1.2" - esbuild: "npm:0.25.1" - esbuild-wasm: "npm:0.25.1" + esbuild: "npm:0.25.5" + esbuild-wasm: "npm:0.25.5" fast-glob: "npm:3.3.3" http-proxy-middleware: "npm:3.0.5" istanbul-lib-instrument: "npm:6.0.3" jsonc-parser: "npm:3.3.1" karma-source-map-support: "npm:1.4.0" - less: "npm:4.2.2" - less-loader: "npm:12.2.0" + less: "npm:4.3.0" + less-loader: "npm:12.3.0" license-webpack-plugin: "npm:4.0.2" loader-utils: "npm:3.3.1" mini-css-extract-plugin: "npm:2.9.2" - open: "npm:10.1.0" - ora: "npm:5.4.1" + open: "npm:10.1.2" + ora: "npm:8.2.0" picomatch: "npm:4.0.2" - piscina: "npm:4.8.0" - postcss: "npm:8.5.2" + piscina: "npm:5.1.1" + postcss: "npm:8.5.3" postcss-loader: "npm:8.1.1" resolve-url-loader: "npm:5.0.0" - rxjs: "npm:7.8.1" - sass: "npm:1.85.0" + rxjs: "npm:7.8.2" + sass: "npm:1.88.0" sass-loader: "npm:16.0.5" - semver: "npm:7.7.1" + semver: "npm:7.7.2" source-map-loader: "npm:5.0.0" source-map-support: "npm:0.5.21" - terser: "npm:5.39.0" + terser: "npm:5.39.1" tree-kill: "npm:1.2.2" tslib: "npm:2.8.1" - webpack: "npm:5.98.0" + webpack: "npm:5.99.8" webpack-dev-middleware: "npm:7.4.2" - webpack-dev-server: "npm:5.2.0" + webpack-dev-server: "npm:5.2.1" webpack-merge: "npm:6.0.1" webpack-subresource-integrity: "npm:5.1.0" peerDependencies: - "@angular/compiler-cli": ^19.0.0 || ^19.2.0-next.0 - "@angular/localize": ^19.0.0 || ^19.2.0-next.0 - "@angular/platform-server": ^19.0.0 || ^19.2.0-next.0 - "@angular/service-worker": ^19.0.0 || ^19.2.0-next.0 - "@angular/ssr": ^19.2.10 + "@angular/compiler-cli": ^20.0.0 + "@angular/core": ^20.0.0 + "@angular/localize": ^20.0.0 + "@angular/platform-browser": ^20.0.0 + "@angular/platform-server": ^20.0.0 + "@angular/service-worker": ^20.0.0 + "@angular/ssr": ^20.0.4 "@web/test-runner": ^0.20.0 browser-sync: ^3.0.2 jest: ^29.5.0 jest-environment-jsdom: ^29.5.0 karma: ^6.3.0 - ng-packagr: ^19.0.0 || ^19.2.0-next.0 + ng-packagr: ^20.0.0 protractor: ^7.0.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 - typescript: ">=5.5 <5.9" + typescript: ">=5.8 <5.9" dependenciesMeta: esbuild: optional: true peerDependenciesMeta: + "@angular/core": + optional: true "@angular/localize": optional: true + "@angular/platform-browser": + optional: true "@angular/platform-server": optional: true "@angular/service-worker": @@ -153,20 +144,20 @@ __metadata: optional: true tailwindcss: optional: true - checksum: 10c0/c853722cf9e916083441d60e742eda2ea3f348f5bddd0c1e8da37836db6f36aec59712fbd388a88057f83fa18a6c1465c04e9602b1f1218d49037565c9472eda + checksum: 10c0/6fd9faa11743439367b5cbfcb33050978d54fe4e68c1c6acc1f62794591a82024a0b62ff38818aa86eef1ece2efd21b64bcaeb790841465616d859147fde937f languageName: node linkType: hard -"@angular-devkit/build-webpack@npm:0.1902.10": - version: 0.1902.10 - resolution: "@angular-devkit/build-webpack@npm:0.1902.10" +"@angular-devkit/build-webpack@npm:0.2000.4": + version: 0.2000.4 + resolution: "@angular-devkit/build-webpack@npm:0.2000.4" dependencies: - "@angular-devkit/architect": "npm:0.1902.10" - rxjs: "npm:7.8.1" + "@angular-devkit/architect": "npm:0.2000.4" + rxjs: "npm:7.8.2" peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 - checksum: 10c0/193d42127b4a27c502ee888ab97c3a60fd2a81a73bb76b418dd32fe3ac1f4ac91d5507ba2cdd37816716e43d68f4cf203a114c52646faf811169ced750480e42 + checksum: 10c0/c3a48218055ceb91945727562879b56a0c68f6c68c26dc2edc876893e67a338a56bc95eb74c0cf8e06d73099ec478b67dc1a4247c7f54ccc38a942a2e971eee4 languageName: node linkType: hard @@ -189,7 +180,7 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/core@npm:18.2.19, @angular-devkit/core@npm:>= 18.0.0 < 19.0.0, @angular-devkit/core@npm:^18.0.0": +"@angular-devkit/core@npm:18.2.19, @angular-devkit/core@npm:^18.0.0": version: 18.2.19 resolution: "@angular-devkit/core@npm:18.2.19" dependencies: @@ -213,39 +204,39 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/core@npm:19.2.10, @angular-devkit/core@npm:^19.1.4": - version: 19.2.10 - resolution: "@angular-devkit/core@npm:19.2.10" +"@angular-devkit/core@npm:20.0.4, @angular-devkit/core@npm:>= 20.0.0 < 21.0.0, @angular-devkit/core@npm:^20.0.4": + version: 20.0.4 + resolution: "@angular-devkit/core@npm:20.0.4" dependencies: ajv: "npm:8.17.1" ajv-formats: "npm:3.0.1" jsonc-parser: "npm:3.3.1" picomatch: "npm:4.0.2" - rxjs: "npm:7.8.1" + rxjs: "npm:7.8.2" source-map: "npm:0.7.4" peerDependencies: chokidar: ^4.0.0 peerDependenciesMeta: chokidar: optional: true - checksum: 10c0/a095df28d41f920650bfaf3dbd6de712519f6335abf503f2b7315966abcb2df4e625bf2c26765b03296e06bcc21aafb96271da7f0338b26fba277d08158f4904 + checksum: 10c0/1d3919f8911f36d800346a01e950a7dc2969b605493945a4338f0a9bc17e427be7ac3025cbcd6b7282643df42ef0a89ea016319b6bb85fc96a5273dd7696725e languageName: node linkType: hard -"@angular-devkit/schematics@npm:19.2.10, @angular-devkit/schematics@npm:^19.1.4": - version: 19.2.10 - resolution: "@angular-devkit/schematics@npm:19.2.10" +"@angular-devkit/schematics@npm:20.0.4, @angular-devkit/schematics@npm:>= 20.0.0 < 21.0.0, @angular-devkit/schematics@npm:^20.0.4": + version: 20.0.4 + resolution: "@angular-devkit/schematics@npm:20.0.4" dependencies: - "@angular-devkit/core": "npm:19.2.10" + "@angular-devkit/core": "npm:20.0.4" jsonc-parser: "npm:3.3.1" magic-string: "npm:0.30.17" - ora: "npm:5.4.1" - rxjs: "npm:7.8.1" - checksum: 10c0/ea4fb356b78b5e9b0cb07a764f3bca6fab5a7eb5204d71c877351ed18910f34d569aecb9aa9ea2e85cfa8d3a3c68803f47eeb2d565284ca53acb3c6f1556052a + ora: "npm:8.2.0" + rxjs: "npm:7.8.2" + checksum: 10c0/574e24144dba760dc29b93061b3f1f9cae649531f50c6c62ba90d46b2d53ca20d15173f645a40174f28c9e096f35726a1aebae48a6c46257f6e4a8cf1f534d8f languageName: node linkType: hard -"@angular-devkit/schematics@npm:>= 18.0.0 < 19.0.0, @angular-devkit/schematics@npm:^18.0.0": +"@angular-devkit/schematics@npm:^18.0.0": version: 18.2.19 resolution: "@angular-devkit/schematics@npm:18.2.19" dependencies: @@ -263,160 +254,170 @@ __metadata: languageName: node linkType: hard -"@angular-eslint/builder@npm:^18.0.0": - version: 18.4.3 - resolution: "@angular-eslint/builder@npm:18.4.3" +"@angular-eslint/builder@npm:^20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/builder@npm:20.1.1" dependencies: - "@angular-devkit/architect": "npm:>= 0.1800.0 < 0.1900.0" - "@angular-devkit/core": "npm:>= 18.0.0 < 19.0.0" + "@angular-devkit/architect": "npm:>= 0.2000.0 < 0.2100.0" + "@angular-devkit/core": "npm:>= 20.0.0 < 21.0.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/649eb002a3baeae27d395836a52e19b5f4d1f8c39066554447cd34640c95d4fa6f7ff746a21b6f4da68e257495682e5c731cd40121029d62bf41b9eebee847e1 + checksum: 10c0/fd266276a583005f030dd98fae50fa735a3e64cfc33ee2dd02f0e55f241dcd9f6145db57851d9b8ed5e7985bbf7d0651de2b1f00593d9c96f2b7093802f58f83 languageName: node linkType: hard -"@angular-eslint/bundled-angular-compiler@npm:18.4.3": - version: 18.4.3 - resolution: "@angular-eslint/bundled-angular-compiler@npm:18.4.3" - checksum: 10c0/da11e3a2ec0f46504b4bed7c3e1af94d118b52b9b59b07152ae5f102e09f4fbef790694b52b6e5260d08b4403de2497192abfa1c879431fbff10adbc558ebac4 +"@angular-eslint/bundled-angular-compiler@npm:20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/bundled-angular-compiler@npm:20.1.1" + checksum: 10c0/a62c2264b8d159c8787d081705b1362f592ae5d7025d6feaf9a7bc845a16d770f598772644bb22f122efd833068958c1c00239982428ad200fc5ef4fe17011eb languageName: node linkType: hard -"@angular-eslint/eslint-plugin-template@npm:18.4.3, @angular-eslint/eslint-plugin-template@npm:^18.0.0": - version: 18.4.3 - resolution: "@angular-eslint/eslint-plugin-template@npm:18.4.3" +"@angular-eslint/eslint-plugin-template@npm:20.1.1, @angular-eslint/eslint-plugin-template@npm:^20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/eslint-plugin-template@npm:20.1.1" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.3" - "@angular-eslint/utils": "npm:18.4.3" + "@angular-eslint/bundled-angular-compiler": "npm:20.1.1" + "@angular-eslint/utils": "npm:20.1.1" aria-query: "npm:5.3.2" axobject-query: "npm:4.1.0" peerDependencies: + "@angular-eslint/template-parser": 20.1.1 "@typescript-eslint/types": ^7.11.0 || ^8.0.0 "@typescript-eslint/utils": ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/31de5fe38040b5cd8b96674b183dd950aa4f07367c2499185f5687106c3f9944c81cbf3583430fae69849fcd6d430149428138bf69fa84b786777b0c9817393e + checksum: 10c0/57bcabbfb8c3327a4fa22aaf7239cd087fd7f3782c68d85e56d3c8e29e68078c3f2b3f262daf221ecc3053173655dc0a4857832b150852cf90c498d77d4ef99e languageName: node linkType: hard -"@angular-eslint/eslint-plugin@npm:18.4.3, @angular-eslint/eslint-plugin@npm:^18.0.0": - version: 18.4.3 - resolution: "@angular-eslint/eslint-plugin@npm:18.4.3" +"@angular-eslint/eslint-plugin@npm:20.1.1, @angular-eslint/eslint-plugin@npm:^20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/eslint-plugin@npm:20.1.1" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.3" - "@angular-eslint/utils": "npm:18.4.3" + "@angular-eslint/bundled-angular-compiler": "npm:20.1.1" + "@angular-eslint/utils": "npm:20.1.1" + ts-api-utils: "npm:^2.1.0" peerDependencies: "@typescript-eslint/utils": ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/79c497b1d924c440c62d4d79d736b722bbd8ff8813dda53c68e23211b6130d3d83b5e01716809e1139f27b24ac8703217bb2dc04404ccd1e58f070f845a2bbe6 + checksum: 10c0/ff3d195765c2fdc0b9af9c61d119e029237208043951d6369a50e9e5e3b4e0da2ba2d892cda703dfb659c92ec6289ba8cc686977ec4a6c0dcebb03419701b821 languageName: node linkType: hard -"@angular-eslint/schematics@npm:^18.0.0": - version: 18.4.3 - resolution: "@angular-eslint/schematics@npm:18.4.3" +"@angular-eslint/schematics@npm:^20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/schematics@npm:20.1.1" dependencies: - "@angular-devkit/core": "npm:>= 18.0.0 < 19.0.0" - "@angular-devkit/schematics": "npm:>= 18.0.0 < 19.0.0" - "@angular-eslint/eslint-plugin": "npm:18.4.3" - "@angular-eslint/eslint-plugin-template": "npm:18.4.3" - ignore: "npm:6.0.2" - semver: "npm:7.6.3" + "@angular-devkit/core": "npm:>= 20.0.0 < 21.0.0" + "@angular-devkit/schematics": "npm:>= 20.0.0 < 21.0.0" + "@angular-eslint/eslint-plugin": "npm:20.1.1" + "@angular-eslint/eslint-plugin-template": "npm:20.1.1" + ignore: "npm:7.0.5" + semver: "npm:7.7.2" strip-json-comments: "npm:3.1.1" - checksum: 10c0/eba9185745f5de5feb283952e307725ec4702f4762fa1a975f98b4dd4d19893bc4764414ef4239c71727b194252ec07ff540c70ad99fc7d5165160d6d83c8f98 + checksum: 10c0/c690f0c9691309c4da7017696814b62d2d23a10ebf5e23fb81f0fd68b3d2e049c48323b95ad534ba3e2751e0118754f96611eaaf829e83d214474560aa045737 languageName: node linkType: hard -"@angular-eslint/template-parser@npm:^18.0.0": - version: 18.4.3 - resolution: "@angular-eslint/template-parser@npm:18.4.3" +"@angular-eslint/template-parser@npm:^20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/template-parser@npm:20.1.1" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.3" + "@angular-eslint/bundled-angular-compiler": "npm:20.1.1" eslint-scope: "npm:^8.0.2" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/0d045243a57f9a821fba9482bf3ea098d4f8a84172b69002f8d2587eb551085a64562dba1fb6368a18ad54e242ba47fba0c8b1a3505619fea7a4b4cb1ffe835f + checksum: 10c0/8594aacdba2ae82191744790f1b711dd6feef3941c29a337a1a5cf296eff451cb47c5c20434fa7a3e6f76c07187e02dc1ad43d3db4fabc66fe17941fb929c571 languageName: node linkType: hard -"@angular-eslint/utils@npm:18.4.3": - version: 18.4.3 - resolution: "@angular-eslint/utils@npm:18.4.3" +"@angular-eslint/utils@npm:20.1.1": + version: 20.1.1 + resolution: "@angular-eslint/utils@npm:20.1.1" dependencies: - "@angular-eslint/bundled-angular-compiler": "npm:18.4.3" + "@angular-eslint/bundled-angular-compiler": "npm:20.1.1" peerDependencies: "@typescript-eslint/utils": ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: "*" - checksum: 10c0/3291b3ecc4214fe93b70272317bb45fa836e41a103eff4335aa2385bcbe0196196185b89d214287107ef234ded808d7ffd59290cdb91877dae7f9608ec70aedd + checksum: 10c0/85c65c70330bae9355854a4982dfb85f41c620be06b77ecf7cca760febc3d884ea17026cfdbdcafeb4b520f0fa7f9ceb2888b5e59a8ef60fe45c49d28b7edca8 languageName: node linkType: hard -"@angular/animations@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/animations@npm:19.2.9" +"@angular/animations@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/animations@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: - "@angular/common": 19.2.9 - "@angular/core": 19.2.9 - checksum: 10c0/b58683994612ef0a44cd6f4f3c9d0cc4d2ea517d997f5e25dc45d812a7dddc00ee2a59f8dd152d0229c3036df4e1d30a1812c9fc27ee262d5904e9269c49157b + "@angular/common": 20.0.5 + "@angular/core": 20.0.5 + checksum: 10c0/2a28ad56cceadfeb023fad774f0e970135435f17ef53d963f273d5f3eb6b7b547c6913a5108b2b6544395c3a24c50d66226aa66c58de9443547715d4dedd9541 languageName: node linkType: hard -"@angular/build@npm:19.2.10": - version: 19.2.10 - resolution: "@angular/build@npm:19.2.10" +"@angular/build@npm:20.0.4": + version: 20.0.4 + resolution: "@angular/build@npm:20.0.4" dependencies: "@ampproject/remapping": "npm:2.3.0" - "@angular-devkit/architect": "npm:0.1902.10" - "@babel/core": "npm:7.26.10" - "@babel/helper-annotate-as-pure": "npm:7.25.9" + "@angular-devkit/architect": "npm:0.2000.4" + "@babel/core": "npm:7.27.1" + "@babel/helper-annotate-as-pure": "npm:7.27.1" "@babel/helper-split-export-declaration": "npm:7.24.7" - "@babel/plugin-syntax-import-attributes": "npm:7.26.0" - "@inquirer/confirm": "npm:5.1.6" - "@vitejs/plugin-basic-ssl": "npm:1.2.0" - beasties: "npm:0.3.2" + "@inquirer/confirm": "npm:5.1.10" + "@vitejs/plugin-basic-ssl": "npm:2.0.0" + beasties: "npm:0.3.4" browserslist: "npm:^4.23.0" - esbuild: "npm:0.25.1" - fast-glob: "npm:3.3.3" + esbuild: "npm:0.25.5" https-proxy-agent: "npm:7.0.6" istanbul-lib-instrument: "npm:6.0.3" - listr2: "npm:8.2.5" - lmdb: "npm:3.2.6" + jsonc-parser: "npm:3.3.1" + listr2: "npm:8.3.3" + lmdb: "npm:3.3.0" magic-string: "npm:0.30.17" mrmime: "npm:2.0.1" - parse5-html-rewriting-stream: "npm:7.0.0" + parse5-html-rewriting-stream: "npm:7.1.0" picomatch: "npm:4.0.2" - piscina: "npm:4.8.0" - rollup: "npm:4.34.8" - sass: "npm:1.85.0" - semver: "npm:7.7.1" + piscina: "npm:5.1.1" + rollup: "npm:4.40.2" + sass: "npm:1.88.0" + semver: "npm:7.7.2" source-map-support: "npm:0.5.21" - vite: "npm:6.2.7" + tinyglobby: "npm:0.2.13" + vite: "npm:6.3.5" watchpack: "npm:2.4.2" peerDependencies: - "@angular/compiler": ^19.0.0 || ^19.2.0-next.0 - "@angular/compiler-cli": ^19.0.0 || ^19.2.0-next.0 - "@angular/localize": ^19.0.0 || ^19.2.0-next.0 - "@angular/platform-server": ^19.0.0 || ^19.2.0-next.0 - "@angular/service-worker": ^19.0.0 || ^19.2.0-next.0 - "@angular/ssr": ^19.2.10 + "@angular/compiler": ^20.0.0 + "@angular/compiler-cli": ^20.0.0 + "@angular/core": ^20.0.0 + "@angular/localize": ^20.0.0 + "@angular/platform-browser": ^20.0.0 + "@angular/platform-server": ^20.0.0 + "@angular/service-worker": ^20.0.0 + "@angular/ssr": ^20.0.4 karma: ^6.4.0 less: ^4.2.0 - ng-packagr: ^19.0.0 || ^19.2.0-next.0 + ng-packagr: ^20.0.0 postcss: ^8.4.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 - typescript: ">=5.5 <5.9" + tslib: ^2.3.0 + typescript: ">=5.8 <5.9" + vitest: ^3.1.1 dependenciesMeta: lmdb: optional: true peerDependenciesMeta: + "@angular/core": + optional: true "@angular/localize": optional: true + "@angular/platform-browser": + optional: true "@angular/platform-server": optional: true "@angular/service-worker": @@ -433,172 +434,178 @@ __metadata: optional: true tailwindcss: optional: true - checksum: 10c0/416411d5f2c40a5e91d80372b474cd0eabbb03e56e48ef9bdc02342373ed37b65feeb3824ed2fd278d3ac57f42fc765d29921f7910a1dfa11d045c58939dfb84 + vitest: + optional: true + checksum: 10c0/381154d51663a290346308129677f2f330cca45e8b8b8d6e39387dbf70018163cf048b041b2f2a5c8b7ae801e9bb9d049b1accd23fd2fb35be8b11d5b51fe180 languageName: node linkType: hard -"@angular/cdk@npm:^18.2.14": - version: 18.2.14 - resolution: "@angular/cdk@npm:18.2.14" +"@angular/cdk@npm:^20.0.4": + version: 20.0.4 + resolution: "@angular/cdk@npm:20.0.4" dependencies: parse5: "npm:^7.1.2" tslib: "npm:^2.3.0" peerDependencies: - "@angular/common": ^18.0.0 || ^19.0.0 - "@angular/core": ^18.0.0 || ^19.0.0 + "@angular/common": ^20.0.0 || ^21.0.0 + "@angular/core": ^20.0.0 || ^21.0.0 rxjs: ^6.5.3 || ^7.4.0 - dependenciesMeta: - parse5: - optional: true - checksum: 10c0/d5fb364044b1bc2fb36ac4158586f6ac74f94c9e7c74b20a295671105bf92266bcd8f710132c57569d0174b21438dbfc250bd2649ebff544b38ac3e03ada02ac + checksum: 10c0/5e46962dcfe41605377a7f59b991c79c73b78cc50d087c0fb8bc849d76e114a0d7f4a9d15e8e0a3bc609fa1e842743d360c693a973df0cba5167e21b3da4a310 languageName: node linkType: hard -"@angular/cli@npm:^19.1.6": - version: 19.2.10 - resolution: "@angular/cli@npm:19.2.10" +"@angular/cli@npm:^20.0.4": + version: 20.0.4 + resolution: "@angular/cli@npm:20.0.4" dependencies: - "@angular-devkit/architect": "npm:0.1902.10" - "@angular-devkit/core": "npm:19.2.10" - "@angular-devkit/schematics": "npm:19.2.10" - "@inquirer/prompts": "npm:7.3.2" - "@listr2/prompt-adapter-inquirer": "npm:2.0.18" - "@schematics/angular": "npm:19.2.10" + "@angular-devkit/architect": "npm:0.2000.4" + "@angular-devkit/core": "npm:20.0.4" + "@angular-devkit/schematics": "npm:20.0.4" + "@inquirer/prompts": "npm:7.5.1" + "@listr2/prompt-adapter-inquirer": "npm:2.0.22" + "@schematics/angular": "npm:20.0.4" "@yarnpkg/lockfile": "npm:1.1.0" ini: "npm:5.0.0" jsonc-parser: "npm:3.3.1" - listr2: "npm:8.2.5" + listr2: "npm:8.3.3" npm-package-arg: "npm:12.0.2" npm-pick-manifest: "npm:10.0.0" - pacote: "npm:20.0.0" + pacote: "npm:21.0.0" resolve: "npm:1.22.10" - semver: "npm:7.7.1" - symbol-observable: "npm:4.0.0" + semver: "npm:7.7.2" yargs: "npm:17.7.2" bin: ng: bin/ng.js - checksum: 10c0/5f276697832ae901c8766ba9157ac7aade886c0cf901a9882719f67b2fbdcbf71961cab9670418beef4427d6b71ea65d814307691cfb6b191333f7a082e8df2b + checksum: 10c0/16f94b7e52a0710b878746fd1b260a24aec0aa89850f40d398722e4dcc0dd3e314edba31b6f15aa99270b6af4833c4057a4cbb953f481961314e9bd7b67b1449 languageName: node linkType: hard -"@angular/common@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/common@npm:19.2.9" +"@angular/common@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/common@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: - "@angular/core": 19.2.9 + "@angular/core": 20.0.5 rxjs: ^6.5.3 || ^7.4.0 - checksum: 10c0/5f5367b1ca81f1c346cebec5cf4e5ba0b1187b8b0a0638bea7030e4d6d82339d852651c011c8a527e0864db334829a294c747d1a3828bdd530fdf7dccffd5e4f + checksum: 10c0/0e496406f734797b8858432671e8d8843e0308b545ea99cada67cf325741340069efc2ca83c34499d154181f053b0852736a66aa9484e6e66bd4c860641f49f1 languageName: node linkType: hard -"@angular/compiler-cli@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/compiler-cli@npm:19.2.9" +"@angular/compiler-cli@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/compiler-cli@npm:20.0.5" dependencies: - "@babel/core": "npm:7.26.9" + "@babel/core": "npm:7.27.4" "@jridgewell/sourcemap-codec": "npm:^1.4.14" chokidar: "npm:^4.0.0" convert-source-map: "npm:^1.5.1" reflect-metadata: "npm:^0.2.0" semver: "npm:^7.0.0" tslib: "npm:^2.3.0" - yargs: "npm:^17.2.1" + yargs: "npm:^18.0.0" peerDependencies: - "@angular/compiler": 19.2.9 - typescript: ">=5.5 <5.9" + "@angular/compiler": 20.0.5 + typescript: ">=5.8 <5.9" + peerDependenciesMeta: + typescript: + optional: true bin: ng-xi18n: bundles/src/bin/ng_xi18n.js ngc: bundles/src/bin/ngc.js - ngcc: bundles/ngcc/index.js - checksum: 10c0/2edd9bad1fe6dda2c1b214ac1942695c6e2818c0d2d8f296153ebb5f94fc5b477627002892e73e5b384e059953c19263504964c5850c4f7626f6106239e1c612 + checksum: 10c0/219a1a5db601507af59b335c0ccb37fabbdd389b64ddc1a835414a87a9dc10b30827e0c76b86121b03f96b38d46db86c876d544d53f6e442dba4632d2ddd42b0 languageName: node linkType: hard -"@angular/compiler@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/compiler@npm:19.2.9" +"@angular/compiler@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/compiler@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" - checksum: 10c0/63fe1e3f9f9ce81c79ae674bfbb15b4391d90fba9a19f26c424c6cdbb6d075e71fc48c6ba8cedeab3fe4aa8a6138f1a6ff63a0290d8b0e3af0744a0dc45f4fbe + checksum: 10c0/fcf106f28c29b262f4e0a9774c00a4ad9a671df0e55abcfa9a40f27327d85aca13093e52ba3ed5f68aa962a1aa6aa08792e34a43f5088a65bd5cf0f52986a510 languageName: node linkType: hard -"@angular/core@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/core@npm:19.2.9" +"@angular/core@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/core@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: + "@angular/compiler": 20.0.5 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 - checksum: 10c0/c53fe99fed7cd5317d7c1cbe493a18fb7f09f04962119d74d1f924029d8ba924150563aa188bc08404810d167e08b71ab023bfa81d0af1f2b608956cd2a4219e + peerDependenciesMeta: + "@angular/compiler": + optional: true + zone.js: + optional: true + checksum: 10c0/0c77d18163335253687f1ea885789657ed818f1ff81a9a776153ee60601674c332a27f733ddcd7ac7bea511d38dd5ee43f12de91f14f2635ff9c1b905e5b4ce9 languageName: node linkType: hard -"@angular/forms@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/forms@npm:19.2.9" +"@angular/forms@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/forms@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: - "@angular/common": 19.2.9 - "@angular/core": 19.2.9 - "@angular/platform-browser": 19.2.9 + "@angular/common": 20.0.5 + "@angular/core": 20.0.5 + "@angular/platform-browser": 20.0.5 rxjs: ^6.5.3 || ^7.4.0 - checksum: 10c0/d45fb669066af1285cc79b42d84158271222ea653c70176387d72bd62b87dbd3fc82ed6449ee1ad5cbeeebdb1f35dda5ef27580f53874ddf58ce991f64270c65 + checksum: 10c0/544704c95009218d464aea803ffc750963c7fab1e69c1f49a328240f8a2a8323d0c3af0da3d4ee4c895398a24cc30e653b7cace56226add1435342def0b76e1c languageName: node linkType: hard -"@angular/language-service@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/language-service@npm:19.2.9" - checksum: 10c0/81886eabedeedc106a5311e4bfa8a4802411023f3225e27bc54081517b57b1cebdb84794f8857243a8a3c41b3ee1895ff0ff51be1407edf6f37cb4c489529d62 +"@angular/language-service@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/language-service@npm:20.0.5" + checksum: 10c0/478d713ccfb6d571b037b43a8d0b6a0721a6f3ef09a1b2d69709bac0c552ee9a49c9b3c6d682d94ebe2a90b32f93d0755c7b80658434742dc398895b66921505 languageName: node linkType: hard -"@angular/platform-browser-dynamic@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/platform-browser-dynamic@npm:19.2.9" +"@angular/platform-browser-dynamic@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/platform-browser-dynamic@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: - "@angular/common": 19.2.9 - "@angular/compiler": 19.2.9 - "@angular/core": 19.2.9 - "@angular/platform-browser": 19.2.9 - checksum: 10c0/39ef227907fe169fee370521aab97077183948099edb7117067038a33ed72be32dfc84058afb90ca713752cec7be7282a60b17d4a684febf62fbe204c4604684 + "@angular/common": 20.0.5 + "@angular/compiler": 20.0.5 + "@angular/core": 20.0.5 + "@angular/platform-browser": 20.0.5 + checksum: 10c0/ad0b7a39799592bc33f4511c38d0b03629c4536e3bd1f7dc7d7e550548858bf4509b7260c9f1f604f68400ed303c43e3e50c23a947c318c390621bbbaa851b29 languageName: node linkType: hard -"@angular/platform-browser@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/platform-browser@npm:19.2.9" +"@angular/platform-browser@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/platform-browser@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: - "@angular/animations": 19.2.9 - "@angular/common": 19.2.9 - "@angular/core": 19.2.9 + "@angular/animations": 20.0.5 + "@angular/common": 20.0.5 + "@angular/core": 20.0.5 peerDependenciesMeta: "@angular/animations": optional: true - checksum: 10c0/069597039feb51be3ec3dd18f48432cd47a612400e16937b2e93483ecf6daa0d89fab573d55daf220c8d878f8d822d5e9c1bf5bdb7824bd2df3f43785405d209 + checksum: 10c0/44c1add8d39e738906cecd5e0efa9a84033b17dc452b44ed076aab2850d0024a5771e9fc4e00b558a2947ae3a2ad9660e7fd2ba35d35a4944d76d87403e28743 languageName: node linkType: hard -"@angular/router@npm:^19.1.4": - version: 19.2.9 - resolution: "@angular/router@npm:19.2.9" +"@angular/router@npm:^20.0.5": + version: 20.0.5 + resolution: "@angular/router@npm:20.0.5" dependencies: tslib: "npm:^2.3.0" peerDependencies: - "@angular/common": 19.2.9 - "@angular/core": 19.2.9 - "@angular/platform-browser": 19.2.9 + "@angular/common": 20.0.5 + "@angular/core": 20.0.5 + "@angular/platform-browser": 20.0.5 rxjs: ^6.5.3 || ^7.4.0 - checksum: 10c0/78d906af2d3c6a26d853a349f33922f9e68c08174bf7acce3f7c84b9f3635c46b658500f49f630d762455c6b006e58500ae54df5224c661d47e9193405494f8e + checksum: 10c0/746bf7e9ee0ca112b4b0364fb5f0d39329df164f4adf68bddf1181a4f1d8e3062af4689adb1903ea59e0e51cad7c18fd7b4b251ab20e8c8ea50b426077ac9892 languageName: node linkType: hard @@ -613,60 +620,14 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.26.8, @babel/compat-data@npm:^7.27.2": +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.27.2": version: 7.27.2 resolution: "@babel/compat-data@npm:7.27.2" checksum: 10c0/077c9e01af3b90decee384a6a44dcf353898e980cee22ec7941f9074655dbbe97ec317345536cdc7ef7391521e1497930c522a3816af473076dd524be7fccd32 languageName: node linkType: hard -"@babel/core@npm:7.26.10": - version: 7.26.10 - resolution: "@babel/core@npm:7.26.10" - dependencies: - "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.10" - "@babel/helper-compilation-targets": "npm:^7.26.5" - "@babel/helper-module-transforms": "npm:^7.26.0" - "@babel/helpers": "npm:^7.26.10" - "@babel/parser": "npm:^7.26.10" - "@babel/template": "npm:^7.26.9" - "@babel/traverse": "npm:^7.26.10" - "@babel/types": "npm:^7.26.10" - convert-source-map: "npm:^2.0.0" - debug: "npm:^4.1.0" - gensync: "npm:^1.0.0-beta.2" - json5: "npm:^2.2.3" - semver: "npm:^6.3.1" - checksum: 10c0/e046e0e988ab53841b512ee9d263ca409f6c46e2a999fe53024688b92db394346fa3aeae5ea0866331f62133982eee05a675d22922a4603c3f603aa09a581d62 - languageName: node - linkType: hard - -"@babel/core@npm:7.26.9": - version: 7.26.9 - resolution: "@babel/core@npm:7.26.9" - dependencies: - "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.26.2" - "@babel/generator": "npm:^7.26.9" - "@babel/helper-compilation-targets": "npm:^7.26.5" - "@babel/helper-module-transforms": "npm:^7.26.0" - "@babel/helpers": "npm:^7.26.9" - "@babel/parser": "npm:^7.26.9" - "@babel/template": "npm:^7.26.9" - "@babel/traverse": "npm:^7.26.9" - "@babel/types": "npm:^7.26.9" - convert-source-map: "npm:^2.0.0" - debug: "npm:^4.1.0" - gensync: "npm:^1.0.0-beta.2" - json5: "npm:^2.2.3" - semver: "npm:^6.3.1" - checksum: 10c0/ed7212ff42a9453765787019b7d191b167afcacd4bd8fec10b055344ef53fa0cc648c9a80159ae4ecf870016a6318731e087042dcb68d1a2a9d34eb290dc014b - languageName: node - linkType: hard - -"@babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9": +"@babel/core@npm:7.27.1, @babel/core@npm:^7.12.3, @babel/core@npm:^7.23.9": version: 7.27.1 resolution: "@babel/core@npm:7.27.1" dependencies: @@ -689,20 +650,30 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:7.26.10": - version: 7.26.10 - resolution: "@babel/generator@npm:7.26.10" +"@babel/core@npm:7.27.4": + version: 7.27.4 + resolution: "@babel/core@npm:7.27.4" dependencies: - "@babel/parser": "npm:^7.26.10" - "@babel/types": "npm:^7.26.10" - "@jridgewell/gen-mapping": "npm:^0.3.5" - "@jridgewell/trace-mapping": "npm:^0.3.25" - jsesc: "npm:^3.0.2" - checksum: 10c0/88b3b3ea80592fc89349c4e1a145e1386e4042866d2507298adf452bf972f68d13bf699a845e6ab8c028bd52c2247013eb1221b86e1db5c9779faacba9c4b10e + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.27.1" + "@babel/generator": "npm:^7.27.3" + "@babel/helper-compilation-targets": "npm:^7.27.2" + "@babel/helper-module-transforms": "npm:^7.27.3" + "@babel/helpers": "npm:^7.27.4" + "@babel/parser": "npm:^7.27.4" + "@babel/template": "npm:^7.27.2" + "@babel/traverse": "npm:^7.27.4" + "@babel/types": "npm:^7.27.3" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/d2d17b106a8d91d3eda754bb3f26b53a12eb7646df73c2b2d2e9b08d90529186bc69e3823f70a96ec6e5719dc2372fb54e14ad499da47ceeb172d2f7008787b5 languageName: node linkType: hard -"@babel/generator@npm:^7.26.10, @babel/generator@npm:^7.26.9, @babel/generator@npm:^7.27.1": +"@babel/generator@npm:7.27.1, @babel/generator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/generator@npm:7.27.1" dependencies: @@ -715,16 +686,20 @@ __metadata: languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:7.25.9": - version: 7.25.9 - resolution: "@babel/helper-annotate-as-pure@npm:7.25.9" +"@babel/generator@npm:^7.27.3, @babel/generator@npm:^7.27.5": + version: 7.27.5 + resolution: "@babel/generator@npm:7.27.5" dependencies: - "@babel/types": "npm:^7.25.9" - checksum: 10c0/095b6ba50489d797733abebc4596a81918316a99e3632755c9f02508882912b00c2ae5e468532a25a5c2108d109ddbe9b7da78333ee7cc13817fc50c00cf06fe + "@babel/parser": "npm:^7.27.5" + "@babel/types": "npm:^7.27.3" + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + jsesc: "npm:^3.0.2" + checksum: 10c0/8f649ef4cd81765c832bb11de4d6064b035ffebdecde668ba7abee68a7b0bce5c9feabb5dc5bb8aeba5bd9e5c2afa3899d852d2bd9ca77a711ba8c8379f416f0 languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.27.1": +"@babel/helper-annotate-as-pure@npm:7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-annotate-as-pure@npm:7.27.1" dependencies: @@ -733,7 +708,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.26.5, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2": +"@babel/helper-annotate-as-pure@npm:^7.27.3": + version: 7.27.3 + resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" + dependencies: + "@babel/types": "npm:^7.27.3" + checksum: 10c0/94996ce0a05b7229f956033e6dcd69393db2b0886d0db6aff41e704390402b8cdcca11f61449cb4f86cfd9e61b5ad3a73e4fa661eeed7846b125bd1c33dbc633 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2": version: 7.27.2 resolution: "@babel/helper-compilation-targets@npm:7.27.2" dependencies: @@ -801,7 +785,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.25.9, @babel/helper-module-imports@npm:^7.27.1": +"@babel/helper-module-imports@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-module-imports@npm:7.27.1" dependencies: @@ -811,7 +795,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.26.0, @babel/helper-module-transforms@npm:^7.27.1": +"@babel/helper-module-transforms@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-module-transforms@npm:7.27.1" dependencies: @@ -824,6 +808,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.27.3": + version: 7.27.3 + resolution: "@babel/helper-module-transforms@npm:7.27.3" + dependencies: + "@babel/helper-module-imports": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.27.1" + "@babel/traverse": "npm:^7.27.3" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/fccb4f512a13b4c069af51e1b56b20f54024bcf1591e31e978a30f3502567f34f90a80da6a19a6148c249216292a8074a0121f9e52602510ef0f32dbce95ca01 + languageName: node + linkType: hard + "@babel/helper-optimise-call-expression@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-optimise-call-expression@npm:7.27.1" @@ -833,14 +830,14 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.25.9, @babel/helper-plugin-utils@npm:^7.26.5, @babel/helper-plugin-utils@npm:^7.27.1": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" checksum: 10c0/94cf22c81a0c11a09b197b41ab488d416ff62254ce13c57e62912c85700dc2e99e555225787a4099ff6bae7a1812d622c80fbaeda824b79baa10a6c5ac4cf69b languageName: node linkType: hard -"@babel/helper-remap-async-to-generator@npm:^7.25.9, @babel/helper-remap-async-to-generator@npm:^7.27.1": +"@babel/helper-remap-async-to-generator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-remap-async-to-generator@npm:7.27.1" dependencies: @@ -899,7 +896,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.25.9, @babel/helper-validator-option@npm:^7.27.1": +"@babel/helper-validator-option@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-validator-option@npm:7.27.1" checksum: 10c0/6fec5f006eba40001a20f26b1ef5dbbda377b7b68c8ad518c05baa9af3f396e780bdfded24c4eef95d14bb7b8fd56192a6ed38d5d439b97d10efc5f1a191d148 @@ -917,7 +914,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.26.10, @babel/helpers@npm:^7.26.9, @babel/helpers@npm:^7.27.1": +"@babel/helpers@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helpers@npm:7.27.1" dependencies: @@ -927,7 +924,17 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.26.10, @babel/parser@npm:^7.26.9, @babel/parser@npm:^7.27.1, @babel/parser@npm:^7.27.2": +"@babel/helpers@npm:^7.27.4": + version: 7.27.6 + resolution: "@babel/helpers@npm:7.27.6" + dependencies: + "@babel/template": "npm:^7.27.2" + "@babel/types": "npm:^7.27.6" + checksum: 10c0/448bac96ef8b0f21f2294a826df9de6bf4026fd023f8a6bb6c782fe3e61946801ca24381490b8e58d861fee75cd695a1882921afbf1f53b0275ee68c938bd6d3 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.14.7, @babel/parser@npm:^7.23.9, @babel/parser@npm:^7.27.1, @babel/parser@npm:^7.27.2": version: 7.27.2 resolution: "@babel/parser@npm:7.27.2" dependencies: @@ -938,7 +945,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.25.9": +"@babel/parser@npm:^7.27.4, @babel/parser@npm:^7.27.5, @babel/parser@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/parser@npm:7.27.7" + dependencies: + "@babel/types": "npm:^7.27.7" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/f6202faeb873f0b3083022e50a5046fe07266d337c0a3bd80a491f8435ba6d9e383d49725e3dcd666b3b52c0dccb4e0f1f1004915762345f7eeed5ba54ea9fd2 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.27.1" dependencies: @@ -950,7 +968,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.25.9": +"@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-bugfix-safari-class-field-initializer-scope@npm:7.27.1" dependencies: @@ -961,7 +979,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.25.9": +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.27.1" dependencies: @@ -972,7 +990,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.25.9": +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.27.1" dependencies: @@ -985,7 +1003,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.25.9": +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.27.1" dependencies: @@ -1006,7 +1024,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-assertions@npm:^7.26.0": +"@babel/plugin-syntax-import-assertions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-syntax-import-assertions@npm:7.27.1" dependencies: @@ -1017,18 +1035,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-syntax-import-attributes@npm:7.26.0": - version: 7.26.0 - resolution: "@babel/plugin-syntax-import-attributes@npm:7.26.0" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.25.9" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/e594c185b12bfe0bbe7ca78dfeebe870e6d569a12128cac86f3164a075fe0ff70e25ddbd97fd0782906b91f65560c9dc6957716b7b4a68aba2516c9b7455e352 - languageName: node - linkType: hard - -"@babel/plugin-syntax-import-attributes@npm:^7.26.0": +"@babel/plugin-syntax-import-attributes@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-syntax-import-attributes@npm:7.27.1" dependencies: @@ -1051,7 +1058,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-arrow-functions@npm:^7.25.9": +"@babel/plugin-transform-arrow-functions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-arrow-functions@npm:7.27.1" dependencies: @@ -1062,20 +1069,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:7.26.8": - version: 7.26.8 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.26.8" - dependencies: - "@babel/helper-plugin-utils": "npm:^7.26.5" - "@babel/helper-remap-async-to-generator": "npm:^7.25.9" - "@babel/traverse": "npm:^7.26.8" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/f6fefce963fe2e6268dde1958975d7adbce65fba94ca6f4bc554c90da03104ad1dd2e66d03bc0462da46868498428646e30b03a218ef0e5a84bfc87a7e375cec - languageName: node - linkType: hard - -"@babel/plugin-transform-async-generator-functions@npm:^7.26.8": +"@babel/plugin-transform-async-generator-functions@npm:7.27.1, @babel/plugin-transform-async-generator-functions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-async-generator-functions@npm:7.27.1" dependencies: @@ -1088,20 +1082,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-to-generator@npm:7.25.9": - version: 7.25.9 - resolution: "@babel/plugin-transform-async-to-generator@npm:7.25.9" - dependencies: - "@babel/helper-module-imports": "npm:^7.25.9" - "@babel/helper-plugin-utils": "npm:^7.25.9" - "@babel/helper-remap-async-to-generator": "npm:^7.25.9" - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 10c0/c443d9e462ddef733ae56360064f32fc800105803d892e4ff32d7d6a6922b3765fa97b9ddc9f7f1d3f9d8c2d95721d85bef9dbf507804214c6cf6466b105c168 - languageName: node - linkType: hard - -"@babel/plugin-transform-async-to-generator@npm:^7.25.9": +"@babel/plugin-transform-async-to-generator@npm:7.27.1, @babel/plugin-transform-async-to-generator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-async-to-generator@npm:7.27.1" dependencies: @@ -1114,7 +1095,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoped-functions@npm:^7.26.5": +"@babel/plugin-transform-block-scoped-functions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.27.1" dependencies: @@ -1125,18 +1106,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.25.9": - version: 7.27.1 - resolution: "@babel/plugin-transform-block-scoping@npm:7.27.1" +"@babel/plugin-transform-block-scoping@npm:^7.27.1": + version: 7.27.5 + resolution: "@babel/plugin-transform-block-scoping@npm:7.27.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/d3f357beeb92fbdf3045aea2ba286a60dafc9c2d2a9f89065bb3c4bea9cc48934ee6689df3db0439d9ec518eda5e684f3156cab792b7c38c33ece2f8204ddee8 + checksum: 10c0/5c1a61f312f18d3807c4df25868161301a7bd0807092b86951fa6b9918e07ee382d58d61a204c3f9ad0b72b8f6f1d18586f8e485c355a3e959c26a070397e95e languageName: node linkType: hard -"@babel/plugin-transform-class-properties@npm:^7.25.9": +"@babel/plugin-transform-class-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-class-properties@npm:7.27.1" dependencies: @@ -1148,7 +1129,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-class-static-block@npm:^7.26.0": +"@babel/plugin-transform-class-static-block@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-class-static-block@npm:7.27.1" dependencies: @@ -1160,23 +1141,23 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.25.9": - version: 7.27.1 - resolution: "@babel/plugin-transform-classes@npm:7.27.1" +"@babel/plugin-transform-classes@npm:^7.27.1": + version: 7.27.7 + resolution: "@babel/plugin-transform-classes@npm:7.27.7" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.27.1" - "@babel/helper-compilation-targets": "npm:^7.27.1" + "@babel/helper-annotate-as-pure": "npm:^7.27.3" + "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-plugin-utils": "npm:^7.27.1" "@babel/helper-replace-supers": "npm:^7.27.1" - "@babel/traverse": "npm:^7.27.1" + "@babel/traverse": "npm:^7.27.7" globals: "npm:^11.1.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/1071f4cb1ed5deb5e6f8d0442f2293a540cac5caa5ab3c25ad0571aadcbf961f61e26d367a67894976165a543e02f3a19e40b63b909afbed6e710801a590635c + checksum: 10c0/188131e30543e9726c1f7f80df111f9b2f8a05b476627190444970f26dc392d9bd42070b47e91a0ece1afcbb7e7e672b9d12f7572ce4bcffa9c70b1fb31554b8 languageName: node linkType: hard -"@babel/plugin-transform-computed-properties@npm:^7.25.9": +"@babel/plugin-transform-computed-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-computed-properties@npm:7.27.1" dependencies: @@ -1188,7 +1169,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.25.9, @babel/plugin-transform-destructuring@npm:^7.27.1": +"@babel/plugin-transform-destructuring@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-destructuring@npm:7.27.1" dependencies: @@ -1199,7 +1180,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dotall-regex@npm:^7.25.9": +"@babel/plugin-transform-destructuring@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/plugin-transform-destructuring@npm:7.27.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.27.1" + "@babel/traverse": "npm:^7.27.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/0439b47c193c2e1c55994d27c61e1c3762856833d166eb1d43a3cd9825cdafa40766e7830446d98723f1ce0db218374083ab064cb93d80b824c494ac6642422c + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-dotall-regex@npm:7.27.1" dependencies: @@ -1211,7 +1204,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-keys@npm:^7.25.9": +"@babel/plugin-transform-duplicate-keys@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-duplicate-keys@npm:7.27.1" dependencies: @@ -1222,7 +1215,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.25.9": +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-duplicate-named-capturing-groups-regex@npm:7.27.1" dependencies: @@ -1234,7 +1227,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dynamic-import@npm:^7.25.9": +"@babel/plugin-transform-dynamic-import@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-dynamic-import@npm:7.27.1" dependencies: @@ -1245,7 +1238,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-exponentiation-operator@npm:^7.26.3": +"@babel/plugin-transform-exponentiation-operator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.27.1" dependencies: @@ -1256,7 +1249,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-export-namespace-from@npm:^7.25.9": +"@babel/plugin-transform-export-namespace-from@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-export-namespace-from@npm:7.27.1" dependencies: @@ -1267,7 +1260,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-for-of@npm:^7.26.9": +"@babel/plugin-transform-for-of@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-for-of@npm:7.27.1" dependencies: @@ -1279,7 +1272,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-function-name@npm:^7.25.9": +"@babel/plugin-transform-function-name@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-function-name@npm:7.27.1" dependencies: @@ -1292,7 +1285,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-json-strings@npm:^7.25.9": +"@babel/plugin-transform-json-strings@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-json-strings@npm:7.27.1" dependencies: @@ -1303,7 +1296,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-literals@npm:^7.25.9": +"@babel/plugin-transform-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-literals@npm:7.27.1" dependencies: @@ -1314,7 +1307,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.25.9": +"@babel/plugin-transform-logical-assignment-operators@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.27.1" dependencies: @@ -1325,7 +1318,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-member-expression-literals@npm:^7.25.9": +"@babel/plugin-transform-member-expression-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-member-expression-literals@npm:7.27.1" dependencies: @@ -1336,7 +1329,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-amd@npm:^7.25.9": +"@babel/plugin-transform-modules-amd@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-modules-amd@npm:7.27.1" dependencies: @@ -1348,7 +1341,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-commonjs@npm:^7.26.3": +"@babel/plugin-transform-modules-commonjs@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-modules-commonjs@npm:7.27.1" dependencies: @@ -1360,7 +1353,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-systemjs@npm:^7.25.9": +"@babel/plugin-transform-modules-systemjs@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-modules-systemjs@npm:7.27.1" dependencies: @@ -1374,7 +1367,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-modules-umd@npm:^7.25.9": +"@babel/plugin-transform-modules-umd@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-modules-umd@npm:7.27.1" dependencies: @@ -1386,7 +1379,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.25.9": +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.27.1" dependencies: @@ -1398,7 +1391,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-new-target@npm:^7.25.9": +"@babel/plugin-transform-new-target@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-new-target@npm:7.27.1" dependencies: @@ -1409,7 +1402,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.26.6": +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.27.1" dependencies: @@ -1420,7 +1413,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.25.9": +"@babel/plugin-transform-numeric-separator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-numeric-separator@npm:7.27.1" dependencies: @@ -1431,21 +1424,22 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.25.9": - version: 7.27.2 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.27.2" +"@babel/plugin-transform-object-rest-spread@npm:^7.27.2": + version: 7.27.7 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.27.7" dependencies: "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-plugin-utils": "npm:^7.27.1" - "@babel/plugin-transform-destructuring": "npm:^7.27.1" - "@babel/plugin-transform-parameters": "npm:^7.27.1" + "@babel/plugin-transform-destructuring": "npm:^7.27.7" + "@babel/plugin-transform-parameters": "npm:^7.27.7" + "@babel/traverse": "npm:^7.27.7" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/5e255b262dd65c8700078d9f6ed87bd45f951a905dda6b3414be28d7b2781b18e6b812e9d71421e61360c9cf51e1e619c1d48348593bb7399496f61f5f221446 + checksum: 10c0/8902c97849f2f8368295610d084fe783c31d1c7c4cab65b0a144d617ddd4c0ff9bf073c55846dc118ced140fed1f4c64345a2dfca82999a65bf0c099f060eae7 languageName: node linkType: hard -"@babel/plugin-transform-object-super@npm:^7.25.9": +"@babel/plugin-transform-object-super@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-object-super@npm:7.27.1" dependencies: @@ -1457,7 +1451,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-catch-binding@npm:^7.25.9": +"@babel/plugin-transform-optional-catch-binding@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.27.1" dependencies: @@ -1468,7 +1462,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.25.9, @babel/plugin-transform-optional-chaining@npm:^7.27.1": +"@babel/plugin-transform-optional-chaining@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-optional-chaining@npm:7.27.1" dependencies: @@ -1480,7 +1474,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.25.9, @babel/plugin-transform-parameters@npm:^7.27.1": +"@babel/plugin-transform-parameters@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-parameters@npm:7.27.1" dependencies: @@ -1491,7 +1485,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-private-methods@npm:^7.25.9": +"@babel/plugin-transform-parameters@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/plugin-transform-parameters@npm:7.27.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.27.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/f2da3804e047d9f1cfb27be6c014e2c7f6cf5e1e38290d1cb3cb2607859e3d6facb4ee8c8c1e336e9fbb440091a174ce95ce156582d7e8bf9c0e735d11681f0f + languageName: node + linkType: hard + +"@babel/plugin-transform-private-methods@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-private-methods@npm:7.27.1" dependencies: @@ -1503,7 +1508,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-private-property-in-object@npm:^7.25.9": +"@babel/plugin-transform-private-property-in-object@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-private-property-in-object@npm:7.27.1" dependencies: @@ -1516,7 +1521,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-property-literals@npm:^7.25.9": +"@babel/plugin-transform-property-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-property-literals@npm:7.27.1" dependencies: @@ -1527,18 +1532,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.25.9": - version: 7.27.1 - resolution: "@babel/plugin-transform-regenerator@npm:7.27.1" +"@babel/plugin-transform-regenerator@npm:^7.27.1": + version: 7.27.5 + resolution: "@babel/plugin-transform-regenerator@npm:7.27.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/42395908899310bb107d9ca31ebd4c302e14c582e3ad3ebfe1498fabafc43155c8f10850265c1e686a2afcf50d1f402cc5c5218fba72e167852607a4d8d6492e + checksum: 10c0/4ace8ced76b421cd44dd9fa08bebc2f3fd76ec84e532cd1027738f411afdbc239789edd6c96dd1db412fc4a42cead5c1ac98a8aef94f35102f5de1049e64c07a languageName: node linkType: hard -"@babel/plugin-transform-regexp-modifiers@npm:^7.26.0": +"@babel/plugin-transform-regexp-modifiers@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-regexp-modifiers@npm:7.27.1" dependencies: @@ -1550,7 +1555,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-reserved-words@npm:^7.25.9": +"@babel/plugin-transform-reserved-words@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-reserved-words@npm:7.27.1" dependencies: @@ -1561,23 +1566,23 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:7.26.10": - version: 7.26.10 - resolution: "@babel/plugin-transform-runtime@npm:7.26.10" +"@babel/plugin-transform-runtime@npm:7.27.1": + version: 7.27.1 + resolution: "@babel/plugin-transform-runtime@npm:7.27.1" dependencies: - "@babel/helper-module-imports": "npm:^7.25.9" - "@babel/helper-plugin-utils": "npm:^7.26.5" + "@babel/helper-module-imports": "npm:^7.27.1" + "@babel/helper-plugin-utils": "npm:^7.27.1" babel-plugin-polyfill-corejs2: "npm:^0.4.10" babel-plugin-polyfill-corejs3: "npm:^0.11.0" babel-plugin-polyfill-regenerator: "npm:^0.6.1" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/4b70a63b904a3f7faa6ca95f9034d2f29330764820b06cf1814dda4ab0482b233a28241e98d8497bc1690dd31972e72861d8534ae0e37f26e04637e7d615e43d + checksum: 10c0/7abbae60a6441ba8546dee3fcbc00b38038304250ba2419adaf0c76267bff43420ff75b7049003a24a829e01d9fde2ac8a422352af6d88aebd31996a83f04c2f languageName: node linkType: hard -"@babel/plugin-transform-shorthand-properties@npm:^7.25.9": +"@babel/plugin-transform-shorthand-properties@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-shorthand-properties@npm:7.27.1" dependencies: @@ -1588,7 +1593,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-spread@npm:^7.25.9": +"@babel/plugin-transform-spread@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-spread@npm:7.27.1" dependencies: @@ -1600,7 +1605,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-sticky-regex@npm:^7.25.9": +"@babel/plugin-transform-sticky-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-sticky-regex@npm:7.27.1" dependencies: @@ -1611,7 +1616,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-template-literals@npm:^7.26.8": +"@babel/plugin-transform-template-literals@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-template-literals@npm:7.27.1" dependencies: @@ -1622,7 +1627,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typeof-symbol@npm:^7.26.7": +"@babel/plugin-transform-typeof-symbol@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-typeof-symbol@npm:7.27.1" dependencies: @@ -1633,7 +1638,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-escapes@npm:^7.25.9": +"@babel/plugin-transform-unicode-escapes@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-escapes@npm:7.27.1" dependencies: @@ -1644,7 +1649,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-property-regex@npm:^7.25.9": +"@babel/plugin-transform-unicode-property-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.27.1" dependencies: @@ -1656,7 +1661,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-regex@npm:^7.25.9": +"@babel/plugin-transform-unicode-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-regex@npm:7.27.1" dependencies: @@ -1668,7 +1673,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-unicode-sets-regex@npm:^7.25.9": +"@babel/plugin-transform-unicode-sets-regex@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.27.1" dependencies: @@ -1680,73 +1685,73 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.26.9": - version: 7.26.9 - resolution: "@babel/preset-env@npm:7.26.9" +"@babel/preset-env@npm:7.27.2": + version: 7.27.2 + resolution: "@babel/preset-env@npm:7.27.2" dependencies: - "@babel/compat-data": "npm:^7.26.8" - "@babel/helper-compilation-targets": "npm:^7.26.5" - "@babel/helper-plugin-utils": "npm:^7.26.5" - "@babel/helper-validator-option": "npm:^7.25.9" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.25.9" - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "npm:^7.25.9" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.25.9" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.25.9" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.25.9" + "@babel/compat-data": "npm:^7.27.2" + "@babel/helper-compilation-targets": "npm:^7.27.2" + "@babel/helper-plugin-utils": "npm:^7.27.1" + "@babel/helper-validator-option": "npm:^7.27.1" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.27.1" + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "npm:^7.27.1" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.27.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.27.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.27.1" "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-import-assertions": "npm:^7.26.0" - "@babel/plugin-syntax-import-attributes": "npm:^7.26.0" + "@babel/plugin-syntax-import-assertions": "npm:^7.27.1" + "@babel/plugin-syntax-import-attributes": "npm:^7.27.1" "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" - "@babel/plugin-transform-arrow-functions": "npm:^7.25.9" - "@babel/plugin-transform-async-generator-functions": "npm:^7.26.8" - "@babel/plugin-transform-async-to-generator": "npm:^7.25.9" - "@babel/plugin-transform-block-scoped-functions": "npm:^7.26.5" - "@babel/plugin-transform-block-scoping": "npm:^7.25.9" - "@babel/plugin-transform-class-properties": "npm:^7.25.9" - "@babel/plugin-transform-class-static-block": "npm:^7.26.0" - "@babel/plugin-transform-classes": "npm:^7.25.9" - "@babel/plugin-transform-computed-properties": "npm:^7.25.9" - "@babel/plugin-transform-destructuring": "npm:^7.25.9" - "@babel/plugin-transform-dotall-regex": "npm:^7.25.9" - "@babel/plugin-transform-duplicate-keys": "npm:^7.25.9" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.25.9" - "@babel/plugin-transform-dynamic-import": "npm:^7.25.9" - "@babel/plugin-transform-exponentiation-operator": "npm:^7.26.3" - "@babel/plugin-transform-export-namespace-from": "npm:^7.25.9" - "@babel/plugin-transform-for-of": "npm:^7.26.9" - "@babel/plugin-transform-function-name": "npm:^7.25.9" - "@babel/plugin-transform-json-strings": "npm:^7.25.9" - "@babel/plugin-transform-literals": "npm:^7.25.9" - "@babel/plugin-transform-logical-assignment-operators": "npm:^7.25.9" - "@babel/plugin-transform-member-expression-literals": "npm:^7.25.9" - "@babel/plugin-transform-modules-amd": "npm:^7.25.9" - "@babel/plugin-transform-modules-commonjs": "npm:^7.26.3" - "@babel/plugin-transform-modules-systemjs": "npm:^7.25.9" - "@babel/plugin-transform-modules-umd": "npm:^7.25.9" - "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.25.9" - "@babel/plugin-transform-new-target": "npm:^7.25.9" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.26.6" - "@babel/plugin-transform-numeric-separator": "npm:^7.25.9" - "@babel/plugin-transform-object-rest-spread": "npm:^7.25.9" - "@babel/plugin-transform-object-super": "npm:^7.25.9" - "@babel/plugin-transform-optional-catch-binding": "npm:^7.25.9" - "@babel/plugin-transform-optional-chaining": "npm:^7.25.9" - "@babel/plugin-transform-parameters": "npm:^7.25.9" - "@babel/plugin-transform-private-methods": "npm:^7.25.9" - "@babel/plugin-transform-private-property-in-object": "npm:^7.25.9" - "@babel/plugin-transform-property-literals": "npm:^7.25.9" - "@babel/plugin-transform-regenerator": "npm:^7.25.9" - "@babel/plugin-transform-regexp-modifiers": "npm:^7.26.0" - "@babel/plugin-transform-reserved-words": "npm:^7.25.9" - "@babel/plugin-transform-shorthand-properties": "npm:^7.25.9" - "@babel/plugin-transform-spread": "npm:^7.25.9" - "@babel/plugin-transform-sticky-regex": "npm:^7.25.9" - "@babel/plugin-transform-template-literals": "npm:^7.26.8" - "@babel/plugin-transform-typeof-symbol": "npm:^7.26.7" - "@babel/plugin-transform-unicode-escapes": "npm:^7.25.9" - "@babel/plugin-transform-unicode-property-regex": "npm:^7.25.9" - "@babel/plugin-transform-unicode-regex": "npm:^7.25.9" - "@babel/plugin-transform-unicode-sets-regex": "npm:^7.25.9" + "@babel/plugin-transform-arrow-functions": "npm:^7.27.1" + "@babel/plugin-transform-async-generator-functions": "npm:^7.27.1" + "@babel/plugin-transform-async-to-generator": "npm:^7.27.1" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.27.1" + "@babel/plugin-transform-block-scoping": "npm:^7.27.1" + "@babel/plugin-transform-class-properties": "npm:^7.27.1" + "@babel/plugin-transform-class-static-block": "npm:^7.27.1" + "@babel/plugin-transform-classes": "npm:^7.27.1" + "@babel/plugin-transform-computed-properties": "npm:^7.27.1" + "@babel/plugin-transform-destructuring": "npm:^7.27.1" + "@babel/plugin-transform-dotall-regex": "npm:^7.27.1" + "@babel/plugin-transform-duplicate-keys": "npm:^7.27.1" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.27.1" + "@babel/plugin-transform-dynamic-import": "npm:^7.27.1" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.27.1" + "@babel/plugin-transform-export-namespace-from": "npm:^7.27.1" + "@babel/plugin-transform-for-of": "npm:^7.27.1" + "@babel/plugin-transform-function-name": "npm:^7.27.1" + "@babel/plugin-transform-json-strings": "npm:^7.27.1" + "@babel/plugin-transform-literals": "npm:^7.27.1" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.27.1" + "@babel/plugin-transform-member-expression-literals": "npm:^7.27.1" + "@babel/plugin-transform-modules-amd": "npm:^7.27.1" + "@babel/plugin-transform-modules-commonjs": "npm:^7.27.1" + "@babel/plugin-transform-modules-systemjs": "npm:^7.27.1" + "@babel/plugin-transform-modules-umd": "npm:^7.27.1" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.27.1" + "@babel/plugin-transform-new-target": "npm:^7.27.1" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.27.1" + "@babel/plugin-transform-numeric-separator": "npm:^7.27.1" + "@babel/plugin-transform-object-rest-spread": "npm:^7.27.2" + "@babel/plugin-transform-object-super": "npm:^7.27.1" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.27.1" + "@babel/plugin-transform-optional-chaining": "npm:^7.27.1" + "@babel/plugin-transform-parameters": "npm:^7.27.1" + "@babel/plugin-transform-private-methods": "npm:^7.27.1" + "@babel/plugin-transform-private-property-in-object": "npm:^7.27.1" + "@babel/plugin-transform-property-literals": "npm:^7.27.1" + "@babel/plugin-transform-regenerator": "npm:^7.27.1" + "@babel/plugin-transform-regexp-modifiers": "npm:^7.27.1" + "@babel/plugin-transform-reserved-words": "npm:^7.27.1" + "@babel/plugin-transform-shorthand-properties": "npm:^7.27.1" + "@babel/plugin-transform-spread": "npm:^7.27.1" + "@babel/plugin-transform-sticky-regex": "npm:^7.27.1" + "@babel/plugin-transform-template-literals": "npm:^7.27.1" + "@babel/plugin-transform-typeof-symbol": "npm:^7.27.1" + "@babel/plugin-transform-unicode-escapes": "npm:^7.27.1" + "@babel/plugin-transform-unicode-property-regex": "npm:^7.27.1" + "@babel/plugin-transform-unicode-regex": "npm:^7.27.1" + "@babel/plugin-transform-unicode-sets-regex": "npm:^7.27.1" "@babel/preset-modules": "npm:0.1.6-no-external-plugins" babel-plugin-polyfill-corejs2: "npm:^0.4.10" babel-plugin-polyfill-corejs3: "npm:^0.11.0" @@ -1755,7 +1760,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10c0/6812ca76bd38165a58fe8354bab5e7204e1aa17d8b9270bd8f8babb08cc7fa94cd29525fe41b553f2ba0e84033d566f10da26012b8ee0f81897005c5225d0051 + checksum: 10c0/fd7ec310832a9ff26ed8d56bc0832cdbdb3a188e022050b74790796650649fb8373568af05b320b58b3ff922507979bad50ff95a4d504ab0081134480103504e languageName: node linkType: hard @@ -1772,16 +1777,14 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.26.10": - version: 7.26.10 - resolution: "@babel/runtime@npm:7.26.10" - dependencies: - regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/6dc6d88c7908f505c4f7770fb4677dfa61f68f659b943c2be1f2a99cb6680343462867abf2d49822adc435932919b36c77ac60125793e719ea8745f2073d3745 +"@babel/runtime@npm:7.27.1": + version: 7.27.1 + resolution: "@babel/runtime@npm:7.27.1" + checksum: 10c0/530a7332f86ac5a7442250456823a930906911d895c0b743bf1852efc88a20a016ed4cd26d442d0ca40ae6d5448111e02a08dd638a4f1064b47d080e2875dc05 languageName: node linkType: hard -"@babel/template@npm:^7.26.9, @babel/template@npm:^7.27.1": +"@babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2": version: 7.27.2 resolution: "@babel/template@npm:7.27.2" dependencies: @@ -1792,7 +1795,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.26.10, @babel/traverse@npm:^7.26.8, @babel/traverse@npm:^7.26.9, @babel/traverse@npm:^7.27.1": +"@babel/traverse@npm:^7.27.1": version: 7.27.1 resolution: "@babel/traverse@npm:7.27.1" dependencies: @@ -1807,7 +1810,22 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.10, @babel/types@npm:^7.26.9, @babel/types@npm:^7.27.1, @babel/types@npm:^7.4.4": +"@babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.27.4, @babel/traverse@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/traverse@npm:7.27.7" + dependencies: + "@babel/code-frame": "npm:^7.27.1" + "@babel/generator": "npm:^7.27.5" + "@babel/parser": "npm:^7.27.7" + "@babel/template": "npm:^7.27.2" + "@babel/types": "npm:^7.27.7" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: 10c0/941fecd0248546f059d58230590a2765d128ef072c8521c9e0bcf6037abf28a0ea4736003d0d695513128d07fe00a7bc57acaada2ed905941d44619b9f49cf0c + languageName: node + linkType: hard + +"@babel/types@npm:^7.24.7, @babel/types@npm:^7.27.1, @babel/types@npm:^7.4.4": version: 7.27.1 resolution: "@babel/types@npm:7.27.1" dependencies: @@ -1817,6 +1835,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/types@npm:7.27.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.27.1" + checksum: 10c0/1d1dcb5fa7cfba2b4034a3ab99ba17049bfc4af9e170935575246cdb1cee68b04329a0111506d9ae83fb917c47dbd4394a6db5e32fbd041b7834ffbb17ca086b + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -1851,13 +1879,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/aix-ppc64@npm:0.25.1" - conditions: os=aix & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.25.4": version: 0.25.4 resolution: "@esbuild/aix-ppc64@npm:0.25.4" @@ -1865,10 +1886,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/android-arm64@npm:0.25.1" - conditions: os=android & cpu=arm64 +"@esbuild/aix-ppc64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/aix-ppc64@npm:0.25.5" + conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -1879,10 +1900,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/android-arm@npm:0.25.1" - conditions: os=android & cpu=arm +"@esbuild/android-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-arm64@npm:0.25.5" + conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -1893,10 +1914,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/android-x64@npm:0.25.1" - conditions: os=android & cpu=x64 +"@esbuild/android-arm@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-arm@npm:0.25.5" + conditions: os=android & cpu=arm languageName: node linkType: hard @@ -1907,10 +1928,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/darwin-arm64@npm:0.25.1" - conditions: os=darwin & cpu=arm64 +"@esbuild/android-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-x64@npm:0.25.5" + conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -1921,10 +1942,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/darwin-x64@npm:0.25.1" - conditions: os=darwin & cpu=x64 +"@esbuild/darwin-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/darwin-arm64@npm:0.25.5" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -1935,10 +1956,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/freebsd-arm64@npm:0.25.1" - conditions: os=freebsd & cpu=arm64 +"@esbuild/darwin-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/darwin-x64@npm:0.25.5" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -1949,10 +1970,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/freebsd-x64@npm:0.25.1" - conditions: os=freebsd & cpu=x64 +"@esbuild/freebsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/freebsd-arm64@npm:0.25.5" + conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -1963,10 +1984,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-arm64@npm:0.25.1" - conditions: os=linux & cpu=arm64 +"@esbuild/freebsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/freebsd-x64@npm:0.25.5" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -1977,10 +1998,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-arm@npm:0.25.1" - conditions: os=linux & cpu=arm +"@esbuild/linux-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-arm64@npm:0.25.5" + conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -1991,10 +2012,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-ia32@npm:0.25.1" - conditions: os=linux & cpu=ia32 +"@esbuild/linux-arm@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-arm@npm:0.25.5" + conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -2005,10 +2026,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-loong64@npm:0.25.1" - conditions: os=linux & cpu=loong64 +"@esbuild/linux-ia32@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-ia32@npm:0.25.5" + conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -2019,10 +2040,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-mips64el@npm:0.25.1" - conditions: os=linux & cpu=mips64el +"@esbuild/linux-loong64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-loong64@npm:0.25.5" + conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -2033,10 +2054,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-ppc64@npm:0.25.1" - conditions: os=linux & cpu=ppc64 +"@esbuild/linux-mips64el@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-mips64el@npm:0.25.5" + conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -2047,10 +2068,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-riscv64@npm:0.25.1" - conditions: os=linux & cpu=riscv64 +"@esbuild/linux-ppc64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-ppc64@npm:0.25.5" + conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -2061,10 +2082,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-s390x@npm:0.25.1" - conditions: os=linux & cpu=s390x +"@esbuild/linux-riscv64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-riscv64@npm:0.25.5" + conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -2075,10 +2096,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/linux-x64@npm:0.25.1" - conditions: os=linux & cpu=x64 +"@esbuild/linux-s390x@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-s390x@npm:0.25.5" + conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -2089,10 +2110,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/netbsd-arm64@npm:0.25.1" - conditions: os=netbsd & cpu=arm64 +"@esbuild/linux-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-x64@npm:0.25.5" + conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -2103,10 +2124,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/netbsd-x64@npm:0.25.1" - conditions: os=netbsd & cpu=x64 +"@esbuild/netbsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/netbsd-arm64@npm:0.25.5" + conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard @@ -2117,10 +2138,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/openbsd-arm64@npm:0.25.1" - conditions: os=openbsd & cpu=arm64 +"@esbuild/netbsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/netbsd-x64@npm:0.25.5" + conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -2131,10 +2152,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/openbsd-x64@npm:0.25.1" - conditions: os=openbsd & cpu=x64 +"@esbuild/openbsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/openbsd-arm64@npm:0.25.5" + conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard @@ -2145,10 +2166,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/sunos-x64@npm:0.25.1" - conditions: os=sunos & cpu=x64 +"@esbuild/openbsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/openbsd-x64@npm:0.25.5" + conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -2159,10 +2180,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/win32-arm64@npm:0.25.1" - conditions: os=win32 & cpu=arm64 +"@esbuild/sunos-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/sunos-x64@npm:0.25.5" + conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -2173,10 +2194,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/win32-ia32@npm:0.25.1" - conditions: os=win32 & cpu=ia32 +"@esbuild/win32-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-arm64@npm:0.25.5" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -2187,10 +2208,10 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.1": - version: 0.25.1 - resolution: "@esbuild/win32-x64@npm:0.25.1" - conditions: os=win32 & cpu=x64 +"@esbuild/win32-ia32@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-ia32@npm:0.25.5" + conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -2201,6 +2222,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-x64@npm:0.25.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.7.0 resolution: "@eslint-community/eslint-utils@npm:4.7.0" @@ -2275,13 +2303,13 @@ __metadata: languageName: node linkType: hard -"@inquirer/checkbox@npm:^4.1.2": - version: 4.1.5 - resolution: "@inquirer/checkbox@npm:4.1.5" +"@inquirer/checkbox@npm:^4.1.6": + version: 4.1.8 + resolution: "@inquirer/checkbox@npm:4.1.8" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/figures": "npm:^1.0.11" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/figures": "npm:^1.0.12" + "@inquirer/type": "npm:^3.0.7" ansi-escapes: "npm:^4.3.2" yoctocolors-cjs: "npm:^2.1.2" peerDependencies: @@ -2289,46 +2317,46 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/b984fb3ce8af34c327f3a85adcfe9fbd9eaac0c689bb9af79a5d55d508acb01de329747e8c923c9f4962e4006c353ed2dbe79e3fc9ae0f85f5851427dbed75ed + checksum: 10c0/6d726420b179c55b2f0001aaf6e339fa56e9e939afcbda31c386ab2e5d029ef6f2d392ec99c6a6950af1776a399791bbb88a635e4d047f1170b2ed8c5bba1e4c languageName: node linkType: hard -"@inquirer/confirm@npm:5.1.6": - version: 5.1.6 - resolution: "@inquirer/confirm@npm:5.1.6" +"@inquirer/confirm@npm:5.1.10": + version: 5.1.10 + resolution: "@inquirer/confirm@npm:5.1.10" dependencies: - "@inquirer/core": "npm:^10.1.7" - "@inquirer/type": "npm:^3.0.4" + "@inquirer/core": "npm:^10.1.11" + "@inquirer/type": "npm:^3.0.6" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/57b667f8096ec261504b613656e7b7718a238a73e059870a2b8e97c3127bc50085251100ed371250733b7cc5cd68122d8694d6a04a46de95d08bb590a8437b11 + checksum: 10c0/71a1b1c1007b0edd06984c356a9e13764ca917bdbf947a59ce0f55084d36e653daffe56b3806fc9959337aae80ff7b37eeaf01a40746e5f60de86475fdf0502a languageName: node linkType: hard -"@inquirer/confirm@npm:^5.1.6": - version: 5.1.9 - resolution: "@inquirer/confirm@npm:5.1.9" +"@inquirer/confirm@npm:^5.1.10": + version: 5.1.12 + resolution: "@inquirer/confirm@npm:5.1.12" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/e35c134303f8151074479d6704c048676b2684debfde18a46ff0fb7585a3ee31dea551899ddcb48169fbef5dfe64c1948d2d8ac17a6939bedd31bb54c39bbea4 + checksum: 10c0/581aedfe8ce45e177fb4470a12f874f5162a4396636bf4140edc5812ffc8ed0d1fa7e9bbc3a7af618203089a084f489e0b32112947eedc6930a766fad992449e languageName: node linkType: hard -"@inquirer/core@npm:^10.1.10, @inquirer/core@npm:^10.1.7": - version: 10.1.10 - resolution: "@inquirer/core@npm:10.1.10" +"@inquirer/core@npm:^10.1.11, @inquirer/core@npm:^10.1.13": + version: 10.1.13 + resolution: "@inquirer/core@npm:10.1.13" dependencies: - "@inquirer/figures": "npm:^1.0.11" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/figures": "npm:^1.0.12" + "@inquirer/type": "npm:^3.0.7" ansi-escapes: "npm:^4.3.2" cli-width: "npm:^4.1.0" mute-stream: "npm:^2.0.0" @@ -2340,158 +2368,158 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/8d0a3b725e42e40efbdc6ed087283795f1e36e642b119dd7dd3cbf31fce74bdbdb1b987da16159cd2475f45b2ede7e33293ae92bad3ac481832889c230df3fc0 + checksum: 10c0/919208a31307297d5a07a44b9ebe69a999ce1470b31a2e1b5a04538bc36624d2053808cd6c677637a61690af09bdbdd635bd7031b64e3dd86c5b18df3ca7c3f9 languageName: node linkType: hard -"@inquirer/editor@npm:^4.2.7": - version: 4.2.10 - resolution: "@inquirer/editor@npm:4.2.10" +"@inquirer/editor@npm:^4.2.11": + version: 4.2.13 + resolution: "@inquirer/editor@npm:4.2.13" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" external-editor: "npm:^3.1.0" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/b0213ad3ef45bc30427def4742db22126a1e6a59923033d21cae216276d8cf85d2af8abe432e5567ea24a7f6a31e23e7014e31308405cde684060b974e454a22 + checksum: 10c0/e1a27d75f737d7847905c14cf04d66d864eeb0f3e4cb2d36e34b51993741c5b70c22754171820c5d880a740765471455a8a98874285fd4a10b162342898f6c6b languageName: node linkType: hard -"@inquirer/expand@npm:^4.0.9": - version: 4.0.12 - resolution: "@inquirer/expand@npm:4.0.12" +"@inquirer/expand@npm:^4.0.13": + version: 4.0.15 + resolution: "@inquirer/expand@npm:4.0.15" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" yoctocolors-cjs: "npm:^2.1.2" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/f7abfc09ef942b63504677be5cf6fc443fb8090b5d43f7d2fe09983215cc01c6d82351cd1b596e90723b382a0931c9344d3280d54acf47d898782f4af2030b2e + checksum: 10c0/d558e367995a38a31d830de45d1e6831b73a798d6076c7fc8bdb639d3fac947a5d15810f7336b45c7712fc0e21fe8a2728f7f594550a20b6b4a839a18f9086cb languageName: node linkType: hard -"@inquirer/figures@npm:^1.0.11": - version: 1.0.11 - resolution: "@inquirer/figures@npm:1.0.11" - checksum: 10c0/6270e24eebbe42bbc4e7f8e761e906be66b4896787f31ab3e7484ad271c8edc90bce4ec20e232a5da447aee4fc73803397b2dda8cf645f4f7eea83e773b44e1e +"@inquirer/figures@npm:^1.0.12": + version: 1.0.12 + resolution: "@inquirer/figures@npm:1.0.12" + checksum: 10c0/08694288bdf9aa474571ca94272113a5ac443229519ce71447eba9eb7d5a2007901bdc3e92216d929a69746dcbac29683886c20e67b7864a7c7f6c59b99d3269 languageName: node linkType: hard -"@inquirer/input@npm:^4.1.6": - version: 4.1.9 - resolution: "@inquirer/input@npm:4.1.9" +"@inquirer/input@npm:^4.1.10": + version: 4.1.12 + resolution: "@inquirer/input@npm:4.1.12" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/db2e661ee482f3f27bf8cb77f054f99aba30291bd24d63b28db62204c4c5efc496199a9ddc03d01e0f6e6455d6967efb3ef92d2cd91e672905948c8c978c67a1 + checksum: 10c0/17b59547432f54a18ec573fde96c2c13c827f04faf694fc58239ec97e993ac6af151ed2a0521029c9199a4f422742dbe5dc23c20705748eafdc7dd26c7adca3a languageName: node linkType: hard -"@inquirer/number@npm:^3.0.9": - version: 3.0.12 - resolution: "@inquirer/number@npm:3.0.12" +"@inquirer/number@npm:^3.0.13": + version: 3.0.15 + resolution: "@inquirer/number@npm:3.0.15" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/e40726e1c60ba48a374b4867d212bd5e14cb12daae97a6536095906246ba6af91ec7fa68e347ba52607ba5bd84f9e804768d12fbc1250b2cac814187fb5e9628 + checksum: 10c0/724fc0d10611a0a9ea43280a94ed9194b8bb22d9a2af940eb37592d0cebc9e6e219edc4f79d8c176f53fd1b078543a9e4773037c7bde4b8d929a3034406eec90 languageName: node linkType: hard -"@inquirer/password@npm:^4.0.9": - version: 4.0.12 - resolution: "@inquirer/password@npm:4.0.12" +"@inquirer/password@npm:^4.0.13": + version: 4.0.15 + resolution: "@inquirer/password@npm:4.0.15" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" ansi-escapes: "npm:^4.3.2" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/03257985bbbd813c4f0c412effb691737517e348ca2590558864fe09877080daf90eb9910a60d097048fce9cf0c56a900e8f099854a9ae21512ceaadbd986e01 + checksum: 10c0/673d7c33dd0ee951c96f349d4fb66f8762f31c62188546da4d7af544202b638eecef6b8c78e62f43a46c72a5fa0712d94a56ed56f12e1badbb1001128bc991bd languageName: node linkType: hard -"@inquirer/prompts@npm:7.3.2": - version: 7.3.2 - resolution: "@inquirer/prompts@npm:7.3.2" +"@inquirer/prompts@npm:7.5.1": + version: 7.5.1 + resolution: "@inquirer/prompts@npm:7.5.1" dependencies: - "@inquirer/checkbox": "npm:^4.1.2" - "@inquirer/confirm": "npm:^5.1.6" - "@inquirer/editor": "npm:^4.2.7" - "@inquirer/expand": "npm:^4.0.9" - "@inquirer/input": "npm:^4.1.6" - "@inquirer/number": "npm:^3.0.9" - "@inquirer/password": "npm:^4.0.9" - "@inquirer/rawlist": "npm:^4.0.9" - "@inquirer/search": "npm:^3.0.9" - "@inquirer/select": "npm:^4.0.9" + "@inquirer/checkbox": "npm:^4.1.6" + "@inquirer/confirm": "npm:^5.1.10" + "@inquirer/editor": "npm:^4.2.11" + "@inquirer/expand": "npm:^4.0.13" + "@inquirer/input": "npm:^4.1.10" + "@inquirer/number": "npm:^3.0.13" + "@inquirer/password": "npm:^4.0.13" + "@inquirer/rawlist": "npm:^4.1.1" + "@inquirer/search": "npm:^3.0.13" + "@inquirer/select": "npm:^4.2.1" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/a318d7c2a963f753f4868151f2ce5673e214f3a6597430e712bc59ef9605c831b71a6b52a9c5ea2f312b23063d2ee9fd633e127cdc9e4999e95ef15a5e90c7e1 + checksum: 10c0/7f9cf44e1caff3eb61939f8abc9906acfec0d955c25e860212dc9e0e7bd6b9fb046415731e2407eb8a0745d282bb73c03587481090720255c4b828d85b830a08 languageName: node linkType: hard -"@inquirer/rawlist@npm:^4.0.9": - version: 4.1.0 - resolution: "@inquirer/rawlist@npm:4.1.0" +"@inquirer/rawlist@npm:^4.1.1": + version: 4.1.3 + resolution: "@inquirer/rawlist@npm:4.1.3" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/type": "npm:^3.0.7" yoctocolors-cjs: "npm:^2.1.2" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/0e92e8ee7eebd6b6ba7a81d968701f398dd372638f51dd8e3cb1fd3a03520bc0f713e112488d37fdb813f18928f338d82527c575e18a9bebde7ac3273045898c + checksum: 10c0/d653e730188e6849df540186cf7cb0f37f06c64d03f075b5a617145671fb015c27aeb60adb003d1a05a925795968efff0a3ae5a737a8d04c5679aa6fdc423662 languageName: node linkType: hard -"@inquirer/search@npm:^3.0.9": - version: 3.0.12 - resolution: "@inquirer/search@npm:3.0.12" +"@inquirer/search@npm:^3.0.13": + version: 3.0.15 + resolution: "@inquirer/search@npm:3.0.15" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/figures": "npm:^1.0.11" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/figures": "npm:^1.0.12" + "@inquirer/type": "npm:^3.0.7" yoctocolors-cjs: "npm:^2.1.2" peerDependencies: "@types/node": ">=18" peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/ef764f96b561b48e4d9a99716789d1fc0941d40884d1c9fea715c304360b46ec8c6e3edf603f7425a27d7743915564f405a3ccd1a72f0379a714be22887fe6ff + checksum: 10c0/32b29789e72e53a7b6cfdbc1803bd9e466c424d9f0368a145bef9e25c6fbde72af29cdd4667a785fee79de213f11fa76453f8120ea02ac5158dce259565ce7fd languageName: node linkType: hard -"@inquirer/select@npm:^4.0.9": - version: 4.2.0 - resolution: "@inquirer/select@npm:4.2.0" +"@inquirer/select@npm:^4.2.1": + version: 4.2.3 + resolution: "@inquirer/select@npm:4.2.3" dependencies: - "@inquirer/core": "npm:^10.1.10" - "@inquirer/figures": "npm:^1.0.11" - "@inquirer/type": "npm:^3.0.6" + "@inquirer/core": "npm:^10.1.13" + "@inquirer/figures": "npm:^1.0.12" + "@inquirer/type": "npm:^3.0.7" ansi-escapes: "npm:^4.3.2" yoctocolors-cjs: "npm:^2.1.2" peerDependencies: @@ -2499,7 +2527,7 @@ __metadata: peerDependenciesMeta: "@types/node": optional: true - checksum: 10c0/b3cfab393d54e48012336710b8e9267a0dd5551878a7727800da3d78602398720aab8777d5687b2138261fb731b0079d1c3ec0f4d0fee194bb1c4496c97b340b + checksum: 10c0/376535f50a9c2e19e27a5c81930cd1b5afa0b7d86228e5789782955a2d0a89bf5a8890a97943042e1b393094fe236ce97c9ff4bb777c9b44b22c1424f883b063 languageName: node linkType: hard @@ -2512,7 +2540,7 @@ __metadata: languageName: node linkType: hard -"@inquirer/type@npm:^3.0.4, @inquirer/type@npm:^3.0.6": +"@inquirer/type@npm:^3.0.6": version: 3.0.6 resolution: "@inquirer/type@npm:3.0.6" peerDependencies: @@ -2524,6 +2552,18 @@ __metadata: languageName: node linkType: hard +"@inquirer/type@npm:^3.0.7": + version: 3.0.7 + resolution: "@inquirer/type@npm:3.0.7" + peerDependencies: + "@types/node": ">=18" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: 10c0/bbaa33c274a10f70d3a587264e1db6dbfcd8c1458d595c54870d1d5b3fc113ab5063203ec12a098485bb9e2fcef1a87d8c6ecd2a6d44ddc575f5c4715379be5e + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -2655,55 +2695,62 @@ __metadata: languageName: node linkType: hard -"@listr2/prompt-adapter-inquirer@npm:2.0.18": - version: 2.0.18 - resolution: "@listr2/prompt-adapter-inquirer@npm:2.0.18" +"@listr2/prompt-adapter-inquirer@npm:2.0.22": + version: 2.0.22 + resolution: "@listr2/prompt-adapter-inquirer@npm:2.0.22" dependencies: "@inquirer/type": "npm:^1.5.5" peerDependencies: "@inquirer/prompts": ">= 3 < 8" - checksum: 10c0/580d2f0ae414cf3090c2fbfe4623649e448d930b3ff24b0211e64e0e037f1a3ffff5307bc36c10cdc0c4a35fc12f04190585e864c4ce05fbf5f062b41ff29e40 + checksum: 10c0/861fd7c66122551d078b10bfaf8927255be1e9169ed4b6b8844c323bfa1a6da7628dc0b0eb15c6830741d1d87f3b137131388a4f57bcc896b37a68e40cebe615 languageName: node linkType: hard -"@lmdb/lmdb-darwin-arm64@npm:3.2.6": - version: 3.2.6 - resolution: "@lmdb/lmdb-darwin-arm64@npm:3.2.6" +"@lmdb/lmdb-darwin-arm64@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-darwin-arm64@npm:3.3.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-darwin-x64@npm:3.2.6": - version: 3.2.6 - resolution: "@lmdb/lmdb-darwin-x64@npm:3.2.6" +"@lmdb/lmdb-darwin-x64@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-darwin-x64@npm:3.3.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm64@npm:3.2.6": - version: 3.2.6 - resolution: "@lmdb/lmdb-linux-arm64@npm:3.2.6" +"@lmdb/lmdb-linux-arm64@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-linux-arm64@npm:3.3.0" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm@npm:3.2.6": - version: 3.2.6 - resolution: "@lmdb/lmdb-linux-arm@npm:3.2.6" +"@lmdb/lmdb-linux-arm@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-linux-arm@npm:3.3.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@lmdb/lmdb-linux-x64@npm:3.2.6": - version: 3.2.6 - resolution: "@lmdb/lmdb-linux-x64@npm:3.2.6" +"@lmdb/lmdb-linux-x64@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-linux-x64@npm:3.3.0" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-win32-x64@npm:3.2.6": - version: 3.2.6 - resolution: "@lmdb/lmdb-win32-x64@npm:3.2.6" +"@lmdb/lmdb-win32-arm64@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-win32-arm64@npm:3.3.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@lmdb/lmdb-win32-x64@npm:3.3.0": + version: 3.3.0 + resolution: "@lmdb/lmdb-win32-x64@npm:3.3.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2919,14 +2966,14 @@ __metadata: languageName: node linkType: hard -"@ngtools/webpack@npm:19.2.10": - version: 19.2.10 - resolution: "@ngtools/webpack@npm:19.2.10" +"@ngtools/webpack@npm:20.0.4": + version: 20.0.4 + resolution: "@ngtools/webpack@npm:20.0.4" peerDependencies: - "@angular/compiler-cli": ^19.0.0 || ^19.2.0-next.0 - typescript: ">=5.5 <5.9" + "@angular/compiler-cli": ^20.0.0 + typescript: ">=5.8 <5.9" webpack: ^5.54.0 - checksum: 10c0/0b9c43df6c19baf61af16272d73ad915174d87109436f6d96ba807adc3b94c1767cf83f679ec79dbc9eb913767fb3ef521dee948f7bb6ec996224445f3cfb6f9 + checksum: 10c0/52a692495ebf92687786b4231e1bf27c9af553c21be12a18939cdc2b0afec6a902170647f2a2642ecc45d4d7737b4139eafd518fb7cccf970b7fd207a8a858c4 languageName: node linkType: hard @@ -3276,13 +3323,6 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.34.8" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@rollup/rollup-android-arm-eabi@npm:4.40.2": version: 4.40.2 resolution: "@rollup/rollup-android-arm-eabi@npm:4.40.2" @@ -3290,10 +3330,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-android-arm64@npm:4.34.8" - conditions: os=android & cpu=arm64 +"@rollup/rollup-android-arm-eabi@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.44.1" + conditions: os=android & cpu=arm languageName: node linkType: hard @@ -3304,10 +3344,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-darwin-arm64@npm:4.34.8" - conditions: os=darwin & cpu=arm64 +"@rollup/rollup-android-arm64@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-android-arm64@npm:4.44.1" + conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -3318,10 +3358,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-darwin-x64@npm:4.34.8" - conditions: os=darwin & cpu=x64 +"@rollup/rollup-darwin-arm64@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.44.1" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -3332,10 +3372,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.34.8" - conditions: os=freebsd & cpu=arm64 +"@rollup/rollup-darwin-x64@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.44.1" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -3346,10 +3386,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-freebsd-x64@npm:4.34.8" - conditions: os=freebsd & cpu=x64 +"@rollup/rollup-freebsd-arm64@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.44.1" + conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -3360,10 +3400,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.34.8" - conditions: os=linux & cpu=arm & libc=glibc +"@rollup/rollup-freebsd-x64@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-freebsd-x64@npm:4.44.1" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -3374,10 +3414,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.34.8" - conditions: os=linux & cpu=arm & libc=musl +"@rollup/rollup-linux-arm-gnueabihf@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.44.1" + conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard @@ -3388,10 +3428,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.34.8" - conditions: os=linux & cpu=arm64 & libc=glibc +"@rollup/rollup-linux-arm-musleabihf@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.44.1" + conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard @@ -3402,10 +3442,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.34.8" - conditions: os=linux & cpu=arm64 & libc=musl +"@rollup/rollup-linux-arm64-gnu@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.44.1" + conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard @@ -3416,10 +3456,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-loongarch64-gnu@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.34.8" - conditions: os=linux & cpu=loong64 & libc=glibc +"@rollup/rollup-linux-arm64-musl@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.44.1" + conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -3430,10 +3470,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.34.8" - conditions: os=linux & cpu=ppc64 & libc=glibc +"@rollup/rollup-linux-loongarch64-gnu@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.44.1" + conditions: os=linux & cpu=loong64 & libc=glibc languageName: node linkType: hard @@ -3444,10 +3484,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.34.8" - conditions: os=linux & cpu=riscv64 & libc=glibc +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.1" + conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard @@ -3458,6 +3498,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-gnu@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.44.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-musl@npm:4.40.2": version: 4.40.2 resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.40.2" @@ -3465,10 +3512,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.34.8" - conditions: os=linux & cpu=s390x & libc=glibc +"@rollup/rollup-linux-riscv64-musl@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.44.1" + conditions: os=linux & cpu=riscv64 & libc=musl languageName: node linkType: hard @@ -3479,10 +3526,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.34.8" - conditions: os=linux & cpu=x64 & libc=glibc +"@rollup/rollup-linux-s390x-gnu@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.44.1" + conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard @@ -3493,10 +3540,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.34.8" - conditions: os=linux & cpu=x64 & libc=musl +"@rollup/rollup-linux-x64-gnu@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.44.1" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -3507,10 +3554,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.34.8" - conditions: os=win32 & cpu=arm64 +"@rollup/rollup-linux-x64-musl@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.44.1" + conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -3521,10 +3568,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.34.8" - conditions: os=win32 & cpu=ia32 +"@rollup/rollup-win32-arm64-msvc@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.44.1" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -3535,10 +3582,10 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.34.8": - version: 4.34.8 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.34.8" - conditions: os=win32 & cpu=x64 +"@rollup/rollup-win32-ia32-msvc@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.44.1" + conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -3549,6 +3596,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.44.1": + version: 4.44.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.44.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rollup/wasm-node@npm:^4.24.0": version: 4.40.2 resolution: "@rollup/wasm-node@npm:4.40.2" @@ -3564,14 +3618,14 @@ __metadata: languageName: node linkType: hard -"@schematics/angular@npm:19.2.10": - version: 19.2.10 - resolution: "@schematics/angular@npm:19.2.10" +"@schematics/angular@npm:20.0.4": + version: 20.0.4 + resolution: "@schematics/angular@npm:20.0.4" dependencies: - "@angular-devkit/core": "npm:19.2.10" - "@angular-devkit/schematics": "npm:19.2.10" + "@angular-devkit/core": "npm:20.0.4" + "@angular-devkit/schematics": "npm:20.0.4" jsonc-parser: "npm:3.3.1" - checksum: 10c0/dd6bc6aaf762ee58ec1675b088464c9b45b4734f18ed53179a95ed3be5fb7e1792c6e858c635de85d42143882bfb8df5f2f238d408cd0967e7de02f07a7b10be + checksum: 10c0/446d1eafcee89cf6fcd4ae881dbafe7d5e47a32925dcf84eda94200be0c1d433eaa825e704a39007912265b3816cabd67b3752fa20d449d12df992ec82ee6703 languageName: node linkType: hard @@ -3633,13 +3687,6 @@ __metadata: languageName: node linkType: hard -"@sindresorhus/merge-streams@npm:^2.1.0": - version: 2.3.0 - resolution: "@sindresorhus/merge-streams@npm:2.3.0" - checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 - languageName: node - linkType: hard - "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.2 resolution: "@socket.io/component-emitter@npm:3.1.2" @@ -3874,10 +3921,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.6": - version: 1.0.6 - resolution: "@types/estree@npm:1.0.6" - checksum: 10c0/cdfd751f6f9065442cd40957c07fd80361c962869aa853c1c2fd03e101af8b9389d8ff4955a43a6fcfa223dd387a089937f95be0f3eec21ca527039fd2d9859a +"@types/estree@npm:1.0.8": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 languageName: node linkType: hard @@ -3893,7 +3940,7 @@ __metadata: languageName: node linkType: hard -"@types/express-serve-static-core@npm:^4.17.33": +"@types/express-serve-static-core@npm:^4.17.21, @types/express-serve-static-core@npm:^4.17.33": version: 4.19.6 resolution: "@types/express-serve-static-core@npm:4.19.6" dependencies: @@ -4002,7 +4049,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.9": +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.9": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db @@ -4367,12 +4414,12 @@ __metadata: languageName: node linkType: hard -"@vitejs/plugin-basic-ssl@npm:1.2.0": - version: 1.2.0 - resolution: "@vitejs/plugin-basic-ssl@npm:1.2.0" +"@vitejs/plugin-basic-ssl@npm:2.0.0": + version: 2.0.0 + resolution: "@vitejs/plugin-basic-ssl@npm:2.0.0" peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 - checksum: 10c0/0d360fcca01f91ade6e451edbea09a107ff9e95cd3c3766c7a069d1a168709df92d96c0bd1eccc66e2739a153e07c75a45321ec487450c0da942606200d8441d + vite: ^6.0.0 + checksum: 10c0/673f46dc5ee042f6fcfa7ecf514e717e770085f8979d4608cab952f3e9003fe7aed589cc812a67f3dcd5e80655975c6490ce8a07a4b6feef98766003256d4283 languageName: node linkType: hard @@ -5330,21 +5377,21 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:10.4.20": - version: 10.4.20 - resolution: "autoprefixer@npm:10.4.20" +"autoprefixer@npm:10.4.21": + version: 10.4.21 + resolution: "autoprefixer@npm:10.4.21" dependencies: - browserslist: "npm:^4.23.3" - caniuse-lite: "npm:^1.0.30001646" + browserslist: "npm:^4.24.4" + caniuse-lite: "npm:^1.0.30001702" fraction.js: "npm:^4.3.7" normalize-range: "npm:^0.1.2" - picocolors: "npm:^1.0.1" + picocolors: "npm:^1.1.1" postcss-value-parser: "npm:^4.2.0" peerDependencies: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 10c0/e1f00978a26e7c5b54ab12036d8c13833fad7222828fc90914771b1263f51b28c7ddb5803049de4e77696cbd02bb25cfc3634e80533025bb26c26aacdf938940 + checksum: 10c0/de5b71d26d0baff4bbfb3d59f7cf7114a6030c9eeb66167acf49a32c5b61c68e308f1e0f869d92334436a221035d08b51cd1b2f2c4689b8d955149423c16d4d4 languageName: node linkType: hard @@ -5385,16 +5432,15 @@ __metadata: languageName: node linkType: hard -"babel-loader@npm:9.2.1": - version: 9.2.1 - resolution: "babel-loader@npm:9.2.1" +"babel-loader@npm:10.0.0": + version: 10.0.0 + resolution: "babel-loader@npm:10.0.0" dependencies: - find-cache-dir: "npm:^4.0.0" - schema-utils: "npm:^4.0.0" + find-up: "npm:^5.0.0" peerDependencies: "@babel/core": ^7.12.0 - webpack: ">=5" - checksum: 10c0/efb82faff4c7c27e9c15bb28bf11c73200e61cf365118a9514e8d74dd489d0afc2a0d5aaa62cb4254eefc2ab631579224d95a03fd245410f28ea75e24de54ba4 + webpack: ">=5.61.0" + checksum: 10c0/882dfacde3ee24b432ad57e468832cd0821e2a410f6c5b75ff945f069a8956592b28c6c357df5bb03db73d2741ec3db5febb106ac0bb3591c3d4288f2cf4df0e languageName: node linkType: hard @@ -5559,9 +5605,9 @@ __metadata: languageName: node linkType: hard -"beasties@npm:0.3.2": - version: 0.3.2 - resolution: "beasties@npm:0.3.2" +"beasties@npm:0.3.4": + version: 0.3.4 + resolution: "beasties@npm:0.3.4" dependencies: css-select: "npm:^5.1.0" css-what: "npm:^6.1.0" @@ -5571,7 +5617,7 @@ __metadata: picocolors: "npm:^1.1.1" postcss: "npm:^8.4.49" postcss-media-query-parser: "npm:^0.2.3" - checksum: 10c0/ed6d4356f8b0448ce360eabfba80bd3d9f8d6592a6dc2fa78467e6522da62fee87d8116d7b94aa695dc51bef18f332b3962435a414b759939264a8754702faa3 + checksum: 10c0/e87d6eac3c2bb370789ae50a6e0818451694979bef05383283119fe2098ba6b92ab8210d68437adfb816d24b87b91a4716691e6d604e8876d5330ecc0e1c8c35 languageName: node linkType: hard @@ -5721,7 +5767,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.5, browserslist@npm:^4.22.1, browserslist@npm:^4.23.0, browserslist@npm:^4.23.3, browserslist@npm:^4.24.0, browserslist@npm:^4.24.4": +"browserslist@npm:^4.21.5, browserslist@npm:^4.22.1, browserslist@npm:^4.23.0, browserslist@npm:^4.24.0, browserslist@npm:^4.24.4": version: 4.24.5 resolution: "browserslist@npm:4.24.5" dependencies: @@ -5884,7 +5930,14 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001716": +"caniuse-lite@npm:^1.0.30001702": + version: 1.0.30001726 + resolution: "caniuse-lite@npm:1.0.30001726" + checksum: 10c0/2c5f91da7fd9ebf8c6b432818b1498ea28aca8de22b30dafabe2a2a6da1e014f10e67e14f8e68e872a0867b6b4cd6001558dde04e3ab9770c9252ca5c8849d0e + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001716": version: 1.0.30001717 resolution: "caniuse-lite@npm:1.0.30001717" checksum: 10c0/6c0bb1e5182fd578ebe97ee2203250849754a4e17d985839fab527ad27e125a4c4ffce3ece5505217fedf30ea0bbc17ac9f93e9ac525c0389ccba61c6e8345dc @@ -5951,6 +6004,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:^5.3.0": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef + languageName: node + linkType: hard + "chardet@npm:^0.7.0": version: 0.7.0 resolution: "chardet@npm:0.7.0" @@ -6090,7 +6150,7 @@ __metadata: languageName: node linkType: hard -"cli-spinners@npm:^2.5.0": +"cli-spinners@npm:^2.5.0, cli-spinners@npm:^2.9.2": version: 2.9.2 resolution: "cli-spinners@npm:2.9.2" checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 @@ -6147,6 +6207,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^9.0.1": + version: 9.0.1 + resolution: "cliui@npm:9.0.1" + dependencies: + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/13441832e9efe7c7a76bd2b8e683555c478d461a9f249dc5db9b17fe8d4b47fa9277b503914b90bd00e4a151abb6b9b02b2288972ffe2e5e3ca40bcb1c2330d3 + languageName: node + linkType: hard + "clone-deep@npm:^4.0.1": version: 4.0.1 resolution: "clone-deep@npm:4.0.1" @@ -6281,10 +6352,10 @@ __metadata: languageName: node linkType: hard -"commander@npm:^13.0.0": - version: 13.1.0 - resolution: "commander@npm:13.1.0" - checksum: 10c0/7b8c5544bba704fbe84b7cab2e043df8586d5c114a4c5b607f83ae5060708940ed0b5bd5838cf8ce27539cde265c1cbd59ce3c8c6b017ed3eec8943e3a415164 +"commander@npm:^14.0.0": + version: 14.0.0 + resolution: "commander@npm:14.0.0" + checksum: 10c0/73c4babfa558077868d84522b11ef56834165d472b9e86a634cd4c3ae7fc72d59af6377d8878e06bd570fe8f3161eced3cbe383c38f7093272bb65bd242b595b languageName: node linkType: hard @@ -6471,19 +6542,18 @@ __metadata: languageName: node linkType: hard -"copy-webpack-plugin@npm:12.0.2": - version: 12.0.2 - resolution: "copy-webpack-plugin@npm:12.0.2" +"copy-webpack-plugin@npm:13.0.0": + version: 13.0.0 + resolution: "copy-webpack-plugin@npm:13.0.0" dependencies: - fast-glob: "npm:^3.3.2" glob-parent: "npm:^6.0.1" - globby: "npm:^14.0.0" normalize-path: "npm:^3.0.0" schema-utils: "npm:^4.2.0" serialize-javascript: "npm:^6.0.2" + tinyglobby: "npm:^0.2.12" peerDependencies: webpack: ^5.1.0 - checksum: 10c0/1a2715a1280a37b81b7040b89ed962db4aa75475b164f84f266fa4e81f209269b13f8bff10b104dff7558854bafedcdd4f30c40fd23ecd8fa28af45516b459cd + checksum: 10c0/955037f77c6beb249b690710c35bacceb03b61bb5b7c5fc59ac7dff122c706eb794ef601bc3d9bbdb1350bda3e2615e0b43bf33f1ce2ca14ed934d9a89f43637 languageName: node linkType: hard @@ -7294,7 +7364,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0, entities@npm:^4.3.0": +"entities@npm:^4.2.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 @@ -7493,44 +7563,44 @@ __metadata: languageName: node linkType: hard -"esbuild-wasm@npm:0.25.1": - version: 0.25.1 - resolution: "esbuild-wasm@npm:0.25.1" +"esbuild-wasm@npm:0.25.5": + version: 0.25.5 + resolution: "esbuild-wasm@npm:0.25.5" bin: esbuild: bin/esbuild - checksum: 10c0/9cc20c0f1c31c686f26202b86279a80307225ac82e52f1713d2971638baf7afd7e89ab5602648f53e1b9c331b7bfea99a76a75e38bb310ecb18c655fa7a9fd63 - languageName: node - linkType: hard - -"esbuild@npm:0.25.1": - version: 0.25.1 - resolution: "esbuild@npm:0.25.1" - dependencies: - "@esbuild/aix-ppc64": "npm:0.25.1" - "@esbuild/android-arm": "npm:0.25.1" - "@esbuild/android-arm64": "npm:0.25.1" - "@esbuild/android-x64": "npm:0.25.1" - "@esbuild/darwin-arm64": "npm:0.25.1" - "@esbuild/darwin-x64": "npm:0.25.1" - "@esbuild/freebsd-arm64": "npm:0.25.1" - "@esbuild/freebsd-x64": "npm:0.25.1" - "@esbuild/linux-arm": "npm:0.25.1" - "@esbuild/linux-arm64": "npm:0.25.1" - "@esbuild/linux-ia32": "npm:0.25.1" - "@esbuild/linux-loong64": "npm:0.25.1" - "@esbuild/linux-mips64el": "npm:0.25.1" - "@esbuild/linux-ppc64": "npm:0.25.1" - "@esbuild/linux-riscv64": "npm:0.25.1" - "@esbuild/linux-s390x": "npm:0.25.1" - "@esbuild/linux-x64": "npm:0.25.1" - "@esbuild/netbsd-arm64": "npm:0.25.1" - "@esbuild/netbsd-x64": "npm:0.25.1" - "@esbuild/openbsd-arm64": "npm:0.25.1" - "@esbuild/openbsd-x64": "npm:0.25.1" - "@esbuild/sunos-x64": "npm:0.25.1" - "@esbuild/win32-arm64": "npm:0.25.1" - "@esbuild/win32-ia32": "npm:0.25.1" - "@esbuild/win32-x64": "npm:0.25.1" + checksum: 10c0/5893a8e09ec576154a2144989708998ec6081f2c6b6353cdba7df1d0ca9f12db8292302391ba04decdde45119a384cfbba19307c15c05991da826202f7357129 + languageName: node + linkType: hard + +"esbuild@npm:0.25.5": + version: 0.25.5 + resolution: "esbuild@npm:0.25.5" + dependencies: + "@esbuild/aix-ppc64": "npm:0.25.5" + "@esbuild/android-arm": "npm:0.25.5" + "@esbuild/android-arm64": "npm:0.25.5" + "@esbuild/android-x64": "npm:0.25.5" + "@esbuild/darwin-arm64": "npm:0.25.5" + "@esbuild/darwin-x64": "npm:0.25.5" + "@esbuild/freebsd-arm64": "npm:0.25.5" + "@esbuild/freebsd-x64": "npm:0.25.5" + "@esbuild/linux-arm": "npm:0.25.5" + "@esbuild/linux-arm64": "npm:0.25.5" + "@esbuild/linux-ia32": "npm:0.25.5" + "@esbuild/linux-loong64": "npm:0.25.5" + "@esbuild/linux-mips64el": "npm:0.25.5" + "@esbuild/linux-ppc64": "npm:0.25.5" + "@esbuild/linux-riscv64": "npm:0.25.5" + "@esbuild/linux-s390x": "npm:0.25.5" + "@esbuild/linux-x64": "npm:0.25.5" + "@esbuild/netbsd-arm64": "npm:0.25.5" + "@esbuild/netbsd-x64": "npm:0.25.5" + "@esbuild/openbsd-arm64": "npm:0.25.5" + "@esbuild/openbsd-x64": "npm:0.25.5" + "@esbuild/sunos-x64": "npm:0.25.5" + "@esbuild/win32-arm64": "npm:0.25.5" + "@esbuild/win32-ia32": "npm:0.25.5" + "@esbuild/win32-x64": "npm:0.25.5" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -7584,7 +7654,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10c0/80fca30dd0f21aec23fdfab34f0a8d5f55df5097dd7f475f2ab561d45662c32ee306f5649071cd1a0ba0614b164c48ca3dc3ee1551a4daf204b8af90e4d893f5 + checksum: 10c0/aba8cbc11927fa77562722ed5e95541ce2853f67ad7bdc40382b558abc2e0ec57d92ffb820f082ba2047b4ef9f3bc3da068cdebe30dfd3850cfa3827a78d604e languageName: node linkType: hard @@ -8266,7 +8336,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:3.3.3, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2, fast-glob@npm:^3.3.3": +"fast-glob@npm:3.3.3, fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -8444,7 +8514,7 @@ __metadata: languageName: node linkType: hard -"find-cache-dir@npm:^3.3.1, find-cache-dir@npm:^3.3.2": +"find-cache-dir@npm:^3.3.1": version: 3.3.2 resolution: "find-cache-dir@npm:3.3.2" dependencies: @@ -8455,13 +8525,13 @@ __metadata: languageName: node linkType: hard -"find-cache-dir@npm:^4.0.0": - version: 4.0.0 - resolution: "find-cache-dir@npm:4.0.0" +"find-cache-directory@npm:^6.0.0": + version: 6.0.0 + resolution: "find-cache-directory@npm:6.0.0" dependencies: common-path-prefix: "npm:^3.0.0" - pkg-dir: "npm:^7.0.0" - checksum: 10c0/0faa7956974726c8769671de696d24c643ca1e5b8f7a2401283caa9e07a5da093293e0a0f4bd18c920ec981d2ef945c7f5b946cde268dfc9077d833ad0293cff + pkg-dir: "npm:^8.0.0" + checksum: 10c0/a4ac657f63bc3aac120f71d90dde0fe471b80d8d7572f2cd2c38499a27f30d87526b4590d217fb3732f5bf4a2d11e15d85e39ea12965731824e3e4da0e01b246 languageName: node linkType: hard @@ -8481,6 +8551,13 @@ __metadata: languageName: node linkType: hard +"find-up-simple@npm:^1.0.0": + version: 1.0.1 + resolution: "find-up-simple@npm:1.0.1" + checksum: 10c0/ad34de157b7db925d50ff78302fefb28e309f3bc947c93ffca0f9b0bccf9cf1a2dc57d805d5c94ec9fc60f4838f5dbdfd2a48ecd77c23015fa44c6dd5f60bc40 + languageName: node + linkType: hard + "find-up@npm:^4.0.0, find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" @@ -8501,16 +8578,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^6.3.0": - version: 6.3.0 - resolution: "find-up@npm:6.3.0" - dependencies: - locate-path: "npm:^7.1.0" - path-exists: "npm:^5.0.0" - checksum: 10c0/07e0314362d316b2b13f7f11ea4692d5191e718ca3f7264110127520f3347996349bf9e16805abae3e196805814bc66ef4bff2b8904dc4a6476085fc9b0eba07 - languageName: node - linkType: hard - "find-versions@npm:^4.0.0": version: 4.0.0 resolution: "find-versions@npm:4.0.0" @@ -8999,20 +9066,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^14.0.0": - version: 14.1.0 - resolution: "globby@npm:14.1.0" - dependencies: - "@sindresorhus/merge-streams": "npm:^2.1.0" - fast-glob: "npm:^3.3.3" - ignore: "npm:^7.0.3" - path-type: "npm:^6.0.0" - slash: "npm:^5.1.0" - unicorn-magic: "npm:^0.3.0" - checksum: 10c0/527a1063c5958255969620c6fa4444a2b2e9278caddd571d46dfbfa307cb15977afb746e84d682ba5b6c94fc081e8997f80ff05dd235441ba1cb16f86153e58e - languageName: node - linkType: hard - "globby@npm:^5.0.0": version: 5.0.0 resolution: "globby@npm:5.0.0" @@ -9491,10 +9544,10 @@ __metadata: languageName: node linkType: hard -"ignore@npm:6.0.2": - version: 6.0.2 - resolution: "ignore@npm:6.0.2" - checksum: 10c0/9a38feac1861906a78ba0f03e8ef3cd6b0526dce2a1a84e1009324b557763afeb9c3ebcc04666b21f7bbf71adda45e76781bb9e2eaa0903d45dcaded634454f5 +"ignore@npm:7.0.5": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d languageName: node linkType: hard @@ -9505,13 +9558,6 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^7.0.3": - version: 7.0.4 - resolution: "ignore@npm:7.0.4" - checksum: 10c0/90e1f69ce352b9555caecd9cbfd07abe7626d312a6f90efbbb52c7edca6ea8df065d66303863b30154ab1502afb2da8bc59d5b04e1719a52ef75bbf675c488eb - languageName: node - linkType: hard - "image-size@npm:~0.5.0": version: 0.5.5 resolution: "image-size@npm:0.5.5" @@ -9954,6 +10000,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: 10c0/801c8f6064f85199dc6bf99b5dd98db3282e930c3bc197b32f2c5b89313bb578a07d1b8a01365c4348c2927229234f3681eb861b9c2c92bee72ff397390fa600 + languageName: node + linkType: hard + "is-ip@npm:^3.1.0": version: 3.1.0 resolution: "is-ip@npm:3.1.0" @@ -10174,6 +10227,20 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.2": version: 2.0.2 resolution: "is-weakmap@npm:2.0.2" @@ -10926,9 +10993,9 @@ __metadata: languageName: node linkType: hard -"less-loader@npm:12.2.0": - version: 12.2.0 - resolution: "less-loader@npm:12.2.0" +"less-loader@npm:12.3.0": + version: 12.3.0 + resolution: "less-loader@npm:12.3.0" peerDependencies: "@rspack/core": 0.x || 1.x less: ^3.5.0 || ^4.0.0 @@ -10938,46 +11005,11 @@ __metadata: optional: true webpack: optional: true - checksum: 10c0/54eea545727930801d2ccc0b586332cd07d0f922b14ab7c8b3f03199944d770ac363081081ed2fda5f23da904336367cb2bb40007c033970dce25f7f9c906ba2 - languageName: node - linkType: hard - -"less@npm:4.2.2": - version: 4.2.2 - resolution: "less@npm:4.2.2" - dependencies: - copy-anything: "npm:^2.0.1" - errno: "npm:^0.1.1" - graceful-fs: "npm:^4.1.2" - image-size: "npm:~0.5.0" - make-dir: "npm:^2.1.0" - mime: "npm:^1.4.1" - needle: "npm:^3.1.0" - parse-node-version: "npm:^1.0.1" - source-map: "npm:~0.6.0" - tslib: "npm:^2.3.0" - dependenciesMeta: - errno: - optional: true - graceful-fs: - optional: true - image-size: - optional: true - make-dir: - optional: true - mime: - optional: true - needle: - optional: true - source-map: - optional: true - bin: - lessc: bin/lessc - checksum: 10c0/d472c203a41fb3722a9bf5677f5348e59d8b6589bf2e3933a77c305b42b2ebbe1e3accf145f05b6d2415ba1dad08add7803646947bf867eec7a2a279d758d99a + checksum: 10c0/11814ce601fe9a9a148f28643ffcb6041939b1142b21538c2c0a7a220f79e35f7eeffd4ac5f4d9495e41f1f25aabb98652fa18792d22eebb1d151716d8297332 languageName: node linkType: hard -"less@npm:^4.2.0": +"less@npm:4.3.0, less@npm:^4.2.0": version: 4.3.0 resolution: "less@npm:4.3.0" dependencies: @@ -11062,9 +11094,9 @@ __metadata: languageName: node linkType: hard -"listr2@npm:8.2.5": - version: 8.2.5 - resolution: "listr2@npm:8.2.5" +"listr2@npm:8.3.3": + version: 8.3.3 + resolution: "listr2@npm:8.3.3" dependencies: cli-truncate: "npm:^4.0.0" colorette: "npm:^2.0.20" @@ -11072,20 +11104,21 @@ __metadata: log-update: "npm:^6.1.0" rfdc: "npm:^1.4.1" wrap-ansi: "npm:^9.0.0" - checksum: 10c0/f5a9599514b00c27d7eb32d1117c83c61394b2a985ec20e542c798bf91cf42b19340215701522736f5b7b42f557e544afeadec47866e35e5d4f268f552729671 + checksum: 10c0/0792f8a7fd482fa516e21689e012e07081cab3653172ca606090622cfa0024c784a1eba8095a17948a0e9a4aa98a80f7c9c90f78a0dd35173d6802f9cc123a82 languageName: node linkType: hard -"lmdb@npm:3.2.6": - version: 3.2.6 - resolution: "lmdb@npm:3.2.6" - dependencies: - "@lmdb/lmdb-darwin-arm64": "npm:3.2.6" - "@lmdb/lmdb-darwin-x64": "npm:3.2.6" - "@lmdb/lmdb-linux-arm": "npm:3.2.6" - "@lmdb/lmdb-linux-arm64": "npm:3.2.6" - "@lmdb/lmdb-linux-x64": "npm:3.2.6" - "@lmdb/lmdb-win32-x64": "npm:3.2.6" +"lmdb@npm:3.3.0": + version: 3.3.0 + resolution: "lmdb@npm:3.3.0" + dependencies: + "@lmdb/lmdb-darwin-arm64": "npm:3.3.0" + "@lmdb/lmdb-darwin-x64": "npm:3.3.0" + "@lmdb/lmdb-linux-arm": "npm:3.3.0" + "@lmdb/lmdb-linux-arm64": "npm:3.3.0" + "@lmdb/lmdb-linux-x64": "npm:3.3.0" + "@lmdb/lmdb-win32-arm64": "npm:3.3.0" + "@lmdb/lmdb-win32-x64": "npm:3.3.0" msgpackr: "npm:^1.11.2" node-addon-api: "npm:^6.1.0" node-gyp: "npm:latest" @@ -11103,11 +11136,13 @@ __metadata: optional: true "@lmdb/lmdb-linux-x64": optional: true + "@lmdb/lmdb-win32-arm64": + optional: true "@lmdb/lmdb-win32-x64": optional: true bin: download-lmdb-prebuilds: bin/download-prebuilds.js - checksum: 10c0/1b7a4e17351f41ae5cbe79a8db7782f34f24484ffbcba6614b91c7d5d4431284c55d8912065e50d05598de0d6dcd0417608d3705d930a207fbf76019219cc43d + checksum: 10c0/91b22b552ad79ce39d05dc0025613fa9edd61762fadbac280c400fb0d7b680e3880833d7067e1f537ed3ef4376ea58c2a4b1ec79b83425866f2bce116e56f910 languageName: node linkType: hard @@ -11166,15 +11201,6 @@ __metadata: languageName: node linkType: hard -"locate-path@npm:^7.1.0": - version: 7.2.0 - resolution: "locate-path@npm:7.2.0" - dependencies: - p-locate: "npm:^6.0.0" - checksum: 10c0/139e8a7fe11cfbd7f20db03923cacfa5db9e14fa14887ea121345597472b4a63c1a42a8a5187defeeff6acf98fd568da7382aa39682d38f0af27433953a97751 - languageName: node - linkType: hard - "lodash.camelcase@npm:^4.3.0": version: 4.3.0 resolution: "lodash.camelcase@npm:4.3.0" @@ -11213,6 +11239,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 10c0/36636cacedba8f067d2deb4aad44e91a89d9efb3ead27e1846e7b82c9a10ea2e3a7bd6ce28a7ca616bebc60954ff25c67b0f92d20a6a746bb3cc52c3701891f6 + languageName: node + linkType: hard + "log-update@npm:^4.0.0": version: 4.0.0 resolution: "log-update@npm:4.0.0" @@ -11304,7 +11340,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:0.30.17": +"magic-string@npm:0.30.17, magic-string@npm:^0.30.17": version: 0.30.17 resolution: "magic-string@npm:0.30.17" dependencies: @@ -11956,36 +11992,37 @@ __metadata: languageName: node linkType: hard -"ng-packagr@npm:^19.0.0": - version: 19.2.2 - resolution: "ng-packagr@npm:19.2.2" +"ng-packagr@npm:^20.0.1": + version: 20.0.1 + resolution: "ng-packagr@npm:20.0.1" dependencies: + "@ampproject/remapping": "npm:^2.3.0" "@rollup/plugin-json": "npm:^6.1.0" "@rollup/wasm-node": "npm:^4.24.0" ajv: "npm:^8.17.1" ansi-colors: "npm:^4.1.3" browserslist: "npm:^4.22.1" chokidar: "npm:^4.0.1" - commander: "npm:^13.0.0" - convert-source-map: "npm:^2.0.0" + commander: "npm:^14.0.0" dependency-graph: "npm:^1.0.0" esbuild: "npm:^0.25.0" - fast-glob: "npm:^3.3.2" - find-cache-dir: "npm:^3.3.2" + find-cache-directory: "npm:^6.0.0" injection-js: "npm:^2.4.0" jsonc-parser: "npm:^3.3.1" less: "npm:^4.2.0" - ora: "npm:^5.1.0" - piscina: "npm:^4.7.0" + ora: "npm:^8.2.0" + piscina: "npm:^5.0.0" postcss: "npm:^8.4.47" rollup: "npm:^4.24.0" + rollup-plugin-dts: "npm:^6.2.0" rxjs: "npm:^7.8.1" sass: "npm:^1.81.0" + tinyglobby: "npm:^0.2.12" peerDependencies: - "@angular/compiler-cli": ^19.0.0 || ^19.1.0-next.0 || ^19.2.0-next.0 + "@angular/compiler-cli": ^20.0.0 || ^20.1.0-next.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 tslib: ^2.3.0 - typescript: ">=5.5 <5.9" + typescript: ">=5.8 <5.9" dependenciesMeta: rollup: optional: true @@ -11993,8 +12030,8 @@ __metadata: tailwindcss: optional: true bin: - ng-packagr: cli/main.js - checksum: 10c0/1f2802f2ea418b2a88503a3142c1b04bff4a7cd1673bced2fcb5189260fe78fd9b575f6746255268757d17ce456d019e9a96a1d6dd33995bc1212be776e24321 + ng-packagr: src/cli/main.js + checksum: 10c0/4520298e192a62edfaf13f21e9706e083f0bd8f0584b808c5d1906d97cc5faeae5b86ddd72943d52d6ea128035f803c384a7205a64d27d60fe836eb0fa29dd7c languageName: node linkType: hard @@ -12002,26 +12039,26 @@ __metadata: version: 0.0.0-use.local resolution: "ngx-datatable@workspace:." dependencies: - "@angular-devkit/build-angular": "npm:^19.1.4" - "@angular-devkit/core": "npm:^19.1.4" - "@angular-devkit/schematics": "npm:^19.1.4" - "@angular-eslint/builder": "npm:^18.0.0" - "@angular-eslint/eslint-plugin": "npm:^18.0.0" - "@angular-eslint/eslint-plugin-template": "npm:^18.0.0" - "@angular-eslint/schematics": "npm:^18.0.0" - "@angular-eslint/template-parser": "npm:^18.0.0" - "@angular/animations": "npm:^19.1.4" - "@angular/cdk": "npm:^18.2.14" - "@angular/cli": "npm:^19.1.6" - "@angular/common": "npm:^19.1.4" - "@angular/compiler": "npm:^19.1.4" - "@angular/compiler-cli": "npm:^19.1.4" - "@angular/core": "npm:^19.1.4" - "@angular/forms": "npm:^19.1.4" - "@angular/language-service": "npm:^19.1.4" - "@angular/platform-browser": "npm:^19.1.4" - "@angular/platform-browser-dynamic": "npm:^19.1.4" - "@angular/router": "npm:^19.1.4" + "@angular-devkit/build-angular": "npm:^20.0.4" + "@angular-devkit/core": "npm:^20.0.4" + "@angular-devkit/schematics": "npm:^20.0.4" + "@angular-eslint/builder": "npm:^20.1.1" + "@angular-eslint/eslint-plugin": "npm:^20.1.1" + "@angular-eslint/eslint-plugin-template": "npm:^20.1.1" + "@angular-eslint/schematics": "npm:^20.1.1" + "@angular-eslint/template-parser": "npm:^20.1.1" + "@angular/animations": "npm:^20.0.5" + "@angular/cdk": "npm:^20.0.4" + "@angular/cli": "npm:^20.0.4" + "@angular/common": "npm:^20.0.5" + "@angular/compiler": "npm:^20.0.5" + "@angular/compiler-cli": "npm:^20.0.5" + "@angular/core": "npm:^20.0.5" + "@angular/forms": "npm:^20.0.5" + "@angular/language-service": "npm:^20.0.5" + "@angular/platform-browser": "npm:^20.0.5" + "@angular/platform-browser-dynamic": "npm:^20.0.5" + "@angular/router": "npm:^20.0.5" "@swimlane/eslint-config": "npm:^2.0.0" "@swimlane/prettier-config-swimlane": "npm:^3.0.2" "@types/jasmine": "npm:4.3.0" @@ -12053,7 +12090,7 @@ __metadata: karma-coverage-istanbul-reporter: "npm:^3.0.3" karma-jasmine: "npm:5.1.0" karma-jasmine-html-reporter: "npm:2.0.0" - ng-packagr: "npm:^19.0.0" + ng-packagr: "npm:^20.0.1" npm-run-all: "npm:^4.1.5" prettier: "npm:2.7.1" pretty-quick: "npm:3.1.3" @@ -12063,7 +12100,7 @@ __metadata: scss-bundle: "npm:^3.1.1" ts-node: "npm:10.9.1" tslib: "npm:~2.4.0" - typescript: "npm:5.7.3" + typescript: "npm:5.8.3" zone.js: "npm:~0.15.0" languageName: unknown linkType: soft @@ -12223,12 +12260,12 @@ __metadata: languageName: node linkType: hard -"npm-packlist@npm:^9.0.0": - version: 9.0.0 - resolution: "npm-packlist@npm:9.0.0" +"npm-packlist@npm:^10.0.0": + version: 10.0.0 + resolution: "npm-packlist@npm:10.0.0" dependencies: ignore-walk: "npm:^7.0.0" - checksum: 10c0/3eb9e877fff81ed1f97b86a387a13a7d0136a26c4c21d8fab7e49be653e71d604ba63091ec80e3a0b1d1fd879639eab91ddda1a8df45d7631795b83911f2f9b8 + checksum: 10c0/be8cb82c4f9b6fdfba2e3379c538949d3ea7aeb303436db013aaccd8ad1ff49d9f894d7fa4684f9d3016b7944dcc3f0bfc8c3d10c535fa7cd29314a8aad4b80f languageName: node linkType: hard @@ -12481,19 +12518,7 @@ __metadata: languageName: node linkType: hard -"open@npm:10.1.0": - version: 10.1.0 - resolution: "open@npm:10.1.0" - dependencies: - default-browser: "npm:^5.2.1" - define-lazy-prop: "npm:^3.0.0" - is-inside-container: "npm:^1.0.0" - is-wsl: "npm:^3.1.0" - checksum: 10c0/c86d0b94503d5f735f674158d5c5d339c25ec2927562f00ee74590727292ed23e1b8d9336cb41ffa7e1fa4d3641d29b199b4ea37c78cb557d72b511743e90ebb - languageName: node - linkType: hard - -"open@npm:^10.0.3": +"open@npm:10.1.2, open@npm:^10.0.3": version: 10.1.2 resolution: "open@npm:10.1.2" dependencies: @@ -12539,7 +12564,7 @@ __metadata: languageName: node linkType: hard -"ora@npm:5.4.1, ora@npm:^5.1.0": +"ora@npm:5.4.1": version: 5.4.1 resolution: "ora@npm:5.4.1" dependencies: @@ -12556,6 +12581,23 @@ __metadata: languageName: node linkType: hard +"ora@npm:8.2.0, ora@npm:^8.2.0": + version: 8.2.0 + resolution: "ora@npm:8.2.0" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/7d9291255db22e293ea164f520b6042a3e906576ab06c9cf408bf9ef5664ba0a9f3bd258baa4ada058cfcc2163ef9b6696d51237a866682ce33295349ba02c3a + languageName: node + linkType: hard + "ordered-binary@npm:^1.5.3": version: 1.5.3 resolution: "ordered-binary@npm:1.5.3" @@ -12615,15 +12657,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^4.0.0": - version: 4.0.0 - resolution: "p-limit@npm:4.0.0" - dependencies: - yocto-queue: "npm:^1.0.0" - checksum: 10c0/a56af34a77f8df2ff61ddfb29431044557fcbcb7642d5a3233143ebba805fc7306ac1d448de724352861cb99de934bc9ab74f0d16fe6a5460bdbdf938de875ad - languageName: node - linkType: hard - "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -12642,15 +12675,6 @@ __metadata: languageName: node linkType: hard -"p-locate@npm:^6.0.0": - version: 6.0.0 - resolution: "p-locate@npm:6.0.0" - dependencies: - p-limit: "npm:^4.0.0" - checksum: 10c0/d72fa2f41adce59c198270aa4d3c832536c87a1806e0f69dffb7c1a7ca998fb053915ca833d90f166a8c082d3859eabfed95f01698a3214c20df6bb8de046312 - languageName: node - linkType: hard - "p-map@npm:^7.0.2": version: 7.0.3 resolution: "p-map@npm:7.0.3" @@ -12718,9 +12742,9 @@ __metadata: languageName: node linkType: hard -"pacote@npm:20.0.0": - version: 20.0.0 - resolution: "pacote@npm:20.0.0" +"pacote@npm:21.0.0": + version: 21.0.0 + resolution: "pacote@npm:21.0.0" dependencies: "@npmcli/git": "npm:^6.0.0" "@npmcli/installed-package-contents": "npm:^3.0.0" @@ -12731,7 +12755,7 @@ __metadata: fs-minipass: "npm:^3.0.0" minipass: "npm:^7.0.2" npm-package-arg: "npm:^12.0.0" - npm-packlist: "npm:^9.0.0" + npm-packlist: "npm:^10.0.0" npm-pick-manifest: "npm:^10.0.0" npm-registry-fetch: "npm:^18.0.0" proc-log: "npm:^5.0.0" @@ -12741,7 +12765,7 @@ __metadata: tar: "npm:^6.1.11" bin: pacote: bin/index.js - checksum: 10c0/435c385446ecc81b1eb1584f4fa3cb102e630a22877f39b5c1a92eddfeaf222bd027b205e32632be2801e3bcbe525165cdffb5ceca5c13bbc81f8132fe1ba49e + checksum: 10c0/406eabb2185f87526f07b2b7540a96c91f07c8782f9d1651ef022844f021922ee1507161c43dd16616ab3f15a2d13a1bfe217bfd79731020c725373c4e713022 languageName: node linkType: hard @@ -12802,14 +12826,14 @@ __metadata: languageName: node linkType: hard -"parse5-html-rewriting-stream@npm:7.0.0": - version: 7.0.0 - resolution: "parse5-html-rewriting-stream@npm:7.0.0" +"parse5-html-rewriting-stream@npm:7.1.0": + version: 7.1.0 + resolution: "parse5-html-rewriting-stream@npm:7.1.0" dependencies: - entities: "npm:^4.3.0" + entities: "npm:^6.0.0" parse5: "npm:^7.0.0" parse5-sax-parser: "npm:^7.0.0" - checksum: 10c0/658d3e2bae038e515bcce6ab6fba9484332d641f3ba82a6450649e1105492fe0a353101dbf751bddfc063509d06b55260bd4567970df3eaaa8391ae79d25ffbf + checksum: 10c0/e44a2f52a0012ace6c04e4eb7b9733dabdc86d9a6d7ffc30e980b89bfaa6cab7f1e74c2a4d09017037247119589eb0532c1ab0790b6ce64674cbbcc2bbaf0de7 languageName: node linkType: hard @@ -12859,13 +12883,6 @@ __metadata: languageName: node linkType: hard -"path-exists@npm:^5.0.0": - version: 5.0.0 - resolution: "path-exists@npm:5.0.0" - checksum: 10c0/b170f3060b31604cde93eefdb7392b89d832dfbc1bed717c9718cbe0f230c1669b7e75f87e19901da2250b84d092989a0f9e44d2ef41deb09aa3ad28e691a40a - languageName: node - linkType: hard - "path-is-absolute@npm:1.0.1, path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -12934,13 +12951,6 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^6.0.0": - version: 6.0.0 - resolution: "path-type@npm:6.0.0" - checksum: 10c0/55baa8b1187d6dc683d5a9cfcc866168d6adff58e5db91126795376d818eee46391e00b2a4d53e44d844c7524a7d96aa68cc68f4f3e500d3d069a39e6535481c - languageName: node - linkType: hard - "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -12955,7 +12965,7 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.1": +"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 @@ -13022,27 +13032,27 @@ __metadata: languageName: node linkType: hard -"piscina@npm:4.8.0": - version: 4.8.0 - resolution: "piscina@npm:4.8.0" +"piscina@npm:5.1.1": + version: 5.1.1 + resolution: "piscina@npm:5.1.1" dependencies: "@napi-rs/nice": "npm:^1.0.1" dependenciesMeta: "@napi-rs/nice": optional: true - checksum: 10c0/963ee0dc0862e936c88357b21b0b4fa32407ab21e9600756504411f368dcfae7478c8a19e13d0dd8afed56a8252a8e5967ee4413aa33dd436751b7ee2804531e + checksum: 10c0/ca6cca2bff00c9808e199483c65f563d85e2cc5b0d2ce7238892d191e0f04e55dacaf857da2ee9e71c2cab50b55201bf29a6d8c17bbaed061e12bb3a08a71fbf languageName: node linkType: hard -"piscina@npm:^4.7.0": - version: 4.9.2 - resolution: "piscina@npm:4.9.2" +"piscina@npm:^5.0.0": + version: 5.1.2 + resolution: "piscina@npm:5.1.2" dependencies: "@napi-rs/nice": "npm:^1.0.1" dependenciesMeta: "@napi-rs/nice": optional: true - checksum: 10c0/ab67830065ff41523cd901db41b11045cb00a0be43bf79323ff7b4ef2fbce5e3a56ad440d99d6c3944ce94451a0a69fd175500e3220b21efe54142e601322189 + checksum: 10c0/12c8ee0a678d605ada768ced79428f2c4689b699e7c7bdd2f6c2abc794f0f6062fd43b8983ee45980956e25afc8dcb08a4dd2b7bc5647fe023fe45490e22d093 languageName: node linkType: hard @@ -13064,12 +13074,12 @@ __metadata: languageName: node linkType: hard -"pkg-dir@npm:^7.0.0": - version: 7.0.0 - resolution: "pkg-dir@npm:7.0.0" +"pkg-dir@npm:^8.0.0": + version: 8.0.0 + resolution: "pkg-dir@npm:8.0.0" dependencies: - find-up: "npm:^6.3.0" - checksum: 10c0/1afb23d2efb1ec9d8b2c4a0c37bf146822ad2774f074cb05b853be5dca1b40815c5960dd126df30ab8908349262a266f31b771e877235870a3b8fd313beebec5 + find-up-simple: "npm:^1.0.0" + checksum: 10c0/244c6af67540b7eeab823c56f61a6ca414fe48108a484bcb3b0743acc0dfaf106705555c353d65608ccd8ac3d9f696110e9b6bf55ef08f5f6a8d535a72a418e8 languageName: node linkType: hard @@ -13194,18 +13204,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.2": - version: 8.5.2 - resolution: "postcss@npm:8.5.2" - dependencies: - nanoid: "npm:^3.3.8" - picocolors: "npm:^1.1.1" - source-map-js: "npm:^1.2.1" - checksum: 10c0/3044d49bc725029ab62292e8bf9849741251b95f3b754e191bf8b4025414d40ec3b4ac05c5a563d4b50060b5c8e96683eb4d783d8d8fa3867eb7b763cbe66127 - languageName: node - linkType: hard - -"postcss@npm:^8.2.14, postcss@npm:^8.4.33, postcss@npm:^8.4.47, postcss@npm:^8.4.49, postcss@npm:^8.5.3": +"postcss@npm:8.5.3, postcss@npm:^8.2.14, postcss@npm:^8.4.33, postcss@npm:^8.4.47, postcss@npm:^8.4.49, postcss@npm:^8.5.3": version: 8.5.3 resolution: "postcss@npm:8.5.3" dependencies: @@ -13610,13 +13609,6 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.14.0": - version: 0.14.1 - resolution: "regenerator-runtime@npm:0.14.1" - checksum: 10c0/1b16eb2c4bceb1665c89de70dcb64126a22bc8eb958feef3cd68fe11ac6d2a4899b5cd1b80b0774c7c03591dc57d16631a7f69d2daa2ec98100e2f29f7ec4cc4 - languageName: node - linkType: hard - "regex-cache@npm:^0.4.2": version: 0.4.4 resolution: "regex-cache@npm:0.4.4" @@ -13915,30 +13907,47 @@ __metadata: languageName: node linkType: hard -"rollup@npm:4.34.8": - version: 4.34.8 - resolution: "rollup@npm:4.34.8" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.34.8" - "@rollup/rollup-android-arm64": "npm:4.34.8" - "@rollup/rollup-darwin-arm64": "npm:4.34.8" - "@rollup/rollup-darwin-x64": "npm:4.34.8" - "@rollup/rollup-freebsd-arm64": "npm:4.34.8" - "@rollup/rollup-freebsd-x64": "npm:4.34.8" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.34.8" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.34.8" - "@rollup/rollup-linux-arm64-gnu": "npm:4.34.8" - "@rollup/rollup-linux-arm64-musl": "npm:4.34.8" - "@rollup/rollup-linux-loongarch64-gnu": "npm:4.34.8" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.34.8" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.34.8" - "@rollup/rollup-linux-s390x-gnu": "npm:4.34.8" - "@rollup/rollup-linux-x64-gnu": "npm:4.34.8" - "@rollup/rollup-linux-x64-musl": "npm:4.34.8" - "@rollup/rollup-win32-arm64-msvc": "npm:4.34.8" - "@rollup/rollup-win32-ia32-msvc": "npm:4.34.8" - "@rollup/rollup-win32-x64-msvc": "npm:4.34.8" - "@types/estree": "npm:1.0.6" +"rollup-plugin-dts@npm:^6.2.0": + version: 6.2.1 + resolution: "rollup-plugin-dts@npm:6.2.1" + dependencies: + "@babel/code-frame": "npm:^7.26.2" + magic-string: "npm:^0.30.17" + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + dependenciesMeta: + "@babel/code-frame": + optional: true + checksum: 10c0/f21c8726470851a40e6ca68ae580261cee8bc6275775291b9c0fdf93b868ed54f12b11c8c0dddce2c14f5691d6032b6647d094835ab9b6789226efa60e1aa71e + languageName: node + linkType: hard + +"rollup@npm:4.40.2, rollup@npm:^4.24.0, rollup@npm:^4.4.0": + version: 4.40.2 + resolution: "rollup@npm:4.40.2" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.40.2" + "@rollup/rollup-android-arm64": "npm:4.40.2" + "@rollup/rollup-darwin-arm64": "npm:4.40.2" + "@rollup/rollup-darwin-x64": "npm:4.40.2" + "@rollup/rollup-freebsd-arm64": "npm:4.40.2" + "@rollup/rollup-freebsd-x64": "npm:4.40.2" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.40.2" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.40.2" + "@rollup/rollup-linux-arm64-gnu": "npm:4.40.2" + "@rollup/rollup-linux-arm64-musl": "npm:4.40.2" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.40.2" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.40.2" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.40.2" + "@rollup/rollup-linux-riscv64-musl": "npm:4.40.2" + "@rollup/rollup-linux-s390x-gnu": "npm:4.40.2" + "@rollup/rollup-linux-x64-gnu": "npm:4.40.2" + "@rollup/rollup-linux-x64-musl": "npm:4.40.2" + "@rollup/rollup-win32-arm64-msvc": "npm:4.40.2" + "@rollup/rollup-win32-ia32-msvc": "npm:4.40.2" + "@rollup/rollup-win32-x64-msvc": "npm:4.40.2" + "@types/estree": "npm:1.0.7" fsevents: "npm:~2.3.2" dependenciesMeta: "@rollup/rollup-android-arm-eabi": @@ -13967,6 +13976,8 @@ __metadata: optional: true "@rollup/rollup-linux-riscv64-gnu": optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true "@rollup/rollup-linux-s390x-gnu": optional: true "@rollup/rollup-linux-x64-gnu": @@ -13983,35 +13994,35 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/b9e711e33413112fbb761107c3fddc4561dfc74335c393542a829a85ccfb2763bfd17bf2422d84a2e9bee7646e5367018973e97005fdf64e49c2e209612f0eb6 + checksum: 10c0/cbe9b766891da74fbf7c3b50420bb75102e5c59afc0ea45751f7e43a581d2cd93367763f521f820b72e341cf1f6b9951fbdcd3be67a1b0aa774b754525a8b9c7 languageName: node linkType: hard -"rollup@npm:^4.24.0, rollup@npm:^4.30.1, rollup@npm:^4.4.0": - version: 4.40.2 - resolution: "rollup@npm:4.40.2" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.40.2" - "@rollup/rollup-android-arm64": "npm:4.40.2" - "@rollup/rollup-darwin-arm64": "npm:4.40.2" - "@rollup/rollup-darwin-x64": "npm:4.40.2" - "@rollup/rollup-freebsd-arm64": "npm:4.40.2" - "@rollup/rollup-freebsd-x64": "npm:4.40.2" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.40.2" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.40.2" - "@rollup/rollup-linux-arm64-gnu": "npm:4.40.2" - "@rollup/rollup-linux-arm64-musl": "npm:4.40.2" - "@rollup/rollup-linux-loongarch64-gnu": "npm:4.40.2" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.40.2" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.40.2" - "@rollup/rollup-linux-riscv64-musl": "npm:4.40.2" - "@rollup/rollup-linux-s390x-gnu": "npm:4.40.2" - "@rollup/rollup-linux-x64-gnu": "npm:4.40.2" - "@rollup/rollup-linux-x64-musl": "npm:4.40.2" - "@rollup/rollup-win32-arm64-msvc": "npm:4.40.2" - "@rollup/rollup-win32-ia32-msvc": "npm:4.40.2" - "@rollup/rollup-win32-x64-msvc": "npm:4.40.2" - "@types/estree": "npm:1.0.7" +"rollup@npm:^4.34.9": + version: 4.44.1 + resolution: "rollup@npm:4.44.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.44.1" + "@rollup/rollup-android-arm64": "npm:4.44.1" + "@rollup/rollup-darwin-arm64": "npm:4.44.1" + "@rollup/rollup-darwin-x64": "npm:4.44.1" + "@rollup/rollup-freebsd-arm64": "npm:4.44.1" + "@rollup/rollup-freebsd-x64": "npm:4.44.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.44.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.44.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.44.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.44.1" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.44.1" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.44.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.44.1" + "@rollup/rollup-linux-riscv64-musl": "npm:4.44.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.44.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.44.1" + "@rollup/rollup-linux-x64-musl": "npm:4.44.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.44.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.44.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.44.1" + "@types/estree": "npm:1.0.8" fsevents: "npm:~2.3.2" dependenciesMeta: "@rollup/rollup-android-arm-eabi": @@ -14058,7 +14069,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10c0/cbe9b766891da74fbf7c3b50420bb75102e5c59afc0ea45751f7e43a581d2cd93367763f521f820b72e341cf1f6b9951fbdcd3be67a1b0aa774b754525a8b9c7 + checksum: 10c0/6cc0175c626fd9f0fc325c1f1b86d5b5401d687973691dd5205b6b88a666ee0b96f401725da9090e090b31cb5a82ff9a0ef1c3db6dc14906f6c7a48cabad49b4 languageName: node linkType: hard @@ -14087,7 +14098,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.1, rxjs@npm:~7.8.1": +"rxjs@npm:7.8.2, rxjs@npm:^7.8.1, rxjs@npm:~7.8.1": version: 7.8.2 resolution: "rxjs@npm:7.8.2" dependencies: @@ -14195,9 +14206,9 @@ __metadata: languageName: node linkType: hard -"sass@npm:1.85.0": - version: 1.85.0 - resolution: "sass@npm:1.85.0" +"sass@npm:1.88.0": + version: 1.88.0 + resolution: "sass@npm:1.88.0" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -14208,7 +14219,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10c0/a1af0c0596ae1904f66337d0c70a684db6e12210f97be4326cc3dcf18b0f956d7bc45ab2bcc7a8422d433d3eb3c9cb2cc8e60b2dafbdd01fb1ae5a23f5424690 + checksum: 10c0/dcb16dc29116bfa5a90485d24fd8020d2b0d95155bd2e31285901588729343b59fefe44365c5f146b2ba5a9ebadef90b23a7220b902507bdbd91ca2ba0a0b688 languageName: node linkType: hard @@ -14245,7 +14256,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^4.0.0, schema-utils@npm:^4.2.0, schema-utils@npm:^4.3.0": +"schema-utils@npm:^4.0.0, schema-utils@npm:^4.2.0, schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.2": version: 4.3.2 resolution: "schema-utils@npm:4.3.2" dependencies: @@ -14337,30 +14348,30 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.3": - version: 7.6.3 - resolution: "semver@npm:7.6.3" +"semver@npm:7.7.2": + version: 7.7.2 + resolution: "semver@npm:7.7.2" bin: semver: bin/semver.js - checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf + checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea languageName: node linkType: hard -"semver@npm:7.7.1, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": - version: 7.7.1 - resolution: "semver@npm:7.7.1" +"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" bin: semver: bin/semver.js - checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958 + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": - version: 6.3.1 - resolution: "semver@npm:6.3.1" +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.7.1 + resolution: "semver@npm:7.7.1" bin: semver: bin/semver.js - checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958 languageName: node linkType: hard @@ -14629,13 +14640,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^5.1.0": - version: 5.1.0 - resolution: "slash@npm:5.1.0" - checksum: 10c0/eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 - languageName: node - linkType: hard - "slice-ansi@npm:^4.0.0": version: 4.0.0 resolution: "slice-ansi@npm:4.0.0" @@ -14987,6 +14991,13 @@ __metadata: languageName: node linkType: hard +"stdin-discarder@npm:^0.2.2": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 10c0/c78375e82e956d7a64be6e63c809c7f058f5303efcaf62ea48350af072bacdb99c06cba39209b45a071c1acbd49116af30df1df9abb448df78a6005b72f10537 + languageName: node + linkType: hard + "streamroller@npm:^3.1.5": version: 3.1.5 resolution: "streamroller@npm:3.1.5" @@ -15034,7 +15045,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^7.0.0": +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": version: 7.2.0 resolution: "string-width@npm:7.2.0" dependencies: @@ -15220,13 +15231,6 @@ __metadata: languageName: node linkType: hard -"symbol-observable@npm:4.0.0": - version: 4.0.0 - resolution: "symbol-observable@npm:4.0.0" - checksum: 10c0/5e9a3ab08263a6be8cbee76587ad5880dcc62a47002787ed5ebea56b1eb30dc87da6f0183d67e88286806799fbe21c69077fbd677be4be2188e92318d6c6f31d - languageName: node - linkType: hard - "table-layout@npm:^4.1.0": version: 4.1.1 resolution: "table-layout@npm:4.1.1" @@ -15322,7 +15326,21 @@ __metadata: languageName: node linkType: hard -"terser@npm:5.39.0, terser@npm:^5.31.1": +"terser@npm:5.39.1": + version: 5.39.1 + resolution: "terser@npm:5.39.1" + dependencies: + "@jridgewell/source-map": "npm:^0.3.3" + acorn: "npm:^8.8.2" + commander: "npm:^2.20.0" + source-map-support: "npm:~0.5.20" + bin: + terser: bin/terser + checksum: 10c0/d49e06dd4dd03661dac41f45c9cf187b2aa3fe80775235e838398c29311705169387c007f398ab44cd1bd8f89b14a1eea383feaa95c1cae29e3f5b6b606b6b37 + languageName: node + linkType: hard + +"terser@npm:^5.31.1": version: 5.39.0 resolution: "terser@npm:5.39.0" dependencies: @@ -15375,7 +15393,7 @@ __metadata: languageName: node linkType: hard -"tinyglobby@npm:^0.2.12": +"tinyglobby@npm:0.2.13, tinyglobby@npm:^0.2.12": version: 0.2.13 resolution: "tinyglobby@npm:0.2.13" dependencies: @@ -15385,6 +15403,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.13": + version: 0.2.14 + resolution: "tinyglobby@npm:0.2.14" + dependencies: + fdir: "npm:^6.4.4" + picomatch: "npm:^4.0.2" + checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6 + languageName: node + linkType: hard + "tmp@npm:0.0.30": version: 0.0.30 resolution: "tmp@npm:0.0.30" @@ -15503,7 +15531,7 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^2.0.1": +"ts-api-utils@npm:^2.0.1, ts-api-utils@npm:^2.1.0": version: 2.1.0 resolution: "ts-api-utils@npm:2.1.0" peerDependencies: @@ -15717,23 +15745,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.7.3": - version: 5.7.3 - resolution: "typescript@npm:5.7.3" +"typescript@npm:5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/b7580d716cf1824736cc6e628ab4cd8b51877408ba2be0869d2866da35ef8366dd6ae9eb9d0851470a39be17cbd61df1126f9e211d8799d764ea7431d5435afa + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A5.7.3#optional!builtin": - version: 5.7.3 - resolution: "typescript@patch:typescript@npm%3A5.7.3#optional!builtin::version=5.7.3&hash=cef18b" +"typescript@patch:typescript@npm%3A5.8.3#optional!builtin": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/3b56d6afa03d9f6172d0b9cdb10e6b1efc9abc1608efd7a3d2f38773d5d8cfb9bbc68dfb72f0a7de5e8db04fc847f4e4baeddcd5ad9c9feda072234f0d788896 + checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb languageName: node linkType: hard @@ -15820,13 +15848,6 @@ __metadata: languageName: node linkType: hard -"unicorn-magic@npm:^0.3.0": - version: 0.3.0 - resolution: "unicorn-magic@npm:0.3.0" - checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 - languageName: node - linkType: hard - "union-value@npm:^1.0.0": version: 1.0.1 resolution: "union-value@npm:1.0.1" @@ -16010,14 +16031,17 @@ __metadata: languageName: node linkType: hard -"vite@npm:6.2.7": - version: 6.2.7 - resolution: "vite@npm:6.2.7" +"vite@npm:6.3.5": + version: 6.3.5 + resolution: "vite@npm:6.3.5" dependencies: esbuild: "npm:^0.25.0" + fdir: "npm:^6.4.4" fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.2" postcss: "npm:^8.5.3" - rollup: "npm:^4.30.1" + rollup: "npm:^4.34.9" + tinyglobby: "npm:^0.2.13" peerDependencies: "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 jiti: ">=1.21.0" @@ -16058,7 +16082,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/2da5df6bfdc386a3b24d7350c508e075a49a5b5c33eb4a327203eb175398a1da99d185c68bd2287be897032810700d95ea7ce72d1113d86f43de61f0ce4435da + checksum: 10c0/df70201659085133abffc6b88dcdb8a57ef35f742a01311fc56a4cfcda6a404202860729cc65a2c401a724f6e25f9ab40ce4339ed4946f550541531ced6fe41c languageName: node linkType: hard @@ -16161,13 +16185,14 @@ __metadata: languageName: node linkType: hard -"webpack-dev-server@npm:5.2.0": - version: 5.2.0 - resolution: "webpack-dev-server@npm:5.2.0" +"webpack-dev-server@npm:5.2.1": + version: 5.2.1 + resolution: "webpack-dev-server@npm:5.2.1" dependencies: "@types/bonjour": "npm:^3.5.13" "@types/connect-history-api-fallback": "npm:^1.5.4" "@types/express": "npm:^4.17.21" + "@types/express-serve-static-core": "npm:^4.17.21" "@types/serve-index": "npm:^1.9.4" "@types/serve-static": "npm:^1.15.5" "@types/sockjs": "npm:^0.3.36" @@ -16201,7 +16226,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 10c0/afb2e51945ac54ef3039e11e377241e1cb97a8d3f526f39f13c3fa924c530fb6063200c2c3ae4e33e6bcc110d4abed777c09ce18e2d261012853d81f3c5820ab + checksum: 10c0/22bcf2bcc7c72cd2065883ed4368fbcdf20078bc746b07689d10a0546ee99ea00bc50f0474112278ffd8598a5bc237df2bf7bb7f6dcda940a16b1eb91137efea languageName: node linkType: hard @@ -16238,12 +16263,13 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.98.0": - version: 5.98.0 - resolution: "webpack@npm:5.98.0" +"webpack@npm:5.99.8": + version: 5.99.8 + resolution: "webpack@npm:5.99.8" dependencies: "@types/eslint-scope": "npm:^3.7.7" "@types/estree": "npm:^1.0.6" + "@types/json-schema": "npm:^7.0.15" "@webassemblyjs/ast": "npm:^1.14.1" "@webassemblyjs/wasm-edit": "npm:^1.14.1" "@webassemblyjs/wasm-parser": "npm:^1.14.1" @@ -16260,7 +16286,7 @@ __metadata: loader-runner: "npm:^4.2.0" mime-types: "npm:^2.1.27" neo-async: "npm:^2.6.2" - schema-utils: "npm:^4.3.0" + schema-utils: "npm:^4.3.2" tapable: "npm:^2.1.1" terser-webpack-plugin: "npm:^5.3.11" watchpack: "npm:^2.4.1" @@ -16270,7 +16296,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 10c0/bee4fa77f444802f0beafb2ff30eb5454a606163ad7d3cc9a5dcc9d24033c62407bed04601b25dea49ea3969b352c1b530a86c753246f42560a4a084eefb094e + checksum: 10c0/c4852c3b795ed3fba799d2925802a4e259b2de7c2c597f0aaf0e228acfdc6755389ed8c29f1dad86610a9c6ad968c0b57c702b93891d60f09d302af63b2debe0 languageName: node linkType: hard @@ -16610,7 +16636,14 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.2.1, yargs@npm:^17.7.2": +"yargs-parser@npm:^22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 10c0/cb7ef81759c4271cb1d96b9351dbbc9a9ce35d3e1122d2b739bf6c432603824fa02c67cc12dcef6ea80283379d63495686e8f41cc7b06c6576e792aba4d33e1c + languageName: node + linkType: hard + +"yargs@npm:17.7.2, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -16659,6 +16692,20 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^18.0.0": + version: 18.0.0 + resolution: "yargs@npm:18.0.0" + dependencies: + cliui: "npm:^9.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + string-width: "npm:^7.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^22.0.0" + checksum: 10c0/bf290e4723876ea9c638c786a5c42ac28e03c9ca2325e1424bf43b94e5876456292d3ed905b853ebbba6daf43ed29e772ac2a6b3c5fb1b16533245d6211778f3 + languageName: node + linkType: hard + "yauzl@npm:^2.10.0": version: 2.10.0 resolution: "yauzl@npm:2.10.0" @@ -16690,13 +16737,6 @@ __metadata: languageName: node linkType: hard -"yocto-queue@npm:^1.0.0": - version: 1.2.1 - resolution: "yocto-queue@npm:1.2.1" - checksum: 10c0/5762caa3d0b421f4bdb7a1926b2ae2189fc6e4a14469258f183600028eb16db3e9e0306f46e8ebf5a52ff4b81a881f22637afefbef5399d6ad440824e9b27f9f - languageName: node - linkType: hard - "yoctocolors-cjs@npm:^2.1.2": version: 2.1.2 resolution: "yoctocolors-cjs@npm:2.1.2"