s4y-ui 5.8.2 → 5.9.2

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, ViewEncapsulation, effect, 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, effect, ViewEncapsulation, 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';
@@ -1062,6 +1062,7 @@ class TableComponent {
1062
1062
  data = input([]);
1063
1063
  customSort = input(false);
1064
1064
  multipleSort = input(false);
1065
+ initialSorts = input([]);
1065
1066
  sortFunction = output();
1066
1067
  multiSortFunction = output();
1067
1068
  sortField = input(null);
@@ -1069,31 +1070,33 @@ class TableComponent {
1069
1070
  sorts = signal([]);
1070
1071
  sortedData = computed(() => {
1071
1072
  let originalData = [...this.data()];
1072
- const sortBy = this.sortBy();
1073
- const direction = this.sortDirection();
1074
1073
  if (this.customSort())
1075
1074
  return originalData;
1076
- if (!originalData.length || !sortBy)
1075
+ if (this.multipleSort()) {
1076
+ const sorts = this.sorts();
1077
+ if (!sorts.length)
1078
+ return originalData;
1079
+ return originalData
1080
+ .map((item, idx) => ({ item, idx }))
1081
+ .sort((x, y) => {
1082
+ for (const s of sorts) {
1083
+ const key = String(s.key);
1084
+ const res = this.compareValues(x.item[key], y.item[key], s.value);
1085
+ if (res !== 0)
1086
+ return res;
1087
+ }
1088
+ return x.idx - y.idx;
1089
+ })
1090
+ .map((x) => x.item);
1091
+ }
1092
+ const sortBy = this.sortBy();
1093
+ const direction = this.sortDirection();
1094
+ if (!originalData.length || !sortBy || direction === 'none')
1077
1095
  return originalData;
1078
1096
  const header = this.headers().find((h) => h.key === sortBy);
1079
1097
  if (!header || header.sortable === false)
1080
1098
  return originalData;
1081
- if (direction === 'none')
1082
- return originalData;
1083
- let copiedData = [...this.data()];
1084
- return copiedData.sort((a, b) => {
1085
- const valueA = a[sortBy];
1086
- const valueB = b[sortBy];
1087
- if (typeof valueA === 'string' && typeof valueB === 'string') {
1088
- return direction === 'asc'
1089
- ? valueA.localeCompare(valueB)
1090
- : valueB.localeCompare(valueA);
1091
- }
1092
- if (typeof valueA === 'number' && typeof valueB === 'number') {
1093
- return direction === 'asc' ? valueA - valueB : valueB - valueA;
1094
- }
1095
- return 0;
1096
- });
1099
+ return [...originalData].sort((a, b) => this.compareValues(a[sortBy], b[sortBy], direction));
1097
1100
  });
1098
1101
  sortDirection = signal('asc');
1099
1102
  isLoading = input(false);
@@ -1109,10 +1112,68 @@ class TableComponent {
1109
1112
  errorMessageDefault = input('Não foi possível carregar os dados.');
1110
1113
  emptyMessageDefault = input(' Não encontramos dados para exibir.');
1111
1114
  sortBy = signal(undefined);
1112
- constructor() { }
1115
+ constructor() {
1116
+ // 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
+ console.log(normalized);
1136
+ this.sorts.set(normalized);
1137
+ this.multiSortFunction.emit(normalized);
1138
+ });
1139
+ // SINGLE: sortField/sortOrder -> sortBy/sortDirection (faltava isso)
1140
+ effect(() => {
1141
+ if (this.multipleSort())
1142
+ return; // se virou multi, não aplica single preset
1143
+ const field = this.sortField();
1144
+ const order = this.sortOrder();
1145
+ if (!field) {
1146
+ this.sortBy.set(undefined);
1147
+ this.sortDirection.set('none');
1148
+ return;
1149
+ }
1150
+ const header = this.headers().find((h) => String(h.key) === String(field));
1151
+ if (!header || header.sortable === false) {
1152
+ this.sortBy.set(undefined);
1153
+ this.sortDirection.set('none');
1154
+ return;
1155
+ }
1156
+ this.sortBy.set(String(field));
1157
+ this.sortDirection.set(order ?? 'none');
1158
+ });
1159
+ }
1113
1160
  ngAfterViewInit() { }
1161
+ compareValues(a, b, dir) {
1162
+ if (a == null && b == null)
1163
+ return 0;
1164
+ if (a == null)
1165
+ return dir === 'asc' ? -1 : 1;
1166
+ if (b == null)
1167
+ return dir === 'asc' ? 1 : -1;
1168
+ if (typeof a === 'number' && typeof b === 'number') {
1169
+ return dir === 'asc' ? a - b : b - a;
1170
+ }
1171
+ const sa = String(a);
1172
+ const sb = String(b);
1173
+ return dir === 'asc' ? sa.localeCompare(sb) : sb.localeCompare(sa);
1174
+ }
1114
1175
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1115
- 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 }, 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 });
1176
+ 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 });
1116
1177
  }
1117
1178
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableComponent, decorators: [{
1118
1179
  type: Component,
@@ -1141,6 +1202,7 @@ class TableSortDirective {
1141
1202
  el;
1142
1203
  renderer;
1143
1204
  table;
1205
+ injector;
1144
1206
  s4ySortableColumnName = input();
1145
1207
  s4ySortable = input(false);
1146
1208
  oldSortableColumnName;
@@ -1148,16 +1210,17 @@ class TableSortDirective {
1148
1210
  spanElement;
1149
1211
  oldDirection = signal('none');
1150
1212
  newDirection = signal('none');
1151
- constructor(el, renderer, table) {
1213
+ constructor(el, renderer, table, injector) {
1152
1214
  this.el = el;
1153
1215
  this.renderer = renderer;
1154
1216
  this.table = table;
1217
+ this.injector = injector;
1155
1218
  }
1156
1219
  ngAfterContentInit() {
1157
1220
  if (!this.s4ySortable())
1158
1221
  return;
1159
1222
  this.insertSortIcon();
1160
- this.selectedInitialSort();
1223
+ this.setupReactivePaint();
1161
1224
  }
1162
1225
  emitSortableColumnName() {
1163
1226
  if (!this.s4ySortable())
@@ -1166,6 +1229,17 @@ class TableSortDirective {
1166
1229
  const sortByColumnName = this.s4ySortableColumnName();
1167
1230
  if (!sortByColumnName)
1168
1231
  return;
1232
+ if (this.isMultipleSort) {
1233
+ const current = this.table
1234
+ .sorts()
1235
+ .find((s) => String(s.key) === String(sortByColumnName))?.value ??
1236
+ 'none';
1237
+ this.newDirection.set(current);
1238
+ }
1239
+ else {
1240
+ const isThis = String(this.table.sortBy() ?? '') === String(sortByColumnName);
1241
+ this.newDirection.set(isThis ? this.table.sortDirection() : 'none');
1242
+ }
1169
1243
  this.oldDirection.set(this.newDirection());
1170
1244
  this.newDirection.update((direction) => {
1171
1245
  switch (direction) {
@@ -1284,12 +1358,6 @@ class TableSortDirective {
1284
1358
  autoSortable(columnName) {
1285
1359
  this.table.sortBy.set(columnName);
1286
1360
  }
1287
- customSortable() {
1288
- this.table.sortFunction.emit({
1289
- key: this.s4ySortableColumnName(),
1290
- value: this.newDirection(),
1291
- });
1292
- }
1293
1361
  multipleSortable() {
1294
1362
  const key = this.s4ySortableColumnName();
1295
1363
  if (!key)
@@ -1371,9 +1439,6 @@ class TableSortDirective {
1371
1439
  return [];
1372
1440
  return Array.from(table.querySelectorAll('th'));
1373
1441
  }
1374
- getThByColumnName(columnName) {
1375
- return this.getAllThElements().find((th) => th.getAttribute('ng-reflect-s4y-sortable-column-name') === columnName);
1376
- }
1377
1442
  customSortableSingleLast() {
1378
1443
  const key = this.s4ySortableColumnName();
1379
1444
  if (!key)
@@ -1384,15 +1449,50 @@ class TableSortDirective {
1384
1449
  this.table.sortFunction.emit({ key, value });
1385
1450
  this.table.multiSortFunction.emit(updatedList);
1386
1451
  }
1387
- clearAllActiveStyles() {
1388
- const tableEl = this.el.nativeElement.closest('table');
1389
- if (!tableEl)
1390
- return;
1391
- const ths = Array.from(tableEl.querySelectorAll('th'));
1392
- for (const th of ths) {
1393
- this.removeActiveStyle(th);
1394
- this.renderer.removeClass(th, 's4y-th-hover');
1395
- }
1452
+ setupReactivePaint() {
1453
+ effect(() => {
1454
+ if (!this.s4ySortable())
1455
+ return;
1456
+ const key = this.s4ySortableColumnName();
1457
+ if (!key)
1458
+ return;
1459
+ if (this.isMultipleSort) {
1460
+ const found = this.table
1461
+ .sorts()
1462
+ .find((s) => String(s.key) === String(key));
1463
+ const dir = found?.value ?? 'none';
1464
+ this.newDirection.set(dir);
1465
+ if (dir === 'none') {
1466
+ this.removeActiveStyle(this.hostThElement);
1467
+ if (this.spanElement)
1468
+ this.spanElement.innerHTML = this.ascIcon;
1469
+ return;
1470
+ }
1471
+ this.setActiveStyle(this.hostThElement);
1472
+ if (this.spanElement) {
1473
+ this.spanElement.innerHTML =
1474
+ dir === 'desc' ? this.descIcon : this.ascIcon;
1475
+ }
1476
+ return;
1477
+ }
1478
+ const sortBy = this.table.sortBy();
1479
+ const dir = this.table.sortDirection();
1480
+ const isThis = String(sortBy ?? '') === String(key);
1481
+ this.newDirection.set(isThis ? dir : 'none');
1482
+ if (!isThis || dir === 'none') {
1483
+ this.removeActiveStyle(this.hostThElement);
1484
+ if (this.spanElement)
1485
+ this.spanElement.innerHTML = this.ascIcon;
1486
+ return;
1487
+ }
1488
+ this.setActiveStyle(this.hostThElement);
1489
+ if (this.spanElement) {
1490
+ this.spanElement.innerHTML =
1491
+ dir === 'desc' ? this.descIcon : this.ascIcon;
1492
+ }
1493
+ }, {
1494
+ injector: this.injector,
1495
+ });
1396
1496
  }
1397
1497
  get ascIcon() {
1398
1498
  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>';
@@ -1400,7 +1500,7 @@ class TableSortDirective {
1400
1500
  get descIcon() {
1401
1501
  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>';
1402
1502
  }
1403
- 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 }], target: i0.ɵɵFactoryTarget.Directive });
1503
+ 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 });
1404
1504
  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 });
1405
1505
  }
1406
1506
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImport: i0, type: TableSortDirective, decorators: [{
@@ -1409,7 +1509,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.18", ngImpo
1409
1509
  selector: '[s4ySortableColumnName]',
1410
1510
  standalone: true,
1411
1511
  }]
1412
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: TableComponent }], propDecorators: { emitSortableColumnName: [{
1512
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: TableComponent }, { type: i0.Injector }], propDecorators: { emitSortableColumnName: [{
1413
1513
  type: HostListener,
1414
1514
  args: ['click']
1415
1515
  }], mouseOver: [{