@haloduck/ui 2.0.24 → 2.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Input, Component, inject, ChangeDetectorRef, forwardRef, ViewChild, signal, EventEmitter, Output, ViewContainerRef, NgZone, isDevMode, DestroyRef, ElementRef, ChangeDetectionStrategy, HostBinding, Directive } from '@angular/core';
2
+ import { Injectable, Input, Component, inject, ChangeDetectorRef, forwardRef, ViewChild, signal, EventEmitter, Output, ViewContainerRef, NgZone, isDevMode, DestroyRef, ElementRef, ViewChildren, ChangeDetectionStrategy, HostBinding, Directive } from '@angular/core';
3
3
  import { signIn, confirmSignIn, resetPassword, confirmResetPassword } from 'aws-amplify/auth';
4
4
  import * as i1$1 from '@angular/forms';
5
5
  import { NG_VALUE_ACCESSOR, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
6
- import { BehaviorSubject, zip, Subject, takeUntil, tap, combineLatest, switchMap, of, Observable, distinctUntilChanged, map, take } from 'rxjs';
6
+ import { BehaviorSubject, zip, Subject, takeUntil, tap, combineLatest, switchMap, of, Observable, distinctUntilChanged, map, take, firstValueFrom } from 'rxjs';
7
7
  import { ulid } from 'ulid';
8
8
  import * as i1 from '@angular/common';
9
9
  import { CommonModule, DecimalPipe, DatePipe } from '@angular/common';
@@ -2908,9 +2908,11 @@ class TableComponent {
2908
2908
  onRowClick = new EventEmitter();
2909
2909
  onRowDblClick = new EventEmitter();
2910
2910
  loadMoreRow;
2911
- lastDataRow;
2911
+ dataRows;
2912
2912
  scrollContainer;
2913
2913
  intersectionObserver;
2914
+ scrollListenerTarget;
2915
+ didScrollSinceLastLoad = false;
2914
2916
  getColumnValue(row, column) {
2915
2917
  const value = this.getColumnValueRaw(row, column);
2916
2918
  if (column.customRenderFn) {
@@ -3004,11 +3006,19 @@ class TableComponent {
3004
3006
  .pipe(takeUntilDestroyed(this.destroyRef))
3005
3007
  .subscribe(() => {
3006
3008
  this.setupIntersectionObserver();
3007
- this.checkAndAutoLoadIfNoScroll();
3008
3009
  });
3010
+ const container = this.scrollContainer?.nativeElement;
3011
+ this.scrollListenerTarget = container || (typeof window !== 'undefined' ? window : undefined);
3012
+ if (this.scrollListenerTarget) {
3013
+ this.scrollListenerTarget.addEventListener('scroll', this.onScroll, { passive: true });
3014
+ }
3009
3015
  }
3010
3016
  ngOnDestroy() {
3011
3017
  this.cleanupIntersectionObserver();
3018
+ if (this.scrollListenerTarget) {
3019
+ this.scrollListenerTarget.removeEventListener('scroll', this.onScroll);
3020
+ this.scrollListenerTarget = undefined;
3021
+ }
3012
3022
  }
3013
3023
  setupIntersectionObserver() {
3014
3024
  if (!this.useLoadMore)
@@ -3016,9 +3026,14 @@ class TableComponent {
3016
3026
  this.cleanupIntersectionObserver();
3017
3027
  const rootElement = this.scrollContainer?.nativeElement || null;
3018
3028
  // 우선순위: 마지막 데이터 행 -> 로드모어 행
3019
- const target = this.lastDataRow?.nativeElement || this.loadMoreRow?.nativeElement;
3029
+ const lastRowEl = this.dataRows?.last?.nativeElement;
3030
+ const target = lastRowEl || this.loadMoreRow?.nativeElement;
3020
3031
  if (!target)
3021
3032
  return;
3033
+ // 새로운 옵저버 생성 시 초기 교차 무시 플래그 초기화
3034
+ this.hasObservedOnce = false;
3035
+ // 스크롤 발생 전에는 자동 로드를 막음
3036
+ this.didScrollSinceLastLoad = false;
3022
3037
  this.ngZone.runOutsideAngular(() => {
3023
3038
  this.intersectionObserver = new IntersectionObserver(this.onIntersection, {
3024
3039
  root: rootElement || undefined,
@@ -3037,39 +3052,38 @@ class TableComponent {
3037
3052
  hasObservedOnce = false;
3038
3053
  onIntersection = (entries) => {
3039
3054
  for (const entry of entries) {
3040
- if (entry.isIntersecting) {
3041
- if (!this.hasObservedOnce) {
3042
- // 초기 진입 시에는 스킵하여 첫 렌더에서의 즉시 로딩을 방지
3043
- this.hasObservedOnce = true;
3044
- continue;
3055
+ if (!entry.isIntersecting)
3056
+ continue;
3057
+ const container = this.scrollContainer?.nativeElement;
3058
+ const allowWithoutScroll = !container || container.scrollHeight <= container.clientHeight;
3059
+ if (!this.hasObservedOnce && !allowWithoutScroll) {
3060
+ // 내부 스크롤 컨테이너가 있고 첫 교차는 무시
3061
+ this.hasObservedOnce = true;
3062
+ continue;
3063
+ }
3064
+ // 마지막 키가 존재, 로딩 아님, 그리고 (실제 스크롤 발생 or 스크롤 불필요)일 때만 로드
3065
+ Promise.all([
3066
+ firstValueFrom(this.lastEvaluatedKey.pipe(take(1))),
3067
+ firstValueFrom(this.isLoading.pipe(take(1))),
3068
+ ]).then(([lastKey, isLoading]) => {
3069
+ const scrolledOk = this.didScrollSinceLastLoad || allowWithoutScroll;
3070
+ if (!(lastKey && !isLoading && scrolledOk)) {
3071
+ return;
3045
3072
  }
3073
+ // 중복 호출 방지: 즉시 관찰 해제 후 로드 트리거
3074
+ this.cleanupIntersectionObserver();
3075
+ this.didScrollSinceLastLoad = false;
3046
3076
  this.ngZone.run(() => this.onLoadMoreClicked());
3047
- }
3077
+ });
3048
3078
  }
3049
3079
  };
3050
- checkAndAutoLoadIfNoScroll() {
3051
- if (!this.useLoadMore)
3052
- return;
3053
- const container = this.scrollContainer?.nativeElement;
3054
- if (!container)
3055
- return;
3056
- this.ngZone.runOutsideAngular(() => {
3057
- requestAnimationFrame(() => {
3058
- const hasNoScroll = container.scrollHeight <= container.clientHeight;
3059
- if (!hasNoScroll)
3060
- return;
3061
- combineLatest([this.lastEvaluatedKey.pipe(take(1)), this.isLoading.pipe(take(1))])
3062
- .subscribe(([lastKey, isLoading]) => {
3063
- if (lastKey && !isLoading) {
3064
- this.ngZone.run(() => this.onLoadMoreClicked());
3065
- }
3066
- });
3067
- });
3068
- });
3069
- }
3080
+ onScroll = () => {
3081
+ this.didScrollSinceLastLoad = true;
3082
+ };
3083
+ // Note: 무스크롤 자동 로드는 연속 로딩을 유발할 수 있어 제거
3070
3084
  constructor() { }
3071
3085
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3072
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: TableComponent, isStandalone: true, selector: "haloduck-table", inputs: { tableLayout: "tableLayout", showHeader: "showHeader", useLoadMore: "useLoadMore", columns: "columns", rows: "rows", isLoading: "isLoading", isPaging: "isPaging", sort: "sort", expandedTemplate: "expandedTemplate", customTemplates: "customTemplates", lastEvaluatedKey: "lastEvaluatedKey" }, outputs: { onSortChange: "onSortChange", onLoadMore: "onLoadMore", onRowClick: "onRowClick", onRowDblClick: "onRowDblClick" }, providers: [provideTranslocoScope('haloduck')], viewQueries: [{ propertyName: "loadMoreRow", first: true, predicate: ["loadMoreRow"], descendants: true }, { propertyName: "lastDataRow", first: true, predicate: ["dataRow"], descendants: true, read: ElementRef }, { propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, static: true }], ngImport: i0, template: "<div #scrollContainer class=\"w-full h-full shadow border border-light-inactive dark:border-dark-inactive rounded-lg overflow-auto\">\n <table class=\"w-full h-full\"\n [ngClass]=\"{\n 'table-fixed': tableLayout === 'fixed',\n 'table-auto': tableLayout === 'auto',\n }\">\n <colgroup>\n @for (column of columns; track column.key) {\n @if (!column.hidden || !(column.hidden | async)) {\n <col class=\"{{ column.width || '' }}\" />\n }\n }\n </colgroup>\n @if (showHeader) {\n <thead class=\"bg-light-inactive/50 dark:bg-dark-inactive/50 rounded-tl-lg rounded-tr-lg\">\n <tr>\n @for (column of columns; track column.key) {\n @if (!column.hidden || !(column.hidden | async)) {\n <th scope=\"col\"\n class=\"{{ column.align || '' }} py-3.5 pl-4 pr-3 text-sm font-semibold text-light-on-background dark:text-dark-on-background whitespace-nowrap\">\n <span class=\"group inline-flex\">\n {{ column.label }}\n @if (column.sortable) {\n @switch (getSortDirection(column.sort?.field || column.key) | async)\n {\n @case('desc') {\n <span (click)=\"onUpdateSort(column.sort?.field || column.key, 'asc')\"\n class=\"ml-2 flex-none rounded text-light-on-inactive dark:text-dark-on-inactive\">\n <svg class=\"size-5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\">\n <path fill-rule=\"evenodd\"\n d=\"M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z\"\n clip-rule=\"evenodd\" />\n </svg>\n </span>\n }\n @case('asc') {\n <span (click)=\"onUpdateSort(column.sort?.field || column.key, 'desc')\"\n class=\"ml-2 flex-none rounded text-light-on-inactive dark:text-dark-on-inactive\">\n <svg class=\"size-5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\">\n <path fill-rule=\"evenodd\"\n d=\"M14.78 11.78a.75.75 0 0 1-1.06 0L10 8.06l-3.72 3.72a.75.75 0 1 1-1.06-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06Z\"\n clip-rule=\"evenodd\" />\n </svg>\n </span>\n }\n @default {\n <span (click)=\"onUpdateSort(column.sort?.field || column.key, 'asc')\"\n class=\"invisible ml-2 flex-none rounded text-light-on-inactive dark:text-dark-on-inactive group-hover:visible group-focus:visible\">\n <svg class=\"size-5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\">\n <path fill-rule=\"evenodd\"\n d=\"M14.78 11.78a.75.75 0 0 1-1.06 0L10 8.06l-3.72 3.72a.75.75 0 1 1-1.06-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06Z\"\n clip-rule=\"evenodd\" />\n </svg>\n </span>\n }\n }\n }\n </span>\n </th>\n }\n }\n </tr>\n </thead>\n }\n <tbody class=\"overflow-scroll\">\n @for (row of rows | async; track row['id']; let lastRow = $last) {\n <tr #dataRow class=\"border-t border-light-inactive dark:border-dark-inactive {{ row.bgColor || '' }}\"\n (click)=\"onRowClicked(row)\"\n (dblclick)=\"onRowDblClicked(row)\"\n [ngClass]=\"{'rounded-b-lg': lastRow && (lastEvaluatedKey | async) === null, 'even:bg-light-alternative dark:even:bg-dark-alternative odd:bg-light-background dark:odd:bg-dark-background': !row.bgColor }\"\n [ngStyle]=\"{ 'background-color': row.bgColor || '' }\">\n @for (column of columns; track column.key) {\n @if (!column.hidden || !(column.hidden | async)) {\n <td class=\"relative overflow-visible {{ column.align || '' }} whitespace-nowrap px-3 text-sm text-light-on-background dark:text-dark-on-background\"\n [ngClass]=\"{\n 'first:rounded-bl-lg last:rounded-br-lg': lastRow && (lastEvaluatedKey | async) === null,\n 'text-wrap': row.isExpanded && column.type !== 'custom',\n 'overflow-x-hidden text-ellipsis' : !row.isExpanded && column.type !== 'custom',\n 'py-2': column.type === 'custom',\n 'py-4': column.type !== 'custom'\n }\">\n @if (column.type === 'custom') {\n @if (column.customRenderTemplate) {\n <ng-container [ngTemplateOutlet]=\"customTemplates[column.customRenderTemplate]\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"></ng-container>\n }\n }\n @else {\n {{ getColumnValue(row, column) | async }}\n }\n </td>\n }\n }\n </tr>\n\n @if (row.isExpanded && expandedTemplate) {\n <tr>\n <td [attr.colspan]=\"columns.length\">\n </td>\n </tr>\n <tr class=\"even:bg-light-alternative dark:even:bg-dark-alternative odd:bg-light-background dark:odd:bg-dark-background\">\n <td [attr.colspan]=\"columns.length\"\n class=\"whitespace-nowrap px-3 pb-4 text-sm text-center\">\n <ng-container [ngTemplateOutlet]=\"expandedTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row }\"></ng-container>\n </td>\n </tr>\n }\n\n @if (lastRow && (lastEvaluatedKey | async) && !(isLoading | async) && useLoadMore) {\n <tr #loadMoreRow class=\"border-t border-light-inactive dark:border-dark-inactive bg-light-background dark:bg-dark-background\">\n <td [attr.colspan]=\"columns.length\"\n class=\"whitespace-nowrap px-3 py-4 text-sm text-light-on-background dark:text-dark-on-background text-center h-16 bg-light-background dark:bg-dark-background rounded-bl-lg rounded-br-lg\">\n <div (click)=\"onLoadMoreClicked()\"\n class=\"cursor-pointer\">\n {{ 'haloduck.ui.table.Load More...' | transloco }}\n </div>\n </td>\n </tr>\n }\n\n } @empty {\n @if(!(isLoading | async)) {\n <tr>\n <td [attr.colspan]=\"columns.length\"\n class=\"whitespace-nowrap px-3 py-4 text-sm text-light-on-background dark:text-dark-on-background text-center h-16 bg-light-background dark:bg-dark-background rounded-bl-lg rounded-br-lg\">\n {{ 'haloduck.ui.table.No data available.' | transloco }}\n </td>\n </tr>\n }\n }\n\n @if(isLoading | async) {\n @for ( i of [0,1,2,3,4]; track i; let lastRow = $last)\n {\n <tr class=\"bg-light-background dark:bg-dark-background border-t border-light-inactive/50 dark:border-dark-inactive/50\">\n @for (column of columns; track column.key) {\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm\"\n [ngClass]=\"{'first:rounded-bl-lg last:rounded-br-lg': lastRow}\">\n <div class=\"h-4 bg-light-inactive/50 dark:bg-dark-inactive/50 rounded-md animate-pulse\"></div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$1.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3086
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: TableComponent, isStandalone: true, selector: "haloduck-table", inputs: { tableLayout: "tableLayout", showHeader: "showHeader", useLoadMore: "useLoadMore", columns: "columns", rows: "rows", isLoading: "isLoading", isPaging: "isPaging", sort: "sort", expandedTemplate: "expandedTemplate", customTemplates: "customTemplates", lastEvaluatedKey: "lastEvaluatedKey" }, outputs: { onSortChange: "onSortChange", onLoadMore: "onLoadMore", onRowClick: "onRowClick", onRowDblClick: "onRowDblClick" }, providers: [provideTranslocoScope('haloduck')], viewQueries: [{ propertyName: "loadMoreRow", first: true, predicate: ["loadMoreRow"], descendants: true }, { propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, static: true }, { propertyName: "dataRows", predicate: ["dataRow"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div #scrollContainer class=\"w-full h-full shadow border border-light-inactive dark:border-dark-inactive rounded-lg overflow-auto\">\n <table class=\"w-full h-full\"\n [ngClass]=\"{\n 'table-fixed': tableLayout === 'fixed',\n 'table-auto': tableLayout === 'auto',\n }\">\n <colgroup>\n @for (column of columns; track column.key) {\n @if (!column.hidden || !(column.hidden | async)) {\n <col class=\"{{ column.width || '' }}\" />\n }\n }\n </colgroup>\n @if (showHeader) {\n <thead class=\"bg-light-inactive/50 dark:bg-dark-inactive/50 rounded-tl-lg rounded-tr-lg\">\n <tr>\n @for (column of columns; track column.key) {\n @if (!column.hidden || !(column.hidden | async)) {\n <th scope=\"col\"\n class=\"{{ column.align || '' }} py-3.5 pl-4 pr-3 text-sm font-semibold text-light-on-background dark:text-dark-on-background whitespace-nowrap\">\n <span class=\"group inline-flex\">\n {{ column.label }}\n @if (column.sortable) {\n @switch (getSortDirection(column.sort?.field || column.key) | async)\n {\n @case('desc') {\n <span (click)=\"onUpdateSort(column.sort?.field || column.key, 'asc')\"\n class=\"ml-2 flex-none rounded text-light-on-inactive dark:text-dark-on-inactive\">\n <svg class=\"size-5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\">\n <path fill-rule=\"evenodd\"\n d=\"M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z\"\n clip-rule=\"evenodd\" />\n </svg>\n </span>\n }\n @case('asc') {\n <span (click)=\"onUpdateSort(column.sort?.field || column.key, 'desc')\"\n class=\"ml-2 flex-none rounded text-light-on-inactive dark:text-dark-on-inactive\">\n <svg class=\"size-5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\">\n <path fill-rule=\"evenodd\"\n d=\"M14.78 11.78a.75.75 0 0 1-1.06 0L10 8.06l-3.72 3.72a.75.75 0 1 1-1.06-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06Z\"\n clip-rule=\"evenodd\" />\n </svg>\n </span>\n }\n @default {\n <span (click)=\"onUpdateSort(column.sort?.field || column.key, 'asc')\"\n class=\"invisible ml-2 flex-none rounded text-light-on-inactive dark:text-dark-on-inactive group-hover:visible group-focus:visible\">\n <svg class=\"size-5\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\">\n <path fill-rule=\"evenodd\"\n d=\"M14.78 11.78a.75.75 0 0 1-1.06 0L10 8.06l-3.72 3.72a.75.75 0 1 1-1.06-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06Z\"\n clip-rule=\"evenodd\" />\n </svg>\n </span>\n }\n }\n }\n </span>\n </th>\n }\n }\n </tr>\n </thead>\n }\n <tbody class=\"overflow-scroll\">\n @for (row of rows | async; track row['id']; let lastRow = $last) {\n <tr #dataRow class=\"border-t border-light-inactive dark:border-dark-inactive {{ row.bgColor || '' }}\"\n (click)=\"onRowClicked(row)\"\n (dblclick)=\"onRowDblClicked(row)\"\n [ngClass]=\"{'rounded-b-lg': lastRow && (lastEvaluatedKey | async) === null, 'even:bg-light-alternative dark:even:bg-dark-alternative odd:bg-light-background dark:odd:bg-dark-background': !row.bgColor }\"\n [ngStyle]=\"{ 'background-color': row.bgColor || '' }\">\n @for (column of columns; track column.key) {\n @if (!column.hidden || !(column.hidden | async)) {\n <td class=\"relative overflow-visible {{ column.align || '' }} whitespace-nowrap px-3 text-sm text-light-on-background dark:text-dark-on-background\"\n [ngClass]=\"{\n 'first:rounded-bl-lg last:rounded-br-lg': lastRow && (lastEvaluatedKey | async) === null,\n 'text-wrap': row.isExpanded && column.type !== 'custom',\n 'overflow-x-hidden text-ellipsis' : !row.isExpanded && column.type !== 'custom',\n 'py-2': column.type === 'custom',\n 'py-4': column.type !== 'custom'\n }\">\n @if (column.type === 'custom') {\n @if (column.customRenderTemplate) {\n <ng-container [ngTemplateOutlet]=\"customTemplates[column.customRenderTemplate]\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"></ng-container>\n }\n }\n @else {\n {{ getColumnValue(row, column) | async }}\n }\n </td>\n }\n }\n </tr>\n\n @if (row.isExpanded && expandedTemplate) {\n <tr>\n <td [attr.colspan]=\"columns.length\">\n </td>\n </tr>\n <tr class=\"even:bg-light-alternative dark:even:bg-dark-alternative odd:bg-light-background dark:odd:bg-dark-background\">\n <td [attr.colspan]=\"columns.length\"\n class=\"whitespace-nowrap px-3 pb-4 text-sm text-center\">\n <ng-container [ngTemplateOutlet]=\"expandedTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: row }\"></ng-container>\n </td>\n </tr>\n }\n\n @if (lastRow && (lastEvaluatedKey | async) && !(isLoading | async) && useLoadMore) {\n <tr #loadMoreRow class=\"border-t border-light-inactive dark:border-dark-inactive bg-light-background dark:bg-dark-background\">\n <td [attr.colspan]=\"columns.length\"\n class=\"whitespace-nowrap px-3 py-4 text-sm text-light-on-background dark:text-dark-on-background text-center h-16 bg-light-background dark:bg-dark-background rounded-bl-lg rounded-br-lg\">\n <div (click)=\"onLoadMoreClicked()\"\n class=\"cursor-pointer\">\n {{ 'haloduck.ui.table.Load More...' | transloco }}\n </div>\n </td>\n </tr>\n }\n\n } @empty {\n @if(!(isLoading | async)) {\n <tr>\n <td [attr.colspan]=\"columns.length\"\n class=\"whitespace-nowrap px-3 py-4 text-sm text-light-on-background dark:text-dark-on-background text-center h-16 bg-light-background dark:bg-dark-background rounded-bl-lg rounded-br-lg\">\n {{ 'haloduck.ui.table.No data available.' | transloco }}\n </td>\n </tr>\n }\n }\n\n @if(isLoading | async) {\n @for ( i of [0,1,2,3,4]; track i; let lastRow = $last)\n {\n <tr class=\"bg-light-background dark:bg-dark-background border-t border-light-inactive/50 dark:border-dark-inactive/50\">\n @for (column of columns; track column.key) {\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm\"\n [ngClass]=\"{'first:rounded-bl-lg last:rounded-br-lg': lastRow}\">\n <div class=\"h-4 bg-light-inactive/50 dark:bg-dark-inactive/50 rounded-md animate-pulse\"></div>\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$1.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3073
3087
  }
3074
3088
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: TableComponent, decorators: [{
3075
3089
  type: Component,
@@ -3107,8 +3121,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
3107
3121
  }], loadMoreRow: [{
3108
3122
  type: ViewChild,
3109
3123
  args: ['loadMoreRow']
3110
- }], lastDataRow: [{
3111
- type: ViewChild,
3124
+ }], dataRows: [{
3125
+ type: ViewChildren,
3112
3126
  args: ['dataRow', { read: ElementRef }]
3113
3127
  }], scrollContainer: [{
3114
3128
  type: ViewChild,