s4y-ui 6.2.0 → 6.4.0

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { ContentChild, Component, signal, computed, Injectable, inject, input, HostBinding, Input, ChangeDetectionStrategy, NgModule, booleanAttribute, forwardRef, HostListener, Directive, EventEmitter, Output, model, output, effect, ViewEncapsulation, Inject, TemplateRef, ViewChild, ContentChildren, ElementRef, ViewContainerRef, viewChild, Renderer2, DestroyRef, contentChild, InjectionToken, Injector, afterNextRender, SecurityContext, ChangeDetectorRef } from '@angular/core';
2
+ import { ContentChild, Component, signal, computed, Injectable, inject, input, HostBinding, Input, ChangeDetectionStrategy, NgModule, booleanAttribute, forwardRef, HostListener, Directive, EventEmitter, Output, model, output, ViewEncapsulation, effect, Inject, TemplateRef, ViewChild, ContentChildren, ElementRef, ViewContainerRef, viewChild, Renderer2, DestroyRef, contentChild, InjectionToken, Injector, afterNextRender, SecurityContext, ChangeDetectorRef } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule, AsyncPipe, JsonPipe, NgClass, NgTemplateOutlet, DOCUMENT, NgIf, NgStyle } from '@angular/common';
5
5
  import * as i1$1 from '@angular/platform-browser';
@@ -7,15 +7,15 @@ import { DomSanitizer } from '@angular/platform-browser';
7
7
  import * as i1$2 from '@angular/router';
8
8
  import { Router, RouterModule, GuardsCheckEnd } from '@angular/router';
9
9
  import { catchError, map, filter } from 'rxjs/operators';
10
- import { defer, from, throwError, BehaviorSubject, map as map$1, Subject, takeUntil, Subscription, filter as filter$1, fromEvent, isObservable, firstValueFrom, ReplaySubject, take, finalize, catchError as catchError$1, of } from 'rxjs';
10
+ import { defer, from, throwError, Subject, BehaviorSubject, map as map$1, takeUntil, Subscription, filter as filter$1, fromEvent, isObservable, firstValueFrom, ReplaySubject, take, finalize, catchError as catchError$1, of } from 'rxjs';
11
11
  import * as i1$3 from '@angular/cdk/scrolling';
12
12
  import { CdkScrollableModule, CdkScrollable } from '@angular/cdk/scrolling';
13
13
  import * as i2 from '@angular/forms';
14
14
  import { NG_VALUE_ACCESSOR, NgControl, FormsModule } from '@angular/forms';
15
15
  import { trigger, transition, animate, style, group, query, state } from '@angular/animations';
16
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
16
17
  import { Overlay } from '@angular/cdk/overlay';
17
18
  import { TemplatePortal, ComponentPortal, CdkPortalOutlet } from '@angular/cdk/portal';
18
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
19
19
 
20
20
  class DashboardLayoutComponent {
21
21
  asideTemplate;
@@ -1058,6 +1058,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImpo
1058
1058
  }], ctorParameters: () => [] });
1059
1059
 
1060
1060
  class TableComponent {
1061
+ sortStateChanged$ = new Subject();
1061
1062
  headers = input([]);
1062
1063
  data = input([]);
1063
1064
  customSort = input(false);
@@ -1112,54 +1113,104 @@ class TableComponent {
1112
1113
  errorMessageDefault = input('Não foi possível carregar os dados.');
1113
1114
  emptyMessageDefault = input(' Não encontramos dados para exibir.');
1114
1115
  sortBy = signal(undefined);
1116
+ lastPresetSignature = '';
1117
+ ngOnChanges(changes) {
1118
+ if (this.multipleSort()) {
1119
+ if (changes['initialSorts'] || changes['headers']) {
1120
+ this.applyInitialSortsOnce();
1121
+ }
1122
+ return;
1123
+ }
1124
+ if (changes['sortField'] || changes['sortOrder'] || changes['headers']) {
1125
+ this.applySingleSortPreset();
1126
+ }
1127
+ }
1128
+ applyInitialSortsOnce() {
1129
+ const incoming = this.initialSorts() ?? [];
1130
+ const allowedKeys = new Set(this.headers().map((h) => String(h.key)));
1131
+ const normalized = incoming
1132
+ .filter((s) => s.value !== 'none')
1133
+ .filter((s) => allowedKeys.has(String(s.key)))
1134
+ .filter((s) => {
1135
+ const header = this.headers().find((h) => String(h.key) === String(s.key));
1136
+ return !!header && header.sortable !== false;
1137
+ })
1138
+ .map((s) => ({ key: String(s.key), value: s.value }));
1139
+ const signature = JSON.stringify(normalized);
1140
+ if (signature === this.lastPresetSignature)
1141
+ return;
1142
+ this.lastPresetSignature = signature;
1143
+ this.sorts.set(normalized);
1144
+ this.sortStateChanged$.next();
1145
+ }
1146
+ applySingleSortPreset() {
1147
+ const field = this.sortField();
1148
+ const order = this.sortOrder();
1149
+ if (!field || order === 'none') {
1150
+ this.sortBy.set(undefined);
1151
+ this.sortDirection.set('none');
1152
+ return;
1153
+ }
1154
+ const header = this.headers().find((h) => String(h.key) === String(field));
1155
+ if (!header || header.sortable === false) {
1156
+ this.sortBy.set(undefined);
1157
+ this.sortDirection.set('none');
1158
+ return;
1159
+ }
1160
+ this.sortBy.set(String(field));
1161
+ this.sortDirection.set(order ?? 'none');
1162
+ this.sortStateChanged$.next();
1163
+ }
1115
1164
  constructor() {
1116
1165
  // MULTI: initialSorts -> sorts (já tem)
1117
- effect(() => {
1118
- if (!this.multipleSort())
1119
- return;
1120
- const allowedKeys = new Set(this.headers().map((h) => String(h.key)));
1121
- const incoming = this.initialSorts() ?? [];
1122
- const map = new Map();
1123
- for (const s of incoming) {
1124
- const key = String(s.key);
1125
- if (!allowedKeys.has(key))
1126
- continue;
1127
- const header = this.headers().find((h) => String(h.key) === key);
1128
- if (!header || header.sortable === false)
1129
- continue;
1130
- if (s.value === 'none')
1131
- continue;
1132
- map.set(key, s.value);
1133
- }
1134
- const normalized = Array.from(map.entries()).map(([key, value]) => ({ key, value }));
1135
- this.sorts.set(normalized);
1136
- this.multiSortFunction.emit(normalized);
1137
- console.log('[TABLE] headers:', this.headers());
1138
- console.log('[TABLE] incoming initialSorts:', this.initialSorts());
1139
- console.log('[TABLE] normalized:', normalized);
1140
- }, {
1141
- allowSignalWrites: true,
1142
- });
1166
+ // effect(
1167
+ // () => {
1168
+ // if (!this.multipleSort()) return;
1169
+ // const allowedKeys = new Set(this.headers().map((h) => String(h.key)));
1170
+ // const incoming = this.initialSorts() ?? [];
1171
+ // const map = new Map<string, ISortDirection>();
1172
+ // for (const s of incoming) {
1173
+ // const key = String(s.key);
1174
+ // if (!allowedKeys.has(key)) continue;
1175
+ // const header = this.headers().find((h) => String(h.key) === key);
1176
+ // if (!header || header.sortable === false) continue;
1177
+ // if (s.value === 'none') continue;
1178
+ // map.set(key, s.value);
1179
+ // }
1180
+ // const normalized: ICustomSort[] = Array.from(map.entries()).map(
1181
+ // ([key, value]) => ({ key, value }),
1182
+ // );
1183
+ // this.sorts.set(normalized);
1184
+ // this.multiSortFunction.emit(normalized);
1185
+ // console.log('[TABLE] headers:', this.headers());
1186
+ // console.log('[TABLE] incoming initialSorts:', this.initialSorts());
1187
+ // console.log('[TABLE] normalized:', normalized);
1188
+ // },
1189
+ // {
1190
+ // allowSignalWrites: true,
1191
+ // },
1192
+ // );
1143
1193
  // SINGLE: sortField/sortOrder -> sortBy/sortDirection (faltava isso)
1144
- effect(() => {
1145
- if (this.multipleSort())
1146
- return; // se virou multi, não aplica single preset
1147
- const field = this.sortField();
1148
- const order = this.sortOrder();
1149
- if (!field) {
1150
- this.sortBy.set(undefined);
1151
- this.sortDirection.set('none');
1152
- return;
1153
- }
1154
- const header = this.headers().find((h) => String(h.key) === String(field));
1155
- if (!header || header.sortable === false) {
1156
- this.sortBy.set(undefined);
1157
- this.sortDirection.set('none');
1158
- return;
1159
- }
1160
- this.sortBy.set(String(field));
1161
- this.sortDirection.set(order ?? 'none');
1162
- });
1194
+ // effect(() => {
1195
+ // if (this.multipleSort()) return; // se virou multi, não aplica single preset
1196
+ // const field = this.sortField();
1197
+ // const order = this.sortOrder();
1198
+ // if (!field) {
1199
+ // this.sortBy.set(undefined);
1200
+ // this.sortDirection.set('none');
1201
+ // return;
1202
+ // }
1203
+ // const header = this.headers().find(
1204
+ // (h) => String(h.key) === String(field),
1205
+ // );
1206
+ // if (!header || header.sortable === false) {
1207
+ // this.sortBy.set(undefined);
1208
+ // this.sortDirection.set('none');
1209
+ // return;
1210
+ // }
1211
+ // this.sortBy.set(String(field));
1212
+ // this.sortDirection.set(order ?? 'none');
1213
+ // });
1163
1214
  }
1164
1215
  ngAfterViewInit() { }
1165
1216
  compareValues(a, b, dir) {
@@ -1177,7 +1228,7 @@ class TableComponent {
1177
1228
  return dir === 'asc' ? sa.localeCompare(sb) : sb.localeCompare(sa);
1178
1229
  }
1179
1230
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1180
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: TableComponent, isStandalone: true, selector: "s4y-table", inputs: { headers: { classPropertyName: "headers", publicName: "headers", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, customSort: { classPropertyName: "customSort", publicName: "customSort", isSignal: true, isRequired: false, transformFunction: null }, multipleSort: { classPropertyName: "multipleSort", publicName: "multipleSort", isSignal: true, isRequired: false, transformFunction: null }, initialSorts: { classPropertyName: "initialSorts", publicName: "initialSorts", isSignal: true, isRequired: false, transformFunction: null }, sortField: { classPropertyName: "sortField", publicName: "sortField", isSignal: true, isRequired: false, transformFunction: null }, sortOrder: { classPropertyName: "sortOrder", publicName: "sortOrder", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null }, tableStyle: { classPropertyName: "tableStyle", publicName: "tableStyle", isSignal: true, isRequired: false, transformFunction: null }, errorMessageDefault: { classPropertyName: "errorMessageDefault", publicName: "errorMessageDefault", isSignal: true, isRequired: false, transformFunction: null }, emptyMessageDefault: { classPropertyName: "emptyMessageDefault", publicName: "emptyMessageDefault", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortFunction: "sortFunction", multiSortFunction: "multiSortFunction" }, queries: [{ propertyName: "rowTemplate", first: true, predicate: ["rowTemplate"], descendants: true }, { propertyName: "headTemplate", first: true, predicate: ["headTemplate"], descendants: true }, { propertyName: "emptyTemplate", first: true, predicate: ["emptyTemplate"], descendants: true }, { propertyName: "errorTemplate", first: true, predicate: ["errorTemplate"], descendants: true }, { propertyName: "customEmptyTemplate", first: true, predicate: ["customEmptyTemplate"], descendants: true }, { propertyName: "customErrorTemplate", first: true, predicate: ["customErrorTemplate"], descendants: true }], ngImport: i0, template: "<div class=\"s4y-table-container\" [ngStyle]=\"tableStyle()\" cdkScrollable>\r\n <table class=\"s4y-table\">\r\n <thead>\r\n <ng-container [ngTemplateOutlet]=\"headTemplate\"></ng-container>\r\n </thead>\r\n <tbody>\r\n @if (isLoading()) {\r\n <tr class=\"loading\">\r\n <td [colSpan]=\"headers().length\">\r\n <s4y-spinner></s4y-spinner>\r\n </td>\r\n </tr>\r\n } @else if (hasError()) {\r\n @if (errorTemplate) {\r\n <tr class=\"error\">\r\n <td [colSpan]=\"headers().length\">\r\n <ng-container [ngTemplateOutlet]=\"errorTemplate\"></ng-container>\r\n </td>\r\n </tr>\r\n } @else if (customErrorTemplate) {\r\n <ng-container [ngTemplateOutlet]=\"customErrorTemplate\"></ng-container>\r\n } @else {\r\n <tr class=\"error\">\r\n <td [colSpan]=\"headers().length\">\r\n {{ errorMessageDefault() }}\r\n </td>\r\n </tr>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"tableContent\"></ng-container>\r\n }\r\n </tbody>\r\n </table>\r\n</div>\r\n\r\n<ng-template #SortIcon>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n >\r\n <path\r\n d=\"M4.86885 11H2.6665L6 3H8L11.3334 11H9.13113L8.7213 10H5.27869L4.86885 11ZM6.09836 8H7.90163L7 5.8L6.09836 8ZM18.9999 16V3H16.9999V16H13.9999L17.9999 21L21.9999 16H18.9999ZM10.9999 13H2.99992V15H7.85414L2.99992 19V21H10.9999V19H6.14605L10.9999 15V13Z\"\r\n ></path>\r\n </svg>\r\n</ng-template>\r\n\r\n<ng-template #tableContent>\r\n @for (item of sortedData(); track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{ $implicit: item, idx: $index }\"\r\n >\r\n </ng-container>\r\n } @empty {\r\n @if (emptyTemplate) {\r\n <tr class=\"empty-content\">\r\n <td [colSpan]=\"headers().length\">\r\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\"></ng-container>\r\n </td>\r\n </tr>\r\n } @else if (customEmptyTemplate) {\r\n <ng-container [ngTemplateOutlet]=\"customEmptyTemplate\"></ng-container>\r\n } @else {\r\n <tr class=\"empty-content\">\r\n <td [colSpan]=\"headers().length\">\r\n {{ emptyMessageDefault() }}\r\n </td>\r\n </tr>\r\n }\r\n }\r\n</ng-template>\r\n", styles: [".s4y-table-container{overflow:auto;min-height:30rem;width:100%;container-type:inline-size}@media screen and (min-width: 320px) and (max-width: 480px){.s4y-table-container{padding-bottom:1.4rem;margin:0 auto}}@media screen and (min-width: 481px) and (max-width: 767px){.s4y-table-container{padding-bottom:1.4rem;margin:0 auto}}@media screen and (min-width: 768px) and (max-width: 1024px){.s4y-table-container{padding-bottom:1.4rem;margin:0 auto}}.s4y-table{border:1px solid var(--gray-300);border-collapse:collapse;width:100%}@media screen and (min-width: 320px) and (max-width: 480px){.s4y-table tr{white-space:nowrap}}@media screen and (min-width: 481px) and (max-width: 767px){.s4y-table tr{white-space:nowrap}}@media screen and (min-width: 768px) and (max-width: 1024px){.s4y-table tr{white-space:nowrap}}.s4y-table .s4y-table-head-th__content{display:flex;justify-content:space-between}.s4y-table thead{background-color:#eaeaea}.s4y-table thead tr{font-size:1.3rem;height:5rem;font-weight:700}.s4y-table thead th{transition:.2s ease}.s4y-table tbody tr{background-color:#fff;font-size:1.3rem;font-weight:400;height:5rem;border-bottom:1px solid var(--gray-300)}.s4y-table tfoot{background-color:#eaeaea}.s4y-table th,.s4y-table td{text-align:start;padding:0 .4rem}.s4y-table td:first-child,.s4y-table td:last-child,.s4y-table th:first-child,.s4y-table th:last-child{padding:0 1.4rem}.s4y-table td,.s4y-table th{padding:0 1.4rem}.s4y-table tr.empty-content,.s4y-table tr.loading,.s4y-table tr.error{border:none}.s4y-table tr.empty-content td,.s4y-table tr.loading td,.s4y-table tr.error td{text-align:center;height:28rem;background-color:transparent;color:var(--gray-900);font-weight:700;border:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: SpinnerComponent, selector: "s4y-spinner", inputs: ["size", "color"] }, { kind: "ngmodule", type: CdkScrollableModule }, { kind: "directive", type: i1$3.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], encapsulation: i0.ViewEncapsulation.None });
1231
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.18", type: TableComponent, isStandalone: true, selector: "s4y-table", inputs: { headers: { classPropertyName: "headers", publicName: "headers", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, customSort: { classPropertyName: "customSort", publicName: "customSort", isSignal: true, isRequired: false, transformFunction: null }, multipleSort: { classPropertyName: "multipleSort", publicName: "multipleSort", isSignal: true, isRequired: false, transformFunction: null }, initialSorts: { classPropertyName: "initialSorts", publicName: "initialSorts", isSignal: true, isRequired: false, transformFunction: null }, sortField: { classPropertyName: "sortField", publicName: "sortField", isSignal: true, isRequired: false, transformFunction: null }, sortOrder: { classPropertyName: "sortOrder", publicName: "sortOrder", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null }, tableStyle: { classPropertyName: "tableStyle", publicName: "tableStyle", isSignal: true, isRequired: false, transformFunction: null }, errorMessageDefault: { classPropertyName: "errorMessageDefault", publicName: "errorMessageDefault", isSignal: true, isRequired: false, transformFunction: null }, emptyMessageDefault: { classPropertyName: "emptyMessageDefault", publicName: "emptyMessageDefault", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortFunction: "sortFunction", multiSortFunction: "multiSortFunction" }, queries: [{ propertyName: "rowTemplate", first: true, predicate: ["rowTemplate"], descendants: true }, { propertyName: "headTemplate", first: true, predicate: ["headTemplate"], descendants: true }, { propertyName: "emptyTemplate", first: true, predicate: ["emptyTemplate"], descendants: true }, { propertyName: "errorTemplate", first: true, predicate: ["errorTemplate"], descendants: true }, { propertyName: "customEmptyTemplate", first: true, predicate: ["customEmptyTemplate"], descendants: true }, { propertyName: "customErrorTemplate", first: true, predicate: ["customErrorTemplate"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"s4y-table-container\" [ngStyle]=\"tableStyle()\" cdkScrollable>\r\n <table class=\"s4y-table\">\r\n <thead>\r\n <ng-container [ngTemplateOutlet]=\"headTemplate\"></ng-container>\r\n </thead>\r\n <tbody>\r\n @if (isLoading()) {\r\n <tr class=\"loading\">\r\n <td [colSpan]=\"headers().length\">\r\n <s4y-spinner></s4y-spinner>\r\n </td>\r\n </tr>\r\n } @else if (hasError()) {\r\n @if (errorTemplate) {\r\n <tr class=\"error\">\r\n <td [colSpan]=\"headers().length\">\r\n <ng-container [ngTemplateOutlet]=\"errorTemplate\"></ng-container>\r\n </td>\r\n </tr>\r\n } @else if (customErrorTemplate) {\r\n <ng-container [ngTemplateOutlet]=\"customErrorTemplate\"></ng-container>\r\n } @else {\r\n <tr class=\"error\">\r\n <td [colSpan]=\"headers().length\">\r\n {{ errorMessageDefault() }}\r\n </td>\r\n </tr>\r\n }\r\n } @else {\r\n <ng-container [ngTemplateOutlet]=\"tableContent\"></ng-container>\r\n }\r\n </tbody>\r\n </table>\r\n</div>\r\n\r\n<ng-template #SortIcon>\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n >\r\n <path\r\n d=\"M4.86885 11H2.6665L6 3H8L11.3334 11H9.13113L8.7213 10H5.27869L4.86885 11ZM6.09836 8H7.90163L7 5.8L6.09836 8ZM18.9999 16V3H16.9999V16H13.9999L17.9999 21L21.9999 16H18.9999ZM10.9999 13H2.99992V15H7.85414L2.99992 19V21H10.9999V19H6.14605L10.9999 15V13Z\"\r\n ></path>\r\n </svg>\r\n</ng-template>\r\n\r\n<ng-template #tableContent>\r\n @for (item of sortedData(); track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"rowTemplate\"\r\n [ngTemplateOutletContext]=\"{ $implicit: item, idx: $index }\"\r\n >\r\n </ng-container>\r\n } @empty {\r\n @if (emptyTemplate) {\r\n <tr class=\"empty-content\">\r\n <td [colSpan]=\"headers().length\">\r\n <ng-container [ngTemplateOutlet]=\"emptyTemplate\"></ng-container>\r\n </td>\r\n </tr>\r\n } @else if (customEmptyTemplate) {\r\n <ng-container [ngTemplateOutlet]=\"customEmptyTemplate\"></ng-container>\r\n } @else {\r\n <tr class=\"empty-content\">\r\n <td [colSpan]=\"headers().length\">\r\n {{ emptyMessageDefault() }}\r\n </td>\r\n </tr>\r\n }\r\n }\r\n</ng-template>\r\n", styles: [".s4y-table-container{overflow:auto;min-height:30rem;width:100%;container-type:inline-size}@media screen and (min-width: 320px) and (max-width: 480px){.s4y-table-container{padding-bottom:1.4rem;margin:0 auto}}@media screen and (min-width: 481px) and (max-width: 767px){.s4y-table-container{padding-bottom:1.4rem;margin:0 auto}}@media screen and (min-width: 768px) and (max-width: 1024px){.s4y-table-container{padding-bottom:1.4rem;margin:0 auto}}.s4y-table{border:1px solid var(--gray-300);border-collapse:collapse;width:100%}@media screen and (min-width: 320px) and (max-width: 480px){.s4y-table tr{white-space:nowrap}}@media screen and (min-width: 481px) and (max-width: 767px){.s4y-table tr{white-space:nowrap}}@media screen and (min-width: 768px) and (max-width: 1024px){.s4y-table tr{white-space:nowrap}}.s4y-table .s4y-table-head-th__content{display:flex;justify-content:space-between}.s4y-table thead{background-color:#eaeaea}.s4y-table thead tr{font-size:1.3rem;height:5rem;font-weight:700}.s4y-table thead th{transition:.2s ease}.s4y-table tbody tr{background-color:#fff;font-size:1.3rem;font-weight:400;height:5rem;border-bottom:1px solid var(--gray-300)}.s4y-table tfoot{background-color:#eaeaea}.s4y-table th,.s4y-table td{text-align:start;padding:0 .4rem}.s4y-table td:first-child,.s4y-table td:last-child,.s4y-table th:first-child,.s4y-table th:last-child{padding:0 1.4rem}.s4y-table td,.s4y-table th{padding:0 1.4rem}.s4y-table tr.empty-content,.s4y-table tr.loading,.s4y-table tr.error{border:none}.s4y-table tr.empty-content td,.s4y-table tr.loading td,.s4y-table tr.error td{text-align:center;height:28rem;background-color:transparent;color:var(--gray-900);font-weight:700;border:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: SpinnerComponent, selector: "s4y-spinner", inputs: ["size", "color"] }, { kind: "ngmodule", type: CdkScrollableModule }, { kind: "directive", type: i1$3.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], encapsulation: i0.ViewEncapsulation.None });
1181
1232
  }
1182
1233
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableComponent, decorators: [{
1183
1234
  type: Component,
@@ -1206,7 +1257,7 @@ class TableSortDirective {
1206
1257
  el;
1207
1258
  renderer;
1208
1259
  table;
1209
- injector;
1260
+ destroyRef;
1210
1261
  s4ySortableColumnName = input();
1211
1262
  s4ySortable = input(false);
1212
1263
  oldSortableColumnName;
@@ -1214,17 +1265,20 @@ class TableSortDirective {
1214
1265
  spanElement;
1215
1266
  oldDirection = signal('none');
1216
1267
  newDirection = signal('none');
1217
- constructor(el, renderer, table, injector) {
1268
+ constructor(el, renderer, table, destroyRef) {
1218
1269
  this.el = el;
1219
1270
  this.renderer = renderer;
1220
1271
  this.table = table;
1221
- this.injector = injector;
1272
+ this.destroyRef = destroyRef;
1222
1273
  }
1223
1274
  ngAfterContentInit() {
1224
1275
  if (!this.s4ySortable())
1225
1276
  return;
1226
1277
  this.insertSortIcon();
1227
- this.setupReactivePaint();
1278
+ this.repaint();
1279
+ this.table.sortStateChanged$
1280
+ .pipe(takeUntilDestroyed(this.destroyRef))
1281
+ .subscribe(() => this.repaint());
1228
1282
  }
1229
1283
  emitSortableColumnName() {
1230
1284
  if (!this.s4ySortable())
@@ -1272,6 +1326,7 @@ class TableSortDirective {
1272
1326
  this.clearHoverStyleTH();
1273
1327
  this.handleStyleTH();
1274
1328
  this.handleDirectionIcon();
1329
+ this.table.sortStateChanged$.next();
1275
1330
  }
1276
1331
  mouseOver() {
1277
1332
  if (!this.hostThElement)
@@ -1453,37 +1508,19 @@ class TableSortDirective {
1453
1508
  this.table.sortFunction.emit({ key, value });
1454
1509
  this.table.multiSortFunction.emit(updatedList);
1455
1510
  }
1456
- setupReactivePaint() {
1457
- effect(() => {
1458
- if (!this.s4ySortable())
1459
- return;
1460
- const key = this.s4ySortableColumnName();
1461
- if (!key)
1462
- return;
1463
- if (this.isMultipleSort) {
1464
- const found = this.table
1465
- .sorts()
1466
- .find((s) => String(s.key) === String(key));
1467
- const dir = found?.value ?? 'none';
1468
- this.newDirection.set(dir);
1469
- if (dir === 'none') {
1470
- this.removeActiveStyle(this.hostThElement);
1471
- if (this.spanElement)
1472
- this.spanElement.innerHTML = this.ascIcon;
1473
- return;
1474
- }
1475
- this.setActiveStyle(this.hostThElement);
1476
- if (this.spanElement) {
1477
- this.spanElement.innerHTML =
1478
- dir === 'desc' ? this.descIcon : this.ascIcon;
1479
- }
1480
- return;
1481
- }
1482
- const sortBy = this.table.sortBy();
1483
- const dir = this.table.sortDirection();
1484
- const isThis = String(sortBy ?? '') === String(key);
1485
- this.newDirection.set(isThis ? dir : 'none');
1486
- if (!isThis || dir === 'none') {
1511
+ repaint() {
1512
+ if (!this.s4ySortable())
1513
+ return;
1514
+ const key = this.s4ySortableColumnName();
1515
+ if (!key)
1516
+ return;
1517
+ if (this.isMultipleSort) {
1518
+ const found = this.table
1519
+ .sorts()
1520
+ .find((s) => String(s.key) === String(key));
1521
+ const dir = found?.value ?? 'none';
1522
+ this.newDirection.set(dir);
1523
+ if (dir === 'none') {
1487
1524
  this.removeActiveStyle(this.hostThElement);
1488
1525
  if (this.spanElement)
1489
1526
  this.spanElement.innerHTML = this.ascIcon;
@@ -1494,9 +1531,24 @@ class TableSortDirective {
1494
1531
  this.spanElement.innerHTML =
1495
1532
  dir === 'desc' ? this.descIcon : this.ascIcon;
1496
1533
  }
1497
- }, {
1498
- injector: this.injector,
1499
- });
1534
+ return;
1535
+ }
1536
+ // single
1537
+ const sortBy = this.table.sortBy();
1538
+ const dir = this.table.sortDirection();
1539
+ const isThis = String(sortBy ?? '') === String(key);
1540
+ this.newDirection.set(isThis ? dir : 'none');
1541
+ if (!isThis || dir === 'none') {
1542
+ this.removeActiveStyle(this.hostThElement);
1543
+ if (this.spanElement)
1544
+ this.spanElement.innerHTML = this.ascIcon;
1545
+ return;
1546
+ }
1547
+ this.setActiveStyle(this.hostThElement);
1548
+ if (this.spanElement) {
1549
+ this.spanElement.innerHTML =
1550
+ dir === 'desc' ? this.descIcon : this.ascIcon;
1551
+ }
1500
1552
  }
1501
1553
  get ascIcon() {
1502
1554
  return '<svg style="height: 1.4rem; width: 1.4rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.86885 11H2.6665L6 3H8L11.3334 11H9.13113L8.7213 10H5.27869L4.86885 11ZM6.09836 8H7.90163L7 5.8L6.09836 8ZM21.9999 8L17.9999 3L13.9999 8H16.9999V21H18.9999V8H21.9999ZM10.9999 13H2.99992V15H7.85414L2.99992 19V21H10.9999V19H6.14605L10.9999 15V13Z"></path></svg>';
@@ -1504,7 +1556,7 @@ class TableSortDirective {
1504
1556
  get descIcon() {
1505
1557
  return '<svg style="height: 1.4rem; width: 1.4rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.86885 11H2.6665L6 3H8L11.3334 11H9.13113L8.7213 10H5.27869L4.86885 11ZM6.09836 8H7.90163L7 5.8L6.09836 8ZM18.9999 16V3H16.9999V16H13.9999L17.9999 21L21.9999 16H18.9999ZM10.9999 13H2.99992V15H7.85414L2.99992 19V21H10.9999V19H6.14605L10.9999 15V13Z"></path></svg>';
1506
1558
  }
1507
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableSortDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: TableComponent }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Directive });
1559
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableSortDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: TableComponent }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Directive });
1508
1560
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.18", type: TableSortDirective, isStandalone: true, selector: "[s4ySortableColumnName]", inputs: { s4ySortableColumnName: { classPropertyName: "s4ySortableColumnName", publicName: "s4ySortableColumnName", isSignal: true, isRequired: false, transformFunction: null }, s4ySortable: { classPropertyName: "s4ySortable", publicName: "s4ySortable", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "emitSortableColumnName()", "mouseenter": "mouseOver()", "mouseleave": "mouseLeave()" } }, ngImport: i0 });
1509
1561
  }
1510
1562
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableSortDirective, decorators: [{
@@ -1513,7 +1565,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImpo
1513
1565
  selector: '[s4ySortableColumnName]',
1514
1566
  standalone: true,
1515
1567
  }]
1516
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: TableComponent }, { type: i0.Injector }], propDecorators: { emitSortableColumnName: [{
1568
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: TableComponent }, { type: i0.DestroyRef }], propDecorators: { emitSortableColumnName: [{
1517
1569
  type: HostListener,
1518
1570
  args: ['click']
1519
1571
  }], mouseOver: [{