sapenlinea-components 0.9.76 → 0.9.77
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.
- package/fesm2022/sapenlinea-components.mjs +330 -87
- package/fesm2022/sapenlinea-components.mjs.map +1 -1
- package/index.d.ts +49 -12
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import PubSub from 'pubsub-js';
|
|
|
8
8
|
import { BarChart as BarChart$1, LineChart as LineChart$1, PieChart, HeatmapChart } from 'echarts/charts';
|
|
9
9
|
import * as echarts from 'echarts/core';
|
|
10
10
|
import { NgxEchartsDirective, provideEchartsCore } from 'ngx-echarts';
|
|
11
|
-
import { GridComponent, TooltipComponent, GraphicComponent, DataZoomComponent, VisualMapComponent, LegendComponent } from 'echarts/components';
|
|
11
|
+
import { GridComponent, TooltipComponent, GraphicComponent, DataZoomComponent, VisualMapComponent, LegendComponent, TitleComponent } from 'echarts/components';
|
|
12
12
|
import { CanvasRenderer } from 'echarts/renderers';
|
|
13
13
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
14
14
|
|
|
@@ -584,9 +584,10 @@ class DateTimePicker {
|
|
|
584
584
|
// Estado AM/PM
|
|
585
585
|
selectedAmPm = signal('AM', ...(ngDevMode ? [{ debugName: "selectedAmPm" }] : []));
|
|
586
586
|
documentClickListener;
|
|
587
|
+
resizeListener;
|
|
587
588
|
/**
|
|
588
589
|
* Valor que se muestra en el campo de texto.
|
|
589
|
-
*
|
|
590
|
+
* Modo 'date': DD/MM/YYYY. Modo 'datetime': DD/MM/YYYY HH:MM (24h).
|
|
590
591
|
*/
|
|
591
592
|
displayValue = computed(() => {
|
|
592
593
|
const text = this.inputTextValue().trim();
|
|
@@ -596,10 +597,7 @@ class DateTimePicker {
|
|
|
596
597
|
const date = this.selectedDate();
|
|
597
598
|
if (!date)
|
|
598
599
|
return '';
|
|
599
|
-
|
|
600
|
-
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
601
|
-
const year = date.getFullYear();
|
|
602
|
-
return `${day}/${month}/${year}`;
|
|
600
|
+
return this.formatDateForInput(date);
|
|
603
601
|
}, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
|
|
604
602
|
monthName = computed(() => {
|
|
605
603
|
const months = [
|
|
@@ -654,6 +652,7 @@ class DateTimePicker {
|
|
|
654
652
|
}
|
|
655
653
|
ngOnDestroy() {
|
|
656
654
|
this.removeDocumentListener();
|
|
655
|
+
this.removePositioningListeners();
|
|
657
656
|
}
|
|
658
657
|
writeValue(value) {
|
|
659
658
|
let date = null;
|
|
@@ -698,11 +697,10 @@ class DateTimePicker {
|
|
|
698
697
|
if (this.isBlocked())
|
|
699
698
|
return;
|
|
700
699
|
this.markAsTouched();
|
|
701
|
-
|
|
702
|
-
const spaceBelow = window.innerHeight - rect.bottom;
|
|
703
|
-
this.openUpward.set(spaceBelow < 420);
|
|
700
|
+
this.updatePosition();
|
|
704
701
|
this.isOpen.set(true);
|
|
705
702
|
this.addDocumentListener();
|
|
703
|
+
this.addPositioningListeners();
|
|
706
704
|
// Si hay fecha seleccionada, navegar a ese mes/año
|
|
707
705
|
const selected = this.selectedDate();
|
|
708
706
|
if (selected) {
|
|
@@ -712,9 +710,34 @@ class DateTimePicker {
|
|
|
712
710
|
}
|
|
713
711
|
close() {
|
|
714
712
|
this.isOpen.set(false);
|
|
713
|
+
this.openUpward.set(false);
|
|
715
714
|
this.removeDocumentListener();
|
|
715
|
+
this.removePositioningListeners();
|
|
716
716
|
this.markAsTouched();
|
|
717
717
|
}
|
|
718
|
+
updatePosition() {
|
|
719
|
+
const scrollY = window.scrollY;
|
|
720
|
+
const maxScrollY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
|
721
|
+
const canScrollDown = scrollY < maxScrollY - 1;
|
|
722
|
+
this.openUpward.set(!canScrollDown);
|
|
723
|
+
}
|
|
724
|
+
addPositioningListeners() {
|
|
725
|
+
this.removePositioningListeners();
|
|
726
|
+
this.ngZone.runOutsideAngular(() => {
|
|
727
|
+
this.resizeListener = () => {
|
|
728
|
+
if (this.isOpen()) {
|
|
729
|
+
this.ngZone.run(() => this.updatePosition());
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
window.addEventListener('resize', this.resizeListener);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
removePositioningListeners() {
|
|
736
|
+
if (this.resizeListener) {
|
|
737
|
+
window.removeEventListener('resize', this.resizeListener);
|
|
738
|
+
this.resizeListener = undefined;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
718
741
|
selectDay(day) {
|
|
719
742
|
if (!day || this.isBlocked())
|
|
720
743
|
return;
|
|
@@ -868,17 +891,19 @@ class DateTimePicker {
|
|
|
868
891
|
this.markAsTouched();
|
|
869
892
|
this.dateChange.emit(newDate);
|
|
870
893
|
}
|
|
871
|
-
// Manejo de input de texto (DD/MM/YYYY)
|
|
894
|
+
// Manejo de input de texto (DD/MM/YYYY o DD/MM/YYYY HH:MM)
|
|
872
895
|
onInputChange(event) {
|
|
873
896
|
if (this.isBlocked())
|
|
874
897
|
return;
|
|
875
898
|
const input = event.target;
|
|
876
|
-
|
|
899
|
+
const value = input.value;
|
|
900
|
+
const isDateTime = this.mode() === 'datetime';
|
|
877
901
|
// Eliminar todo lo que no sea número
|
|
878
902
|
const numbersOnly = value.replace(/\D/g, '');
|
|
879
|
-
// Limitar a 8 dígitos
|
|
880
|
-
const
|
|
881
|
-
|
|
903
|
+
// Limitar a 8 dígitos para fecha o 12 para datetime
|
|
904
|
+
const maxDigits = isDateTime ? 12 : 8;
|
|
905
|
+
const limitedNumbers = numbersOnly.slice(0, maxDigits);
|
|
906
|
+
// Formatear con separadores: DD/MM/YYYY [ HH:MM]
|
|
882
907
|
let formatted = '';
|
|
883
908
|
if (limitedNumbers.length > 0) {
|
|
884
909
|
formatted = limitedNumbers.slice(0, 2);
|
|
@@ -888,12 +913,18 @@ class DateTimePicker {
|
|
|
888
913
|
if (limitedNumbers.length > 4) {
|
|
889
914
|
formatted += '/' + limitedNumbers.slice(4, 8);
|
|
890
915
|
}
|
|
916
|
+
if (isDateTime && limitedNumbers.length > 8) {
|
|
917
|
+
formatted += ' ' + limitedNumbers.slice(8, 10);
|
|
918
|
+
}
|
|
919
|
+
if (isDateTime && limitedNumbers.length > 10) {
|
|
920
|
+
formatted += ':' + limitedNumbers.slice(10, 12);
|
|
921
|
+
}
|
|
891
922
|
}
|
|
892
923
|
this.inputTextValue.set(formatted);
|
|
893
924
|
input.value = formatted;
|
|
894
925
|
this.markAsTouched();
|
|
895
|
-
|
|
896
|
-
if (formatted.length ===
|
|
926
|
+
const completeLength = isDateTime ? 16 : 10;
|
|
927
|
+
if (formatted.length === completeLength) {
|
|
897
928
|
const parsedDate = this.parseDateInput(formatted);
|
|
898
929
|
if (parsedDate) {
|
|
899
930
|
if (this.isDateDisabled(parsedDate)) {
|
|
@@ -913,9 +944,8 @@ class DateTimePicker {
|
|
|
913
944
|
}
|
|
914
945
|
}
|
|
915
946
|
/**
|
|
916
|
-
* Selects the date segment
|
|
917
|
-
*
|
|
918
|
-
* Format: DD/MM/YYYY → segments at positions 0-1, 3-4, 6-9
|
|
947
|
+
* Selects the date/time segment under the cursor on click.
|
|
948
|
+
* Format: DD/MM/YYYY [HH:MM] → DD=0-1, MM=3-4, YYYY=6-9, HH=11-12, MM=14-15
|
|
919
949
|
*/
|
|
920
950
|
onInputClick(event) {
|
|
921
951
|
event.stopPropagation();
|
|
@@ -924,29 +954,31 @@ class DateTimePicker {
|
|
|
924
954
|
const input = event.target;
|
|
925
955
|
const pos = input.selectionStart ?? 0;
|
|
926
956
|
const value = input.value;
|
|
927
|
-
// Only do segment selection when the value looks like a date (has slashes)
|
|
928
957
|
if (!value || !value.includes('/'))
|
|
929
958
|
return;
|
|
930
|
-
|
|
931
|
-
// DD = 0..1, slash at 2, MM = 3..4, slash at 5, YYYY = 6..9
|
|
959
|
+
const hasTime = this.mode() === 'datetime' && value.includes(':');
|
|
932
960
|
let start;
|
|
933
961
|
let end;
|
|
934
962
|
if (pos <= 2) {
|
|
935
|
-
// Day segment
|
|
936
963
|
start = 0;
|
|
937
964
|
end = 2;
|
|
938
965
|
}
|
|
939
966
|
else if (pos <= 5) {
|
|
940
|
-
// Month segment
|
|
941
967
|
start = 3;
|
|
942
968
|
end = 5;
|
|
943
969
|
}
|
|
944
|
-
else {
|
|
945
|
-
// Year segment
|
|
970
|
+
else if (pos <= 10 || !hasTime) {
|
|
946
971
|
start = 6;
|
|
947
972
|
end = 10;
|
|
948
973
|
}
|
|
949
|
-
|
|
974
|
+
else if (pos <= 13) {
|
|
975
|
+
start = 11;
|
|
976
|
+
end = 13;
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
start = 14;
|
|
980
|
+
end = 16;
|
|
981
|
+
}
|
|
950
982
|
setTimeout(() => {
|
|
951
983
|
input.setSelectionRange(start, end);
|
|
952
984
|
}, 0);
|
|
@@ -959,16 +991,27 @@ class DateTimePicker {
|
|
|
959
991
|
parseDateInput(input) {
|
|
960
992
|
if (!input || input.trim() === '')
|
|
961
993
|
return null;
|
|
962
|
-
const
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
994
|
+
const trimmed = input.trim();
|
|
995
|
+
const dateTimeMatch = trimmed.match(/^(\d{2})\/(\d{2})\/(\d{4})\s(\d{2}):(\d{2})$/);
|
|
996
|
+
const dateMatch = trimmed.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
|
|
997
|
+
const match = dateTimeMatch || dateMatch;
|
|
998
|
+
if (!match)
|
|
999
|
+
return null;
|
|
1000
|
+
const day = parseInt(match[1], 10);
|
|
1001
|
+
const month = parseInt(match[2], 10) - 1;
|
|
1002
|
+
const year = parseInt(match[3], 10);
|
|
1003
|
+
const hour = match[4] ? parseInt(match[4], 10) : 0;
|
|
1004
|
+
const minute = match[5] ? parseInt(match[5], 10) : 0;
|
|
1005
|
+
if (day >= 1 && day <= 31 &&
|
|
1006
|
+
month >= 0 && month <= 11 &&
|
|
1007
|
+
year >= 1900 && year <= 2100 &&
|
|
1008
|
+
hour >= 0 && hour <= 23 &&
|
|
1009
|
+
minute >= 0 && minute <= 59) {
|
|
1010
|
+
const date = new Date(year, month, day, hour, minute);
|
|
1011
|
+
if (date.getDate() === day &&
|
|
1012
|
+
date.getMonth() === month &&
|
|
1013
|
+
date.getFullYear() === year) {
|
|
1014
|
+
return date;
|
|
972
1015
|
}
|
|
973
1016
|
}
|
|
974
1017
|
return null;
|
|
@@ -977,7 +1020,13 @@ class DateTimePicker {
|
|
|
977
1020
|
const day = date.getDate().toString().padStart(2, '0');
|
|
978
1021
|
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
979
1022
|
const year = date.getFullYear();
|
|
980
|
-
|
|
1023
|
+
const dateStr = `${day}/${month}/${year}`;
|
|
1024
|
+
if (this.mode() === 'datetime') {
|
|
1025
|
+
const hour = date.getHours().toString().padStart(2, '0');
|
|
1026
|
+
const minute = date.getMinutes().toString().padStart(2, '0');
|
|
1027
|
+
return `${dateStr} ${hour}:${minute}`;
|
|
1028
|
+
}
|
|
1029
|
+
return dateStr;
|
|
981
1030
|
}
|
|
982
1031
|
updateInternalState(date) {
|
|
983
1032
|
this.selectedDate.set(date);
|
|
@@ -2360,9 +2409,13 @@ class SelectCustomSearch {
|
|
|
2360
2409
|
}, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
|
|
2361
2410
|
onChange = (value) => { };
|
|
2362
2411
|
onTouched = () => { };
|
|
2412
|
+
resizeListener;
|
|
2363
2413
|
constructor(elementRef) {
|
|
2364
2414
|
this.elementRef = elementRef;
|
|
2365
2415
|
}
|
|
2416
|
+
ngOnDestroy() {
|
|
2417
|
+
this.removePositioningListeners();
|
|
2418
|
+
}
|
|
2366
2419
|
writeValue(value) {
|
|
2367
2420
|
this.selectedValue.set(value);
|
|
2368
2421
|
}
|
|
@@ -2388,20 +2441,41 @@ class SelectCustomSearch {
|
|
|
2388
2441
|
open() {
|
|
2389
2442
|
if (this.isBlocked())
|
|
2390
2443
|
return;
|
|
2391
|
-
|
|
2392
|
-
const spaceBelow = window.innerHeight - rect.bottom;
|
|
2393
|
-
this.openUpward.set(spaceBelow < 260);
|
|
2444
|
+
this.updatePosition();
|
|
2394
2445
|
this.isOpen.set(true);
|
|
2395
2446
|
this.searchTerm.set('');
|
|
2396
|
-
|
|
2447
|
+
requestAnimationFrame(() => {
|
|
2397
2448
|
const input = this.elementRef.nativeElement.querySelector('.search-input');
|
|
2398
2449
|
if (input)
|
|
2399
|
-
input.focus();
|
|
2450
|
+
input.focus({ preventScroll: true });
|
|
2400
2451
|
});
|
|
2452
|
+
this.addPositioningListeners();
|
|
2401
2453
|
}
|
|
2402
2454
|
close() {
|
|
2403
2455
|
this.isOpen.set(false);
|
|
2404
2456
|
this.searchTerm.set('');
|
|
2457
|
+
this.openUpward.set(false);
|
|
2458
|
+
this.removePositioningListeners();
|
|
2459
|
+
}
|
|
2460
|
+
updatePosition() {
|
|
2461
|
+
const scrollY = window.scrollY;
|
|
2462
|
+
const maxScrollY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
|
2463
|
+
const canScrollDown = scrollY < maxScrollY - 1;
|
|
2464
|
+
this.openUpward.set(!canScrollDown);
|
|
2465
|
+
}
|
|
2466
|
+
addPositioningListeners() {
|
|
2467
|
+
this.removePositioningListeners();
|
|
2468
|
+
this.resizeListener = () => {
|
|
2469
|
+
if (this.isOpen())
|
|
2470
|
+
this.updatePosition();
|
|
2471
|
+
};
|
|
2472
|
+
window.addEventListener('resize', this.resizeListener);
|
|
2473
|
+
}
|
|
2474
|
+
removePositioningListeners() {
|
|
2475
|
+
if (this.resizeListener) {
|
|
2476
|
+
window.removeEventListener('resize', this.resizeListener);
|
|
2477
|
+
this.resizeListener = undefined;
|
|
2478
|
+
}
|
|
2405
2479
|
}
|
|
2406
2480
|
selectOption(option) {
|
|
2407
2481
|
if (this.isBlocked())
|
|
@@ -2717,11 +2791,11 @@ class DynamicFormFields {
|
|
|
2717
2791
|
submitBtn?.click();
|
|
2718
2792
|
}
|
|
2719
2793
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DynamicFormFields, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2720
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DynamicFormFields, isStandalone: true, selector: "lib-dynamic-form-fields", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (form()) {\n<form class=\"form\" [formGroup]=\"form()\" [class.form--compact]=\"compact()\" (keydown)=\"onEnter($event)\">\n @for (sec of sections(); track $index) {\n <section class=\"section\">\n @if (sec.title) {\n <h3 class=\"section-title\">{{ sec.title }}</h3>\n } @if (sec.description) {\n <p class=\"section-desc\">{{ sec.description }}</p>\n }\n\n <div class=\"grid\">\n @for (run of getFieldRuns(sec.fields); track $index) {\n\n @if (run.isGroup) {\n <!-- toggle: wrapper que siempre ocupa la fila completa -->\n <div class=\"col col--group-wrapper\">\n @for (f of run.fields; track f.key) {\n <div class=\"group-item\">\n <lib-toggle-custom [key]=\"f.key\" [label]=\"f.label ?? ''\" [formControlName]=\"f.key\" />\n </div>\n }\n </div>\n\n } @else {\n <!-- Campos normales (incluye checkbox y radio con su col configurable) -->\n @for (f of run.fields; track f.key) {\n <div class=\"col\" [style.--col-span]=\"f.col || 6\">\n @if (['text','number','email', 'password', 'time'].includes(f.type))\n {\n <input\n class=\"input\"\n [type]=\"f.type\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n (input)=\"onUppercaseInput($event, f.type, f.key)\"\n />\n } @if (['date', 'datetime-local'].includes(f.type)) {\n <lib-date-time-picker\n [mode]=\"getDatePickerMode(f.type)\"\n [placeholder]=\"\n f.placeholder ||\n (f.type === 'date'\n ? 'Seleccionar fecha'\n : 'Seleccionar fecha y hora')\n \"\n [formControlName]=\"f.key\"\n [minDate]=\"f.minDate || null\"\n [maxDate]=\"f.maxDate || null\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'textarea') {\n <textarea\n class=\"input textarea\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n rows=\"6\"\n [readonly]=\"f.readonly || false\"\n ></textarea>\n } @if (f.type === 'select') {\n <lib-select-custom-search\n [options]=\"f.options ?? []\"\n [placeholder]=\"f.placeholder || 'Seleccionar...'\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'radio') {\n <div class=\"radio-group\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"radio\">\n <input\n type=\"radio\"\n [value]=\"o.value\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n <span class=\"radio-mark\"></span>\n <span class=\"radio-label\">{{ o.label }}</span>\n </label>\n }\n </div>\n } @if (f.type === 'checkbox') {\n <div class=\"checkbox-group\"\n [class.checkbox-group--cards]=\"f.variant === 'cards'\"\n [class.checkbox-group--lowercase]=\"f.uppercase === false\"\n [class.checkbox-group--readonly]=\"f.readonly && !f.disabled\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"checkbox\">\n <input\n type=\"checkbox\"\n [checked]=\"getCheckboxArray(f.key)?.at($index)?.value\"\n (change)=\"onCheckboxInteract($event, f, $index)\"\n [disabled]=\"f.disabled || f.readonly || false\"\n />\n <span class=\"checkbox-mark\"></span>\n <span class=\"checkbox-text\">\n <span class=\"checkbox-label\">{{ o.label }}</span>\n @if (o.subtitle) {\n <span class=\"checkbox-subtitle\">{{ o.subtitle }}</span>\n }\n </span>\n </label>\n }\n </div>\n } @if (f.type === 'disabled') {\n <input\n class=\"input input--disabled\"\n [placeholder]=\"f.placeholder || 'Autom\u00E1tico'\"\n disabled\n />\n } @if (ctrl(f.key)?.touched && ctrl(f.key)?.invalid) {\n <div class=\"error\">\n @if (ctrl(f.key)?.errors?.['required']) {\n <span>Campo requerido</span>\n } @if (ctrl(f.key)?.errors?.['email']) {\n <span>Correo inv\u00E1lido</span>\n } @if (ctrl(f.key)?.errors?.['pattern']) {\n <span>\n @switch (f.patternType) { @case ('numbers') { Solo se permiten\n n\u00FAmeros } @case ('phone') { Formato de tel\u00E9fono inv\u00E1lido } @case\n ('text') { Solo se permiten letras y espacios } @case ('username') {\n Solo se permiten letras, n\u00FAmeros, puntos y guiones bajos (no al\n inicio ni al final) } @case ('alphanumeric') { Solo se permiten\n letras y n\u00FAmeros } @default { Formato inv\u00E1lido } }\n </span>\n } @if (ctrl(f.key)?.errors?.['notMatching']) {\n <span>Las contrase\u00F1as no coinciden</span>\n }\n </div>\n }\n\n @if(f.label && f.type !== 'toggle') {\n <label\n [class.label-radio]=\"f.type === 'radio'\"\n [class.label-checkbox]=\"f.type === 'checkbox'\"\n [class.label-disabled]=\"f.readonly || f.disabled\"\n class=\"label\"\n >\n {{ f.label }} </label>\n }\n </div>\n }\n }\n\n }\n </div>\n </section>\n }\n</form>\n}\n", styles: [".form{width:100%}.form{display:grid}.section{padding:20px 0 0}.section-title{font-size:1.6rem;font-weight:700;margin-bottom:30px}.section-desc{margin:0 0 .75rem;color:#666}.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}.form.form--compact .grid{gap:12px}.col{grid-column:span var(--col-span, 6);min-width:0;width:100%;position:relative;padding-bottom:20px}.col--group-wrapper{grid-column:span 12;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;padding-bottom:0}.form.form--compact .col--group-wrapper{gap:12px}.group-item--full{grid-column:span 4}.form.form--compact .col{padding-bottom:0}.label{position:absolute;top:-8px;left:12px;font-size:1.2rem;color:#454733;background-color:var(--sl-form-surface, #E3E3D1);padding:0 4px;font-weight:500;text-transform:capitalize}.input{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent}input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=date]::-webkit-calendar-picker-indicator{position:absolute;inset:0;width:auto;height:auto;color:transparent;background:transparent}.input:focus{outline:none;border-color:#a9a97f}.input--disabled{color:#888}.input:-webkit-autofill,.input:-webkit-autofill:hover,.input:-webkit-autofill:focus,.input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733;caret-color:#454733;transition:background-color 9999s ease-in-out 0s}.input:-moz-autofill,.textarea:-moz-autofill{box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset}.input[readonly]:-webkit-autofill,.input:disabled:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#a9a97f}.label-radio{font-size:1.4rem;position:static;padding-left:0}.radio-group,.checkbox-group{display:flex;gap:2rem;padding:.5rem 0}.form.form--compact .checkbox-group{padding:0 8px 0 0}.checkbox,.radio{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:1.4rem;color:#1c1c12;-webkit-user-select:none;user-select:none}.checkbox:has(.checkbox-subtitle){align-items:flex-start}.checkbox:has(.checkbox-subtitle) .checkbox-mark,.checkbox-group--cards .checkbox-mark{margin-top:2px}.checkbox-text{display:flex;flex-direction:column;gap:2px}.checkbox-subtitle{font-size:1.2rem;color:#787861;font-weight:400;line-height:1.3}.checkbox-label,.checkbox-subtitle{text-transform:uppercase}.checkbox-group--lowercase .checkbox-label,.checkbox-group--lowercase .checkbox-subtitle{text-transform:none}.checkbox:hover{color:#454733}.checkbox input,.radio input{position:absolute;opacity:0;pointer-events:none}.checkbox-mark{width:18px;height:18px;border:2px solid #787861;border-radius:4px;background-color:transparent;position:relative;display:inline-flex;align-items:center;justify-content:center;transition:all .2s ease}.checkbox input:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox input:checked+.checkbox-mark:after{content:\"\";position:absolute;width:6px;height:10px;border:solid #fff;border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg);top:50%;left:50%}.radio-mark{width:18px;height:18px;border:2px solid #787861;border-radius:50%;background-color:transparent;position:relative;transition:all .2s ease}.radio input:checked+.radio-mark{border-color:#596300}.radio input:checked+.radio-mark:after{content:\"\";width:8px;height:8px;background-color:#454733;border-radius:50%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.radio:hover .radio-mark{border-color:#a9a97f}.checkbox input:disabled+.checkbox-mark,.radio input:disabled+.radio-mark{border-color:#a9a97f;background-color:var(--sl-form-surface, #E3E3D1);cursor:not-allowed}.checkbox:has(input:disabled),.radio:has(input:disabled){cursor:not-allowed}.checkbox:has(input:disabled) .checkbox-text,.radio:has(input:disabled) .radio-label{color:#1c1c1266}.checkbox-group--cards .checkbox:has(input:disabled){opacity:.5;pointer-events:none}.checkbox-group--readonly .checkbox{opacity:.6;pointer-events:none;cursor:not-allowed}.checkbox-group--readonly .checkbox input:disabled+.checkbox-mark{border-color:#787861;background-color:transparent}.checkbox-group--readonly .checkbox input:disabled:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox-group--cards.checkbox-group--readonly .checkbox{opacity:.8;pointer-events:none}.checkbox-group--cards .checkbox{display:flex;align-items:flex-start;width:100%;gap:12px;padding:14px 16px;background-color:#f0f0db;font-size:14px;font-weight:300;text-transform:uppercase;border-radius:10px;border:1px solid transparent;transition:all .2s ease;cursor:pointer;position:relative}.checkbox-group--cards .checkbox:hover{background-color:#dee58f}.error{position:absolute;bottom:0;left:0;font-size:1.2rem;color:#b00020;width:100%;height:15px;display:none}.col:has(.error) .error{display:block}.textarea{resize:vertical;min-height:100px;max-height:300px;line-height:1.5}.textarea:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733}select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background-image:url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\");background-repeat:no-repeat;background-size:15px;background-position:right 15px center;cursor:pointer}select:invalid{color:#787861}select.placeholder-selected{color:#787861}select:not(.placeholder-selected){color:#000}select option{color:#454733;cursor:pointer}input[type=date]{color:#787861!important}input[type=date]:valid{color:#454733!important}.label-disabled{color:#1c1c1266}.input:disabled,.input--disabled,.input.disabled,.input[readonly]{border:1px solid #a9a97f;color:#a9a97f;cursor:not-allowed;pointer-events:none}@media (max-width: 768px){.grid{grid-template-columns:1fr}.col{grid-column:span 1!important}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SelectCustomSearch, selector: "lib-select-custom-search", inputs: ["options", "placeholder", "readonly"], outputs: ["selectionChange"] }, { kind: "component", type: DateTimePicker, selector: "lib-date-time-picker", inputs: ["mode", "placeholder", "minDate", "maxDate", "readonly"], outputs: ["dateChange"] }, { kind: "component", type: ToggleCustom, selector: "lib-toggle-custom", inputs: ["label", "key", "disabled", "checked"], outputs: ["checkedChange", "toggleChange"] }] });
|
|
2794
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DynamicFormFields, isStandalone: true, selector: "lib-dynamic-form-fields", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (form()) {\n<form class=\"form\" [formGroup]=\"form()\" [class.form--compact]=\"compact()\" (keydown)=\"onEnter($event)\">\n @for (sec of sections(); track $index) {\n <section class=\"section\">\n @if (sec.title) {\n <h3 class=\"section-title\">{{ sec.title }}</h3>\n } @if (sec.description) {\n <p class=\"section-desc\">{{ sec.description }}</p>\n }\n\n <div class=\"grid\">\n @for (run of getFieldRuns(sec.fields); track $index) {\n\n @if (run.isGroup) {\n <!-- toggle: wrapper que siempre ocupa la fila completa -->\n <div class=\"col col--group-wrapper\">\n @for (f of run.fields; track f.key) {\n <div class=\"group-item\">\n <lib-toggle-custom [key]=\"f.key\" [label]=\"f.label ?? ''\" [formControlName]=\"f.key\" />\n </div>\n }\n </div>\n\n } @else {\n <!-- Campos normales (incluye checkbox y radio con su col configurable) -->\n @for (f of run.fields; track f.key) {\n <div class=\"col\" [style.--col-span]=\"f.col || 6\">\n @if (['text','number','email', 'password', 'time'].includes(f.type))\n {\n <input\n class=\"input\"\n [type]=\"f.type\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n (input)=\"onUppercaseInput($event, f.type, f.key)\"\n />\n } @if (['date', 'datetime-local'].includes(f.type)) {\n <lib-date-time-picker\n [mode]=\"getDatePickerMode(f.type)\"\n [placeholder]=\"\n f.placeholder ||\n (f.type === 'date'\n ? 'Seleccionar fecha'\n : 'Seleccionar fecha y hora')\n \"\n [formControlName]=\"f.key\"\n [minDate]=\"f.minDate || null\"\n [maxDate]=\"f.maxDate || null\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'textarea') {\n <textarea\n class=\"input textarea\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n rows=\"6\"\n [readonly]=\"f.readonly || false\"\n ></textarea>\n } @if (f.type === 'select') {\n <lib-select-custom-search\n [options]=\"f.options ?? []\"\n [placeholder]=\"f.placeholder || 'Seleccionar...'\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'radio') {\n <div class=\"radio-group\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"radio\">\n <input\n type=\"radio\"\n [value]=\"o.value\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n <span class=\"radio-mark\"></span>\n <span class=\"radio-label\">{{ o.label }}</span>\n </label>\n }\n </div>\n } @if (f.type === 'checkbox') {\n <div class=\"checkbox-group\"\n [class.checkbox-group--cards]=\"f.variant === 'cards'\"\n [class.checkbox-group--lowercase]=\"f.uppercase === false\"\n [class.checkbox-group--readonly]=\"f.readonly && !f.disabled\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"checkbox\">\n <input\n type=\"checkbox\"\n [checked]=\"getCheckboxArray(f.key)?.at($index)?.value\"\n (change)=\"onCheckboxInteract($event, f, $index)\"\n [disabled]=\"f.disabled || f.readonly || false\"\n />\n <span class=\"checkbox-mark\"></span>\n <span class=\"checkbox-text\">\n <span class=\"checkbox-label\">{{ o.label }}</span>\n @if (o.subtitle) {\n <span class=\"checkbox-subtitle\">{{ o.subtitle }}</span>\n }\n </span>\n </label>\n }\n </div>\n } @if (f.type === 'disabled') {\n <input\n class=\"input input--disabled\"\n [placeholder]=\"f.placeholder || 'Autom\u00E1tico'\"\n disabled\n />\n } @if (ctrl(f.key)?.touched && ctrl(f.key)?.invalid) {\n <div class=\"error\">\n @if (ctrl(f.key)?.errors?.['required']) {\n <span>Campo requerido</span>\n } @if (ctrl(f.key)?.errors?.['email']) {\n <span>Correo inv\u00E1lido</span>\n } @if (ctrl(f.key)?.errors?.['pattern']) {\n <span>\n @switch (f.patternType) { @case ('numbers') { Solo se permiten\n n\u00FAmeros } @case ('phone') { Formato de tel\u00E9fono inv\u00E1lido } @case\n ('text') { Solo se permiten letras y espacios } @case ('username') {\n Solo se permiten letras, n\u00FAmeros, puntos y guiones bajos (no al\n inicio ni al final) } @case ('alphanumeric') { Solo se permiten\n letras y n\u00FAmeros } @default { Formato inv\u00E1lido } }\n </span>\n } @if (ctrl(f.key)?.errors?.['notMatching']) {\n <span>Las contrase\u00F1as no coinciden</span>\n }\n </div>\n }\n\n @if(f.label && f.type !== 'toggle') {\n <label\n [class.label-radio]=\"f.type === 'radio'\"\n [class.label-checkbox]=\"f.type === 'checkbox'\"\n [class.label-disabled]=\"f.readonly || f.disabled\"\n class=\"label\"\n >\n {{ f.label }} </label>\n }\n </div>\n }\n }\n\n }\n </div>\n </section>\n }\n</form>\n}\n", styles: [".form{width:100%}.form{display:grid}.section{padding:20px 0 0}.section-title{font-size:1.6rem;font-weight:700;margin-bottom:30px}.section-desc{margin:0 0 .75rem;color:#666}.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}.form.form--compact .grid{gap:12px}.col{grid-column:span var(--col-span, 6);min-width:0;width:100%;position:relative;padding-bottom:20px}.col--group-wrapper{grid-column:span 12;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;padding-bottom:0}.form.form--compact .col--group-wrapper{gap:12px}.group-item--full{grid-column:span 4}.form.form--compact .col{padding-bottom:0}.label{position:absolute;top:-8px;left:12px;font-size:1.2rem;color:#454733;background-color:var(--sl-form-surface, #E3E3D1);padding:0 4px;font-weight:500;text-transform:capitalize}.input{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent}input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=date]::-webkit-calendar-picker-indicator{position:absolute;inset:0;width:auto;height:auto;color:transparent;background:transparent}.input:focus{outline:none;border-color:#a9a97f}.input--disabled{color:#888}.input:-webkit-autofill,.input:-webkit-autofill:hover,.input:-webkit-autofill:focus,.input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733;caret-color:#454733;transition:background-color 9999s ease-in-out 0s}.input:-moz-autofill,.textarea:-moz-autofill{box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset}.input[readonly]:-webkit-autofill,.input:disabled:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#a9a97f}.label-radio{font-size:1.4rem;position:static;padding-left:0}.radio-group,.checkbox-group{display:flex;gap:2rem;padding:.5rem 0}.form.form--compact .checkbox-group{padding:0 8px 0 0}.checkbox,.radio{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:1.4rem;color:#1c1c12;-webkit-user-select:none;user-select:none}.checkbox:has(.checkbox-subtitle){align-items:flex-start}.checkbox:has(.checkbox-subtitle) .checkbox-mark,.checkbox-group--cards .checkbox-mark{margin-top:2px}.checkbox-text{display:flex;flex-direction:column;gap:2px}.checkbox-subtitle{font-size:1.2rem;color:#787861;font-weight:400;line-height:1.3}.checkbox-label,.checkbox-subtitle{text-transform:uppercase}.checkbox-group--lowercase .checkbox-label,.checkbox-group--lowercase .checkbox-subtitle{text-transform:none}.checkbox:hover{color:#454733}.checkbox input,.radio input{position:absolute;opacity:0;pointer-events:none}.checkbox-mark{width:18px;height:18px;border:2px solid #787861;border-radius:4px;background-color:transparent;position:relative;display:inline-flex;align-items:center;justify-content:center;transition:all .2s ease}.checkbox input:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox input:checked+.checkbox-mark:after{content:\"\";position:absolute;width:6px;height:10px;border:solid #fff;border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg);top:50%;left:50%}.radio-mark{width:18px;height:18px;border:2px solid #787861;border-radius:50%;background-color:transparent;position:relative;transition:all .2s ease}.radio input:checked+.radio-mark{border-color:#596300}.radio input:checked+.radio-mark:after{content:\"\";width:8px;height:8px;background-color:#454733;border-radius:50%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.radio:hover .radio-mark{border-color:#a9a97f}.checkbox input:disabled+.checkbox-mark,.radio input:disabled+.radio-mark{border-color:#a9a97f;background-color:var(--sl-form-surface, #E3E3D1);cursor:not-allowed}.checkbox:has(input:disabled),.radio:has(input:disabled){cursor:not-allowed}.checkbox:has(input:disabled) .checkbox-text,.radio:has(input:disabled) .radio-label{color:#1c1c1266}.checkbox-group--cards .checkbox:has(input:disabled){opacity:.5;pointer-events:none}.checkbox-group--readonly .checkbox{opacity:.6;pointer-events:none;cursor:not-allowed}.checkbox-group--readonly .checkbox input:disabled+.checkbox-mark{border-color:#787861;background-color:transparent}.checkbox-group--readonly .checkbox input:disabled:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox-group--cards.checkbox-group--readonly .checkbox{opacity:.8;pointer-events:none}.checkbox-group--cards .checkbox{display:flex;align-items:flex-start;width:100%;gap:12px;padding:14px 16px;background-color:#f0f0db;font-size:14px;font-weight:300;text-transform:uppercase;border-radius:10px;border:1px solid transparent;transition:all .2s ease;cursor:pointer;position:relative}.checkbox-group--cards .checkbox:hover{background-color:#dee58f}.error{position:absolute;bottom:0;left:0;font-size:1.2rem;color:#b00020;width:100%;height:15px;display:none}.col:has(.error) .error{display:block}.textarea{resize:vertical;min-height:100px;max-height:300px;line-height:1.5;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#a9a97f transparent}.textarea::-webkit-scrollbar{width:8px}.textarea::-webkit-scrollbar-track{background:transparent;margin:4px 0}.textarea::-webkit-scrollbar-thumb{background:#a9a97f;border-radius:4px}.textarea::-webkit-scrollbar-thumb:hover{background:#787861}.textarea:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733}select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background-image:url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\");background-repeat:no-repeat;background-size:15px;background-position:right 15px center;cursor:pointer}select:invalid{color:#787861}select.placeholder-selected{color:#787861}select:not(.placeholder-selected){color:#000}select option{color:#454733;cursor:pointer}input[type=date]{color:#787861!important}input[type=date]:valid{color:#454733!important}.label-disabled{color:#1c1c1266}.input:disabled,.input--disabled,.input.disabled,.input[readonly]{border:1px solid #a9a97f;color:#a9a97f;cursor:not-allowed;pointer-events:none}@media (max-width: 768px){.grid{grid-template-columns:1fr}.col{grid-column:span 1!important}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SelectCustomSearch, selector: "lib-select-custom-search", inputs: ["options", "placeholder", "readonly"], outputs: ["selectionChange"] }, { kind: "component", type: DateTimePicker, selector: "lib-date-time-picker", inputs: ["mode", "placeholder", "minDate", "maxDate", "readonly"], outputs: ["dateChange"] }, { kind: "component", type: ToggleCustom, selector: "lib-toggle-custom", inputs: ["label", "key", "disabled", "checked"], outputs: ["checkedChange", "toggleChange"] }] });
|
|
2721
2795
|
}
|
|
2722
2796
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DynamicFormFields, decorators: [{
|
|
2723
2797
|
type: Component,
|
|
2724
|
-
args: [{ selector: 'lib-dynamic-form-fields', standalone: true, imports: [ReactiveFormsModule, SelectCustomSearch, DateTimePicker, ToggleCustom], template: "@if (form()) {\n<form class=\"form\" [formGroup]=\"form()\" [class.form--compact]=\"compact()\" (keydown)=\"onEnter($event)\">\n @for (sec of sections(); track $index) {\n <section class=\"section\">\n @if (sec.title) {\n <h3 class=\"section-title\">{{ sec.title }}</h3>\n } @if (sec.description) {\n <p class=\"section-desc\">{{ sec.description }}</p>\n }\n\n <div class=\"grid\">\n @for (run of getFieldRuns(sec.fields); track $index) {\n\n @if (run.isGroup) {\n <!-- toggle: wrapper que siempre ocupa la fila completa -->\n <div class=\"col col--group-wrapper\">\n @for (f of run.fields; track f.key) {\n <div class=\"group-item\">\n <lib-toggle-custom [key]=\"f.key\" [label]=\"f.label ?? ''\" [formControlName]=\"f.key\" />\n </div>\n }\n </div>\n\n } @else {\n <!-- Campos normales (incluye checkbox y radio con su col configurable) -->\n @for (f of run.fields; track f.key) {\n <div class=\"col\" [style.--col-span]=\"f.col || 6\">\n @if (['text','number','email', 'password', 'time'].includes(f.type))\n {\n <input\n class=\"input\"\n [type]=\"f.type\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n (input)=\"onUppercaseInput($event, f.type, f.key)\"\n />\n } @if (['date', 'datetime-local'].includes(f.type)) {\n <lib-date-time-picker\n [mode]=\"getDatePickerMode(f.type)\"\n [placeholder]=\"\n f.placeholder ||\n (f.type === 'date'\n ? 'Seleccionar fecha'\n : 'Seleccionar fecha y hora')\n \"\n [formControlName]=\"f.key\"\n [minDate]=\"f.minDate || null\"\n [maxDate]=\"f.maxDate || null\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'textarea') {\n <textarea\n class=\"input textarea\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n rows=\"6\"\n [readonly]=\"f.readonly || false\"\n ></textarea>\n } @if (f.type === 'select') {\n <lib-select-custom-search\n [options]=\"f.options ?? []\"\n [placeholder]=\"f.placeholder || 'Seleccionar...'\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'radio') {\n <div class=\"radio-group\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"radio\">\n <input\n type=\"radio\"\n [value]=\"o.value\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n <span class=\"radio-mark\"></span>\n <span class=\"radio-label\">{{ o.label }}</span>\n </label>\n }\n </div>\n } @if (f.type === 'checkbox') {\n <div class=\"checkbox-group\"\n [class.checkbox-group--cards]=\"f.variant === 'cards'\"\n [class.checkbox-group--lowercase]=\"f.uppercase === false\"\n [class.checkbox-group--readonly]=\"f.readonly && !f.disabled\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"checkbox\">\n <input\n type=\"checkbox\"\n [checked]=\"getCheckboxArray(f.key)?.at($index)?.value\"\n (change)=\"onCheckboxInteract($event, f, $index)\"\n [disabled]=\"f.disabled || f.readonly || false\"\n />\n <span class=\"checkbox-mark\"></span>\n <span class=\"checkbox-text\">\n <span class=\"checkbox-label\">{{ o.label }}</span>\n @if (o.subtitle) {\n <span class=\"checkbox-subtitle\">{{ o.subtitle }}</span>\n }\n </span>\n </label>\n }\n </div>\n } @if (f.type === 'disabled') {\n <input\n class=\"input input--disabled\"\n [placeholder]=\"f.placeholder || 'Autom\u00E1tico'\"\n disabled\n />\n } @if (ctrl(f.key)?.touched && ctrl(f.key)?.invalid) {\n <div class=\"error\">\n @if (ctrl(f.key)?.errors?.['required']) {\n <span>Campo requerido</span>\n } @if (ctrl(f.key)?.errors?.['email']) {\n <span>Correo inv\u00E1lido</span>\n } @if (ctrl(f.key)?.errors?.['pattern']) {\n <span>\n @switch (f.patternType) { @case ('numbers') { Solo se permiten\n n\u00FAmeros } @case ('phone') { Formato de tel\u00E9fono inv\u00E1lido } @case\n ('text') { Solo se permiten letras y espacios } @case ('username') {\n Solo se permiten letras, n\u00FAmeros, puntos y guiones bajos (no al\n inicio ni al final) } @case ('alphanumeric') { Solo se permiten\n letras y n\u00FAmeros } @default { Formato inv\u00E1lido } }\n </span>\n } @if (ctrl(f.key)?.errors?.['notMatching']) {\n <span>Las contrase\u00F1as no coinciden</span>\n }\n </div>\n }\n\n @if(f.label && f.type !== 'toggle') {\n <label\n [class.label-radio]=\"f.type === 'radio'\"\n [class.label-checkbox]=\"f.type === 'checkbox'\"\n [class.label-disabled]=\"f.readonly || f.disabled\"\n class=\"label\"\n >\n {{ f.label }} </label>\n }\n </div>\n }\n }\n\n }\n </div>\n </section>\n }\n</form>\n}\n", styles: [".form{width:100%}.form{display:grid}.section{padding:20px 0 0}.section-title{font-size:1.6rem;font-weight:700;margin-bottom:30px}.section-desc{margin:0 0 .75rem;color:#666}.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}.form.form--compact .grid{gap:12px}.col{grid-column:span var(--col-span, 6);min-width:0;width:100%;position:relative;padding-bottom:20px}.col--group-wrapper{grid-column:span 12;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;padding-bottom:0}.form.form--compact .col--group-wrapper{gap:12px}.group-item--full{grid-column:span 4}.form.form--compact .col{padding-bottom:0}.label{position:absolute;top:-8px;left:12px;font-size:1.2rem;color:#454733;background-color:var(--sl-form-surface, #E3E3D1);padding:0 4px;font-weight:500;text-transform:capitalize}.input{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent}input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=date]::-webkit-calendar-picker-indicator{position:absolute;inset:0;width:auto;height:auto;color:transparent;background:transparent}.input:focus{outline:none;border-color:#a9a97f}.input--disabled{color:#888}.input:-webkit-autofill,.input:-webkit-autofill:hover,.input:-webkit-autofill:focus,.input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733;caret-color:#454733;transition:background-color 9999s ease-in-out 0s}.input:-moz-autofill,.textarea:-moz-autofill{box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset}.input[readonly]:-webkit-autofill,.input:disabled:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#a9a97f}.label-radio{font-size:1.4rem;position:static;padding-left:0}.radio-group,.checkbox-group{display:flex;gap:2rem;padding:.5rem 0}.form.form--compact .checkbox-group{padding:0 8px 0 0}.checkbox,.radio{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:1.4rem;color:#1c1c12;-webkit-user-select:none;user-select:none}.checkbox:has(.checkbox-subtitle){align-items:flex-start}.checkbox:has(.checkbox-subtitle) .checkbox-mark,.checkbox-group--cards .checkbox-mark{margin-top:2px}.checkbox-text{display:flex;flex-direction:column;gap:2px}.checkbox-subtitle{font-size:1.2rem;color:#787861;font-weight:400;line-height:1.3}.checkbox-label,.checkbox-subtitle{text-transform:uppercase}.checkbox-group--lowercase .checkbox-label,.checkbox-group--lowercase .checkbox-subtitle{text-transform:none}.checkbox:hover{color:#454733}.checkbox input,.radio input{position:absolute;opacity:0;pointer-events:none}.checkbox-mark{width:18px;height:18px;border:2px solid #787861;border-radius:4px;background-color:transparent;position:relative;display:inline-flex;align-items:center;justify-content:center;transition:all .2s ease}.checkbox input:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox input:checked+.checkbox-mark:after{content:\"\";position:absolute;width:6px;height:10px;border:solid #fff;border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg);top:50%;left:50%}.radio-mark{width:18px;height:18px;border:2px solid #787861;border-radius:50%;background-color:transparent;position:relative;transition:all .2s ease}.radio input:checked+.radio-mark{border-color:#596300}.radio input:checked+.radio-mark:after{content:\"\";width:8px;height:8px;background-color:#454733;border-radius:50%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.radio:hover .radio-mark{border-color:#a9a97f}.checkbox input:disabled+.checkbox-mark,.radio input:disabled+.radio-mark{border-color:#a9a97f;background-color:var(--sl-form-surface, #E3E3D1);cursor:not-allowed}.checkbox:has(input:disabled),.radio:has(input:disabled){cursor:not-allowed}.checkbox:has(input:disabled) .checkbox-text,.radio:has(input:disabled) .radio-label{color:#1c1c1266}.checkbox-group--cards .checkbox:has(input:disabled){opacity:.5;pointer-events:none}.checkbox-group--readonly .checkbox{opacity:.6;pointer-events:none;cursor:not-allowed}.checkbox-group--readonly .checkbox input:disabled+.checkbox-mark{border-color:#787861;background-color:transparent}.checkbox-group--readonly .checkbox input:disabled:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox-group--cards.checkbox-group--readonly .checkbox{opacity:.8;pointer-events:none}.checkbox-group--cards .checkbox{display:flex;align-items:flex-start;width:100%;gap:12px;padding:14px 16px;background-color:#f0f0db;font-size:14px;font-weight:300;text-transform:uppercase;border-radius:10px;border:1px solid transparent;transition:all .2s ease;cursor:pointer;position:relative}.checkbox-group--cards .checkbox:hover{background-color:#dee58f}.error{position:absolute;bottom:0;left:0;font-size:1.2rem;color:#b00020;width:100%;height:15px;display:none}.col:has(.error) .error{display:block}.textarea{resize:vertical;min-height:100px;max-height:300px;line-height:1.5}.textarea:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733}select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background-image:url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\");background-repeat:no-repeat;background-size:15px;background-position:right 15px center;cursor:pointer}select:invalid{color:#787861}select.placeholder-selected{color:#787861}select:not(.placeholder-selected){color:#000}select option{color:#454733;cursor:pointer}input[type=date]{color:#787861!important}input[type=date]:valid{color:#454733!important}.label-disabled{color:#1c1c1266}.input:disabled,.input--disabled,.input.disabled,.input[readonly]{border:1px solid #a9a97f;color:#a9a97f;cursor:not-allowed;pointer-events:none}@media (max-width: 768px){.grid{grid-template-columns:1fr}.col{grid-column:span 1!important}}\n"] }]
|
|
2798
|
+
args: [{ selector: 'lib-dynamic-form-fields', standalone: true, imports: [ReactiveFormsModule, SelectCustomSearch, DateTimePicker, ToggleCustom], template: "@if (form()) {\n<form class=\"form\" [formGroup]=\"form()\" [class.form--compact]=\"compact()\" (keydown)=\"onEnter($event)\">\n @for (sec of sections(); track $index) {\n <section class=\"section\">\n @if (sec.title) {\n <h3 class=\"section-title\">{{ sec.title }}</h3>\n } @if (sec.description) {\n <p class=\"section-desc\">{{ sec.description }}</p>\n }\n\n <div class=\"grid\">\n @for (run of getFieldRuns(sec.fields); track $index) {\n\n @if (run.isGroup) {\n <!-- toggle: wrapper que siempre ocupa la fila completa -->\n <div class=\"col col--group-wrapper\">\n @for (f of run.fields; track f.key) {\n <div class=\"group-item\">\n <lib-toggle-custom [key]=\"f.key\" [label]=\"f.label ?? ''\" [formControlName]=\"f.key\" />\n </div>\n }\n </div>\n\n } @else {\n <!-- Campos normales (incluye checkbox y radio con su col configurable) -->\n @for (f of run.fields; track f.key) {\n <div class=\"col\" [style.--col-span]=\"f.col || 6\">\n @if (['text','number','email', 'password', 'time'].includes(f.type))\n {\n <input\n class=\"input\"\n [type]=\"f.type\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n (input)=\"onUppercaseInput($event, f.type, f.key)\"\n />\n } @if (['date', 'datetime-local'].includes(f.type)) {\n <lib-date-time-picker\n [mode]=\"getDatePickerMode(f.type)\"\n [placeholder]=\"\n f.placeholder ||\n (f.type === 'date'\n ? 'Seleccionar fecha'\n : 'Seleccionar fecha y hora')\n \"\n [formControlName]=\"f.key\"\n [minDate]=\"f.minDate || null\"\n [maxDate]=\"f.maxDate || null\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'textarea') {\n <textarea\n class=\"input textarea\"\n [placeholder]=\"f.placeholder\"\n [formControlName]=\"f.key\"\n rows=\"6\"\n [readonly]=\"f.readonly || false\"\n ></textarea>\n } @if (f.type === 'select') {\n <lib-select-custom-search\n [options]=\"f.options ?? []\"\n [placeholder]=\"f.placeholder || 'Seleccionar...'\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n } @if (f.type === 'radio') {\n <div class=\"radio-group\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"radio\">\n <input\n type=\"radio\"\n [value]=\"o.value\"\n [formControlName]=\"f.key\"\n [readonly]=\"f.readonly || false\"\n />\n <span class=\"radio-mark\"></span>\n <span class=\"radio-label\">{{ o.label }}</span>\n </label>\n }\n </div>\n } @if (f.type === 'checkbox') {\n <div class=\"checkbox-group\"\n [class.checkbox-group--cards]=\"f.variant === 'cards'\"\n [class.checkbox-group--lowercase]=\"f.uppercase === false\"\n [class.checkbox-group--readonly]=\"f.readonly && !f.disabled\">\n @for (o of f.options ?? []; track o.value) {\n <label class=\"checkbox\">\n <input\n type=\"checkbox\"\n [checked]=\"getCheckboxArray(f.key)?.at($index)?.value\"\n (change)=\"onCheckboxInteract($event, f, $index)\"\n [disabled]=\"f.disabled || f.readonly || false\"\n />\n <span class=\"checkbox-mark\"></span>\n <span class=\"checkbox-text\">\n <span class=\"checkbox-label\">{{ o.label }}</span>\n @if (o.subtitle) {\n <span class=\"checkbox-subtitle\">{{ o.subtitle }}</span>\n }\n </span>\n </label>\n }\n </div>\n } @if (f.type === 'disabled') {\n <input\n class=\"input input--disabled\"\n [placeholder]=\"f.placeholder || 'Autom\u00E1tico'\"\n disabled\n />\n } @if (ctrl(f.key)?.touched && ctrl(f.key)?.invalid) {\n <div class=\"error\">\n @if (ctrl(f.key)?.errors?.['required']) {\n <span>Campo requerido</span>\n } @if (ctrl(f.key)?.errors?.['email']) {\n <span>Correo inv\u00E1lido</span>\n } @if (ctrl(f.key)?.errors?.['pattern']) {\n <span>\n @switch (f.patternType) { @case ('numbers') { Solo se permiten\n n\u00FAmeros } @case ('phone') { Formato de tel\u00E9fono inv\u00E1lido } @case\n ('text') { Solo se permiten letras y espacios } @case ('username') {\n Solo se permiten letras, n\u00FAmeros, puntos y guiones bajos (no al\n inicio ni al final) } @case ('alphanumeric') { Solo se permiten\n letras y n\u00FAmeros } @default { Formato inv\u00E1lido } }\n </span>\n } @if (ctrl(f.key)?.errors?.['notMatching']) {\n <span>Las contrase\u00F1as no coinciden</span>\n }\n </div>\n }\n\n @if(f.label && f.type !== 'toggle') {\n <label\n [class.label-radio]=\"f.type === 'radio'\"\n [class.label-checkbox]=\"f.type === 'checkbox'\"\n [class.label-disabled]=\"f.readonly || f.disabled\"\n class=\"label\"\n >\n {{ f.label }} </label>\n }\n </div>\n }\n }\n\n }\n </div>\n </section>\n }\n</form>\n}\n", styles: [".form{width:100%}.form{display:grid}.section{padding:20px 0 0}.section-title{font-size:1.6rem;font-weight:700;margin-bottom:30px}.section-desc{margin:0 0 .75rem;color:#666}.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}.form.form--compact .grid{gap:12px}.col{grid-column:span var(--col-span, 6);min-width:0;width:100%;position:relative;padding-bottom:20px}.col--group-wrapper{grid-column:span 12;display:grid;grid-template-columns:repeat(4,1fr);gap:16px;padding-bottom:0}.form.form--compact .col--group-wrapper{gap:12px}.group-item--full{grid-column:span 4}.form.form--compact .col{padding-bottom:0}.label{position:absolute;top:-8px;left:12px;font-size:1.2rem;color:#454733;background-color:var(--sl-form-surface, #E3E3D1);padding:0 4px;font-weight:500;text-transform:capitalize}.input{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent}input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}input[type=date]::-webkit-calendar-picker-indicator{position:absolute;inset:0;width:auto;height:auto;color:transparent;background:transparent}.input:focus{outline:none;border-color:#a9a97f}.input--disabled{color:#888}.input:-webkit-autofill,.input:-webkit-autofill:hover,.input:-webkit-autofill:focus,.input:-webkit-autofill:active{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733;caret-color:#454733;transition:background-color 9999s ease-in-out 0s}.input:-moz-autofill,.textarea:-moz-autofill{box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset}.input[readonly]:-webkit-autofill,.input:disabled:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#a9a97f}.label-radio{font-size:1.4rem;position:static;padding-left:0}.radio-group,.checkbox-group{display:flex;gap:2rem;padding:.5rem 0}.form.form--compact .checkbox-group{padding:0 8px 0 0}.checkbox,.radio{display:flex;align-items:center;gap:10px;cursor:pointer;font-size:1.4rem;color:#1c1c12;-webkit-user-select:none;user-select:none}.checkbox:has(.checkbox-subtitle){align-items:flex-start}.checkbox:has(.checkbox-subtitle) .checkbox-mark,.checkbox-group--cards .checkbox-mark{margin-top:2px}.checkbox-text{display:flex;flex-direction:column;gap:2px}.checkbox-subtitle{font-size:1.2rem;color:#787861;font-weight:400;line-height:1.3}.checkbox-label,.checkbox-subtitle{text-transform:uppercase}.checkbox-group--lowercase .checkbox-label,.checkbox-group--lowercase .checkbox-subtitle{text-transform:none}.checkbox:hover{color:#454733}.checkbox input,.radio input{position:absolute;opacity:0;pointer-events:none}.checkbox-mark{width:18px;height:18px;border:2px solid #787861;border-radius:4px;background-color:transparent;position:relative;display:inline-flex;align-items:center;justify-content:center;transition:all .2s ease}.checkbox input:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox input:checked+.checkbox-mark:after{content:\"\";position:absolute;width:6px;height:10px;border:solid #fff;border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg);top:50%;left:50%}.radio-mark{width:18px;height:18px;border:2px solid #787861;border-radius:50%;background-color:transparent;position:relative;transition:all .2s ease}.radio input:checked+.radio-mark{border-color:#596300}.radio input:checked+.radio-mark:after{content:\"\";width:8px;height:8px;background-color:#454733;border-radius:50%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.radio:hover .radio-mark{border-color:#a9a97f}.checkbox input:disabled+.checkbox-mark,.radio input:disabled+.radio-mark{border-color:#a9a97f;background-color:var(--sl-form-surface, #E3E3D1);cursor:not-allowed}.checkbox:has(input:disabled),.radio:has(input:disabled){cursor:not-allowed}.checkbox:has(input:disabled) .checkbox-text,.radio:has(input:disabled) .radio-label{color:#1c1c1266}.checkbox-group--cards .checkbox:has(input:disabled){opacity:.5;pointer-events:none}.checkbox-group--readonly .checkbox{opacity:.6;pointer-events:none;cursor:not-allowed}.checkbox-group--readonly .checkbox input:disabled+.checkbox-mark{border-color:#787861;background-color:transparent}.checkbox-group--readonly .checkbox input:disabled:checked+.checkbox-mark{background-color:#596300;border-color:#596300}.checkbox-group--cards.checkbox-group--readonly .checkbox{opacity:.8;pointer-events:none}.checkbox-group--cards .checkbox{display:flex;align-items:flex-start;width:100%;gap:12px;padding:14px 16px;background-color:#f0f0db;font-size:14px;font-weight:300;text-transform:uppercase;border-radius:10px;border:1px solid transparent;transition:all .2s ease;cursor:pointer;position:relative}.checkbox-group--cards .checkbox:hover{background-color:#dee58f}.error{position:absolute;bottom:0;left:0;font-size:1.2rem;color:#b00020;width:100%;height:15px;display:none}.col:has(.error) .error{display:block}.textarea{resize:vertical;min-height:100px;max-height:300px;line-height:1.5;overflow-y:auto;scrollbar-width:thin;scrollbar-color:#a9a97f transparent}.textarea::-webkit-scrollbar{width:8px}.textarea::-webkit-scrollbar-track{background:transparent;margin:4px 0}.textarea::-webkit-scrollbar-thumb{background:#a9a97f;border-radius:4px}.textarea::-webkit-scrollbar-thumb:hover{background:#787861}.textarea:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px var(--sl-form-surface, #E3E3D1) inset;-webkit-text-fill-color:#454733}select{appearance:none;-webkit-appearance:none;-moz-appearance:none;background-image:url(\"data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e\");background-repeat:no-repeat;background-size:15px;background-position:right 15px center;cursor:pointer}select:invalid{color:#787861}select.placeholder-selected{color:#787861}select:not(.placeholder-selected){color:#000}select option{color:#454733;cursor:pointer}input[type=date]{color:#787861!important}input[type=date]:valid{color:#454733!important}.label-disabled{color:#1c1c1266}.input:disabled,.input--disabled,.input.disabled,.input[readonly]{border:1px solid #a9a97f;color:#a9a97f;cursor:not-allowed;pointer-events:none}@media (max-width: 768px){.grid{grid-template-columns:1fr}.col{grid-column:span 1!important}}\n"] }]
|
|
2725
2799
|
}], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }] } });
|
|
2726
2800
|
|
|
2727
2801
|
class WizardForm {
|
|
@@ -2973,6 +3047,7 @@ class DialogAlertComponent {
|
|
|
2973
3047
|
key: "observations",
|
|
2974
3048
|
label: "Observaciones",
|
|
2975
3049
|
type: "textarea",
|
|
3050
|
+
readonly: true,
|
|
2976
3051
|
placeholder: "Escriba las observaciones...",
|
|
2977
3052
|
col: 12,
|
|
2978
3053
|
}
|
|
@@ -4638,7 +4713,8 @@ echarts.use([
|
|
|
4638
4713
|
GraphicComponent,
|
|
4639
4714
|
DataZoomComponent,
|
|
4640
4715
|
VisualMapComponent,
|
|
4641
|
-
LegendComponent
|
|
4716
|
+
LegendComponent,
|
|
4717
|
+
TitleComponent
|
|
4642
4718
|
]);
|
|
4643
4719
|
const UI_CHART_TOKENS = {
|
|
4644
4720
|
fontFamily: 'Inter, sans-serif',
|
|
@@ -4682,20 +4758,25 @@ const UI_CHART_TOKENS = {
|
|
|
4682
4758
|
class BaseChart {
|
|
4683
4759
|
options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
4684
4760
|
chartClick = output();
|
|
4761
|
+
chartInit = output();
|
|
4685
4762
|
onChartClick(event) {
|
|
4686
4763
|
this.chartClick.emit(event);
|
|
4687
4764
|
}
|
|
4765
|
+
onChartInit(chart) {
|
|
4766
|
+
this.chartInit.emit(chart);
|
|
4767
|
+
}
|
|
4688
4768
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BaseChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4689
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: BaseChart, isStandalone: true, selector: "lib-base-chart", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { chartClick: "chartClick" }, providers: [provideEchartsCore({ echarts })], ngImport: i0, template: "<div echarts class=\"ui-base-chart\" [options]=\"options()\" [autoResize]=\"true\" (chartClick)=\"onChartClick($event)\">\r\n</div>", styles: [".ui-base-chart{display:block;width:100%;height:100%;min-height:320px}\n"], dependencies: [{ kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4769
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: BaseChart, isStandalone: true, selector: "lib-base-chart", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { chartClick: "chartClick", chartInit: "chartInit" }, providers: [provideEchartsCore({ echarts })], ngImport: i0, template: "<div echarts class=\"ui-base-chart\" [options]=\"options()\" [autoResize]=\"true\" (chartClick)=\"onChartClick($event)\" (chartInit)=\"onChartInit($event)\">\r\n</div>", styles: [".ui-base-chart{display:block;width:100%;height:100%;min-height:320px}\n"], dependencies: [{ kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4690
4770
|
}
|
|
4691
4771
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BaseChart, decorators: [{
|
|
4692
4772
|
type: Component,
|
|
4693
|
-
args: [{ selector: 'lib-base-chart', imports: [NgxEchartsDirective], providers: [provideEchartsCore({ echarts })], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div echarts class=\"ui-base-chart\" [options]=\"options()\" [autoResize]=\"true\" (chartClick)=\"onChartClick($event)\">\r\n</div>", styles: [".ui-base-chart{display:block;width:100%;height:100%;min-height:320px}\n"] }]
|
|
4694
|
-
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }] } });
|
|
4773
|
+
args: [{ selector: 'lib-base-chart', imports: [NgxEchartsDirective], providers: [provideEchartsCore({ echarts })], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div echarts class=\"ui-base-chart\" [options]=\"options()\" [autoResize]=\"true\" (chartClick)=\"onChartClick($event)\" (chartInit)=\"onChartInit($event)\">\r\n</div>", styles: [".ui-base-chart{display:block;width:100%;height:100%;min-height:320px}\n"] }]
|
|
4774
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }], chartInit: [{ type: i0.Output, args: ["chartInit"] }] } });
|
|
4695
4775
|
|
|
4696
4776
|
function buildBarChartOptions(data, multiSeries, horizontal = false) {
|
|
4697
|
-
const
|
|
4698
|
-
const
|
|
4777
|
+
const threshold = horizontal ? 6 : 12;
|
|
4778
|
+
const showScroll = data.length > threshold;
|
|
4779
|
+
const zoomEnd = showScroll ? Math.floor((threshold / data.length) * 100) : 100;
|
|
4699
4780
|
const isMulti = !!multiSeries && multiSeries.length > 0;
|
|
4700
4781
|
const xAxisData = data.map(item => item.label);
|
|
4701
4782
|
const series = isMulti
|
|
@@ -4900,7 +4981,7 @@ class BarChart {
|
|
|
4900
4981
|
toggleExpand = () => this.isExpanded.update(v => !v);
|
|
4901
4982
|
options = computed(() => buildBarChartOptions(this.data(), this.multiSeries(), this.horizontal()), ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
4902
4983
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BarChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4903
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BarChart, isStandalone: true, selector: "lib-bar-chart", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, multiSeries: { classPropertyName: "multiSeries", publicName: "multiSeries", isSignal: true, isRequired: false, transformFunction: null }, horizontal: { classPropertyName: "horizontal", publicName: "horizontal", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"bar-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"bar-chart-header\">\r\n <h3 class=\"bar-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" class=\"chart-wrapper\" />\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}\r\n", styles: [":host{display:block;width:100%;min-width:0}.bar-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.bar-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.bar-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.bar-chart-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.bar-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick"] }] });
|
|
4984
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: BarChart, isStandalone: true, selector: "lib-bar-chart", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, multiSeries: { classPropertyName: "multiSeries", publicName: "multiSeries", isSignal: true, isRequired: false, transformFunction: null }, horizontal: { classPropertyName: "horizontal", publicName: "horizontal", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"bar-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"bar-chart-header\">\r\n <h3 class=\"bar-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" class=\"chart-wrapper\" />\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}\r\n", styles: [":host{display:block;width:100%;min-width:0}.bar-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.bar-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.bar-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.bar-chart-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.bar-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick", "chartInit"] }] });
|
|
4904
4985
|
}
|
|
4905
4986
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: BarChart, decorators: [{
|
|
4906
4987
|
type: Component,
|
|
@@ -5122,7 +5203,7 @@ class LineChart {
|
|
|
5122
5203
|
toggleExpand = () => this.isExpanded.update(v => !v);
|
|
5123
5204
|
options = computed(() => buildLineChartOptions(this.data(), this.multiSeries()), ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
5124
5205
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: LineChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5125
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: LineChart, isStandalone: true, selector: "lib-line-chart", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, multiSeries: { classPropertyName: "multiSeries", publicName: "multiSeries", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"line-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"line-chart-header\">\r\n <h3 class=\"line-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" class=\"chart-wrapper\" />\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}\r\n", styles: [":host{display:block;width:100%;min-width:0}.line-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.line-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.line-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.line-chart-header{display:flex;justify-content:space-between;align-items:center}.line-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick"] }] });
|
|
5206
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: LineChart, isStandalone: true, selector: "lib-line-chart", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, multiSeries: { classPropertyName: "multiSeries", publicName: "multiSeries", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"line-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"line-chart-header\">\r\n <h3 class=\"line-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" class=\"chart-wrapper\" />\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}\r\n", styles: [":host{display:block;width:100%;min-width:0}.line-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.line-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.line-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.line-chart-header{display:flex;justify-content:space-between;align-items:center}.line-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick", "chartInit"] }] });
|
|
5126
5207
|
}
|
|
5127
5208
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: LineChart, decorators: [{
|
|
5128
5209
|
type: Component,
|
|
@@ -5136,6 +5217,25 @@ function buildDonutChartOptions(data) {
|
|
|
5136
5217
|
const total = getTotal(data);
|
|
5137
5218
|
const showLegend = data.length > 10;
|
|
5138
5219
|
const splitLegend = data.length > 14;
|
|
5220
|
+
// Legend tooltip: shows name + value on hover
|
|
5221
|
+
const dataMap = new Map(data.map(d => [d.label, d.value]));
|
|
5222
|
+
const legendTooltip = {
|
|
5223
|
+
show: true,
|
|
5224
|
+
backgroundColor: '#ffffff',
|
|
5225
|
+
borderColor: '#d9dcc7',
|
|
5226
|
+
borderWidth: 1,
|
|
5227
|
+
textStyle: {
|
|
5228
|
+
color: UI_CHART_TOKENS.text,
|
|
5229
|
+
fontSize: 12,
|
|
5230
|
+
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5231
|
+
},
|
|
5232
|
+
extraCssText: 'border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,0.10);padding:8px 10px;',
|
|
5233
|
+
formatter: (params) => {
|
|
5234
|
+
const name = typeof params === 'string' ? params : params?.name;
|
|
5235
|
+
const val = dataMap.get(name);
|
|
5236
|
+
return val !== undefined ? `${name}: <b>${val}</b>` : name;
|
|
5237
|
+
},
|
|
5238
|
+
};
|
|
5139
5239
|
let legendConfig = {};
|
|
5140
5240
|
if (showLegend) {
|
|
5141
5241
|
if (splitLegend) {
|
|
@@ -5153,6 +5253,7 @@ function buildDonutChartOptions(data) {
|
|
|
5153
5253
|
itemWidth: 12,
|
|
5154
5254
|
itemHeight: 12,
|
|
5155
5255
|
itemGap: 10,
|
|
5256
|
+
tooltip: legendTooltip,
|
|
5156
5257
|
textStyle: {
|
|
5157
5258
|
fontSize: 11,
|
|
5158
5259
|
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
@@ -5171,6 +5272,7 @@ function buildDonutChartOptions(data) {
|
|
|
5171
5272
|
itemWidth: 12,
|
|
5172
5273
|
itemHeight: 12,
|
|
5173
5274
|
itemGap: 10,
|
|
5275
|
+
tooltip: legendTooltip,
|
|
5174
5276
|
textStyle: {
|
|
5175
5277
|
fontSize: 11,
|
|
5176
5278
|
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
@@ -5193,6 +5295,7 @@ function buildDonutChartOptions(data) {
|
|
|
5193
5295
|
itemWidth: 12,
|
|
5194
5296
|
itemHeight: 12,
|
|
5195
5297
|
itemGap: 10,
|
|
5298
|
+
tooltip: legendTooltip,
|
|
5196
5299
|
textStyle: {
|
|
5197
5300
|
fontSize: 11,
|
|
5198
5301
|
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
@@ -5357,15 +5460,29 @@ class DonutChart {
|
|
|
5357
5460
|
this.drilledGroup.set(clickedItem.label);
|
|
5358
5461
|
}
|
|
5359
5462
|
}
|
|
5463
|
+
chartInstance = null;
|
|
5464
|
+
onChartInit(chart) {
|
|
5465
|
+
this.chartInstance = chart;
|
|
5466
|
+
chart.on('legendselectchanged', (event) => {
|
|
5467
|
+
const selected = event.selected;
|
|
5468
|
+
const currentData = this.displayData();
|
|
5469
|
+
const visibleTotal = currentData
|
|
5470
|
+
.filter(item => selected[item.label] !== false)
|
|
5471
|
+
.reduce((sum, item) => sum + item.value, 0);
|
|
5472
|
+
chart.setOption({
|
|
5473
|
+
title: { text: `${visibleTotal}` }
|
|
5474
|
+
});
|
|
5475
|
+
});
|
|
5476
|
+
}
|
|
5360
5477
|
resetDrillDown() {
|
|
5361
5478
|
this.drilledGroup.set(null);
|
|
5362
5479
|
}
|
|
5363
5480
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DonutChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5364
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DonutChart, isStandalone: true, selector: "lib-donut-chart", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, drillDown: { classPropertyName: "drillDown", publicName: "drillDown", isSignal: true, isRequired: false, transformFunction: null }, groupByPrefix: { classPropertyName: "groupByPrefix", publicName: "groupByPrefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sectionClick: "sectionClick" }, ngImport: i0, template: "<div class=\"donut-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"donut-chart-header\">\r\n <h3 class=\"donut-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" (chartClick)=\"onChartClick($event)\" class=\"chart-wrapper\" />\r\n \r\n @if (drilledGroup()) {\r\n <button class=\"back-btn\" (click)=\"resetDrillDown()\" type=\"button\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\"/></svg>\r\n <span>Volver</span>\r\n </button>\r\n }\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.donut-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.donut-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.donut-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.donut-chart-header{display:flex;justify-content:space-between;align-items:center}.donut-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}.back-btn{position:absolute;top:24px;right:60px;display:flex;align-items:center;gap:6px;background-color:#d9df88;border:1px solid rgba(97,102,31,.2);padding:6px 12px;border-radius:12px;cursor:pointer;color:#61661f;font-family:Inter,sans-serif;font-size:13px;font-weight:500;box-shadow:0 4px 12px #0000000d;transition:all .2s ease;z-index:100}.back-btn:hover{background-color:#61661f;color:#fff;box-shadow:0 6px 14px #61661f33;transform:translateY(-1px)}.back-btn:active{transform:translateY(0)}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick"] }, { kind: "ngmodule", type: CommonModule }] });
|
|
5481
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DonutChart, isStandalone: true, selector: "lib-donut-chart", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, drillDown: { classPropertyName: "drillDown", publicName: "drillDown", isSignal: true, isRequired: false, transformFunction: null }, groupByPrefix: { classPropertyName: "groupByPrefix", publicName: "groupByPrefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sectionClick: "sectionClick" }, host: { properties: { "attr.title": "null" } }, ngImport: i0, template: "<div class=\"donut-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"donut-chart-header\">\r\n <h3 class=\"donut-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" (chartClick)=\"onChartClick($event)\" (chartInit)=\"onChartInit($event)\" class=\"chart-wrapper\" />\r\n \r\n @if (drilledGroup()) {\r\n <button class=\"back-btn\" (click)=\"resetDrillDown()\" type=\"button\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\"/></svg>\r\n <span>Volver</span>\r\n </button>\r\n }\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.donut-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.donut-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.donut-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.donut-chart-header{display:flex;justify-content:space-between;align-items:center}.donut-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}.back-btn{position:absolute;top:24px;right:60px;display:flex;align-items:center;gap:6px;background-color:#d9df88;border:1px solid rgba(97,102,31,.2);padding:6px 12px;border-radius:12px;cursor:pointer;color:#61661f;font-family:Inter,sans-serif;font-size:13px;font-weight:500;box-shadow:0 4px 12px #0000000d;transition:all .2s ease;z-index:100}.back-btn:hover{background-color:#61661f;color:#fff;box-shadow:0 6px 14px #61661f33;transform:translateY(-1px)}.back-btn:active{transform:translateY(0)}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick", "chartInit"] }, { kind: "ngmodule", type: CommonModule }] });
|
|
5365
5482
|
}
|
|
5366
5483
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DonutChart, decorators: [{
|
|
5367
5484
|
type: Component,
|
|
5368
|
-
args: [{ selector: 'lib-donut-chart', standalone: true, imports: [BaseChart, CommonModule], template: "<div class=\"donut-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"donut-chart-header\">\r\n <h3 class=\"donut-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" (chartClick)=\"onChartClick($event)\" class=\"chart-wrapper\" />\r\n \r\n @if (drilledGroup()) {\r\n <button class=\"back-btn\" (click)=\"resetDrillDown()\" type=\"button\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\"/></svg>\r\n <span>Volver</span>\r\n </button>\r\n }\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.donut-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.donut-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.donut-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.donut-chart-header{display:flex;justify-content:space-between;align-items:center}.donut-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}.back-btn{position:absolute;top:24px;right:60px;display:flex;align-items:center;gap:6px;background-color:#d9df88;border:1px solid rgba(97,102,31,.2);padding:6px 12px;border-radius:12px;cursor:pointer;color:#61661f;font-family:Inter,sans-serif;font-size:13px;font-weight:500;box-shadow:0 4px 12px #0000000d;transition:all .2s ease;z-index:100}.back-btn:hover{background-color:#61661f;color:#fff;box-shadow:0 6px 14px #61661f33;transform:translateY(-1px)}.back-btn:active{transform:translateY(0)}\n"] }]
|
|
5485
|
+
args: [{ selector: 'lib-donut-chart', standalone: true, imports: [BaseChart, CommonModule], host: { '[attr.title]': 'null' }, template: "<div class=\"donut-chart-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"donut-chart-header\">\r\n <h3 class=\"donut-chart-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" (chartClick)=\"onChartClick($event)\" (chartInit)=\"onChartInit($event)\" class=\"chart-wrapper\" />\r\n \r\n @if (drilledGroup()) {\r\n <button class=\"back-btn\" (click)=\"resetDrillDown()\" type=\"button\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\"/></svg>\r\n <span>Volver</span>\r\n </button>\r\n }\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.donut-chart-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.donut-chart-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.donut-chart-container.expanded .chart-wrapper{min-height:unset;height:100%}.donut-chart-header{display:flex;justify-content:space-between;align-items:center}.donut-chart-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}.back-btn{position:absolute;top:24px;right:60px;display:flex;align-items:center;gap:6px;background-color:#d9df88;border:1px solid rgba(97,102,31,.2);padding:6px 12px;border-radius:12px;cursor:pointer;color:#61661f;font-family:Inter,sans-serif;font-size:13px;font-weight:500;box-shadow:0 4px 12px #0000000d;transition:all .2s ease;z-index:100}.back-btn:hover{background-color:#61661f;color:#fff;box-shadow:0 6px 14px #61661f33;transform:translateY(-1px)}.back-btn:active{transform:translateY(0)}\n"] }]
|
|
5369
5486
|
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], drillDown: [{ type: i0.Input, args: [{ isSignal: true, alias: "drillDown", required: false }] }], groupByPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupByPrefix", required: false }] }], sectionClick: [{ type: i0.Output, args: ["sectionClick"] }] } });
|
|
5370
5487
|
|
|
5371
5488
|
function buildHeatmapOptions(config) {
|
|
@@ -5446,18 +5563,24 @@ function buildHeatmapOptions(config) {
|
|
|
5446
5563
|
},
|
|
5447
5564
|
},
|
|
5448
5565
|
visualMap: {
|
|
5566
|
+
type: 'piecewise',
|
|
5449
5567
|
min: minVal,
|
|
5450
5568
|
max: maxVal,
|
|
5451
|
-
|
|
5569
|
+
splitNumber: 5,
|
|
5452
5570
|
orient: 'horizontal',
|
|
5453
5571
|
left: 'center',
|
|
5454
5572
|
bottom: '0',
|
|
5455
|
-
itemWidth:
|
|
5456
|
-
itemHeight:
|
|
5573
|
+
itemWidth: 14,
|
|
5574
|
+
itemHeight: 14,
|
|
5575
|
+
itemGap: 8,
|
|
5576
|
+
itemSymbol: 'rect',
|
|
5577
|
+
showLabel: false, // Hide the "10-20" piece labels
|
|
5578
|
+
text: ['Más', 'Menos'], // Labels at the max and min ends
|
|
5457
5579
|
textStyle: {
|
|
5458
5580
|
color: UI_CHART_TOKENS.muted,
|
|
5459
|
-
fontSize:
|
|
5581
|
+
fontSize: 12,
|
|
5460
5582
|
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5583
|
+
fontWeight: 500
|
|
5461
5584
|
},
|
|
5462
5585
|
inRange: {
|
|
5463
5586
|
color: [
|
|
@@ -5504,7 +5627,7 @@ class Heatmap {
|
|
|
5504
5627
|
}
|
|
5505
5628
|
options = computed(() => buildHeatmapOptions(this.config()), ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
5506
5629
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Heatmap, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5507
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: Heatmap, isStandalone: true, selector: "lib-heatmap", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"heatmap-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"heatmap-header\">\r\n <h3 class=\"heatmap-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" class=\"chart-wrapper\" />\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.heatmap-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.heatmap-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.heatmap-container.expanded .chart-wrapper{min-height:unset;height:100%}.heatmap-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.heatmap-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick"] }] });
|
|
5630
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: Heatmap, isStandalone: true, selector: "lib-heatmap", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"heatmap-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"heatmap-header\">\r\n <h3 class=\"heatmap-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <lib-base-chart [options]=\"options()\" class=\"chart-wrapper\" />\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.heatmap-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.heatmap-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{flex:1;min-height:300px;display:block;width:100%;min-width:0}.heatmap-container.expanded .chart-wrapper{min-height:unset;height:100%}.heatmap-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.heatmap-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: BaseChart, selector: "lib-base-chart", inputs: ["options"], outputs: ["chartClick", "chartInit"] }] });
|
|
5508
5631
|
}
|
|
5509
5632
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Heatmap, decorators: [{
|
|
5510
5633
|
type: Component,
|
|
@@ -5513,21 +5636,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5513
5636
|
|
|
5514
5637
|
class GeoAPIMaps {
|
|
5515
5638
|
points = input([], ...(ngDevMode ? [{ debugName: "points" }] : []));
|
|
5639
|
+
cameras = input([], ...(ngDevMode ? [{ debugName: "cameras" }] : []));
|
|
5640
|
+
mapContainer;
|
|
5516
5641
|
map;
|
|
5517
5642
|
markers = [];
|
|
5643
|
+
cameraMarkers = [];
|
|
5518
5644
|
constructor() {
|
|
5645
|
+
// Reactivo a cambios de puntos
|
|
5519
5646
|
effect(() => {
|
|
5520
5647
|
const pts = this.points();
|
|
5521
5648
|
if (this.map && pts.length > 0) {
|
|
5522
5649
|
this.renderPoints(pts);
|
|
5523
5650
|
}
|
|
5524
5651
|
});
|
|
5652
|
+
// Reactivo a cambios de cámaras
|
|
5653
|
+
effect(() => {
|
|
5654
|
+
const cams = this.cameras();
|
|
5655
|
+
if (this.map && cams.length > 0) {
|
|
5656
|
+
this.renderCameras(cams);
|
|
5657
|
+
}
|
|
5658
|
+
});
|
|
5525
5659
|
}
|
|
5526
5660
|
ngAfterViewInit() {
|
|
5527
5661
|
this.initMap();
|
|
5528
5662
|
}
|
|
5529
5663
|
initMap() {
|
|
5530
|
-
const mapElement =
|
|
5664
|
+
const mapElement = this.mapContainer?.nativeElement;
|
|
5531
5665
|
if (!mapElement)
|
|
5532
5666
|
return;
|
|
5533
5667
|
this.map = new google.maps.Map(mapElement, {
|
|
@@ -5549,6 +5683,10 @@ class GeoAPIMaps {
|
|
|
5549
5683
|
if (this.points().length > 0) {
|
|
5550
5684
|
this.renderPoints(this.points());
|
|
5551
5685
|
}
|
|
5686
|
+
// Render cameras if they already exist when map is ready
|
|
5687
|
+
if (this.cameras().length > 0) {
|
|
5688
|
+
this.renderCameras(this.cameras());
|
|
5689
|
+
}
|
|
5552
5690
|
}
|
|
5553
5691
|
goToLocation(lat, lng) {
|
|
5554
5692
|
if (!this.map)
|
|
@@ -5583,33 +5721,42 @@ class GeoAPIMaps {
|
|
|
5583
5721
|
map: this.map,
|
|
5584
5722
|
content: dot,
|
|
5585
5723
|
});
|
|
5724
|
+
const statusColor = point.immobilized ? '#ef4444' : '#3b82f6';
|
|
5586
5725
|
const content = `
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5726
|
+
<div style="padding:0 12px 12px 12px;font-family:'Inter',-apple-system,sans-serif;
|
|
5727
|
+
min-width:200px;color:#374151;">
|
|
5728
|
+
<div style="font-weight:700;font-size:15px;color:#111827;">
|
|
5729
|
+
${point.subpoenaNumber}
|
|
5730
|
+
${point.immobilized ? '<span style="font-size:11px;background:#ef4444;color:white;border-radius:4px;padding:1px 6px;margin-bottom:8px;vertical-align:middle;">Inmovilizado</span>' : ''}
|
|
5731
|
+
</div>
|
|
5732
|
+
|
|
5733
|
+
<div style="font-size:12px;color:#9ca3af;margin-bottom:10px;display:flex;gap:8px;">
|
|
5734
|
+
<span>📍 ${point.locationLabel}</span>
|
|
5735
|
+
</div>
|
|
5736
|
+
|
|
5737
|
+
<div style="display:flex;flex-direction:column;gap:4px;">
|
|
5738
|
+
<div style="display:flex;justify-content:space-between;gap:16px;font-size:13px;">
|
|
5739
|
+
<span style="color:#9ca3af;">Fecha</span>
|
|
5740
|
+
<span style="font-weight:600;color:#374151;">${new Date(point.dateTime).toLocaleString('es-CO', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false })}</span>
|
|
5590
5741
|
</div>
|
|
5591
|
-
<div style="
|
|
5592
|
-
|
|
5742
|
+
<div style="display:flex;justify-content:space-between;gap:16px;font-size:13px;">
|
|
5743
|
+
<span style="color:#9ca3af;">Infracción</span>
|
|
5744
|
+
<span style="font-weight:600;color:#374151;">${point.infractionCode}</span>
|
|
5593
5745
|
</div>
|
|
5594
|
-
<div style="
|
|
5595
|
-
|
|
5746
|
+
<div style="display:flex;justify-content:space-between;gap:16px;font-size:13px;">
|
|
5747
|
+
<span style="color:#9ca3af;">Placa</span>
|
|
5748
|
+
<span style="font-weight:600;color:#374151;">${point.plate}</span>
|
|
5596
5749
|
</div>
|
|
5597
|
-
<div style="
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
<div style="font-size: 13px;">
|
|
5603
|
-
<span style="color: #9ca3af;">Placa:</span>
|
|
5604
|
-
<span style="font-weight: 500; color: #374151;">${point.plate}</span>
|
|
5605
|
-
</div>
|
|
5606
|
-
<div style="display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; margin-top: 4px; color: ${point.immobilized ? '#ef4444' : '#3b82f6'};">
|
|
5607
|
-
<span style="width: 8px; height: 8px; border-radius: 50%; background-color: ${point.immobilized ? '#ef4444' : '#3b82f6'};"></span>
|
|
5750
|
+
<div style="border-top:1px solid #e5e7eb;margin-top:6px;padding-top:6px;
|
|
5751
|
+
display:flex;justify-content:space-between;font-size:13px;font-weight:700;">
|
|
5752
|
+
<span style="color:${statusColor};">Estado</span>
|
|
5753
|
+
<span style="display:flex;align-items:center;gap:6px;color:${statusColor};">
|
|
5754
|
+
<span style="width:8px;height:8px;border-radius:50%;background-color:${statusColor};"></span>
|
|
5608
5755
|
${point.immobilized ? 'Inmovilizado' : 'Normal'}
|
|
5609
|
-
</
|
|
5756
|
+
</span>
|
|
5610
5757
|
</div>
|
|
5611
5758
|
</div>
|
|
5612
|
-
|
|
5759
|
+
</div>`;
|
|
5613
5760
|
marker.addListener('mouseover', () => {
|
|
5614
5761
|
if (!lockedMarker) {
|
|
5615
5762
|
infoWindow.setContent(content);
|
|
@@ -5638,28 +5785,124 @@ class GeoAPIMaps {
|
|
|
5638
5785
|
this.map.fitBounds(bounds);
|
|
5639
5786
|
}
|
|
5640
5787
|
}
|
|
5788
|
+
// ─── GeoPointCameras markers ─────────────────────────────────────────────────
|
|
5789
|
+
clearCameraMarkers() {
|
|
5790
|
+
this.cameraMarkers.forEach(m => (m.map = null));
|
|
5791
|
+
this.cameraMarkers = [];
|
|
5792
|
+
}
|
|
5793
|
+
renderCameras(cameras) {
|
|
5794
|
+
this.clearCameraMarkers();
|
|
5795
|
+
const infoWindow = new google.maps.InfoWindow();
|
|
5796
|
+
let lockedMarker = null;
|
|
5797
|
+
// 1. Encontrar el total mayor para destacarlo
|
|
5798
|
+
const maxTotal = Math.max(...cameras.map(c => c.total));
|
|
5799
|
+
// 2. Centrar el mapa para que se vean todas las cámaras al inicio
|
|
5800
|
+
if (cameras.length > 0) {
|
|
5801
|
+
const bounds = new google.maps.LatLngBounds();
|
|
5802
|
+
cameras.forEach(c => bounds.extend({ lat: c.latitude, lng: c.longitude }));
|
|
5803
|
+
this.map.fitBounds(bounds);
|
|
5804
|
+
}
|
|
5805
|
+
cameras.forEach(cam => {
|
|
5806
|
+
// 3. Color diferente si es el de mayor total
|
|
5807
|
+
const isTop = cam.total === maxTotal;
|
|
5808
|
+
const bgColor = isTop ? '#dc2626' : '#7c3aed'; // rojo para el top, morado para el resto
|
|
5809
|
+
const pin = document.createElement('div');
|
|
5810
|
+
pin.innerHTML = `
|
|
5811
|
+
<div style="
|
|
5812
|
+
background:${bgColor}; color:white; border-radius:8px;
|
|
5813
|
+
padding:4px 8px; font-size:12px; font-weight:700;
|
|
5814
|
+
border:2px solid white; box-shadow:0 2px 6px rgba(0,0,0,.3);
|
|
5815
|
+
display:flex; align-items:center; gap:4px; cursor:pointer; white-space:nowrap;">
|
|
5816
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24"
|
|
5817
|
+
fill="white">
|
|
5818
|
+
<path d="M15 8v8H5V8h10m1-2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0
|
|
5819
|
+
1-1v-3.5l4 4v-11l-4 4V7a1 1 0 0 0-1-1z"/>
|
|
5820
|
+
</svg>
|
|
5821
|
+
${cam.total}
|
|
5822
|
+
</div>`;
|
|
5823
|
+
const marker = new google.maps.marker.AdvancedMarkerElement({
|
|
5824
|
+
position: { lat: cam.latitude, lng: cam.longitude },
|
|
5825
|
+
map: this.map,
|
|
5826
|
+
content: pin,
|
|
5827
|
+
title: cam.name,
|
|
5828
|
+
});
|
|
5829
|
+
const codesHtml = Object.entries(cam.by_code)
|
|
5830
|
+
.map(([code, count]) => `
|
|
5831
|
+
<div style="display:flex;justify-content:space-between;gap:16px;font-size:13px;">
|
|
5832
|
+
<span style="color:#9ca3af;">${code}</span>
|
|
5833
|
+
<span style="font-weight:600;color:#374151;">${count}</span>
|
|
5834
|
+
</div>`)
|
|
5835
|
+
.join('');
|
|
5836
|
+
const content = `
|
|
5837
|
+
<div style="padding:0 12px 12px 12px;font-family:'Inter',-apple-system,sans-serif;
|
|
5838
|
+
min-width:200px;color:#374151;">
|
|
5839
|
+
<div style="font-weight:700;font-size:15px;color:#111827;">
|
|
5840
|
+
${cam.name}
|
|
5841
|
+
${isTop ? '<span style="font-size:11px;background:#dc2626;color:white;border-radius:4px;padding:1px 6px;margin-bottom:8px;vertical-align:middle;">Más Infracciones</span>' : ''}
|
|
5842
|
+
</div>
|
|
5843
|
+
<!-- <div style="font-size:12px;color:#9ca3af;margin-bottom:4px;">ID: ${cam.id}</div> -->
|
|
5844
|
+
|
|
5845
|
+
<!-- 4. Coordenadas en el tooltip -->
|
|
5846
|
+
<div style="font-size:12px;color:#9ca3af;margin-bottom:10px;display:flex;gap:8px;">
|
|
5847
|
+
<span>📍 ${cam.locationLabel}</span>
|
|
5848
|
+
</div>
|
|
5849
|
+
|
|
5850
|
+
<div style="display:flex;flex-direction:column;gap:4px;">
|
|
5851
|
+
<div style="margin-top:8px;font-size:14px;color:#6b7280;">Código(s) de Infracciones</div>
|
|
5852
|
+
${codesHtml}
|
|
5853
|
+
<div style="border-top:1px solid #e5e7eb;margin-top:6px;padding-top:6px;
|
|
5854
|
+
display:flex;justify-content:space-between;font-size:13px;font-weight:700;">
|
|
5855
|
+
<span style="color:${bgColor};">Total</span>
|
|
5856
|
+
<span style="color:${bgColor};">${cam.total}</span>
|
|
5857
|
+
</div>
|
|
5858
|
+
</div>
|
|
5859
|
+
</div>`;
|
|
5860
|
+
marker.addListener('mouseover', () => {
|
|
5861
|
+
if (!lockedMarker) {
|
|
5862
|
+
infoWindow.setContent(content);
|
|
5863
|
+
infoWindow.open(this.map, marker);
|
|
5864
|
+
}
|
|
5865
|
+
});
|
|
5866
|
+
marker.addListener('mouseout', () => { if (!lockedMarker)
|
|
5867
|
+
infoWindow.close(); });
|
|
5868
|
+
marker.addListener('click', () => {
|
|
5869
|
+
lockedMarker = marker;
|
|
5870
|
+
infoWindow.setContent(content);
|
|
5871
|
+
infoWindow.open(this.map, marker);
|
|
5872
|
+
});
|
|
5873
|
+
infoWindow.addListener('closeclick', () => { lockedMarker = null; });
|
|
5874
|
+
this.cameraMarkers.push(marker);
|
|
5875
|
+
});
|
|
5876
|
+
}
|
|
5641
5877
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: GeoAPIMaps, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5642
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: GeoAPIMaps, isStandalone: true, selector: "lib-geo-api-maps", inputs: { points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div
|
|
5878
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.16", type: GeoAPIMaps, isStandalone: true, selector: "lib-geo-api-maps", inputs: { points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: false, transformFunction: null }, cameras: { classPropertyName: "cameras", publicName: "cameras", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "mapContainer", first: true, predicate: ["mapContainer"], descendants: true, static: true }], ngImport: i0, template: "<div #mapContainer class=\"map\"></div>\r\n", styles: [":host{display:block;width:100%;height:100%;position:relative}.map{position:absolute;inset:0;z-index:1;border-radius:12px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
5643
5879
|
}
|
|
5644
5880
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: GeoAPIMaps, decorators: [{
|
|
5645
5881
|
type: Component,
|
|
5646
|
-
args: [{ selector: 'lib-geo-api-maps', standalone: true, imports: [CommonModule], template: "<div
|
|
5647
|
-
}], ctorParameters: () => [], propDecorators: { points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: false }] }] } }
|
|
5882
|
+
args: [{ selector: 'lib-geo-api-maps', standalone: true, imports: [CommonModule], template: "<div #mapContainer class=\"map\"></div>\r\n", styles: [":host{display:block;width:100%;height:100%;position:relative}.map{position:absolute;inset:0;z-index:1;border-radius:12px}\n"] }]
|
|
5883
|
+
}], ctorParameters: () => [], propDecorators: { points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: false }] }], cameras: [{ type: i0.Input, args: [{ isSignal: true, alias: "cameras", required: false }] }], mapContainer: [{
|
|
5884
|
+
type: ViewChild,
|
|
5885
|
+
args: ['mapContainer', { static: true }]
|
|
5886
|
+
}] } });
|
|
5648
5887
|
|
|
5649
5888
|
class MapGeo {
|
|
5650
5889
|
title = input('Mapa Geográfico', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
5651
5890
|
isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
|
|
5652
5891
|
points = input([], ...(ngDevMode ? [{ debugName: "points" }] : []));
|
|
5892
|
+
cameras = input([], ...(ngDevMode ? [{ debugName: "cameras" }] : []));
|
|
5893
|
+
immobilizedCount = computed(() => this.points().filter(p => p.immobilized).length, ...(ngDevMode ? [{ debugName: "immobilizedCount" }] : []));
|
|
5894
|
+
normalCount = computed(() => this.points().filter(p => !p.immobilized).length, ...(ngDevMode ? [{ debugName: "normalCount" }] : []));
|
|
5895
|
+
totalCameras = computed(() => this.cameras().length, ...(ngDevMode ? [{ debugName: "totalCameras" }] : []));
|
|
5653
5896
|
toggleExpand() {
|
|
5654
5897
|
this.isExpanded.update((value) => !value);
|
|
5655
5898
|
}
|
|
5656
5899
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MapGeo, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5657
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MapGeo, isStandalone: true, selector: "lib-map-geo", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"map-geo-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"map-geo-header\">\r\n <h3 class=\"map-geo-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <div class=\"chart-wrapper\">\r\n <lib-geo-api-maps [points]=\"points()\" />\r\n </div>\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.map-geo-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.map-geo-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{position:relative;width:100%;height:450px;min-height:450px;display:block;min-width:0}.map-geo-container.expanded .chart-wrapper{flex:1;height:auto;min-height:0}.map-geo-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.map-geo-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"], dependencies: [{ kind: "component", type: GeoAPIMaps, selector: "lib-geo-api-maps", inputs: ["points"] }] });
|
|
5900
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MapGeo, isStandalone: true, selector: "lib-map-geo", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, points: { classPropertyName: "points", publicName: "points", isSignal: true, isRequired: false, transformFunction: null }, cameras: { classPropertyName: "cameras", publicName: "cameras", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"map-geo-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"map-geo-header\">\r\n <h3 class=\"map-geo-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <div class=\"chart-wrapper\">\r\n <lib-geo-api-maps [points]=\"points()\" [cameras]=\"cameras()\" />\r\n </div>\r\n @if (points().length > 0) {\r\n <div class=\"map-summary\">\r\n <span class=\"map-badge blue\">\r\n <span class=\"map-dot blue\"></span>\r\n Normal: {{ normalCount() }}\r\n </span>\r\n <span class=\"map-badge red\">\r\n <span class=\"map-dot red\"></span>\r\n Inmovilizado: {{ immobilizedCount() }}\r\n </span>\r\n <span class=\"map-badge total\">Total: {{ points().length }}</span>\r\n </div>\r\n }\r\n @if (cameras().length > 0) {\r\n <div class=\"map-summary\">\r\n <span class=\"map-badge purple\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M15 8v8H5V8h10m1-2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3.5l4 4v-11l-4 4V7a1 1 0 0 0-1-1z\"/>\r\n </svg>\r\n C\u00E1maras: {{ totalCameras() }}\r\n </span>\r\n </div>\r\n }\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.map-geo-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.map-geo-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{position:relative;width:100%;height:450px;min-height:450px;display:block;min-width:0}.map-geo-container.expanded .chart-wrapper{flex:1;height:auto;min-height:0}.map-geo-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.map-geo-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}.map-summary{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.map-badge{display:inline-flex;align-items:center;gap:5px;font-family:Inter,sans-serif;font-size:12px;font-weight:600;padding:4px 10px;border-radius:20px;white-space:nowrap}.map-badge.blue{background-color:#3b82f61f;color:#2563eb}.map-badge.red{background-color:#ef44441f;color:#dc2626}.map-badge.purple{background-color:#7c3aed1f;color:#7c3aed}.map-badge.total{background-color:#3741511a;color:#374151}.map-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.map-dot.blue{background-color:#3b82f6}.map-dot.red{background-color:#ef4444}\n"], dependencies: [{ kind: "component", type: GeoAPIMaps, selector: "lib-geo-api-maps", inputs: ["points", "cameras"] }] });
|
|
5658
5901
|
}
|
|
5659
5902
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MapGeo, decorators: [{
|
|
5660
5903
|
type: Component,
|
|
5661
|
-
args: [{ selector: 'lib-map-geo', imports: [GeoAPIMaps], template: "<div class=\"map-geo-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"map-geo-header\">\r\n <h3 class=\"map-geo-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <div class=\"chart-wrapper\">\r\n <lib-geo-api-maps [points]=\"points()\" />\r\n </div>\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.map-geo-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.map-geo-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{position:relative;width:100%;height:450px;min-height:450px;display:block;min-width:0}.map-geo-container.expanded .chart-wrapper{flex:1;height:auto;min-height:0}.map-geo-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.map-geo-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}\n"] }]
|
|
5662
|
-
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: false }] }] } });
|
|
5904
|
+
args: [{ selector: 'lib-map-geo', imports: [GeoAPIMaps], template: "<div class=\"map-geo-container\" [class.expanded]=\"isExpanded()\">\r\n <div class=\"map-geo-header\">\r\n <h3 class=\"map-geo-title\">{{ title() }}</h3>\r\n <button class=\"expand-btn\" (click)=\"toggleExpand()\" type=\"button\" [attr.aria-label]=\"isExpanded() ? 'Minimize chart' : 'Maximize chart'\">\r\n @if (isExpanded()) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-minimize-2\"><polyline points=\"4 14 10 14 10 20\"/><polyline points=\"20 10 14 10 14 4\"/><line x1=\"14\" x2=\"21\" y1=\"10\" y2=\"3\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"lucide lucide-maximize-2\"><polyline points=\"15 3 21 3 21 9\"/><polyline points=\"9 21 3 21 3 15\"/><line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\"/><line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\"/></svg>\r\n }\r\n </button>\r\n </div>\r\n <div class=\"chart-wrapper\">\r\n <lib-geo-api-maps [points]=\"points()\" [cameras]=\"cameras()\" />\r\n </div>\r\n @if (points().length > 0) {\r\n <div class=\"map-summary\">\r\n <span class=\"map-badge blue\">\r\n <span class=\"map-dot blue\"></span>\r\n Normal: {{ normalCount() }}\r\n </span>\r\n <span class=\"map-badge red\">\r\n <span class=\"map-dot red\"></span>\r\n Inmovilizado: {{ immobilizedCount() }}\r\n </span>\r\n <span class=\"map-badge total\">Total: {{ points().length }}</span>\r\n </div>\r\n }\r\n @if (cameras().length > 0) {\r\n <div class=\"map-summary\">\r\n <span class=\"map-badge purple\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\r\n <path d=\"M15 8v8H5V8h10m1-2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3.5l4 4v-11l-4 4V7a1 1 0 0 0-1-1z\"/>\r\n </svg>\r\n C\u00E1maras: {{ totalCameras() }}\r\n </span>\r\n </div>\r\n }\r\n</div>\r\n@if (isExpanded()) {\r\n <div class=\"overlay\" (click)=\"toggleExpand()\"></div>\r\n}", styles: [":host{display:block;width:100%;min-width:0}.map-geo-container{width:100%;min-width:0;display:flex;flex-direction:column;gap:16px;background-color:#f0f0db;padding:24px 16px 16px;border-radius:20px;position:relative;transition:all .3s ease}.map-geo-container.expanded{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;z-index:9999;box-shadow:0 25px 50px -12px #00000040;margin:0}.chart-wrapper{position:relative;width:100%;height:450px;min-height:450px;display:block;min-width:0}.map-geo-container.expanded .chart-wrapper{flex:1;height:auto;min-height:0}.map-geo-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.map-geo-title{font-size:20px;font-weight:600;color:#333;font-family:Inter,sans-serif}.expand-btn{background:transparent;border:none;cursor:pointer;color:#61661f;padding:6px;border-radius:8px;display:flex;align-items:center;justify-content:center;transition:background-color .2s}.expand-btn:hover{background-color:#61661f1a}.overlay{position:fixed;top:0;left:0;width:100vw;height:100vh;background-color:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:9998}.map-summary{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.map-badge{display:inline-flex;align-items:center;gap:5px;font-family:Inter,sans-serif;font-size:12px;font-weight:600;padding:4px 10px;border-radius:20px;white-space:nowrap}.map-badge.blue{background-color:#3b82f61f;color:#2563eb}.map-badge.red{background-color:#ef44441f;color:#dc2626}.map-badge.purple{background-color:#7c3aed1f;color:#7c3aed}.map-badge.total{background-color:#3741511a;color:#374151}.map-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.map-dot.blue{background-color:#3b82f6}.map-dot.red{background-color:#ef4444}\n"] }]
|
|
5905
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], points: [{ type: i0.Input, args: [{ isSignal: true, alias: "points", required: false }] }], cameras: [{ type: i0.Input, args: [{ isSignal: true, alias: "cameras", required: false }] }] } });
|
|
5663
5906
|
|
|
5664
5907
|
class TableChart {
|
|
5665
5908
|
title = input('Table Chart', ...(ngDevMode ? [{ debugName: "title" }] : []));
|