sapenlinea-components 0.9.75 → 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 +420 -122
- package/fesm2022/sapenlinea-components.mjs.map +1 -1
- package/index.d.ts +49 -11
- 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,
|
|
@@ -5135,6 +5216,104 @@ function getTotal(data) {
|
|
|
5135
5216
|
function buildDonutChartOptions(data) {
|
|
5136
5217
|
const total = getTotal(data);
|
|
5137
5218
|
const showLegend = data.length > 10;
|
|
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
|
+
};
|
|
5239
|
+
let legendConfig = {};
|
|
5240
|
+
if (showLegend) {
|
|
5241
|
+
if (splitLegend) {
|
|
5242
|
+
const half = Math.ceil(data.length / 2);
|
|
5243
|
+
const leftData = data.slice(0, half).map(d => d.label);
|
|
5244
|
+
const rightData = data.slice(half).map(d => d.label);
|
|
5245
|
+
legendConfig = {
|
|
5246
|
+
legend: [
|
|
5247
|
+
{
|
|
5248
|
+
data: leftData,
|
|
5249
|
+
type: 'scroll',
|
|
5250
|
+
orient: 'vertical',
|
|
5251
|
+
left: '5%',
|
|
5252
|
+
top: 'center',
|
|
5253
|
+
itemWidth: 12,
|
|
5254
|
+
itemHeight: 12,
|
|
5255
|
+
itemGap: 10,
|
|
5256
|
+
tooltip: legendTooltip,
|
|
5257
|
+
textStyle: {
|
|
5258
|
+
fontSize: 11,
|
|
5259
|
+
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5260
|
+
color: UI_CHART_TOKENS.muted,
|
|
5261
|
+
},
|
|
5262
|
+
pageTextStyle: { color: UI_CHART_TOKENS.muted },
|
|
5263
|
+
pageIconColor: UI_CHART_TOKENS.centerText,
|
|
5264
|
+
pageIconInactiveColor: '#ccc',
|
|
5265
|
+
},
|
|
5266
|
+
{
|
|
5267
|
+
data: rightData,
|
|
5268
|
+
type: 'scroll',
|
|
5269
|
+
orient: 'vertical',
|
|
5270
|
+
right: '5%',
|
|
5271
|
+
top: 'center',
|
|
5272
|
+
itemWidth: 12,
|
|
5273
|
+
itemHeight: 12,
|
|
5274
|
+
itemGap: 10,
|
|
5275
|
+
tooltip: legendTooltip,
|
|
5276
|
+
textStyle: {
|
|
5277
|
+
fontSize: 11,
|
|
5278
|
+
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5279
|
+
color: UI_CHART_TOKENS.muted,
|
|
5280
|
+
},
|
|
5281
|
+
pageTextStyle: { color: UI_CHART_TOKENS.muted },
|
|
5282
|
+
pageIconColor: UI_CHART_TOKENS.centerText,
|
|
5283
|
+
pageIconInactiveColor: '#ccc',
|
|
5284
|
+
}
|
|
5285
|
+
]
|
|
5286
|
+
};
|
|
5287
|
+
}
|
|
5288
|
+
else {
|
|
5289
|
+
legendConfig = {
|
|
5290
|
+
legend: {
|
|
5291
|
+
type: 'scroll',
|
|
5292
|
+
orient: 'vertical',
|
|
5293
|
+
right: '5%',
|
|
5294
|
+
top: 'center',
|
|
5295
|
+
itemWidth: 12,
|
|
5296
|
+
itemHeight: 12,
|
|
5297
|
+
itemGap: 10,
|
|
5298
|
+
tooltip: legendTooltip,
|
|
5299
|
+
textStyle: {
|
|
5300
|
+
fontSize: 11,
|
|
5301
|
+
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5302
|
+
color: UI_CHART_TOKENS.muted,
|
|
5303
|
+
},
|
|
5304
|
+
pageTextStyle: {
|
|
5305
|
+
color: UI_CHART_TOKENS.muted,
|
|
5306
|
+
},
|
|
5307
|
+
pageIconColor: UI_CHART_TOKENS.centerText,
|
|
5308
|
+
pageIconInactiveColor: '#ccc',
|
|
5309
|
+
}
|
|
5310
|
+
};
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
let centerX = '50%';
|
|
5314
|
+
if (showLegend) {
|
|
5315
|
+
centerX = splitLegend ? '50%' : '35%';
|
|
5316
|
+
}
|
|
5138
5317
|
return {
|
|
5139
5318
|
animation: true,
|
|
5140
5319
|
backgroundColor: 'transparent',
|
|
@@ -5164,32 +5343,12 @@ function buildDonutChartOptions(data) {
|
|
|
5164
5343
|
`,
|
|
5165
5344
|
formatter: (params) => `${params.name}: ${params.value}`,
|
|
5166
5345
|
},
|
|
5167
|
-
...
|
|
5168
|
-
legend: {
|
|
5169
|
-
type: 'scroll',
|
|
5170
|
-
orient: 'horizontal',
|
|
5171
|
-
bottom: 0,
|
|
5172
|
-
left: 'center',
|
|
5173
|
-
itemWidth: 12,
|
|
5174
|
-
itemHeight: 12,
|
|
5175
|
-
itemGap: 10,
|
|
5176
|
-
textStyle: {
|
|
5177
|
-
fontSize: 11,
|
|
5178
|
-
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5179
|
-
color: UI_CHART_TOKENS.muted,
|
|
5180
|
-
},
|
|
5181
|
-
pageTextStyle: {
|
|
5182
|
-
color: UI_CHART_TOKENS.muted,
|
|
5183
|
-
},
|
|
5184
|
-
pageIconColor: UI_CHART_TOKENS.centerText,
|
|
5185
|
-
pageIconInactiveColor: '#ccc',
|
|
5186
|
-
},
|
|
5187
|
-
} : {}),
|
|
5346
|
+
...legendConfig,
|
|
5188
5347
|
series: [
|
|
5189
5348
|
{
|
|
5190
5349
|
type: 'pie',
|
|
5191
5350
|
radius: ['44%', '72%'],
|
|
5192
|
-
center: [
|
|
5351
|
+
center: [centerX, '50%'],
|
|
5193
5352
|
avoidLabelOverlap: true,
|
|
5194
5353
|
minAngle: 4,
|
|
5195
5354
|
itemStyle: {
|
|
@@ -5242,21 +5401,19 @@ function buildDonutChartOptions(data) {
|
|
|
5242
5401
|
})),
|
|
5243
5402
|
},
|
|
5244
5403
|
],
|
|
5245
|
-
|
|
5246
|
-
{
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5257
|
-
},
|
|
5404
|
+
title: {
|
|
5405
|
+
text: `${total}`,
|
|
5406
|
+
left: centerX,
|
|
5407
|
+
top: '48%',
|
|
5408
|
+
textAlign: 'center',
|
|
5409
|
+
textVerticalAlign: 'middle',
|
|
5410
|
+
textStyle: {
|
|
5411
|
+
color: UI_CHART_TOKENS.centerText,
|
|
5412
|
+
fontSize: 24,
|
|
5413
|
+
fontWeight: 700,
|
|
5414
|
+
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5258
5415
|
},
|
|
5259
|
-
|
|
5416
|
+
},
|
|
5260
5417
|
};
|
|
5261
5418
|
}
|
|
5262
5419
|
class DonutChart {
|
|
@@ -5303,15 +5460,29 @@ class DonutChart {
|
|
|
5303
5460
|
this.drilledGroup.set(clickedItem.label);
|
|
5304
5461
|
}
|
|
5305
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
|
+
}
|
|
5306
5477
|
resetDrillDown() {
|
|
5307
5478
|
this.drilledGroup.set(null);
|
|
5308
5479
|
}
|
|
5309
5480
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DonutChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5310
|
-
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 }] });
|
|
5311
5482
|
}
|
|
5312
5483
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DonutChart, decorators: [{
|
|
5313
5484
|
type: Component,
|
|
5314
|
-
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"] }]
|
|
5315
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"] }] } });
|
|
5316
5487
|
|
|
5317
5488
|
function buildHeatmapOptions(config) {
|
|
@@ -5392,17 +5563,24 @@ function buildHeatmapOptions(config) {
|
|
|
5392
5563
|
},
|
|
5393
5564
|
},
|
|
5394
5565
|
visualMap: {
|
|
5566
|
+
type: 'piecewise',
|
|
5395
5567
|
min: minVal,
|
|
5396
5568
|
max: maxVal,
|
|
5569
|
+
splitNumber: 5,
|
|
5397
5570
|
orient: 'horizontal',
|
|
5398
5571
|
left: 'center',
|
|
5399
5572
|
bottom: '0',
|
|
5400
|
-
itemWidth:
|
|
5401
|
-
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
|
|
5402
5579
|
textStyle: {
|
|
5403
5580
|
color: UI_CHART_TOKENS.muted,
|
|
5404
|
-
fontSize:
|
|
5581
|
+
fontSize: 12,
|
|
5405
5582
|
fontFamily: UI_CHART_TOKENS.fontFamily,
|
|
5583
|
+
fontWeight: 500
|
|
5406
5584
|
},
|
|
5407
5585
|
inRange: {
|
|
5408
5586
|
color: [
|
|
@@ -5449,7 +5627,7 @@ class Heatmap {
|
|
|
5449
5627
|
}
|
|
5450
5628
|
options = computed(() => buildHeatmapOptions(this.config()), ...(ngDevMode ? [{ debugName: "options" }] : []));
|
|
5451
5629
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Heatmap, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5452
|
-
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"] }] });
|
|
5453
5631
|
}
|
|
5454
5632
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: Heatmap, decorators: [{
|
|
5455
5633
|
type: Component,
|
|
@@ -5458,21 +5636,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5458
5636
|
|
|
5459
5637
|
class GeoAPIMaps {
|
|
5460
5638
|
points = input([], ...(ngDevMode ? [{ debugName: "points" }] : []));
|
|
5639
|
+
cameras = input([], ...(ngDevMode ? [{ debugName: "cameras" }] : []));
|
|
5640
|
+
mapContainer;
|
|
5461
5641
|
map;
|
|
5462
5642
|
markers = [];
|
|
5643
|
+
cameraMarkers = [];
|
|
5463
5644
|
constructor() {
|
|
5645
|
+
// Reactivo a cambios de puntos
|
|
5464
5646
|
effect(() => {
|
|
5465
5647
|
const pts = this.points();
|
|
5466
5648
|
if (this.map && pts.length > 0) {
|
|
5467
5649
|
this.renderPoints(pts);
|
|
5468
5650
|
}
|
|
5469
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
|
+
});
|
|
5470
5659
|
}
|
|
5471
5660
|
ngAfterViewInit() {
|
|
5472
5661
|
this.initMap();
|
|
5473
5662
|
}
|
|
5474
5663
|
initMap() {
|
|
5475
|
-
const mapElement =
|
|
5664
|
+
const mapElement = this.mapContainer?.nativeElement;
|
|
5476
5665
|
if (!mapElement)
|
|
5477
5666
|
return;
|
|
5478
5667
|
this.map = new google.maps.Map(mapElement, {
|
|
@@ -5494,6 +5683,10 @@ class GeoAPIMaps {
|
|
|
5494
5683
|
if (this.points().length > 0) {
|
|
5495
5684
|
this.renderPoints(this.points());
|
|
5496
5685
|
}
|
|
5686
|
+
// Render cameras if they already exist when map is ready
|
|
5687
|
+
if (this.cameras().length > 0) {
|
|
5688
|
+
this.renderCameras(this.cameras());
|
|
5689
|
+
}
|
|
5497
5690
|
}
|
|
5498
5691
|
goToLocation(lat, lng) {
|
|
5499
5692
|
if (!this.map)
|
|
@@ -5528,33 +5721,42 @@ class GeoAPIMaps {
|
|
|
5528
5721
|
map: this.map,
|
|
5529
5722
|
content: dot,
|
|
5530
5723
|
});
|
|
5724
|
+
const statusColor = point.immobilized ? '#ef4444' : '#3b82f6';
|
|
5531
5725
|
const content = `
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
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>
|
|
5535
5741
|
</div>
|
|
5536
|
-
<div style="
|
|
5537
|
-
|
|
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>
|
|
5538
5745
|
</div>
|
|
5539
|
-
<div style="
|
|
5540
|
-
|
|
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>
|
|
5541
5749
|
</div>
|
|
5542
|
-
<div style="
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
<div style="font-size: 13px;">
|
|
5548
|
-
<span style="color: #9ca3af;">Placa:</span>
|
|
5549
|
-
<span style="font-weight: 500; color: #374151;">${point.plate}</span>
|
|
5550
|
-
</div>
|
|
5551
|
-
<div style="display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; margin-top: 4px; color: ${point.immobilized ? '#ef4444' : '#3b82f6'};">
|
|
5552
|
-
<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>
|
|
5553
5755
|
${point.immobilized ? 'Inmovilizado' : 'Normal'}
|
|
5554
|
-
</
|
|
5756
|
+
</span>
|
|
5555
5757
|
</div>
|
|
5556
5758
|
</div>
|
|
5557
|
-
|
|
5759
|
+
</div>`;
|
|
5558
5760
|
marker.addListener('mouseover', () => {
|
|
5559
5761
|
if (!lockedMarker) {
|
|
5560
5762
|
infoWindow.setContent(content);
|
|
@@ -5583,28 +5785,124 @@ class GeoAPIMaps {
|
|
|
5583
5785
|
this.map.fitBounds(bounds);
|
|
5584
5786
|
}
|
|
5585
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
|
+
}
|
|
5586
5877
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: GeoAPIMaps, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5587
|
-
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 }] });
|
|
5588
5879
|
}
|
|
5589
5880
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: GeoAPIMaps, decorators: [{
|
|
5590
5881
|
type: Component,
|
|
5591
|
-
args: [{ selector: 'lib-geo-api-maps', standalone: true, imports: [CommonModule], template: "<div
|
|
5592
|
-
}], 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
|
+
}] } });
|
|
5593
5887
|
|
|
5594
5888
|
class MapGeo {
|
|
5595
5889
|
title = input('Mapa Geográfico', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
5596
5890
|
isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
|
|
5597
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" }] : []));
|
|
5598
5896
|
toggleExpand() {
|
|
5599
5897
|
this.isExpanded.update((value) => !value);
|
|
5600
5898
|
}
|
|
5601
5899
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MapGeo, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5602
|
-
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"] }] });
|
|
5603
5901
|
}
|
|
5604
5902
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MapGeo, decorators: [{
|
|
5605
5903
|
type: Component,
|
|
5606
|
-
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"] }]
|
|
5607
|
-
}], 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 }] }] } });
|
|
5608
5906
|
|
|
5609
5907
|
class TableChart {
|
|
5610
5908
|
title = input('Table Chart', ...(ngDevMode ? [{ debugName: "title" }] : []));
|