sapenlinea-components 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,117 +1,45 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, signal, computed, forwardRef, Component, EventEmitter, Output, Input, HostListener } from '@angular/core';
3
- import { NG_VALUE_ACCESSOR } from '@angular/forms';
2
+ import { input, output, signal, computed, forwardRef, Component, EventEmitter, Output, Input, HostListener, effect } from '@angular/core';
3
+ import * as i1$1 from '@angular/forms';
4
+ import { ReactiveFormsModule, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
4
5
  import * as i1 from '@angular/common';
5
6
  import { CommonModule } from '@angular/common';
6
7
 
7
8
  class DateTimeFilter {
8
9
  elementRef;
9
10
  ngZone;
10
- mode = input('date', ...(ngDevMode ? [{ debugName: "mode" }] : []));
11
- placeholder = input('Seleccionar fecha', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
12
- minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
13
- maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
14
- filter = input.required(...(ngDevMode ? [{ debugName: "filter" }] : []));
11
+ // Inputs
12
+ filters = input([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
13
+ // Outputs
15
14
  dateSelected = output();
16
- filters = [
17
- { label: 'Fecha inicio', value: 'inicio', type: 'date' },
18
- { label: 'Fecha fin', value: 'fin', type: 'date' },
19
- { label: 'Fecha Nacimiento', value: 'born', type: 'date' },
20
- ];
21
15
  dateChange = output();
16
+ // Internal State
17
+ activeFilterType = signal(null, ...(ngDevMode ? [{ debugName: "activeFilterType" }] : []));
22
18
  isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
23
19
  selectedDate = signal(null, ...(ngDevMode ? [{ debugName: "selectedDate" }] : []));
24
20
  isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
25
21
  isTouched = signal(false, ...(ngDevMode ? [{ debugName: "isTouched" }] : []));
26
- // Navegación del calendario
22
+ // Computed Properties based on active filter
23
+ activeFilter = computed(() => {
24
+ const type = this.activeFilterType();
25
+ return this.filters().find(f => f.value === type) || null;
26
+ }, ...(ngDevMode ? [{ debugName: "activeFilter" }] : []));
27
+ mode = computed(() => {
28
+ const filter = this.activeFilter();
29
+ return filter?.type === 'datetime' ? 'datetime' : 'date';
30
+ }, ...(ngDevMode ? [{ debugName: "mode" }] : []));
31
+ placeholder = computed(() => this.activeFilter()?.placeholder || 'Seleccionar fecha', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
32
+ minDate = computed(() => this.activeFilter()?.minDate || null, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
33
+ maxDate = computed(() => this.activeFilter()?.maxDate || null, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
34
+ filterValue = computed(() => this.activeFilter()?.value || '', ...(ngDevMode ? [{ debugName: "filterValue" }] : []));
35
+ // Calendar Navigation
27
36
  currentMonth = signal(new Date().getMonth(), ...(ngDevMode ? [{ debugName: "currentMonth" }] : []));
28
37
  currentYear = signal(new Date().getFullYear(), ...(ngDevMode ? [{ debugName: "currentYear" }] : []));
29
- // Hora en formato 24h (para el backend)
38
+ // Time State
30
39
  selectedHour = signal(0, ...(ngDevMode ? [{ debugName: "selectedHour" }] : []));
31
40
  selectedMinute = signal(0, ...(ngDevMode ? [{ debugName: "selectedMinute" }] : []));
32
- // Estado AM/PM
33
41
  selectedAmPm = signal('AM', ...(ngDevMode ? [{ debugName: "selectedAmPm" }] : []));
34
42
  documentClickListener;
35
- showFilters = false;
36
- showDatePicker = false;
37
- activeFilterType = null;
38
- selectDate(date) {
39
- this.dateSelected.emit({
40
- filter: this.filter(),
41
- value: date
42
- });
43
- }
44
- getDatePickerMode(type) {
45
- switch (type) {
46
- case 'date':
47
- return 'date';
48
- case 'datetime':
49
- return 'datetime';
50
- default:
51
- return 'date';
52
- }
53
- }
54
- displayValue = computed(() => {
55
- const date = this.selectedDate();
56
- if (!date)
57
- return '';
58
- const options = {
59
- day: '2-digit',
60
- month: '2-digit',
61
- year: 'numeric',
62
- };
63
- if (this.mode() === 'datetime') {
64
- options.hour = '2-digit';
65
- options.minute = '2-digit';
66
- options.hour12 = true; // Cambiado a formato 12 horas
67
- }
68
- return date.toLocaleDateString('es-ES', options);
69
- }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
70
- monthName = computed(() => {
71
- const months = [
72
- 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
73
- 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'
74
- ];
75
- return months[this.currentMonth()];
76
- }, ...(ngDevMode ? [{ debugName: "monthName" }] : []));
77
- calendarDays = computed(() => {
78
- const year = this.currentYear();
79
- const month = this.currentMonth();
80
- const firstDay = new Date(year, month, 1);
81
- const lastDay = new Date(year, month + 1, 0);
82
- const daysInMonth = lastDay.getDate();
83
- const startingDayOfWeek = firstDay.getDay();
84
- const days = [];
85
- // Días vacíos al inicio
86
- for (let i = 0; i < startingDayOfWeek; i++) {
87
- days.push(null);
88
- }
89
- // Días del mes
90
- for (let day = 1; day <= daysInMonth; day++) {
91
- days.push(day);
92
- }
93
- return days;
94
- }, ...(ngDevMode ? [{ debugName: "calendarDays" }] : []));
95
- // Horas en formato 12h
96
- hours12 = computed(() => {
97
- return Array.from({ length: 12 }, (_, i) => {
98
- const hour12 = i === 0 ? 12 : i;
99
- return {
100
- value: hour12,
101
- display: hour12.toString().padStart(2, '0')
102
- };
103
- });
104
- }, ...(ngDevMode ? [{ debugName: "hours12" }] : []));
105
- // Hora seleccionada en formato 12h
106
- selectedHour12 = computed(() => {
107
- const hour24 = this.selectedHour();
108
- if (hour24 === 0)
109
- return 12;
110
- if (hour24 > 12)
111
- return hour24 - 12;
112
- return hour24;
113
- }, ...(ngDevMode ? [{ debugName: "selectedHour12" }] : []));
114
- minutes = computed(() => Array.from({ length: 60 }, (_, i) => i), ...(ngDevMode ? [{ debugName: "minutes" }] : []));
115
43
  onChange = () => { };
116
44
  onTouched = () => { };
117
45
  constructor(elementRef, ngZone) {
@@ -121,32 +49,23 @@ class DateTimeFilter {
121
49
  ngOnDestroy() {
122
50
  this.removeDocumentListener();
123
51
  }
52
+ // ControlValueAccessor Implementation
124
53
  writeValue(value) {
125
54
  let date = null;
126
55
  if (value instanceof Date) {
127
56
  date = value;
128
57
  }
129
58
  else if (typeof value === 'string' && value) {
130
- // Manejar tanto fechas ISO como otros formatos de string
131
59
  const parsedDate = new Date(value);
132
60
  if (!isNaN(parsedDate.getTime())) {
133
61
  date = parsedDate;
134
62
  }
135
63
  }
136
64
  if (date) {
137
- this.selectedDate.set(date);
138
- this.currentMonth.set(date.getMonth());
139
- this.currentYear.set(date.getFullYear());
140
- this.selectedHour.set(date.getHours());
141
- this.selectedMinute.set(date.getMinutes());
142
- this.selectedAmPm.set(date.getHours() >= 12 ? 'PM' : 'AM');
65
+ this.updateInternalState(date);
143
66
  }
144
67
  else {
145
- this.selectedDate.set(null);
146
- // Valores por defecto
147
- this.selectedHour.set(0);
148
- this.selectedMinute.set(0);
149
- this.selectedAmPm.set('AM');
68
+ this.resetInternalState();
150
69
  }
151
70
  }
152
71
  registerOnChange(fn) {
@@ -158,14 +77,15 @@ class DateTimeFilter {
158
77
  setDisabledState(isDisabled) {
159
78
  this.isDisabled.set(isDisabled);
160
79
  }
161
- toggle() {
80
+ // Public Methods
81
+ toggle(filterValue) {
162
82
  if (this.isDisabled())
163
83
  return;
164
- this.markAsTouched();
165
- if (this.isOpen()) {
84
+ if (this.activeFilterType() === filterValue && this.isOpen()) {
166
85
  this.close();
167
86
  }
168
87
  else {
88
+ this.activeFilterType.set(filterValue);
169
89
  this.open();
170
90
  }
171
91
  }
@@ -175,7 +95,6 @@ class DateTimeFilter {
175
95
  this.markAsTouched();
176
96
  this.isOpen.set(true);
177
97
  this.addDocumentListener();
178
- // Si hay fecha seleccionada, navegar a ese mes/año
179
98
  const selected = this.selectedDate();
180
99
  if (selected) {
181
100
  this.currentMonth.set(selected.getMonth());
@@ -186,7 +105,13 @@ class DateTimeFilter {
186
105
  this.isOpen.set(false);
187
106
  this.removeDocumentListener();
188
107
  this.markAsTouched();
108
+ // Don't clear activeFilterType here to keep the chip active if needed,
109
+ // or clear it if you want the chip to look inactive when closed.
110
+ // For now, let's keep it active only if a date is selected?
111
+ // Or maybe we want to close the dropdown but keep the filter "selected" in UI?
112
+ // Let's just close the dropdown.
189
113
  }
114
+ // Calendar Logic
190
115
  selectDay(day) {
191
116
  if (!day || this.isDisabled())
192
117
  return;
@@ -197,53 +122,116 @@ class DateTimeFilter {
197
122
  this.onChange(newDate.toISOString());
198
123
  this.markAsTouched();
199
124
  this.dateChange.emit(newDate);
200
- // Notificar selección de fecha al padre
201
125
  this.dateSelected.emit({
202
- filter: this.filter(),
126
+ filter: this.filterValue(),
203
127
  value: newDate,
204
128
  });
205
- // Solo cerrar automáticamente si es modo 'date'
206
129
  if (this.mode() === 'date') {
207
130
  this.close();
208
131
  }
209
132
  }
210
- setHour12(hour12) {
211
- if (this.isDisabled())
212
- return;
213
- // Convertir de 12h a 24h
214
- let hour24;
215
- const amPm = this.selectedAmPm();
216
- if (hour12 === 12) {
217
- hour24 = amPm === 'AM' ? 0 : 12;
218
- }
219
- else {
220
- hour24 = amPm === 'AM' ? hour12 : hour12 + 12;
133
+ // ... (Rest of the helper methods like previousMonth, nextMonth, etc. remain largely the same)
134
+ displayValue = computed(() => {
135
+ const date = this.selectedDate();
136
+ if (!date)
137
+ return '';
138
+ const options = {
139
+ day: '2-digit',
140
+ month: '2-digit',
141
+ year: 'numeric',
142
+ };
143
+ if (this.mode() === 'datetime') {
144
+ options.hour = '2-digit';
145
+ options.minute = '2-digit';
146
+ options.hour12 = true;
221
147
  }
222
- this.selectedHour.set(hour24);
223
- this.updateDateTime();
148
+ return date.toLocaleDateString('es-ES', options);
149
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
150
+ monthName = computed(() => {
151
+ const months = [
152
+ 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
153
+ 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'
154
+ ];
155
+ return months[this.currentMonth()];
156
+ }, ...(ngDevMode ? [{ debugName: "monthName" }] : []));
157
+ calendarDays = computed(() => {
158
+ const year = this.currentYear();
159
+ const month = this.currentMonth();
160
+ const firstDay = new Date(year, month, 1);
161
+ const lastDay = new Date(year, month + 1, 0);
162
+ const daysInMonth = lastDay.getDate();
163
+ const startingDayOfWeek = firstDay.getDay();
164
+ const days = [];
165
+ for (let i = 0; i < startingDayOfWeek; i++)
166
+ days.push(null);
167
+ for (let day = 1; day <= daysInMonth; day++)
168
+ days.push(day);
169
+ return days;
170
+ }, ...(ngDevMode ? [{ debugName: "calendarDays" }] : []));
171
+ hours12 = computed(() => {
172
+ return Array.from({ length: 12 }, (_, i) => {
173
+ const hour12 = i === 0 ? 12 : i;
174
+ return {
175
+ value: hour12,
176
+ display: hour12.toString().padStart(2, '0')
177
+ };
178
+ });
179
+ }, ...(ngDevMode ? [{ debugName: "hours12" }] : []));
180
+ selectedHour12 = computed(() => {
181
+ const hour24 = this.selectedHour();
182
+ if (hour24 === 0)
183
+ return 12;
184
+ if (hour24 > 12)
185
+ return hour24 - 12;
186
+ return hour24;
187
+ }, ...(ngDevMode ? [{ debugName: "selectedHour12" }] : []));
188
+ minutes = computed(() => Array.from({ length: 60 }, (_, i) => i), ...(ngDevMode ? [{ debugName: "minutes" }] : []));
189
+ // Helper methods
190
+ updateInternalState(date) {
191
+ this.selectedDate.set(date);
192
+ this.currentMonth.set(date.getMonth());
193
+ this.currentYear.set(date.getFullYear());
194
+ this.selectedHour.set(date.getHours());
195
+ this.selectedMinute.set(date.getMinutes());
196
+ this.selectedAmPm.set(date.getHours() >= 12 ? 'PM' : 'AM');
224
197
  }
225
- setMinute(minute) {
226
- if (this.isDisabled())
227
- return;
228
- this.selectedMinute.set(minute);
229
- this.updateDateTime();
198
+ resetInternalState() {
199
+ this.selectedDate.set(null);
200
+ this.selectedHour.set(0);
201
+ this.selectedMinute.set(0);
202
+ this.selectedAmPm.set('AM');
230
203
  }
231
- setAmPm(amPm) {
232
- if (this.isDisabled())
233
- return;
234
- const currentHour24 = this.selectedHour();
235
- const currentHour12 = this.selectedHour12();
236
- let newHour24;
237
- if (amPm === 'AM') {
238
- newHour24 = currentHour12 === 12 ? 0 : currentHour12;
204
+ markAsTouched() {
205
+ if (!this.isTouched()) {
206
+ this.isTouched.set(true);
207
+ this.onTouched();
239
208
  }
240
- else {
241
- newHour24 = currentHour12 === 12 ? 12 : currentHour12 + 12;
209
+ }
210
+ addDocumentListener() {
211
+ if (this.documentClickListener)
212
+ this.removeDocumentListener();
213
+ this.ngZone.runOutsideAngular(() => {
214
+ this.documentClickListener = (event) => {
215
+ const target = event.target;
216
+ if (!this.elementRef.nativeElement.contains(target)) {
217
+ this.ngZone.run(() => {
218
+ if (this.isOpen()) {
219
+ this.markAsTouched();
220
+ this.close();
221
+ }
222
+ });
223
+ }
224
+ };
225
+ setTimeout(() => document.addEventListener('click', this.documentClickListener, true), 10);
226
+ });
227
+ }
228
+ removeDocumentListener() {
229
+ if (this.documentClickListener) {
230
+ document.removeEventListener('click', this.documentClickListener, true);
231
+ this.documentClickListener = undefined;
242
232
  }
243
- this.selectedAmPm.set(amPm);
244
- this.selectedHour.set(newHour24);
245
- this.updateDateTime();
246
233
  }
234
+ // Navigation methods
247
235
  previousMonth() {
248
236
  const currentMonth = this.currentMonth();
249
237
  const currentYear = this.currentYear();
@@ -266,73 +254,78 @@ class DateTimeFilter {
266
254
  this.currentMonth.set(currentMonth + 1);
267
255
  }
268
256
  }
269
- previousYear() {
270
- this.currentYear.set(this.currentYear() - 1);
271
- }
272
- nextYear() {
273
- this.currentYear.set(this.currentYear() + 1);
274
- }
257
+ previousYear() { this.currentYear.set(this.currentYear() - 1); }
258
+ nextYear() { this.currentYear.set(this.currentYear() + 1); }
275
259
  today() {
276
260
  const today = new Date();
277
- this.currentMonth.set(today.getMonth());
278
- this.currentYear.set(today.getFullYear());
279
- this.selectedDate.set(today);
280
- this.selectedHour.set(today.getHours());
281
- this.selectedMinute.set(today.getMinutes());
282
- this.selectedAmPm.set(today.getHours() >= 12 ? 'PM' : 'AM');
261
+ this.updateInternalState(today);
283
262
  this.onChange(today.toISOString());
284
263
  this.markAsTouched();
285
264
  this.dateChange.emit(today);
286
- // Notificar selección de fecha al padre
287
- this.dateSelected.emit({
288
- filter: this.filter(),
289
- value: today,
290
- });
291
- if (this.mode() === 'date') {
265
+ this.dateSelected.emit({ filter: this.filterValue(), value: today });
266
+ if (this.mode() === 'date')
292
267
  this.close();
293
- }
294
268
  }
295
269
  clear() {
296
- this.selectedDate.set(null);
270
+ this.resetInternalState();
297
271
  this.onChange(null);
298
272
  this.markAsTouched();
299
273
  this.dateChange.emit(null);
300
274
  this.close();
301
275
  }
302
- onKeyDown(event) {
276
+ // Time setters
277
+ setHour12(hour12) {
303
278
  if (this.isDisabled())
304
279
  return;
305
- if (event.key === 'Escape') {
306
- this.close();
307
- }
308
- else if (event.key === 'Tab') {
309
- this.markAsTouched();
310
- this.close();
311
- }
280
+ const amPm = this.selectedAmPm();
281
+ const hour24 = hour12 === 12 ? (amPm === 'AM' ? 0 : 12) : (amPm === 'AM' ? hour12 : hour12 + 12);
282
+ this.selectedHour.set(hour24);
283
+ this.updateDateTime();
284
+ }
285
+ setMinute(minute) {
286
+ if (this.isDisabled())
287
+ return;
288
+ this.selectedMinute.set(minute);
289
+ this.updateDateTime();
290
+ }
291
+ setAmPm(amPm) {
292
+ if (this.isDisabled())
293
+ return;
294
+ const currentHour12 = this.selectedHour12();
295
+ const newHour24 = amPm === 'AM' ? (currentHour12 === 12 ? 0 : currentHour12) : (currentHour12 === 12 ? 12 : currentHour12 + 12);
296
+ this.selectedAmPm.set(amPm);
297
+ this.selectedHour.set(newHour24);
298
+ this.updateDateTime();
299
+ }
300
+ updateDateTime() {
301
+ const selected = this.selectedDate();
302
+ if (!selected)
303
+ return;
304
+ const newDate = new Date(selected);
305
+ newDate.setHours(this.selectedHour());
306
+ newDate.setMinutes(this.selectedMinute());
307
+ this.selectedDate.set(newDate);
308
+ this.onChange(newDate.toISOString());
309
+ this.markAsTouched();
310
+ this.dateChange.emit(newDate);
312
311
  }
312
+ // Validation helpers
313
313
  isDaySelected(day) {
314
314
  if (!day)
315
315
  return false;
316
316
  const selected = this.selectedDate();
317
- if (!selected)
318
- return false;
319
- return (selected.getDate() === day &&
320
- selected.getMonth() === this.currentMonth() &&
321
- selected.getFullYear() === this.currentYear());
317
+ return !!selected && selected.getDate() === day && selected.getMonth() === this.currentMonth() && selected.getFullYear() === this.currentYear();
322
318
  }
323
319
  isToday(day) {
324
320
  if (!day)
325
321
  return false;
326
322
  const today = new Date();
327
- return (today.getDate() === day &&
328
- today.getMonth() === this.currentMonth() &&
329
- today.getFullYear() === this.currentYear());
323
+ return today.getDate() === day && today.getMonth() === this.currentMonth() && today.getFullYear() === this.currentYear();
330
324
  }
331
325
  isDayDisabled(day) {
332
326
  if (!day)
333
327
  return true;
334
- const date = new Date(this.currentYear(), this.currentMonth(), day);
335
- return this.isDateDisabled(date);
328
+ return this.isDateDisabled(new Date(this.currentYear(), this.currentMonth(), day));
336
329
  }
337
330
  isDateDisabled(date) {
338
331
  const minDate = this.minDate();
@@ -343,97 +336,25 @@ class DateTimeFilter {
343
336
  return true;
344
337
  return false;
345
338
  }
346
- updateDateTime() {
347
- const selected = this.selectedDate();
348
- if (!selected)
349
- return;
350
- const newDate = new Date(selected);
351
- newDate.setHours(this.selectedHour());
352
- newDate.setMinutes(this.selectedMinute());
353
- this.selectedDate.set(newDate);
354
- this.onChange(newDate.toISOString());
355
- this.markAsTouched();
356
- this.dateChange.emit(newDate);
357
- }
358
- markAsTouched() {
359
- if (!this.isTouched()) {
360
- this.isTouched.set(true);
361
- this.onTouched();
362
- }
363
- }
364
- addDocumentListener() {
365
- if (this.documentClickListener) {
366
- this.removeDocumentListener();
367
- }
368
- this.ngZone.runOutsideAngular(() => {
369
- this.documentClickListener = (event) => {
370
- const target = event.target;
371
- if (!this.elementRef.nativeElement.contains(target)) {
372
- this.ngZone.run(() => {
373
- if (this.isOpen()) {
374
- this.markAsTouched();
375
- this.close();
376
- }
377
- });
378
- }
379
- };
380
- setTimeout(() => {
381
- document.addEventListener('click', this.documentClickListener, true);
382
- }, 10);
383
- });
384
- }
385
- removeDocumentListener() {
386
- if (this.documentClickListener) {
387
- document.removeEventListener('click', this.documentClickListener, true);
388
- this.documentClickListener = undefined;
389
- }
390
- }
391
- markTouched() {
392
- this.markAsTouched();
393
- }
394
- /**
395
- * Obtiene la fecha seleccionada en formato ISO string
396
- * @returns string ISO o null si no hay fecha seleccionada
397
- */
398
- getISOString() {
399
- const date = this.selectedDate();
400
- return date ? date.toISOString() : null;
401
- }
402
- /**
403
- * Establece la fecha desde un string ISO
404
- * @param isoString - Fecha en formato ISO
405
- */
406
- setFromISOString(isoString) {
407
- if (isoString) {
408
- const date = new Date(isoString);
409
- if (!isNaN(date.getTime())) {
410
- this.writeValue(date);
411
- this.onChange(isoString);
412
- }
413
- }
414
- else {
415
- this.clear();
416
- }
417
- }
418
339
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: DateTimeFilter, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
419
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: DateTimeFilter, isStandalone: true, selector: "app-date-time-filter", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { dateSelected: "dateSelected", dateChange: "dateChange" }, providers: [
340
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: DateTimeFilter, isStandalone: true, selector: "app-date-time-filter", inputs: { filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dateSelected: "dateSelected", dateChange: "dateChange" }, providers: [
420
341
  {
421
342
  provide: NG_VALUE_ACCESSOR,
422
343
  useExisting: forwardRef(() => DateTimeFilter),
423
344
  multi: true,
424
345
  },
425
- ], ngImport: i0, template: "<div class=\"datetime-container\">\r\n \r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"datetime-content\">\r\n <!-- Secci\u00F3n del calendario -->\r\n <div class=\"calendar-section\">\r\n <!-- Navegaci\u00F3n -->\r\n <div class=\"calendar-nav\">\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousYear()\"\r\n title=\"A\u00F1o anterior\"\r\n >\r\n \u2039\u2039\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousMonth()\"\r\n title=\"Mes anterior\"\r\n >\r\n \u2039\r\n </button>\r\n </div>\r\n\r\n <div class=\"current-date\">{{ monthName() }} {{ currentYear() }}</div>\r\n\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextMonth()\"\r\n title=\"Siguiente mes\"\r\n >\r\n \u203A\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextYear()\"\r\n title=\"Siguiente a\u00F1o\"\r\n >\r\n \u203A\u203A\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- D\u00EDas de la semana -->\r\n <div class=\"weekdays\">\r\n <div class=\"weekday\">Dom</div>\r\n <div class=\"weekday\">Lun</div>\r\n <div class=\"weekday\">Mar</div>\r\n <div class=\"weekday\">Mi\u00E9</div>\r\n <div class=\"weekday\">Jue</div>\r\n <div class=\"weekday\">Vie</div>\r\n <div class=\"weekday\">S\u00E1b</div>\r\n </div>\r\n\r\n <!-- D\u00EDas -->\r\n <div class=\"calendar-grid\">\r\n @for (day of calendarDays(); track $index) {\r\n <div\r\n class=\"calendar-day\"\r\n [class.selected]=\"isDaySelected(day)\"\r\n [class.today]=\"isToday(day)\"\r\n [class.disabled]=\"isDayDisabled(day)\"\r\n [class.empty]=\"!day\"\r\n (click)=\"selectDay(day)\"\r\n >\r\n {{ day || \"\" }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- TIME PICKER -->\r\n @if (mode() === 'datetime') {\r\n <div class=\"time-section\">\r\n <div class=\"time-header\">Horario</div>\r\n\r\n <div class=\"time-selectors\">\r\n <div class=\"time-group\">\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Hora</div>\r\n <div class=\"time-scroll\">\r\n @for (hour of hours12(); track hour.value) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"hour.value === selectedHour12()\"\r\n (click)=\"setHour12(hour.value)\"\r\n >\r\n {{ hour.display }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-separator-vertical\">:</div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Min</div>\r\n <div class=\"time-scroll\">\r\n @for (minute of minutes(); track minute) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"minute === selectedMinute()\"\r\n (click)=\"setMinute(minute)\"\r\n >\r\n {{ minute.toString().padStart(2, \"0\") }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">AM/PM</div>\r\n <div class=\"time-scroll ampm\">\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'AM'\"\r\n (click)=\"setAmPm('AM')\"\r\n >\r\n AM\r\n </div>\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'PM'\"\r\n (click)=\"setAmPm('PM')\"\r\n >\r\n PM\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- botones -->\r\n <div class=\"action-buttons\">\r\n <button type=\"button\" class=\"action-btn secondary\" (click)=\"today()\">\r\n Hoy\r\n </button>\r\n\r\n @if (mode() === 'datetime') {\r\n <button type=\"button\" class=\"action-btn primary\" (click)=\"close()\">\r\n Aceptar\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".datetime-container{position:relative;width:100%}.datetime-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto;transition:border-color .2s}.datetime-header:hover,.datetime-header:focus{outline:none;border-color:#a9a97f}.datetime-header.active{border-color:#a9a97f;outline:none}.datetime-header.disabled{cursor:not-allowed;opacity:.6}.selected-text{color:#454733;font-size:1.3rem;flex:1}.selected-text.placeholder{color:#787861}.header-icons{display:flex;align-items:center;gap:8px}.clear-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#787861;font-size:1.5rem;cursor:pointer;border-radius:50%;transition:all .2s}.clear-icon:hover{background-color:#7878611a;color:#454733}.arrow{width:15px;height:15px;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:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{transform:rotate(180deg)}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:400px}.datetime-content{display:flex}.calendar-section{flex:1;min-width:280px}.calendar-nav{display:flex;justify-content:space-between;align-items:center;padding:15px;border-bottom:1px solid rgba(120,120,97,.2)}.nav-section{display:flex;gap:5px}.nav-btn{background:none;border:none;color:#787861;cursor:pointer;font-size:1.2rem;padding:5px 10px;border-radius:3px;transition:all .2s}.nav-btn:hover{background-color:#a9a97f1a;color:#454733}.current-date{font-weight:500;color:#454733;font-size:1.1rem}.weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#7878611a}.weekday{padding:10px 5px;text-align:center;font-size:.9rem;font-weight:500;color:#787861}.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr)}.calendar-day{padding:12px 5px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;border-right:1px solid rgba(120,120,97,.1);border-bottom:1px solid rgba(120,120,97,.1);transition:all .2s}.calendar-day:hover:not(.disabled):not(.empty){background-color:#a9a97f33}.calendar-day.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.calendar-day.today:not(.selected){background-color:#a9a97f4d;font-weight:500}.calendar-day.disabled{color:#78786166;cursor:not-allowed}.calendar-day.empty{cursor:default}.calendar-day:nth-child(7n){border-right:none}.time-section{border-left:1px solid rgba(120,120,97,.2);min-width:140px;display:flex;flex-direction:column;background:#a9a97f0d}.time-header{padding:15px;border-bottom:1px solid rgba(120,120,97,.2);text-align:center;font-weight:500;color:#454733;background:#a9a97f1a}.time-selectors{display:flex;flex-direction:column;padding:15px;gap:20px;flex:1}.time-group{display:flex;align-items:center;gap:10px}.time-column{flex:1;display:flex;flex-direction:column;align-items:center}.time-label{font-size:.9rem;color:#787861;margin-bottom:10px;font-weight:500}.time-scroll{max-height:150px;overflow-y:auto;border:1px solid rgba(120,120,97,.2);border-radius:3px;width:50px;background:#a9a97f}.time-option{padding:8px 12px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;transition:all .2s}.time-option:hover{background-color:#a9a97f1a}.time-option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.time-separator-vertical{font-size:1.5rem;color:#787861;font-weight:700;align-self:flex-end;margin-bottom:10px}.time-scroll::-webkit-scrollbar{width:4px}.time-scroll::-webkit-scrollbar-track{background:#7878611a}.time-scroll::-webkit-scrollbar-thumb{background:#787861;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#a9a97f}.action-buttons{display:flex;justify-content:space-between;padding:15px;border-top:1px solid rgba(120,120,97,.2);gap:10px}.action-btn{padding:10px 20px;border:none;border-radius:3px;cursor:pointer;font-size:1rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}@media (max-width: 768px){.dropdown{min-width:320px}.datetime-content{flex-direction:column}.time-section{border-left:none;border-top:1px solid rgba(120,120,97,.2)}.time-selectors{flex-direction:row;justify-content:center;padding:15px}.time-group{gap:15px}.datetime-header{padding:12px 15px}.calendar-nav{padding:12px}.calendar-day{padding:10px 3px;font-size:.9rem}.action-buttons{padding:12px}}@media (max-width: 480px){.dropdown{min-width:280px}.time-section{min-width:auto}.time-scroll{width:45px}.time-selectors{padding:10px}}\n"] });
346
+ ], ngImport: i0, template: "<div class=\"datetime-container\">\r\n <!-- \uD83D\uDD25 NUEVA SECCI\u00D3N: CHIPS -->\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n\r\n <div\r\n class=\"chip\"\r\n [class.active]=\"activeFilterType() === item.value\"\r\n (click)=\"toggle(item.value)\"\r\n >\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">{{ displayValue() || item.placeholder || item.label }}</div>\r\n\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n class=\"icon icon-tabler icons-tabler-outline icon-tabler-calendar-event\"\r\n >\r\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\r\n <path\r\n d=\"M4 5m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z\"\r\n />\r\n <path d=\"M16 3l0 4\" />\r\n <path d=\"M8 3l0 4\" />\r\n <path d=\"M4 11l16 0\" />\r\n <path d=\"M8 15h2v2h-2z\" />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n }\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"datetime-content\">\r\n <!-- Secci\u00F3n del calendario -->\r\n <div class=\"calendar-section\">\r\n <!-- Navegaci\u00F3n -->\r\n <div class=\"calendar-nav\">\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousYear()\"\r\n title=\"A\u00F1o anterior\"\r\n >\r\n \u2039\u2039\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousMonth()\"\r\n title=\"Mes anterior\"\r\n >\r\n \u2039\r\n </button>\r\n </div>\r\n\r\n <div class=\"current-date\">{{ monthName() }} {{ currentYear() }}</div>\r\n\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextMonth()\"\r\n title=\"Siguiente mes\"\r\n >\r\n \u203A\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextYear()\"\r\n title=\"Siguiente a\u00F1o\"\r\n >\r\n \u203A\u203A\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- D\u00EDas de la semana -->\r\n <div class=\"weekdays\">\r\n <div class=\"weekday\">Dom</div>\r\n <div class=\"weekday\">Lun</div>\r\n <div class=\"weekday\">Mar</div>\r\n <div class=\"weekday\">Mi\u00E9</div>\r\n <div class=\"weekday\">Jue</div>\r\n <div class=\"weekday\">Vie</div>\r\n <div class=\"weekday\">S\u00E1b</div>\r\n </div>\r\n\r\n <!-- D\u00EDas -->\r\n <div class=\"calendar-grid\">\r\n @for (day of calendarDays(); track $index) {\r\n <div\r\n class=\"calendar-day\"\r\n [class.selected]=\"isDaySelected(day)\"\r\n [class.today]=\"isToday(day)\"\r\n [class.disabled]=\"isDayDisabled(day)\"\r\n [class.empty]=\"!day\"\r\n (click)=\"selectDay(day)\"\r\n >\r\n {{ day || \"\" }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- TIME PICKER -->\r\n @if (mode() === 'datetime') {\r\n <div class=\"time-section\">\r\n <div class=\"time-header\">Horario</div>\r\n\r\n <div class=\"time-selectors\">\r\n <div class=\"time-group\">\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Hora</div>\r\n <div class=\"time-scroll\">\r\n @for (hour of hours12(); track hour.value) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"hour.value === selectedHour12()\"\r\n (click)=\"setHour12(hour.value)\"\r\n >\r\n {{ hour.display }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-separator-vertical\">:</div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Min</div>\r\n <div class=\"time-scroll\">\r\n @for (minute of minutes(); track minute) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"minute === selectedMinute()\"\r\n (click)=\"setMinute(minute)\"\r\n >\r\n {{ minute.toString().padStart(2, \"0\") }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">AM/PM</div>\r\n <div class=\"time-scroll ampm\">\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'AM'\"\r\n (click)=\"setAmPm('AM')\"\r\n >\r\n AM\r\n </div>\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'PM'\"\r\n (click)=\"setAmPm('PM')\"\r\n >\r\n PM\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- botones -->\r\n <div class=\"action-buttons\">\r\n <button type=\"button\" class=\"action-btn secondary\" (click)=\"today()\">\r\n Hoy\r\n </button>\r\n\r\n @if (mode() === 'datetime') {\r\n <button type=\"button\" class=\"action-btn primary\" (click)=\"close()\">\r\n Aceptar\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".datetime-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon.icon-tabler-calendar-event{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.date-picker-container{margin-top:12px;width:100%;display:flex;justify-content:flex-start}.datetime-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto;transition:border-color .2s}.datetime-header:hover,.datetime-header:focus{outline:none;border-color:#a9a97f}.datetime-header.active{border-color:#a9a97f;outline:none}.datetime-header.disabled{cursor:not-allowed;opacity:.6}.selected-text{color:#454733;font-size:1.3rem;flex:1}.selected-text.placeholder{color:#787861}.header-icons{display:flex;align-items:center;gap:8px}.clear-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#787861;font-size:1.5rem;cursor:pointer;border-radius:50%;transition:all .2s}.clear-icon:hover{background-color:#7878611a;color:#454733}.arrow{width:15px;height:15px;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:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{transform:rotate(180deg)}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:400px}.datetime-content{display:flex}.calendar-section{flex:1;min-width:280px}.calendar-nav{display:flex;justify-content:space-between;align-items:center;padding:15px;border-bottom:1px solid rgba(120,120,97,.2)}.nav-section{display:flex;gap:5px}.nav-btn{background:none;border:none;color:#787861;cursor:pointer;font-size:1.2rem;padding:5px 10px;border-radius:3px;transition:all .2s}.nav-btn:hover{background-color:#a9a97f1a;color:#454733}.current-date{font-weight:500;color:#454733;font-size:1.1rem}.weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#7878611a}.weekday{padding:10px 5px;text-align:center;font-size:.9rem;font-weight:500;color:#787861}.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr)}.calendar-day{padding:12px 5px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;border-right:1px solid rgba(120,120,97,.1);border-bottom:1px solid rgba(120,120,97,.1);transition:all .2s}.calendar-day:hover:not(.disabled):not(.empty){background-color:#a9a97f33}.calendar-day.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.calendar-day.today:not(.selected){background-color:#a9a97f4d;font-weight:500}.calendar-day.disabled{color:#78786166;cursor:not-allowed}.calendar-day.empty{cursor:default}.calendar-day:nth-child(7n){border-right:none}.time-section{border-left:1px solid rgba(120,120,97,.2);min-width:140px;display:flex;flex-direction:column;background:#a9a97f0d}.time-header{padding:15px;border-bottom:1px solid rgba(120,120,97,.2);text-align:center;font-weight:500;color:#454733;background:#a9a97f1a}.time-selectors{display:flex;flex-direction:column;padding:15px;gap:20px;flex:1}.time-group{display:flex;align-items:center;gap:10px}.time-column{flex:1;display:flex;flex-direction:column;align-items:center}.time-label{font-size:.9rem;color:#787861;margin-bottom:10px;font-weight:500}.time-scroll{max-height:150px;overflow-y:auto;border:1px solid rgba(120,120,97,.2);border-radius:3px;width:50px;background:#a9a97f}.time-option{padding:8px 12px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;transition:all .2s}.time-option:hover{background-color:#a9a97f1a}.time-option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.time-separator-vertical{font-size:1.5rem;color:#787861;font-weight:700;align-self:flex-end;margin-bottom:10px}.time-scroll::-webkit-scrollbar{width:4px}.time-scroll::-webkit-scrollbar-track{background:#7878611a}.time-scroll::-webkit-scrollbar-thumb{background:#787861;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#a9a97f}.action-buttons{display:flex;justify-content:space-between;padding:15px;border-top:1px solid rgba(120,120,97,.2);gap:10px}.action-btn{padding:10px 20px;border:none;border-radius:3px;cursor:pointer;font-size:1rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}@media (max-width: 768px){.dropdown{min-width:320px}.datetime-content{flex-direction:column}.time-section{border-left:none;border-top:1px solid rgba(120,120,97,.2)}.time-selectors{flex-direction:row;justify-content:center;padding:15px}.time-group{gap:15px}.datetime-header{padding:12px 15px}.calendar-nav{padding:12px}.calendar-day{padding:10px 3px;font-size:.9rem}.action-buttons{padding:12px}}@media (max-width: 480px){.dropdown{min-width:280px}.time-section{min-width:auto}.time-scroll{width:45px}.time-selectors{padding:10px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }] });
426
347
  }
427
348
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: DateTimeFilter, decorators: [{
428
349
  type: Component,
429
- args: [{ selector: 'app-date-time-filter', standalone: true, imports: [], providers: [
350
+ args: [{ selector: 'app-date-time-filter', standalone: true, imports: [CommonModule, ReactiveFormsModule], providers: [
430
351
  {
431
352
  provide: NG_VALUE_ACCESSOR,
432
353
  useExisting: forwardRef(() => DateTimeFilter),
433
354
  multi: true,
434
355
  },
435
- ], template: "<div class=\"datetime-container\">\r\n \r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"datetime-content\">\r\n <!-- Secci\u00F3n del calendario -->\r\n <div class=\"calendar-section\">\r\n <!-- Navegaci\u00F3n -->\r\n <div class=\"calendar-nav\">\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousYear()\"\r\n title=\"A\u00F1o anterior\"\r\n >\r\n \u2039\u2039\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousMonth()\"\r\n title=\"Mes anterior\"\r\n >\r\n \u2039\r\n </button>\r\n </div>\r\n\r\n <div class=\"current-date\">{{ monthName() }} {{ currentYear() }}</div>\r\n\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextMonth()\"\r\n title=\"Siguiente mes\"\r\n >\r\n \u203A\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextYear()\"\r\n title=\"Siguiente a\u00F1o\"\r\n >\r\n \u203A\u203A\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- D\u00EDas de la semana -->\r\n <div class=\"weekdays\">\r\n <div class=\"weekday\">Dom</div>\r\n <div class=\"weekday\">Lun</div>\r\n <div class=\"weekday\">Mar</div>\r\n <div class=\"weekday\">Mi\u00E9</div>\r\n <div class=\"weekday\">Jue</div>\r\n <div class=\"weekday\">Vie</div>\r\n <div class=\"weekday\">S\u00E1b</div>\r\n </div>\r\n\r\n <!-- D\u00EDas -->\r\n <div class=\"calendar-grid\">\r\n @for (day of calendarDays(); track $index) {\r\n <div\r\n class=\"calendar-day\"\r\n [class.selected]=\"isDaySelected(day)\"\r\n [class.today]=\"isToday(day)\"\r\n [class.disabled]=\"isDayDisabled(day)\"\r\n [class.empty]=\"!day\"\r\n (click)=\"selectDay(day)\"\r\n >\r\n {{ day || \"\" }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- TIME PICKER -->\r\n @if (mode() === 'datetime') {\r\n <div class=\"time-section\">\r\n <div class=\"time-header\">Horario</div>\r\n\r\n <div class=\"time-selectors\">\r\n <div class=\"time-group\">\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Hora</div>\r\n <div class=\"time-scroll\">\r\n @for (hour of hours12(); track hour.value) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"hour.value === selectedHour12()\"\r\n (click)=\"setHour12(hour.value)\"\r\n >\r\n {{ hour.display }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-separator-vertical\">:</div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Min</div>\r\n <div class=\"time-scroll\">\r\n @for (minute of minutes(); track minute) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"minute === selectedMinute()\"\r\n (click)=\"setMinute(minute)\"\r\n >\r\n {{ minute.toString().padStart(2, \"0\") }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">AM/PM</div>\r\n <div class=\"time-scroll ampm\">\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'AM'\"\r\n (click)=\"setAmPm('AM')\"\r\n >\r\n AM\r\n </div>\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'PM'\"\r\n (click)=\"setAmPm('PM')\"\r\n >\r\n PM\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- botones -->\r\n <div class=\"action-buttons\">\r\n <button type=\"button\" class=\"action-btn secondary\" (click)=\"today()\">\r\n Hoy\r\n </button>\r\n\r\n @if (mode() === 'datetime') {\r\n <button type=\"button\" class=\"action-btn primary\" (click)=\"close()\">\r\n Aceptar\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".datetime-container{position:relative;width:100%}.datetime-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto;transition:border-color .2s}.datetime-header:hover,.datetime-header:focus{outline:none;border-color:#a9a97f}.datetime-header.active{border-color:#a9a97f;outline:none}.datetime-header.disabled{cursor:not-allowed;opacity:.6}.selected-text{color:#454733;font-size:1.3rem;flex:1}.selected-text.placeholder{color:#787861}.header-icons{display:flex;align-items:center;gap:8px}.clear-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#787861;font-size:1.5rem;cursor:pointer;border-radius:50%;transition:all .2s}.clear-icon:hover{background-color:#7878611a;color:#454733}.arrow{width:15px;height:15px;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:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{transform:rotate(180deg)}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:400px}.datetime-content{display:flex}.calendar-section{flex:1;min-width:280px}.calendar-nav{display:flex;justify-content:space-between;align-items:center;padding:15px;border-bottom:1px solid rgba(120,120,97,.2)}.nav-section{display:flex;gap:5px}.nav-btn{background:none;border:none;color:#787861;cursor:pointer;font-size:1.2rem;padding:5px 10px;border-radius:3px;transition:all .2s}.nav-btn:hover{background-color:#a9a97f1a;color:#454733}.current-date{font-weight:500;color:#454733;font-size:1.1rem}.weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#7878611a}.weekday{padding:10px 5px;text-align:center;font-size:.9rem;font-weight:500;color:#787861}.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr)}.calendar-day{padding:12px 5px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;border-right:1px solid rgba(120,120,97,.1);border-bottom:1px solid rgba(120,120,97,.1);transition:all .2s}.calendar-day:hover:not(.disabled):not(.empty){background-color:#a9a97f33}.calendar-day.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.calendar-day.today:not(.selected){background-color:#a9a97f4d;font-weight:500}.calendar-day.disabled{color:#78786166;cursor:not-allowed}.calendar-day.empty{cursor:default}.calendar-day:nth-child(7n){border-right:none}.time-section{border-left:1px solid rgba(120,120,97,.2);min-width:140px;display:flex;flex-direction:column;background:#a9a97f0d}.time-header{padding:15px;border-bottom:1px solid rgba(120,120,97,.2);text-align:center;font-weight:500;color:#454733;background:#a9a97f1a}.time-selectors{display:flex;flex-direction:column;padding:15px;gap:20px;flex:1}.time-group{display:flex;align-items:center;gap:10px}.time-column{flex:1;display:flex;flex-direction:column;align-items:center}.time-label{font-size:.9rem;color:#787861;margin-bottom:10px;font-weight:500}.time-scroll{max-height:150px;overflow-y:auto;border:1px solid rgba(120,120,97,.2);border-radius:3px;width:50px;background:#a9a97f}.time-option{padding:8px 12px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;transition:all .2s}.time-option:hover{background-color:#a9a97f1a}.time-option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.time-separator-vertical{font-size:1.5rem;color:#787861;font-weight:700;align-self:flex-end;margin-bottom:10px}.time-scroll::-webkit-scrollbar{width:4px}.time-scroll::-webkit-scrollbar-track{background:#7878611a}.time-scroll::-webkit-scrollbar-thumb{background:#787861;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#a9a97f}.action-buttons{display:flex;justify-content:space-between;padding:15px;border-top:1px solid rgba(120,120,97,.2);gap:10px}.action-btn{padding:10px 20px;border:none;border-radius:3px;cursor:pointer;font-size:1rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}@media (max-width: 768px){.dropdown{min-width:320px}.datetime-content{flex-direction:column}.time-section{border-left:none;border-top:1px solid rgba(120,120,97,.2)}.time-selectors{flex-direction:row;justify-content:center;padding:15px}.time-group{gap:15px}.datetime-header{padding:12px 15px}.calendar-nav{padding:12px}.calendar-day{padding:10px 3px;font-size:.9rem}.action-buttons{padding:12px}}@media (max-width: 480px){.dropdown{min-width:280px}.time-section{min-width:auto}.time-scroll{width:45px}.time-selectors{padding:10px}}\n"] }]
436
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: true }] }], dateSelected: [{ type: i0.Output, args: ["dateSelected"] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }] } });
356
+ ], template: "<div class=\"datetime-container\">\r\n <!-- \uD83D\uDD25 NUEVA SECCI\u00D3N: CHIPS -->\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n\r\n <div\r\n class=\"chip\"\r\n [class.active]=\"activeFilterType() === item.value\"\r\n (click)=\"toggle(item.value)\"\r\n >\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">{{ displayValue() || item.placeholder || item.label }}</div>\r\n\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n class=\"icon icon-tabler icons-tabler-outline icon-tabler-calendar-event\"\r\n >\r\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\r\n <path\r\n d=\"M4 5m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z\"\r\n />\r\n <path d=\"M16 3l0 4\" />\r\n <path d=\"M8 3l0 4\" />\r\n <path d=\"M4 11l16 0\" />\r\n <path d=\"M8 15h2v2h-2z\" />\r\n </svg>\r\n </div>\r\n </div>\r\n\r\n }\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"datetime-content\">\r\n <!-- Secci\u00F3n del calendario -->\r\n <div class=\"calendar-section\">\r\n <!-- Navegaci\u00F3n -->\r\n <div class=\"calendar-nav\">\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousYear()\"\r\n title=\"A\u00F1o anterior\"\r\n >\r\n \u2039\u2039\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"previousMonth()\"\r\n title=\"Mes anterior\"\r\n >\r\n \u2039\r\n </button>\r\n </div>\r\n\r\n <div class=\"current-date\">{{ monthName() }} {{ currentYear() }}</div>\r\n\r\n <div class=\"nav-section\">\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextMonth()\"\r\n title=\"Siguiente mes\"\r\n >\r\n \u203A\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"nav-btn\"\r\n (click)=\"nextYear()\"\r\n title=\"Siguiente a\u00F1o\"\r\n >\r\n \u203A\u203A\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- D\u00EDas de la semana -->\r\n <div class=\"weekdays\">\r\n <div class=\"weekday\">Dom</div>\r\n <div class=\"weekday\">Lun</div>\r\n <div class=\"weekday\">Mar</div>\r\n <div class=\"weekday\">Mi\u00E9</div>\r\n <div class=\"weekday\">Jue</div>\r\n <div class=\"weekday\">Vie</div>\r\n <div class=\"weekday\">S\u00E1b</div>\r\n </div>\r\n\r\n <!-- D\u00EDas -->\r\n <div class=\"calendar-grid\">\r\n @for (day of calendarDays(); track $index) {\r\n <div\r\n class=\"calendar-day\"\r\n [class.selected]=\"isDaySelected(day)\"\r\n [class.today]=\"isToday(day)\"\r\n [class.disabled]=\"isDayDisabled(day)\"\r\n [class.empty]=\"!day\"\r\n (click)=\"selectDay(day)\"\r\n >\r\n {{ day || \"\" }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- TIME PICKER -->\r\n @if (mode() === 'datetime') {\r\n <div class=\"time-section\">\r\n <div class=\"time-header\">Horario</div>\r\n\r\n <div class=\"time-selectors\">\r\n <div class=\"time-group\">\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Hora</div>\r\n <div class=\"time-scroll\">\r\n @for (hour of hours12(); track hour.value) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"hour.value === selectedHour12()\"\r\n (click)=\"setHour12(hour.value)\"\r\n >\r\n {{ hour.display }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-separator-vertical\">:</div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Min</div>\r\n <div class=\"time-scroll\">\r\n @for (minute of minutes(); track minute) {\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"minute === selectedMinute()\"\r\n (click)=\"setMinute(minute)\"\r\n >\r\n {{ minute.toString().padStart(2, \"0\") }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">AM/PM</div>\r\n <div class=\"time-scroll ampm\">\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'AM'\"\r\n (click)=\"setAmPm('AM')\"\r\n >\r\n AM\r\n </div>\r\n <div\r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'PM'\"\r\n (click)=\"setAmPm('PM')\"\r\n >\r\n PM\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- botones -->\r\n <div class=\"action-buttons\">\r\n <button type=\"button\" class=\"action-btn secondary\" (click)=\"today()\">\r\n Hoy\r\n </button>\r\n\r\n @if (mode() === 'datetime') {\r\n <button type=\"button\" class=\"action-btn primary\" (click)=\"close()\">\r\n Aceptar\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".datetime-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon.icon-tabler-calendar-event{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.date-picker-container{margin-top:12px;width:100%;display:flex;justify-content:flex-start}.datetime-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto;transition:border-color .2s}.datetime-header:hover,.datetime-header:focus{outline:none;border-color:#a9a97f}.datetime-header.active{border-color:#a9a97f;outline:none}.datetime-header.disabled{cursor:not-allowed;opacity:.6}.selected-text{color:#454733;font-size:1.3rem;flex:1}.selected-text.placeholder{color:#787861}.header-icons{display:flex;align-items:center;gap:8px}.clear-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#787861;font-size:1.5rem;cursor:pointer;border-radius:50%;transition:all .2s}.clear-icon:hover{background-color:#7878611a;color:#454733}.arrow{width:15px;height:15px;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:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{transform:rotate(180deg)}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:400px}.datetime-content{display:flex}.calendar-section{flex:1;min-width:280px}.calendar-nav{display:flex;justify-content:space-between;align-items:center;padding:15px;border-bottom:1px solid rgba(120,120,97,.2)}.nav-section{display:flex;gap:5px}.nav-btn{background:none;border:none;color:#787861;cursor:pointer;font-size:1.2rem;padding:5px 10px;border-radius:3px;transition:all .2s}.nav-btn:hover{background-color:#a9a97f1a;color:#454733}.current-date{font-weight:500;color:#454733;font-size:1.1rem}.weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#7878611a}.weekday{padding:10px 5px;text-align:center;font-size:.9rem;font-weight:500;color:#787861}.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr)}.calendar-day{padding:12px 5px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;border-right:1px solid rgba(120,120,97,.1);border-bottom:1px solid rgba(120,120,97,.1);transition:all .2s}.calendar-day:hover:not(.disabled):not(.empty){background-color:#a9a97f33}.calendar-day.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.calendar-day.today:not(.selected){background-color:#a9a97f4d;font-weight:500}.calendar-day.disabled{color:#78786166;cursor:not-allowed}.calendar-day.empty{cursor:default}.calendar-day:nth-child(7n){border-right:none}.time-section{border-left:1px solid rgba(120,120,97,.2);min-width:140px;display:flex;flex-direction:column;background:#a9a97f0d}.time-header{padding:15px;border-bottom:1px solid rgba(120,120,97,.2);text-align:center;font-weight:500;color:#454733;background:#a9a97f1a}.time-selectors{display:flex;flex-direction:column;padding:15px;gap:20px;flex:1}.time-group{display:flex;align-items:center;gap:10px}.time-column{flex:1;display:flex;flex-direction:column;align-items:center}.time-label{font-size:.9rem;color:#787861;margin-bottom:10px;font-weight:500}.time-scroll{max-height:150px;overflow-y:auto;border:1px solid rgba(120,120,97,.2);border-radius:3px;width:50px;background:#a9a97f}.time-option{padding:8px 12px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;transition:all .2s}.time-option:hover{background-color:#a9a97f1a}.time-option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.time-separator-vertical{font-size:1.5rem;color:#787861;font-weight:700;align-self:flex-end;margin-bottom:10px}.time-scroll::-webkit-scrollbar{width:4px}.time-scroll::-webkit-scrollbar-track{background:#7878611a}.time-scroll::-webkit-scrollbar-thumb{background:#787861;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#a9a97f}.action-buttons{display:flex;justify-content:space-between;padding:15px;border-top:1px solid rgba(120,120,97,.2);gap:10px}.action-btn{padding:10px 20px;border:none;border-radius:3px;cursor:pointer;font-size:1rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}@media (max-width: 768px){.dropdown{min-width:320px}.datetime-content{flex-direction:column}.time-section{border-left:none;border-top:1px solid rgba(120,120,97,.2)}.time-selectors{flex-direction:row;justify-content:center;padding:15px}.time-group{gap:15px}.datetime-header{padding:12px 15px}.calendar-nav{padding:12px}.calendar-day{padding:10px 3px;font-size:.9rem}.action-buttons{padding:12px}}@media (max-width: 480px){.dropdown{min-width:280px}.time-section{min-width:auto}.time-scroll{width:45px}.time-selectors{padding:10px}}\n"] }]
357
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], dateSelected: [{ type: i0.Output, args: ["dateSelected"] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }] } });
437
358
 
438
359
  class PaginationComponent {
439
360
  page = 1;
@@ -498,8 +419,11 @@ class Table {
498
419
  columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : []));
499
420
  data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
500
421
  actions = input([], ...(ngDevMode ? [{ debugName: "actions" }] : []));
422
+ showActions = input(true, ...(ngDevMode ? [{ debugName: "showActions" }] : []));
501
423
  openedMenu = signal(null, ...(ngDevMode ? [{ debugName: "openedMenu" }] : []));
502
424
  selectedRow = signal(null, ...(ngDevMode ? [{ debugName: "selectedRow" }] : []));
425
+ sortColumn = signal(null, ...(ngDevMode ? [{ debugName: "sortColumn" }] : []));
426
+ sortDirection = signal('desc', ...(ngDevMode ? [{ debugName: "sortDirection" }] : []));
503
427
  optionSelected = output();
504
428
  toggleMenu(index) {
505
429
  this.openedMenu.set(this.openedMenu() === index ? null : index);
@@ -523,17 +447,1325 @@ class Table {
523
447
  this.selectedRow.set(null);
524
448
  }
525
449
  }
450
+ getAlignment(col) {
451
+ if (col.align && col.align !== 'auto')
452
+ return col.align;
453
+ switch (col.type) {
454
+ case 'number':
455
+ case 'money':
456
+ case 'percentage':
457
+ return 'right';
458
+ case 'status':
459
+ return 'center';
460
+ default:
461
+ return 'left';
462
+ }
463
+ }
464
+ formatValue(col, value) {
465
+ if (value === null || value === undefined)
466
+ return '';
467
+ switch (col.type) {
468
+ case 'money':
469
+ return new Intl.NumberFormat('es-CO', {
470
+ style: 'currency',
471
+ currency: 'COP',
472
+ minimumFractionDigits: 0
473
+ }).format(value);
474
+ case 'number':
475
+ return new Intl.NumberFormat('es-CO').format(value);
476
+ case 'percentage':
477
+ return `${value}%`;
478
+ case 'date':
479
+ const d = new Date(value);
480
+ if (isNaN(d.getTime()))
481
+ return value;
482
+ const day = String(d.getDate()).padStart(2, '0');
483
+ const month = String(d.getMonth() + 1).padStart(2, '0');
484
+ const year = d.getFullYear();
485
+ return `${day}/${month}/${year}`;
486
+ default:
487
+ return String(value);
488
+ }
489
+ }
490
+ getStatusStyle(value) {
491
+ const map = {
492
+ aprobado: { bg: '#E6F4EA', color: '#1E8E3E' }, // verde
493
+ pendiente: { bg: '#FFF4E5', color: '#E67E22' }, // naranja
494
+ rechazado: { bg: '#FCE8E6', color: '#D93025' }, // rojo
495
+ en_proceso: { bg: '#E8F0FE', color: '#1967D2' }, // azul
496
+ };
497
+ const key = value?.toString().toLowerCase()?.replace(/\s+/g, '_');
498
+ return map[key] || { bg: '#F4F4F4', color: '#5F6368' }; // default gris
499
+ }
500
+ sortData() {
501
+ const col = this.sortColumn();
502
+ if (!col)
503
+ return this.data();
504
+ const dir = this.sortDirection();
505
+ return [...this.data()].sort((a, b) => {
506
+ const x = a[col] ?? '';
507
+ const y = b[col] ?? '';
508
+ // Caso: fechas
509
+ if (this.isDate(x) && this.isDate(y)) {
510
+ return dir === 'asc'
511
+ ? new Date(x).getTime() - new Date(y).getTime()
512
+ : new Date(y).getTime() - new Date(x).getTime();
513
+ }
514
+ // Caso: números
515
+ if (!isNaN(Number(x)) && !isNaN(Number(y))) {
516
+ return dir === 'asc' ? Number(x) - Number(y) : Number(y) - Number(x);
517
+ }
518
+ // Caso: strings
519
+ return dir === 'asc'
520
+ ? x.toString().localeCompare(y.toString())
521
+ : y.toString().localeCompare(x.toString());
522
+ });
523
+ }
524
+ isDate(value) {
525
+ return (typeof value === 'string' &&
526
+ value.length > 5 &&
527
+ !isNaN(Date.parse(value)));
528
+ }
529
+ onSort(col) {
530
+ if (this.sortColumn() === col.key) {
531
+ // cambiar asc <-> desc
532
+ this.sortDirection.set(this.sortDirection() === 'asc' ? 'desc' : 'asc');
533
+ }
534
+ else {
535
+ // nueva columna → empieza en desc (más reciente primero)
536
+ this.sortColumn.set(col.key);
537
+ this.sortDirection.set('desc');
538
+ }
539
+ }
526
540
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: Table, deps: [], target: i0.ɵɵFactoryTarget.Component });
527
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: Table, isStandalone: true, selector: "lib-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, ngImport: i0, template: "<div class=\"table-wrapper\">\r\n <table class=\"inner-table\">\r\n <thead>\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <th>{{ col.label }}</th>\r\n }\r\n\r\n <!-- columna para acciones -->\r\n <th></th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n @for (row of data(); track $index) {\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <td>{{ row[col.key] }}</td>\r\n }\r\n\r\n <!-- Celda de acciones -->\r\n <td class=\"acciones-cell\">\r\n <div class=\"menu-acciones\">\r\n <button class=\"icon-button\" (click)=\"openModal(row)\">\r\n <div class=\"content\">\r\n <div class=\"state-layer\">\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"more-vert\"\r\n width=\"20\"\r\n height=\"20\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\r\n </svg>\r\n </div>\r\n </div>\r\n </button>\r\n @if(selectedRow()?.['id'] === row['id']){\r\n <div class=\"modal-options\" (click)=\"$event.stopPropagation()\">\r\n \r\n <div class=\"modal-content\">\r\n <ul>\r\n @for (option of actions(); track $index) {\r\n <li\r\n class=\"option-item\"\r\n [style.color]=\"option.color\"\r\n (click)=\"onOptionClick(option, row)\"\r\n >\r\n <span class=\"icon\" [class]=\"option.icon\"></span>\r\n <span class=\"label\">{{ option.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n \r\n </div>\r\n }\r\n </div>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n</div>", styles: [".table-wrapper{width:100%;border-radius:20px;border:1px solid #c7c7ad}.inner-table{width:100%;border-collapse:separate;font-size:14px;border-spacing:0;border-radius:20px;background:#ebe8d6}.inner-table thead{background:#f0f0db}.inner-table thead tr th:first-child{border-top-left-radius:20px}.inner-table thead tr th:last-child{border-top-right-radius:20px}.inner-table th{text-align:left;padding:12px 16px;font-weight:600;color:#3a3a3a}.inner-table td{padding:12px 16px;color:#4a4a4a;border-top:1px solid #c7c7ad}.acciones-cell{text-align:center;padding:6px;position:relative}.menu-acciones{display:flex;align-items:center;justify-content:center;position:relative}.icon-button{background-color:var(--secondary-container, #dee58f);width:36px;height:36px;border:none;cursor:pointer;padding:0;border-radius:100%;display:flex;align-items:center;justify-content:center}.icon-button:hover{background:#0000000f}.more-vert{color:#4a4a4a;display:block}.modal-options{display:flex;justify-items:center;align-items:center;background-color:var(--surface-container-lowest, #ffffff);width:200px;border-radius:8px;position:absolute;top:100%;right:0;z-index:99}.modal-content{width:100%;border-radius:8px;border:1px solid var(--outline-variant, #c7c7ad);overflow:hidden}.option-item{display:flex;align-items:center;height:56px;padding:8px 12px;cursor:pointer}.option-item:hover{background-color:#0000001a}.option-item:active{background-color:#dee58f}.icon{margin-right:8px}.option-item .label{font-weight:500;font-size:16px}\n"] });
541
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: Table, isStandalone: true, selector: "lib-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionSelected: "optionSelected" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, ngImport: i0, template: "<div class=\"table-wrapper\">\r\n <table class=\"inner-table\">\r\n <thead>\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <th\r\n (click)=\"col.sortable !== false && onSort(col)\"\r\n [class.sortable]=\"col.sortable !== false\"\r\n [class]=\"getAlignment(col)\"\r\n >\r\n {{ col.label }}\r\n\r\n <!-- iconos de orden opcionales -->\r\n @if (sortColumn() === col.key) {\r\n <span class=\"sort-icon\">\r\n {{ sortDirection() === 'asc' ? '\u25B2' : '\u25BC' }}\r\n </span>\r\n }\r\n </th>\r\n }\r\n\r\n <!-- columna para acciones -->\r\n @if (showActions()) {\r\n <th></th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n @for (row of sortData(); track $index) {\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <td [class]=\"getAlignment(col)\">\r\n @if (col.type === 'status') { @let style =\r\n getStatusStyle(row[col.key]);\r\n\r\n <span\r\n class=\"status-chip\"\r\n [style.background-color]=\"style.bg\"\r\n [style.color]=\"style.color\"\r\n >\r\n <span\r\n class=\"status-dot\"\r\n [style.background-color]=\"style.color\"\r\n ></span>\r\n\r\n {{ row[col.key]?.toString().toUpperCase() }}\r\n </span>\r\n } @else { {{ formatValue(col, row[col.key]) }} }\r\n </td>\r\n }\r\n\r\n <!-- Celda de acciones -->\r\n @if (showActions()) {\r\n <td class=\"acciones-cell\">\r\n <div class=\"menu-acciones\">\r\n <button class=\"icon-button\" (click)=\"openModal(row)\">\r\n <div class=\"content\">\r\n <div class=\"state-layer\">\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"more-vert\"\r\n width=\"20\"\r\n height=\"20\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\r\n </svg>\r\n </div>\r\n </div>\r\n </button>\r\n @if(selectedRow()?.['id'] === row['id']){\r\n <div class=\"modal-options\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"modal-content\">\r\n <ul>\r\n @for (option of actions(); track $index) {\r\n <li\r\n class=\"option-item\"\r\n [style.color]=\"option.color\"\r\n (click)=\"onOptionClick(option, row)\"\r\n >\r\n <span class=\"icon\" [class]=\"option.icon\"></span>\r\n <span class=\"label\">{{ option.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n</div>\r\n", styles: [".table-wrapper{width:100%;border-radius:20px;border:1px solid #c7c7ad}.inner-table{width:100%;border-collapse:separate;font-size:14px;border-spacing:0;border-radius:20px;background:#ebe8d6}.inner-table thead{background:#f0f0db}.inner-table thead tr th:first-child{border-top-left-radius:20px}.inner-table thead tr th:last-child{border-top-right-radius:20px}.inner-table th{text-align:left;padding:12px 16px;font-weight:600;color:#3a3a3a}.inner-table td{padding:12px 16px;color:#4a4a4a;border-top:1px solid #c7c7ad}.acciones-cell{text-align:center;padding:6px;position:relative}.menu-acciones{display:flex;align-items:center;justify-content:center;position:relative}.icon-button{background-color:var(--secondary-container, #dee58f);width:36px;height:36px;border:none;cursor:pointer;padding:0;border-radius:100%;display:flex;align-items:center;justify-content:center}.icon-button:hover{background:#0000000f}.more-vert{color:#4a4a4a;display:block}.modal-options{display:flex;justify-items:center;align-items:center;background-color:var(--surface-container-lowest, #ffffff);width:200px;border-radius:8px;position:absolute;top:100%;right:0;z-index:99}.modal-content{width:100%;border-radius:8px;border:1px solid var(--outline-variant, #c7c7ad);overflow:hidden}.option-item{display:flex;align-items:center;height:56px;padding:8px 12px;cursor:pointer}.option-item:hover{background-color:#0000001a}.option-item:active{background-color:#dee58f}.icon{margin-right:8px}.option-item .label{font-weight:500;font-size:16px}.icon-refresh,.icon-trash{width:24px;height:24px;background-color:currentColor;color:currentColor}.icon-refresh{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\"><path d=\"M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4\"/><path d=\"M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\"><path d=\"M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4\"/><path d=\"M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4\"/></svg>')}.icon-trash{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M20 6a1 1 0 0 1 .117 1.993l-.117 .007h-.081l-.919 11a3 3 0 0 1 -2.824 2.995l-.176 .005h-8c-1.598 0 -2.904 -1.249 -2.992 -2.75l-.005 -.167l-.923 -11.083h-.08a1 1 0 0 1 -.117 -1.993l.117 -.007h16z\"/><path d=\"M14 2a2 2 0 0 1 2 2a1 1 0 0 1 -1.993 .117l-.007 -.117h-4l-.007 .117a1 1 0 0 1 -1.993 -.117a2 2 0 0 1 1.85 -1.995l.15 -.005h4z\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M20 6a1 1 0 0 1 .117 1.993l-.117 .007h-.081l-.919 11a3 3 0 0 1 -2.824 2.995l-.176 .005h-8c-1.598 0 -2.904 -1.249 -2.992 -2.75l-.005 -.167l-.923 -11.083h-.08a1 1 0 0 1 -.117 -1.993l.117 -.007h16z\"/><path d=\"M14 2a2 2 0 0 1 2 2a1 1 0 0 1 -1.993 .117l-.007 -.117h-4l-.007 .117a1 1 0 0 1 -1.993 -.117a2 2 0 0 1 1.85 -1.995l.15 -.005h4z\"/></svg>')}td.left{text-align:left}td.right{text-align:right}td.center{text-align:center}.status-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:8px;font-size:13px;font-weight:500}.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.sortable:hover{opacity:.7}.sort-icon{margin-left:6px;font-size:12px;pointer-events:none}\n"] });
528
542
  }
529
543
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: Table, decorators: [{
530
544
  type: Component,
531
- args: [{ selector: 'lib-table', imports: [], standalone: true, template: "<div class=\"table-wrapper\">\r\n <table class=\"inner-table\">\r\n <thead>\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <th>{{ col.label }}</th>\r\n }\r\n\r\n <!-- columna para acciones -->\r\n <th></th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n @for (row of data(); track $index) {\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <td>{{ row[col.key] }}</td>\r\n }\r\n\r\n <!-- Celda de acciones -->\r\n <td class=\"acciones-cell\">\r\n <div class=\"menu-acciones\">\r\n <button class=\"icon-button\" (click)=\"openModal(row)\">\r\n <div class=\"content\">\r\n <div class=\"state-layer\">\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"more-vert\"\r\n width=\"20\"\r\n height=\"20\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\r\n </svg>\r\n </div>\r\n </div>\r\n </button>\r\n @if(selectedRow()?.['id'] === row['id']){\r\n <div class=\"modal-options\" (click)=\"$event.stopPropagation()\">\r\n \r\n <div class=\"modal-content\">\r\n <ul>\r\n @for (option of actions(); track $index) {\r\n <li\r\n class=\"option-item\"\r\n [style.color]=\"option.color\"\r\n (click)=\"onOptionClick(option, row)\"\r\n >\r\n <span class=\"icon\" [class]=\"option.icon\"></span>\r\n <span class=\"label\">{{ option.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n \r\n </div>\r\n }\r\n </div>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n</div>", styles: [".table-wrapper{width:100%;border-radius:20px;border:1px solid #c7c7ad}.inner-table{width:100%;border-collapse:separate;font-size:14px;border-spacing:0;border-radius:20px;background:#ebe8d6}.inner-table thead{background:#f0f0db}.inner-table thead tr th:first-child{border-top-left-radius:20px}.inner-table thead tr th:last-child{border-top-right-radius:20px}.inner-table th{text-align:left;padding:12px 16px;font-weight:600;color:#3a3a3a}.inner-table td{padding:12px 16px;color:#4a4a4a;border-top:1px solid #c7c7ad}.acciones-cell{text-align:center;padding:6px;position:relative}.menu-acciones{display:flex;align-items:center;justify-content:center;position:relative}.icon-button{background-color:var(--secondary-container, #dee58f);width:36px;height:36px;border:none;cursor:pointer;padding:0;border-radius:100%;display:flex;align-items:center;justify-content:center}.icon-button:hover{background:#0000000f}.more-vert{color:#4a4a4a;display:block}.modal-options{display:flex;justify-items:center;align-items:center;background-color:var(--surface-container-lowest, #ffffff);width:200px;border-radius:8px;position:absolute;top:100%;right:0;z-index:99}.modal-content{width:100%;border-radius:8px;border:1px solid var(--outline-variant, #c7c7ad);overflow:hidden}.option-item{display:flex;align-items:center;height:56px;padding:8px 12px;cursor:pointer}.option-item:hover{background-color:#0000001a}.option-item:active{background-color:#dee58f}.icon{margin-right:8px}.option-item .label{font-weight:500;font-size:16px}\n"] }]
532
- }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], onClickOutside: [{
545
+ args: [{ selector: 'lib-table', imports: [], standalone: true, template: "<div class=\"table-wrapper\">\r\n <table class=\"inner-table\">\r\n <thead>\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <th\r\n (click)=\"col.sortable !== false && onSort(col)\"\r\n [class.sortable]=\"col.sortable !== false\"\r\n [class]=\"getAlignment(col)\"\r\n >\r\n {{ col.label }}\r\n\r\n <!-- iconos de orden opcionales -->\r\n @if (sortColumn() === col.key) {\r\n <span class=\"sort-icon\">\r\n {{ sortDirection() === 'asc' ? '\u25B2' : '\u25BC' }}\r\n </span>\r\n }\r\n </th>\r\n }\r\n\r\n <!-- columna para acciones -->\r\n @if (showActions()) {\r\n <th></th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n @for (row of sortData(); track $index) {\r\n <tr>\r\n @for (col of columns(); track col.key) {\r\n <td [class]=\"getAlignment(col)\">\r\n @if (col.type === 'status') { @let style =\r\n getStatusStyle(row[col.key]);\r\n\r\n <span\r\n class=\"status-chip\"\r\n [style.background-color]=\"style.bg\"\r\n [style.color]=\"style.color\"\r\n >\r\n <span\r\n class=\"status-dot\"\r\n [style.background-color]=\"style.color\"\r\n ></span>\r\n\r\n {{ row[col.key]?.toString().toUpperCase() }}\r\n </span>\r\n } @else { {{ formatValue(col, row[col.key]) }} }\r\n </td>\r\n }\r\n\r\n <!-- Celda de acciones -->\r\n @if (showActions()) {\r\n <td class=\"acciones-cell\">\r\n <div class=\"menu-acciones\">\r\n <button class=\"icon-button\" (click)=\"openModal(row)\">\r\n <div class=\"content\">\r\n <div class=\"state-layer\">\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n class=\"more-vert\"\r\n width=\"20\"\r\n height=\"20\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"2\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n >\r\n <circle cx=\"12\" cy=\"5\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"12\" r=\"1\" />\r\n <circle cx=\"12\" cy=\"19\" r=\"1\" />\r\n </svg>\r\n </div>\r\n </div>\r\n </button>\r\n @if(selectedRow()?.['id'] === row['id']){\r\n <div class=\"modal-options\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"modal-content\">\r\n <ul>\r\n @for (option of actions(); track $index) {\r\n <li\r\n class=\"option-item\"\r\n [style.color]=\"option.color\"\r\n (click)=\"onOptionClick(option, row)\"\r\n >\r\n <span class=\"icon\" [class]=\"option.icon\"></span>\r\n <span class=\"label\">{{ option.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n</div>\r\n", styles: [".table-wrapper{width:100%;border-radius:20px;border:1px solid #c7c7ad}.inner-table{width:100%;border-collapse:separate;font-size:14px;border-spacing:0;border-radius:20px;background:#ebe8d6}.inner-table thead{background:#f0f0db}.inner-table thead tr th:first-child{border-top-left-radius:20px}.inner-table thead tr th:last-child{border-top-right-radius:20px}.inner-table th{text-align:left;padding:12px 16px;font-weight:600;color:#3a3a3a}.inner-table td{padding:12px 16px;color:#4a4a4a;border-top:1px solid #c7c7ad}.acciones-cell{text-align:center;padding:6px;position:relative}.menu-acciones{display:flex;align-items:center;justify-content:center;position:relative}.icon-button{background-color:var(--secondary-container, #dee58f);width:36px;height:36px;border:none;cursor:pointer;padding:0;border-radius:100%;display:flex;align-items:center;justify-content:center}.icon-button:hover{background:#0000000f}.more-vert{color:#4a4a4a;display:block}.modal-options{display:flex;justify-items:center;align-items:center;background-color:var(--surface-container-lowest, #ffffff);width:200px;border-radius:8px;position:absolute;top:100%;right:0;z-index:99}.modal-content{width:100%;border-radius:8px;border:1px solid var(--outline-variant, #c7c7ad);overflow:hidden}.option-item{display:flex;align-items:center;height:56px;padding:8px 12px;cursor:pointer}.option-item:hover{background-color:#0000001a}.option-item:active{background-color:#dee58f}.icon{margin-right:8px}.option-item .label{font-weight:500;font-size:16px}.icon-refresh,.icon-trash{width:24px;height:24px;background-color:currentColor;color:currentColor}.icon-refresh{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\"><path d=\"M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4\"/><path d=\"M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\"><path d=\"M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4\"/><path d=\"M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4\"/></svg>')}.icon-trash{-webkit-mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M20 6a1 1 0 0 1 .117 1.993l-.117 .007h-.081l-.919 11a3 3 0 0 1 -2.824 2.995l-.176 .005h-8c-1.598 0 -2.904 -1.249 -2.992 -2.75l-.005 -.167l-.923 -11.083h-.08a1 1 0 0 1 -.117 -1.993l.117 -.007h16z\"/><path d=\"M14 2a2 2 0 0 1 2 2a1 1 0 0 1 -1.993 .117l-.007 -.117h-4l-.007 .117a1 1 0 0 1 -1.993 -.117a2 2 0 0 1 1.85 -1.995l.15 -.005h4z\"/></svg>');mask-image:url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M20 6a1 1 0 0 1 .117 1.993l-.117 .007h-.081l-.919 11a3 3 0 0 1 -2.824 2.995l-.176 .005h-8c-1.598 0 -2.904 -1.249 -2.992 -2.75l-.005 -.167l-.923 -11.083h-.08a1 1 0 0 1 -.117 -1.993l.117 -.007h16z\"/><path d=\"M14 2a2 2 0 0 1 2 2a1 1 0 0 1 -1.993 .117l-.007 -.117h-4l-.007 .117a1 1 0 0 1 -1.993 -.117a2 2 0 0 1 1.85 -1.995l.15 -.005h4z\"/></svg>')}td.left{text-align:left}td.right{text-align:right}td.center{text-align:center}.status-chip{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:8px;font-size:13px;font-weight:500}.status-dot{width:8px;height:8px;border-radius:50%;display:inline-block}.sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.sortable:hover{opacity:.7}.sort-icon{margin-left:6px;font-size:12px;pointer-events:none}\n"] }]
546
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], onClickOutside: [{
533
547
  type: HostListener,
534
548
  args: ['document:click', ['$event']]
535
549
  }] } });
536
550
 
551
+ class DateTimePicker {
552
+ elementRef;
553
+ ngZone;
554
+ mode = input('date', ...(ngDevMode ? [{ debugName: "mode" }] : []));
555
+ placeholder = input('Seleccionar fecha', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
556
+ minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
557
+ maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
558
+ dateChange = output();
559
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
560
+ selectedDate = signal(null, ...(ngDevMode ? [{ debugName: "selectedDate" }] : []));
561
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
562
+ isTouched = signal(false, ...(ngDevMode ? [{ debugName: "isTouched" }] : []));
563
+ // Navegación del calendario
564
+ currentMonth = signal(new Date().getMonth(), ...(ngDevMode ? [{ debugName: "currentMonth" }] : []));
565
+ currentYear = signal(new Date().getFullYear(), ...(ngDevMode ? [{ debugName: "currentYear" }] : []));
566
+ // Hora en formato 24h (para el backend)
567
+ selectedHour = signal(0, ...(ngDevMode ? [{ debugName: "selectedHour" }] : []));
568
+ selectedMinute = signal(0, ...(ngDevMode ? [{ debugName: "selectedMinute" }] : []));
569
+ // Estado AM/PM
570
+ selectedAmPm = signal('AM', ...(ngDevMode ? [{ debugName: "selectedAmPm" }] : []));
571
+ documentClickListener;
572
+ displayValue = computed(() => {
573
+ const date = this.selectedDate();
574
+ if (!date)
575
+ return '';
576
+ const options = {
577
+ day: '2-digit',
578
+ month: '2-digit',
579
+ year: 'numeric',
580
+ };
581
+ if (this.mode() === 'datetime') {
582
+ options.hour = '2-digit';
583
+ options.minute = '2-digit';
584
+ options.hour12 = true; // Cambiado a formato 12 horas
585
+ }
586
+ return date.toLocaleDateString('es-ES', options);
587
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
588
+ monthName = computed(() => {
589
+ const months = [
590
+ 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio',
591
+ 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'
592
+ ];
593
+ return months[this.currentMonth()];
594
+ }, ...(ngDevMode ? [{ debugName: "monthName" }] : []));
595
+ calendarDays = computed(() => {
596
+ const year = this.currentYear();
597
+ const month = this.currentMonth();
598
+ const firstDay = new Date(year, month, 1);
599
+ const lastDay = new Date(year, month + 1, 0);
600
+ const daysInMonth = lastDay.getDate();
601
+ const startingDayOfWeek = firstDay.getDay();
602
+ const days = [];
603
+ // Días vacíos al inicio
604
+ for (let i = 0; i < startingDayOfWeek; i++) {
605
+ days.push(null);
606
+ }
607
+ // Días del mes
608
+ for (let day = 1; day <= daysInMonth; day++) {
609
+ days.push(day);
610
+ }
611
+ return days;
612
+ }, ...(ngDevMode ? [{ debugName: "calendarDays" }] : []));
613
+ // Horas en formato 12h
614
+ hours12 = computed(() => {
615
+ return Array.from({ length: 12 }, (_, i) => {
616
+ const hour12 = i === 0 ? 12 : i;
617
+ return {
618
+ value: hour12,
619
+ display: hour12.toString().padStart(2, '0')
620
+ };
621
+ });
622
+ }, ...(ngDevMode ? [{ debugName: "hours12" }] : []));
623
+ // Hora seleccionada en formato 12h
624
+ selectedHour12 = computed(() => {
625
+ const hour24 = this.selectedHour();
626
+ if (hour24 === 0)
627
+ return 12;
628
+ if (hour24 > 12)
629
+ return hour24 - 12;
630
+ return hour24;
631
+ }, ...(ngDevMode ? [{ debugName: "selectedHour12" }] : []));
632
+ minutes = computed(() => Array.from({ length: 60 }, (_, i) => i), ...(ngDevMode ? [{ debugName: "minutes" }] : []));
633
+ onChange = (value) => { };
634
+ onTouched = () => { };
635
+ constructor(elementRef, ngZone) {
636
+ this.elementRef = elementRef;
637
+ this.ngZone = ngZone;
638
+ }
639
+ ngOnDestroy() {
640
+ this.removeDocumentListener();
641
+ }
642
+ writeValue(value) {
643
+ let date = null;
644
+ if (value instanceof Date) {
645
+ date = value;
646
+ }
647
+ else if (typeof value === 'string' && value) {
648
+ // Manejar tanto fechas ISO como otros formatos de string
649
+ const parsedDate = new Date(value);
650
+ if (!isNaN(parsedDate.getTime())) {
651
+ date = parsedDate;
652
+ }
653
+ }
654
+ if (date) {
655
+ this.selectedDate.set(date);
656
+ this.currentMonth.set(date.getMonth());
657
+ this.currentYear.set(date.getFullYear());
658
+ this.selectedHour.set(date.getHours());
659
+ this.selectedMinute.set(date.getMinutes());
660
+ this.selectedAmPm.set(date.getHours() >= 12 ? 'PM' : 'AM');
661
+ }
662
+ else {
663
+ this.selectedDate.set(null);
664
+ // Valores por defecto
665
+ this.selectedHour.set(0);
666
+ this.selectedMinute.set(0);
667
+ this.selectedAmPm.set('AM');
668
+ }
669
+ }
670
+ registerOnChange(fn) {
671
+ this.onChange = fn;
672
+ }
673
+ registerOnTouched(fn) {
674
+ this.onTouched = fn;
675
+ }
676
+ setDisabledState(isDisabled) {
677
+ this.isDisabled.set(isDisabled);
678
+ }
679
+ toggle() {
680
+ if (this.isDisabled())
681
+ return;
682
+ this.markAsTouched();
683
+ if (this.isOpen()) {
684
+ this.close();
685
+ }
686
+ else {
687
+ this.open();
688
+ }
689
+ }
690
+ open() {
691
+ if (this.isDisabled())
692
+ return;
693
+ this.markAsTouched();
694
+ this.isOpen.set(true);
695
+ this.addDocumentListener();
696
+ // Si hay fecha seleccionada, navegar a ese mes/año
697
+ const selected = this.selectedDate();
698
+ if (selected) {
699
+ this.currentMonth.set(selected.getMonth());
700
+ this.currentYear.set(selected.getFullYear());
701
+ }
702
+ }
703
+ close() {
704
+ this.isOpen.set(false);
705
+ this.removeDocumentListener();
706
+ this.markAsTouched();
707
+ }
708
+ selectDay(day) {
709
+ if (!day || this.isDisabled())
710
+ return;
711
+ const newDate = new Date(this.currentYear(), this.currentMonth(), day, this.selectedHour(), this.selectedMinute());
712
+ if (this.isDateDisabled(newDate))
713
+ return;
714
+ this.selectedDate.set(newDate);
715
+ this.onChange(newDate.toISOString());
716
+ this.markAsTouched();
717
+ this.dateChange.emit(newDate);
718
+ // Solo cerrar automáticamente si es modo 'date'
719
+ if (this.mode() === 'date') {
720
+ this.close();
721
+ }
722
+ }
723
+ setHour12(hour12) {
724
+ if (this.isDisabled())
725
+ return;
726
+ // Convertir de 12h a 24h
727
+ let hour24;
728
+ const amPm = this.selectedAmPm();
729
+ if (hour12 === 12) {
730
+ hour24 = amPm === 'AM' ? 0 : 12;
731
+ }
732
+ else {
733
+ hour24 = amPm === 'AM' ? hour12 : hour12 + 12;
734
+ }
735
+ this.selectedHour.set(hour24);
736
+ this.updateDateTime();
737
+ }
738
+ setMinute(minute) {
739
+ if (this.isDisabled())
740
+ return;
741
+ this.selectedMinute.set(minute);
742
+ this.updateDateTime();
743
+ }
744
+ setAmPm(amPm) {
745
+ if (this.isDisabled())
746
+ return;
747
+ const currentHour24 = this.selectedHour();
748
+ const currentHour12 = this.selectedHour12();
749
+ let newHour24;
750
+ if (amPm === 'AM') {
751
+ newHour24 = currentHour12 === 12 ? 0 : currentHour12;
752
+ }
753
+ else {
754
+ newHour24 = currentHour12 === 12 ? 12 : currentHour12 + 12;
755
+ }
756
+ this.selectedAmPm.set(amPm);
757
+ this.selectedHour.set(newHour24);
758
+ this.updateDateTime();
759
+ }
760
+ previousMonth() {
761
+ const currentMonth = this.currentMonth();
762
+ const currentYear = this.currentYear();
763
+ if (currentMonth === 0) {
764
+ this.currentMonth.set(11);
765
+ this.currentYear.set(currentYear - 1);
766
+ }
767
+ else {
768
+ this.currentMonth.set(currentMonth - 1);
769
+ }
770
+ }
771
+ nextMonth() {
772
+ const currentMonth = this.currentMonth();
773
+ const currentYear = this.currentYear();
774
+ if (currentMonth === 11) {
775
+ this.currentMonth.set(0);
776
+ this.currentYear.set(currentYear + 1);
777
+ }
778
+ else {
779
+ this.currentMonth.set(currentMonth + 1);
780
+ }
781
+ }
782
+ previousYear() {
783
+ this.currentYear.set(this.currentYear() - 1);
784
+ }
785
+ nextYear() {
786
+ this.currentYear.set(this.currentYear() + 1);
787
+ }
788
+ today() {
789
+ const today = new Date();
790
+ this.currentMonth.set(today.getMonth());
791
+ this.currentYear.set(today.getFullYear());
792
+ this.selectedDate.set(today);
793
+ this.selectedHour.set(today.getHours());
794
+ this.selectedMinute.set(today.getMinutes());
795
+ this.selectedAmPm.set(today.getHours() >= 12 ? 'PM' : 'AM');
796
+ this.onChange(today.toISOString());
797
+ this.markAsTouched();
798
+ this.dateChange.emit(today);
799
+ if (this.mode() === 'date') {
800
+ this.close();
801
+ }
802
+ }
803
+ clear() {
804
+ this.selectedDate.set(null);
805
+ this.onChange(null);
806
+ this.markAsTouched();
807
+ this.dateChange.emit(null);
808
+ this.close();
809
+ }
810
+ onKeyDown(event) {
811
+ if (this.isDisabled())
812
+ return;
813
+ if (event.key === 'Escape') {
814
+ this.close();
815
+ }
816
+ else if (event.key === 'Tab') {
817
+ this.markAsTouched();
818
+ this.close();
819
+ }
820
+ }
821
+ isDaySelected(day) {
822
+ if (!day)
823
+ return false;
824
+ const selected = this.selectedDate();
825
+ if (!selected)
826
+ return false;
827
+ return (selected.getDate() === day &&
828
+ selected.getMonth() === this.currentMonth() &&
829
+ selected.getFullYear() === this.currentYear());
830
+ }
831
+ isToday(day) {
832
+ if (!day)
833
+ return false;
834
+ const today = new Date();
835
+ return (today.getDate() === day &&
836
+ today.getMonth() === this.currentMonth() &&
837
+ today.getFullYear() === this.currentYear());
838
+ }
839
+ isDayDisabled(day) {
840
+ if (!day)
841
+ return true;
842
+ const date = new Date(this.currentYear(), this.currentMonth(), day);
843
+ return this.isDateDisabled(date);
844
+ }
845
+ isDateDisabled(date) {
846
+ const minDate = this.minDate();
847
+ const maxDate = this.maxDate();
848
+ if (minDate && date < minDate)
849
+ return true;
850
+ if (maxDate && date > maxDate)
851
+ return true;
852
+ return false;
853
+ }
854
+ updateDateTime() {
855
+ const selected = this.selectedDate();
856
+ if (!selected)
857
+ return;
858
+ const newDate = new Date(selected);
859
+ newDate.setHours(this.selectedHour());
860
+ newDate.setMinutes(this.selectedMinute());
861
+ this.selectedDate.set(newDate);
862
+ this.onChange(newDate.toISOString());
863
+ this.markAsTouched();
864
+ this.dateChange.emit(newDate);
865
+ }
866
+ markAsTouched() {
867
+ if (!this.isTouched()) {
868
+ this.isTouched.set(true);
869
+ this.onTouched();
870
+ }
871
+ }
872
+ addDocumentListener() {
873
+ if (this.documentClickListener) {
874
+ this.removeDocumentListener();
875
+ }
876
+ this.ngZone.runOutsideAngular(() => {
877
+ this.documentClickListener = (event) => {
878
+ const target = event.target;
879
+ if (!this.elementRef.nativeElement.contains(target)) {
880
+ this.ngZone.run(() => {
881
+ if (this.isOpen()) {
882
+ this.markAsTouched();
883
+ this.close();
884
+ }
885
+ });
886
+ }
887
+ };
888
+ setTimeout(() => {
889
+ document.addEventListener('click', this.documentClickListener, true);
890
+ }, 10);
891
+ });
892
+ }
893
+ removeDocumentListener() {
894
+ if (this.documentClickListener) {
895
+ document.removeEventListener('click', this.documentClickListener, true);
896
+ this.documentClickListener = undefined;
897
+ }
898
+ }
899
+ markTouched() {
900
+ this.markAsTouched();
901
+ }
902
+ /**
903
+ * Obtiene la fecha seleccionada en formato ISO string
904
+ * @returns string ISO o null si no hay fecha seleccionada
905
+ */
906
+ getISOString() {
907
+ const date = this.selectedDate();
908
+ return date ? date.toISOString() : null;
909
+ }
910
+ /**
911
+ * Establece la fecha desde un string ISO
912
+ * @param isoString - Fecha en formato ISO
913
+ */
914
+ setFromISOString(isoString) {
915
+ if (isoString) {
916
+ const date = new Date(isoString);
917
+ if (!isNaN(date.getTime())) {
918
+ this.writeValue(date);
919
+ this.onChange(isoString);
920
+ }
921
+ }
922
+ else {
923
+ this.clear();
924
+ }
925
+ }
926
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: DateTimePicker, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
927
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: DateTimePicker, isStandalone: true, selector: "app-date-time-picker", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dateChange: "dateChange" }, providers: [
928
+ {
929
+ provide: NG_VALUE_ACCESSOR,
930
+ useExisting: forwardRef(() => DateTimePicker),
931
+ multi: true,
932
+ },
933
+ ], ngImport: i0, template: "<div class=\"datetime-container\">\r\n <div \r\n class=\"datetime-header\" \r\n [class.active]=\"isOpen()\" \r\n [class.disabled]=\"isDisabled()\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n tabindex=\"0\"\r\n >\r\n <span class=\"selected-text\" [class.placeholder]=\"!selectedDate()\">\r\n {{ displayValue() || placeholder() }}\r\n </span>\r\n \r\n <div class=\"header-icons\">\r\n @if (selectedDate()) {\r\n <span class=\"clear-icon\" (click)=\"clear(); $event.stopPropagation()\" title=\"Limpiar\">\r\n \u00D7\r\n </span>\r\n }\r\n <span class=\"arrow\" [class.open]=\"isOpen()\"></span>\r\n </div>\r\n </div>\r\n \r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"datetime-content\">\r\n <!-- Secci\u00F3n del calendario -->\r\n <div class=\"calendar-section\">\r\n <!-- Navegaci\u00F3n del calendario -->\r\n <div class=\"calendar-nav\">\r\n <div class=\"nav-section\">\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"previousYear()\" title=\"A\u00F1o anterior\">\r\n \u2039\u2039\r\n </button>\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"previousMonth()\" title=\"Mes anterior\">\r\n \u2039\r\n </button>\r\n </div>\r\n \r\n <div class=\"current-date\">\r\n {{ monthName() }} {{ currentYear() }}\r\n </div>\r\n \r\n <div class=\"nav-section\">\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"nextMonth()\" title=\"Siguiente mes\">\r\n \u203A\r\n </button>\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"nextYear()\" title=\"Siguiente a\u00F1o\">\r\n \u203A\u203A\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <!-- D\u00EDas de la semana -->\r\n <div class=\"weekdays\">\r\n <div class=\"weekday\">Dom</div>\r\n <div class=\"weekday\">Lun</div>\r\n <div class=\"weekday\">Mar</div>\r\n <div class=\"weekday\">Mi\u00E9</div>\r\n <div class=\"weekday\">Jue</div>\r\n <div class=\"weekday\">Vie</div>\r\n <div class=\"weekday\">S\u00E1b</div>\r\n </div>\r\n \r\n <!-- D\u00EDas del mes -->\r\n <div class=\"calendar-grid\">\r\n @for (day of calendarDays(); track $index) {\r\n <div \r\n class=\"calendar-day\" \r\n [class.selected]=\"isDaySelected(day)\"\r\n [class.today]=\"isToday(day)\"\r\n [class.disabled]=\"isDayDisabled(day)\"\r\n [class.empty]=\"!day\"\r\n (click)=\"selectDay(day)\"\r\n >\r\n {{ day || '' }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n \r\n <!-- Selector de tiempo (solo si mode === 'datetime') -->\r\n @if (mode() === 'datetime') {\r\n <div class=\"time-section\">\r\n <div class=\"time-header\">\r\n Horario\r\n </div>\r\n \r\n <div class=\"time-selectors\">\r\n <div class=\"time-group\">\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Hora</div>\r\n <div class=\"time-scroll\">\r\n @for (hour of hours12(); track hour.value) {\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"hour.value === selectedHour12()\"\r\n (click)=\"setHour12(hour.value)\"\r\n >\r\n {{ hour.display }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n \r\n <div class=\"time-separator-vertical\">:</div>\r\n \r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Min</div>\r\n <div class=\"time-scroll\">\r\n @for (minute of minutes(); track minute) {\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"minute === selectedMinute()\"\r\n (click)=\"setMinute(minute)\"\r\n >\r\n {{ minute.toString().padStart(2, '0') }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n \r\n <div class=\"time-column\">\r\n <div class=\"time-label\">AM/PM</div>\r\n <div class=\"time-scroll ampm\">\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'AM'\"\r\n (click)=\"setAmPm('AM')\"\r\n >\r\n AM\r\n </div>\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'PM'\"\r\n (click)=\"setAmPm('PM')\"\r\n >\r\n PM\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n \r\n <!-- Botones de acci\u00F3n -->\r\n <div class=\"action-buttons\">\r\n <button type=\"button\" class=\"action-btn secondary\" (click)=\"today()\">\r\n Hoy\r\n </button>\r\n @if (mode() === 'datetime') {\r\n <button type=\"button\" class=\"action-btn primary\" (click)=\"close()\">\r\n Aceptar\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>", styles: [".datetime-container{position:relative;width:100%}.datetime-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto;transition:border-color .2s}.datetime-header:hover,.datetime-header:focus{outline:none;border-color:#a9a97f}.datetime-header.active{border-color:#a9a97f;outline:none}.datetime-header.disabled{cursor:not-allowed;opacity:.6}.selected-text{color:#454733;font-size:1.3rem;flex:1}.selected-text.placeholder{color:#787861}.header-icons{display:flex;align-items:center;gap:8px}.clear-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#787861;font-size:1.5rem;cursor:pointer;border-radius:50%;transition:all .2s}.clear-icon:hover{background-color:#7878611a;color:#454733}.arrow{width:20px;height:20px;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='icon icon-tabler icons-tabler-outline icon-tabler-calendar-event'%3e%3cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3e%3cpath d='M4 5m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z' /%3e%3cpath d='M16 3l0 4' /%3e%3cpath d='M8 3l0 4' /%3e%3cpath d='M4 11l16 0' /%3e%3cpath d='M8 15h2v2h-2z' /%3e%3c/svg%3e\");background-repeat:no-repeat;background-size:20px;background-position:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='currentColor' class='icon icon-tabler icons-tabler-filled icon-tabler-calendar-event'%3e%3cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3e%3cpath d='M16 2a1 1 0 0 1 .993 .883l.007 .117v1h1a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h1v-1a1 1 0 0 1 1.993 -.117l.007 .117v1h6v-1a1 1 0 0 1 1 -1m3 7h-14v9.625c0 .705 .386 1.286 .883 1.366l.117 .009h12c.513 0 .936 -.53 .993 -1.215l.007 -.16z' /%3e%3cpath d='M8 14h2v2h-2z' /%3e%3c/svg%3e\")}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:400px}.datetime-content{display:flex}.calendar-section{flex:1;min-width:280px}.calendar-nav{display:flex;justify-content:space-between;align-items:center;padding:15px;border-bottom:1px solid rgba(120,120,97,.2)}.nav-section{display:flex;gap:5px}.nav-btn{background:none;border:none;color:#787861;cursor:pointer;font-size:1.2rem;padding:5px 10px;border-radius:3px;transition:all .2s}.nav-btn:hover{background-color:#a9a97f1a;color:#454733}.current-date{font-weight:500;color:#454733;font-size:1.1rem}.weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#7878611a}.weekday{padding:10px 5px;text-align:center;font-size:.9rem;font-weight:500;color:#787861}.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr)}.calendar-day{padding:12px 5px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;border-right:1px solid rgba(120,120,97,.1);border-bottom:1px solid rgba(120,120,97,.1);transition:all .2s}.calendar-day:hover:not(.disabled):not(.empty){background-color:#a9a97f33}.calendar-day.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.calendar-day.today:not(.selected){background-color:#a9a97f4d;font-weight:500}.calendar-day.disabled{color:#78786166;cursor:not-allowed}.calendar-day.empty{cursor:default}.calendar-day:nth-child(7n){border-right:none}.time-section{border-left:1px solid rgba(120,120,97,.2);min-width:140px;display:flex;flex-direction:column;background:#a9a97f0d}.time-header{padding:15px;border-bottom:1px solid rgba(120,120,97,.2);text-align:center;font-weight:500;color:#454733;background:#a9a97f1a}.time-selectors{display:flex;flex-direction:column;padding:15px;gap:20px;flex:1}.time-group{display:flex;align-items:center;gap:10px}.time-column{flex:1;display:flex;flex-direction:column;align-items:center}.time-label{font-size:.9rem;color:#787861;margin-bottom:10px;font-weight:500}.time-scroll{max-height:150px;overflow-y:auto;border:1px solid rgba(120,120,97,.2);border-radius:3px;width:50px;background:#a9a97f}.time-option{padding:8px 12px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;transition:all .2s}.time-option:hover{background-color:#a9a97f1a}.time-option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.time-separator-vertical{font-size:1.5rem;color:#787861;font-weight:700;align-self:flex-end;margin-bottom:10px}.time-scroll::-webkit-scrollbar{width:4px}.time-scroll::-webkit-scrollbar-track{background:#7878611a}.time-scroll::-webkit-scrollbar-thumb{background:#787861;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#a9a97f}.action-buttons{display:flex;justify-content:space-between;padding:15px;border-top:1px solid rgba(120,120,97,.2);gap:10px}.action-btn{padding:10px 20px;border:none;border-radius:3px;cursor:pointer;font-size:1rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}@media (max-width: 768px){.dropdown{min-width:320px}.datetime-content{flex-direction:column}.time-section{border-left:none;border-top:1px solid rgba(120,120,97,.2)}.time-selectors{flex-direction:row;justify-content:center;padding:15px}.time-group{gap:15px}.datetime-header{padding:12px 15px}.calendar-nav{padding:12px}.calendar-day{padding:10px 3px;font-size:.9rem}.action-buttons{padding:12px}}@media (max-width: 480px){.dropdown{min-width:280px}.time-section{min-width:auto}.time-scroll{width:45px}.time-selectors{padding:10px}}\n"] });
934
+ }
935
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: DateTimePicker, decorators: [{
936
+ type: Component,
937
+ args: [{ selector: 'app-date-time-picker', imports: [], providers: [
938
+ {
939
+ provide: NG_VALUE_ACCESSOR,
940
+ useExisting: forwardRef(() => DateTimePicker),
941
+ multi: true,
942
+ },
943
+ ], template: "<div class=\"datetime-container\">\r\n <div \r\n class=\"datetime-header\" \r\n [class.active]=\"isOpen()\" \r\n [class.disabled]=\"isDisabled()\"\r\n (click)=\"toggle()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n tabindex=\"0\"\r\n >\r\n <span class=\"selected-text\" [class.placeholder]=\"!selectedDate()\">\r\n {{ displayValue() || placeholder() }}\r\n </span>\r\n \r\n <div class=\"header-icons\">\r\n @if (selectedDate()) {\r\n <span class=\"clear-icon\" (click)=\"clear(); $event.stopPropagation()\" title=\"Limpiar\">\r\n \u00D7\r\n </span>\r\n }\r\n <span class=\"arrow\" [class.open]=\"isOpen()\"></span>\r\n </div>\r\n </div>\r\n \r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"datetime-content\">\r\n <!-- Secci\u00F3n del calendario -->\r\n <div class=\"calendar-section\">\r\n <!-- Navegaci\u00F3n del calendario -->\r\n <div class=\"calendar-nav\">\r\n <div class=\"nav-section\">\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"previousYear()\" title=\"A\u00F1o anterior\">\r\n \u2039\u2039\r\n </button>\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"previousMonth()\" title=\"Mes anterior\">\r\n \u2039\r\n </button>\r\n </div>\r\n \r\n <div class=\"current-date\">\r\n {{ monthName() }} {{ currentYear() }}\r\n </div>\r\n \r\n <div class=\"nav-section\">\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"nextMonth()\" title=\"Siguiente mes\">\r\n \u203A\r\n </button>\r\n <button type=\"button\" class=\"nav-btn\" (click)=\"nextYear()\" title=\"Siguiente a\u00F1o\">\r\n \u203A\u203A\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <!-- D\u00EDas de la semana -->\r\n <div class=\"weekdays\">\r\n <div class=\"weekday\">Dom</div>\r\n <div class=\"weekday\">Lun</div>\r\n <div class=\"weekday\">Mar</div>\r\n <div class=\"weekday\">Mi\u00E9</div>\r\n <div class=\"weekday\">Jue</div>\r\n <div class=\"weekday\">Vie</div>\r\n <div class=\"weekday\">S\u00E1b</div>\r\n </div>\r\n \r\n <!-- D\u00EDas del mes -->\r\n <div class=\"calendar-grid\">\r\n @for (day of calendarDays(); track $index) {\r\n <div \r\n class=\"calendar-day\" \r\n [class.selected]=\"isDaySelected(day)\"\r\n [class.today]=\"isToday(day)\"\r\n [class.disabled]=\"isDayDisabled(day)\"\r\n [class.empty]=\"!day\"\r\n (click)=\"selectDay(day)\"\r\n >\r\n {{ day || '' }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n \r\n <!-- Selector de tiempo (solo si mode === 'datetime') -->\r\n @if (mode() === 'datetime') {\r\n <div class=\"time-section\">\r\n <div class=\"time-header\">\r\n Horario\r\n </div>\r\n \r\n <div class=\"time-selectors\">\r\n <div class=\"time-group\">\r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Hora</div>\r\n <div class=\"time-scroll\">\r\n @for (hour of hours12(); track hour.value) {\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"hour.value === selectedHour12()\"\r\n (click)=\"setHour12(hour.value)\"\r\n >\r\n {{ hour.display }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n \r\n <div class=\"time-separator-vertical\">:</div>\r\n \r\n <div class=\"time-column\">\r\n <div class=\"time-label\">Min</div>\r\n <div class=\"time-scroll\">\r\n @for (minute of minutes(); track minute) {\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"minute === selectedMinute()\"\r\n (click)=\"setMinute(minute)\"\r\n >\r\n {{ minute.toString().padStart(2, '0') }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n \r\n <div class=\"time-column\">\r\n <div class=\"time-label\">AM/PM</div>\r\n <div class=\"time-scroll ampm\">\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'AM'\"\r\n (click)=\"setAmPm('AM')\"\r\n >\r\n AM\r\n </div>\r\n <div \r\n class=\"time-option\"\r\n [class.selected]=\"selectedAmPm() === 'PM'\"\r\n (click)=\"setAmPm('PM')\"\r\n >\r\n PM\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n \r\n <!-- Botones de acci\u00F3n -->\r\n <div class=\"action-buttons\">\r\n <button type=\"button\" class=\"action-btn secondary\" (click)=\"today()\">\r\n Hoy\r\n </button>\r\n @if (mode() === 'datetime') {\r\n <button type=\"button\" class=\"action-btn primary\" (click)=\"close()\">\r\n Aceptar\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>", styles: [".datetime-container{position:relative;width:100%}.datetime-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto;transition:border-color .2s}.datetime-header:hover,.datetime-header:focus{outline:none;border-color:#a9a97f}.datetime-header.active{border-color:#a9a97f;outline:none}.datetime-header.disabled{cursor:not-allowed;opacity:.6}.selected-text{color:#454733;font-size:1.3rem;flex:1}.selected-text.placeholder{color:#787861}.header-icons{display:flex;align-items:center;gap:8px}.clear-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center;color:#787861;font-size:1.5rem;cursor:pointer;border-radius:50%;transition:all .2s}.clear-icon:hover{background-color:#7878611a;color:#454733}.arrow{width:20px;height:20px;background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='icon icon-tabler icons-tabler-outline icon-tabler-calendar-event'%3e%3cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3e%3cpath d='M4 5m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z' /%3e%3cpath d='M16 3l0 4' /%3e%3cpath d='M8 3l0 4' /%3e%3cpath d='M4 11l16 0' /%3e%3cpath d='M8 15h2v2h-2z' /%3e%3c/svg%3e\");background-repeat:no-repeat;background-size:20px;background-position:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{background-image:url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='currentColor' class='icon icon-tabler icons-tabler-filled icon-tabler-calendar-event'%3e%3cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3e%3cpath d='M16 2a1 1 0 0 1 .993 .883l.007 .117v1h1a3 3 0 0 1 2.995 2.824l.005 .176v12a3 3 0 0 1 -2.824 2.995l-.176 .005h-12a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-12a3 3 0 0 1 2.824 -2.995l.176 -.005h1v-1a1 1 0 0 1 1.993 -.117l.007 .117v1h6v-1a1 1 0 0 1 1 -1m3 7h-14v9.625c0 .705 .386 1.286 .883 1.366l.117 .009h12c.513 0 .936 -.53 .993 -1.215l.007 -.16z' /%3e%3cpath d='M8 14h2v2h-2z' /%3e%3c/svg%3e\")}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:400px}.datetime-content{display:flex}.calendar-section{flex:1;min-width:280px}.calendar-nav{display:flex;justify-content:space-between;align-items:center;padding:15px;border-bottom:1px solid rgba(120,120,97,.2)}.nav-section{display:flex;gap:5px}.nav-btn{background:none;border:none;color:#787861;cursor:pointer;font-size:1.2rem;padding:5px 10px;border-radius:3px;transition:all .2s}.nav-btn:hover{background-color:#a9a97f1a;color:#454733}.current-date{font-weight:500;color:#454733;font-size:1.1rem}.weekdays{display:grid;grid-template-columns:repeat(7,1fr);background:#7878611a}.weekday{padding:10px 5px;text-align:center;font-size:.9rem;font-weight:500;color:#787861}.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr)}.calendar-day{padding:12px 5px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;border-right:1px solid rgba(120,120,97,.1);border-bottom:1px solid rgba(120,120,97,.1);transition:all .2s}.calendar-day:hover:not(.disabled):not(.empty){background-color:#a9a97f33}.calendar-day.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.calendar-day.today:not(.selected){background-color:#a9a97f4d;font-weight:500}.calendar-day.disabled{color:#78786166;cursor:not-allowed}.calendar-day.empty{cursor:default}.calendar-day:nth-child(7n){border-right:none}.time-section{border-left:1px solid rgba(120,120,97,.2);min-width:140px;display:flex;flex-direction:column;background:#a9a97f0d}.time-header{padding:15px;border-bottom:1px solid rgba(120,120,97,.2);text-align:center;font-weight:500;color:#454733;background:#a9a97f1a}.time-selectors{display:flex;flex-direction:column;padding:15px;gap:20px;flex:1}.time-group{display:flex;align-items:center;gap:10px}.time-column{flex:1;display:flex;flex-direction:column;align-items:center}.time-label{font-size:.9rem;color:#787861;margin-bottom:10px;font-weight:500}.time-scroll{max-height:150px;overflow-y:auto;border:1px solid rgba(120,120,97,.2);border-radius:3px;width:50px;background:#a9a97f}.time-option{padding:8px 12px;text-align:center;cursor:pointer;color:#454733;font-size:1rem;transition:all .2s}.time-option:hover{background-color:#a9a97f1a}.time-option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.time-separator-vertical{font-size:1.5rem;color:#787861;font-weight:700;align-self:flex-end;margin-bottom:10px}.time-scroll::-webkit-scrollbar{width:4px}.time-scroll::-webkit-scrollbar-track{background:#7878611a}.time-scroll::-webkit-scrollbar-thumb{background:#787861;border-radius:2px}.time-scroll::-webkit-scrollbar-thumb:hover{background:#a9a97f}.action-buttons{display:flex;justify-content:space-between;padding:15px;border-top:1px solid rgba(120,120,97,.2);gap:10px}.action-btn{padding:10px 20px;border:none;border-radius:3px;cursor:pointer;font-size:1rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}@media (max-width: 768px){.dropdown{min-width:320px}.datetime-content{flex-direction:column}.time-section{border-left:none;border-top:1px solid rgba(120,120,97,.2)}.time-selectors{flex-direction:row;justify-content:center;padding:15px}.time-group{gap:15px}.datetime-header{padding:12px 15px}.calendar-nav{padding:12px}.calendar-day{padding:10px 3px;font-size:.9rem}.action-buttons{padding:12px}}@media (max-width: 480px){.dropdown{min-width:280px}.time-section{min-width:auto}.time-scroll{width:45px}.time-selectors{padding:10px}}\n"] }]
944
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }] } });
945
+
946
+ class InputNumberFilter {
947
+ elementRef;
948
+ ngZone;
949
+ // Inputs
950
+ filters = input([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
951
+ clearTrigger = input(0, ...(ngDevMode ? [{ debugName: "clearTrigger" }] : [])); // Increment this to trigger a clear
952
+ // Outputs
953
+ filterSelected = output();
954
+ valueChange = output();
955
+ // Internal State
956
+ activeFilterType = signal(null, ...(ngDevMode ? [{ debugName: "activeFilterType" }] : []));
957
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
958
+ inputValue = signal('', ...(ngDevMode ? [{ debugName: "inputValue" }] : []));
959
+ filterValues = signal({}, ...(ngDevMode ? [{ debugName: "filterValues" }] : []));
960
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
961
+ isTouched = signal(false, ...(ngDevMode ? [{ debugName: "isTouched" }] : []));
962
+ // Computed Properties based on active filter
963
+ activeFilter = computed(() => {
964
+ const type = this.activeFilterType();
965
+ return this.filters().find(f => f.value === type) || null;
966
+ }, ...(ngDevMode ? [{ debugName: "activeFilter" }] : []));
967
+ placeholder = computed(() => this.activeFilter()?.placeholder || 'Escribir...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
968
+ filterValueKey = computed(() => this.activeFilter()?.value || '', ...(ngDevMode ? [{ debugName: "filterValueKey" }] : []));
969
+ documentClickListener;
970
+ onChange = () => { };
971
+ onTouched = () => { };
972
+ constructor(elementRef, ngZone) {
973
+ this.elementRef = elementRef;
974
+ this.ngZone = ngZone;
975
+ // Watch for clear trigger changes
976
+ effect(() => {
977
+ const trigger = this.clearTrigger();
978
+ if (trigger > 0) {
979
+ this.resetAllFilters();
980
+ }
981
+ });
982
+ }
983
+ ngOnDestroy() {
984
+ this.removeDocumentListener();
985
+ }
986
+ resetAllFilters() {
987
+ this.filterValues.set({});
988
+ this.inputValue.set('');
989
+ this.activeFilterType.set(null);
990
+ this.isOpen.set(false);
991
+ }
992
+ // ControlValueAccessor Implementation
993
+ writeValue(value) {
994
+ this.inputValue.set(value || '');
995
+ }
996
+ registerOnChange(fn) {
997
+ this.onChange = fn;
998
+ }
999
+ registerOnTouched(fn) {
1000
+ this.onTouched = fn;
1001
+ }
1002
+ setDisabledState(isDisabled) {
1003
+ this.isDisabled.set(isDisabled);
1004
+ }
1005
+ // Public Methods
1006
+ toggle(filterValue) {
1007
+ if (this.isDisabled())
1008
+ return;
1009
+ if (this.activeFilterType() === filterValue && this.isOpen()) {
1010
+ this.close();
1011
+ }
1012
+ else {
1013
+ this.activeFilterType.set(filterValue);
1014
+ const currentValues = this.filterValues();
1015
+ this.inputValue.set(currentValues[filterValue] || '');
1016
+ this.open();
1017
+ }
1018
+ }
1019
+ open() {
1020
+ if (this.isDisabled())
1021
+ return;
1022
+ this.markAsTouched();
1023
+ this.isOpen.set(true);
1024
+ this.addDocumentListener();
1025
+ // Focus input after a short delay to allow rendering
1026
+ setTimeout(() => {
1027
+ const inputEl = this.elementRef.nativeElement.querySelector('input');
1028
+ if (inputEl)
1029
+ inputEl.focus();
1030
+ }, 50);
1031
+ }
1032
+ close() {
1033
+ this.isOpen.set(false);
1034
+ this.removeDocumentListener();
1035
+ this.markAsTouched();
1036
+ }
1037
+ onInputChange(event) {
1038
+ const value = event.target.value;
1039
+ this.inputValue.set(value);
1040
+ const type = this.activeFilterType();
1041
+ if (type) {
1042
+ this.filterValues.update(current => ({
1043
+ ...current,
1044
+ [type]: value
1045
+ }));
1046
+ }
1047
+ }
1048
+ applyFilter() {
1049
+ const value = this.inputValue();
1050
+ this.onChange(value);
1051
+ this.valueChange.emit(value);
1052
+ this.filterSelected.emit({
1053
+ filter: this.filterValueKey(),
1054
+ value: value,
1055
+ });
1056
+ this.close();
1057
+ }
1058
+ clear() {
1059
+ this.inputValue.set('');
1060
+ const type = this.activeFilterType();
1061
+ if (type) {
1062
+ this.filterValues.update(current => ({
1063
+ ...current,
1064
+ [type]: ''
1065
+ }));
1066
+ }
1067
+ this.onChange(null);
1068
+ this.valueChange.emit(null);
1069
+ this.close();
1070
+ }
1071
+ markAsTouched() {
1072
+ if (!this.isTouched()) {
1073
+ this.isTouched.set(true);
1074
+ this.onTouched();
1075
+ }
1076
+ }
1077
+ addDocumentListener() {
1078
+ if (this.documentClickListener)
1079
+ this.removeDocumentListener();
1080
+ this.ngZone.runOutsideAngular(() => {
1081
+ this.documentClickListener = (event) => {
1082
+ const target = event.target;
1083
+ if (!this.elementRef.nativeElement.contains(target)) {
1084
+ this.ngZone.run(() => {
1085
+ if (this.isOpen()) {
1086
+ this.markAsTouched();
1087
+ this.close();
1088
+ }
1089
+ });
1090
+ }
1091
+ };
1092
+ setTimeout(() => document.addEventListener('click', this.documentClickListener, true), 10);
1093
+ });
1094
+ }
1095
+ removeDocumentListener() {
1096
+ if (this.documentClickListener) {
1097
+ document.removeEventListener('click', this.documentClickListener, true);
1098
+ this.documentClickListener = undefined;
1099
+ }
1100
+ }
1101
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: InputNumberFilter, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
1102
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: InputNumberFilter, isStandalone: true, selector: "app-input-number-filter", inputs: { filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, clearTrigger: { classPropertyName: "clearTrigger", publicName: "clearTrigger", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterSelected: "filterSelected", valueChange: "valueChange" }, providers: [
1103
+ {
1104
+ provide: NG_VALUE_ACCESSOR,
1105
+ useExisting: forwardRef(() => InputNumberFilter),
1106
+ multi: true,
1107
+ },
1108
+ ], ngImport: i0, template: "<div class=\"input-filter-container\">\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n <div\r\n class=\"chip\"\r\n [class.active]=\"activeFilterType() === item.value\"\r\n (click)=\"toggle(item.value)\"\r\n >\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">\r\n {{ filterValues()[item.value] || item.placeholder || item.label }}\r\n </div>\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"icon icon-tabler icons-tabler-outline icon-tabler-number-123\"><path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M3 10l2 -2v8\" /><path d=\"M9 8h3a1 1 0 0 1 1 1v2a1 1 0 0 1 -1 1h-2a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h3\" /><path d=\"M17 8h2.5a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1 -1.5 1.5h-1.5h1.5a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1 -1.5 1.5h-2.5\" /></svg>\r\n </div>\r\n\r\n @if (isOpen() && activeFilterType() === item.value) {\r\n <div class=\"dropdown\">\r\n <div class=\"input-content\">\r\n <input\r\n type=\"number\"\r\n [value]=\"inputValue()\"\r\n (input)=\"onInputChange($event)\"\r\n [placeholder]=\"placeholder()\"\r\n (keydown.enter)=\"applyFilter()\"\r\n class=\"filter-input\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".input-filter-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon.icon-tabler-abc{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.dropdown{position:absolute;top:100%;left:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:300px;padding:15px;margin-top:10px}.input-content{display:flex;flex-direction:column;gap:15px}.filter-input{width:100%;padding:10px;border:1px solid #787861;border-radius:5px;background-color:#fdfdf5;color:#454733;font-size:1rem}.filter-input:focus{outline:none;border-color:#454733;box-shadow:0 0 0 2px #45473333}.action-buttons{display:flex;justify-content:flex-end;gap:10px}.action-btn{padding:8px 16px;border:none;border-radius:3px;cursor:pointer;font-size:.9rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: FormsModule }] });
1109
+ }
1110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: InputNumberFilter, decorators: [{
1111
+ type: Component,
1112
+ args: [{ selector: 'app-input-number-filter', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule], providers: [
1113
+ {
1114
+ provide: NG_VALUE_ACCESSOR,
1115
+ useExisting: forwardRef(() => InputNumberFilter),
1116
+ multi: true,
1117
+ },
1118
+ ], template: "<div class=\"input-filter-container\">\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n <div\r\n class=\"chip\"\r\n [class.active]=\"activeFilterType() === item.value\"\r\n (click)=\"toggle(item.value)\"\r\n >\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">\r\n {{ filterValues()[item.value] || item.placeholder || item.label }}\r\n </div>\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"icon icon-tabler icons-tabler-outline icon-tabler-number-123\"><path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M3 10l2 -2v8\" /><path d=\"M9 8h3a1 1 0 0 1 1 1v2a1 1 0 0 1 -1 1h-2a1 1 0 0 0 -1 1v2a1 1 0 0 0 1 1h3\" /><path d=\"M17 8h2.5a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1 -1.5 1.5h-1.5h1.5a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1 -1.5 1.5h-2.5\" /></svg>\r\n </div>\r\n\r\n @if (isOpen() && activeFilterType() === item.value) {\r\n <div class=\"dropdown\">\r\n <div class=\"input-content\">\r\n <input\r\n type=\"number\"\r\n [value]=\"inputValue()\"\r\n (input)=\"onInputChange($event)\"\r\n [placeholder]=\"placeholder()\"\r\n (keydown.enter)=\"applyFilter()\"\r\n class=\"filter-input\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".input-filter-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon.icon-tabler-abc{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.dropdown{position:absolute;top:100%;left:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:300px;padding:15px;margin-top:10px}.input-content{display:flex;flex-direction:column;gap:15px}.filter-input{width:100%;padding:10px;border:1px solid #787861;border-radius:5px;background-color:#fdfdf5;color:#454733;font-size:1rem}.filter-input:focus{outline:none;border-color:#454733;box-shadow:0 0 0 2px #45473333}.action-buttons{display:flex;justify-content:flex-end;gap:10px}.action-btn{padding:8px 16px;border:none;border-radius:3px;cursor:pointer;font-size:.9rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}\n"] }]
1119
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], clearTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearTrigger", required: false }] }], filterSelected: [{ type: i0.Output, args: ["filterSelected"] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
1120
+
1121
+ class InputSelectFilter {
1122
+ elementRef;
1123
+ ngZone;
1124
+ // Inputs
1125
+ filters = input([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
1126
+ clearTrigger = input(0, ...(ngDevMode ? [{ debugName: "clearTrigger" }] : [])); // Increment this to trigger a clear
1127
+ // Outputs
1128
+ filterSelected = output();
1129
+ valueChange = output();
1130
+ // Internal State
1131
+ activeFilterType = signal(null, ...(ngDevMode ? [{ debugName: "activeFilterType" }] : []));
1132
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
1133
+ selectedLabel = signal('', ...(ngDevMode ? [{ debugName: "selectedLabel" }] : []));
1134
+ filterValues = signal({}, ...(ngDevMode ? [{ debugName: "filterValues" }] : []));
1135
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
1136
+ isTouched = signal(false, ...(ngDevMode ? [{ debugName: "isTouched" }] : []));
1137
+ // Computed Properties based on active filter
1138
+ activeFilter = computed(() => {
1139
+ const type = this.activeFilterType();
1140
+ return this.filters().find(f => f.value === type) || null;
1141
+ }, ...(ngDevMode ? [{ debugName: "activeFilter" }] : []));
1142
+ placeholder = computed(() => this.activeFilter()?.placeholder || 'Seleccionar...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1143
+ filterValueKey = computed(() => this.activeFilter()?.value || '', ...(ngDevMode ? [{ debugName: "filterValueKey" }] : []));
1144
+ options = computed(() => this.activeFilter()?.options || [], ...(ngDevMode ? [{ debugName: "options" }] : []));
1145
+ documentClickListener;
1146
+ onChange = () => { };
1147
+ onTouched = () => { };
1148
+ constructor(elementRef, ngZone) {
1149
+ this.elementRef = elementRef;
1150
+ this.ngZone = ngZone;
1151
+ // Watch for clear trigger changes
1152
+ effect(() => {
1153
+ const trigger = this.clearTrigger();
1154
+ if (trigger > 0) {
1155
+ this.resetAllFilters();
1156
+ }
1157
+ });
1158
+ }
1159
+ ngOnDestroy() {
1160
+ this.removeDocumentListener();
1161
+ }
1162
+ resetAllFilters() {
1163
+ this.filterValues.set({});
1164
+ this.selectedLabel.set('');
1165
+ this.activeFilterType.set(null);
1166
+ this.isOpen.set(false);
1167
+ }
1168
+ // ControlValueAccessor Implementation
1169
+ writeValue(value) {
1170
+ // If we had a way to map value back to label without activeFilter, we would do it here.
1171
+ // For now, we might need to rely on the consumer or just show the value if label not found.
1172
+ // But since we only know options when activeFilter is set, this is tricky.
1173
+ // However, usually writeValue is for the form control value.
1174
+ // Let's just store it.
1175
+ }
1176
+ registerOnChange(fn) {
1177
+ this.onChange = fn;
1178
+ }
1179
+ registerOnTouched(fn) {
1180
+ this.onTouched = fn;
1181
+ }
1182
+ setDisabledState(isDisabled) {
1183
+ this.isDisabled.set(isDisabled);
1184
+ }
1185
+ // Public Methods
1186
+ toggle(filterValue) {
1187
+ if (this.isDisabled())
1188
+ return;
1189
+ if (this.activeFilterType() === filterValue && this.isOpen()) {
1190
+ this.close();
1191
+ }
1192
+ else {
1193
+ this.activeFilterType.set(filterValue);
1194
+ this.open();
1195
+ }
1196
+ }
1197
+ open() {
1198
+ if (this.isDisabled())
1199
+ return;
1200
+ this.markAsTouched();
1201
+ this.isOpen.set(true);
1202
+ this.addDocumentListener();
1203
+ }
1204
+ close() {
1205
+ this.isOpen.set(false);
1206
+ this.removeDocumentListener();
1207
+ this.markAsTouched();
1208
+ }
1209
+ selectOption(option) {
1210
+ this.selectedLabel.set(option.label);
1211
+ const type = this.activeFilterType();
1212
+ if (type) {
1213
+ this.filterValues.update(current => ({
1214
+ ...current,
1215
+ [type]: option.label // Show label in chip
1216
+ }));
1217
+ }
1218
+ this.onChange(option.value);
1219
+ this.valueChange.emit(option.value);
1220
+ this.filterSelected.emit({
1221
+ filter: this.filterValueKey(),
1222
+ value: option.value,
1223
+ });
1224
+ this.close();
1225
+ }
1226
+ clear() {
1227
+ this.selectedLabel.set('');
1228
+ const type = this.activeFilterType();
1229
+ if (type) {
1230
+ this.filterValues.update(current => ({
1231
+ ...current,
1232
+ [type]: ''
1233
+ }));
1234
+ }
1235
+ this.onChange(null);
1236
+ this.valueChange.emit(null);
1237
+ this.close();
1238
+ }
1239
+ markAsTouched() {
1240
+ if (!this.isTouched()) {
1241
+ this.isTouched.set(true);
1242
+ this.onTouched();
1243
+ }
1244
+ }
1245
+ addDocumentListener() {
1246
+ if (this.documentClickListener)
1247
+ this.removeDocumentListener();
1248
+ this.ngZone.runOutsideAngular(() => {
1249
+ this.documentClickListener = (event) => {
1250
+ const target = event.target;
1251
+ if (!this.elementRef.nativeElement.contains(target)) {
1252
+ this.ngZone.run(() => {
1253
+ if (this.isOpen()) {
1254
+ this.markAsTouched();
1255
+ this.close();
1256
+ }
1257
+ });
1258
+ }
1259
+ };
1260
+ setTimeout(() => document.addEventListener('click', this.documentClickListener, true), 10);
1261
+ });
1262
+ }
1263
+ removeDocumentListener() {
1264
+ if (this.documentClickListener) {
1265
+ document.removeEventListener('click', this.documentClickListener, true);
1266
+ this.documentClickListener = undefined;
1267
+ }
1268
+ }
1269
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: InputSelectFilter, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
1270
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: InputSelectFilter, isStandalone: true, selector: "app-input-select-filter", inputs: { filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, clearTrigger: { classPropertyName: "clearTrigger", publicName: "clearTrigger", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterSelected: "filterSelected", valueChange: "valueChange" }, providers: [
1271
+ {
1272
+ provide: NG_VALUE_ACCESSOR,
1273
+ useExisting: forwardRef(() => InputSelectFilter),
1274
+ multi: true,
1275
+ },
1276
+ ], ngImport: i0, template: "<div class=\"input-filter-container\">\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n <div class=\"chip\" [class.active]=\"activeFilterType() === item.value\" (click)=\"toggle(item.value)\">\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">\r\n {{ filterValues()[item.value] || item.placeholder || item.label }}\r\n </div>\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n class=\"icon icon-tabler icons-tabler-outline icon-tabler-list\">\r\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\r\n <path d=\"M9 6l11 0\" />\r\n <path d=\"M9 12l11 0\" />\r\n <path d=\"M9 18l11 0\" />\r\n <path d=\"M5 6l0 .01\" />\r\n <path d=\"M5 12l0 .01\" />\r\n <path d=\"M5 18l0 .01\" />\r\n </svg>\r\n </div>\r\n </div>\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"options-list\">\r\n @for (option of options(); track option.value) {\r\n <div class=\"option-item\" (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </div>\r\n }\r\n @if (options().length === 0) {\r\n <div class=\"no-options\">No hay opciones</div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>", styles: [".input-filter-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.dropdown{position:absolute;top:100%;left:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:200px;padding:0;overflow:hidden}.options-list{display:flex;flex-direction:column;max-height:200px;overflow-y:auto}.option-item{padding:10px 15px;cursor:pointer;color:#454733;font-size:.9rem;transition:background-color .2s}.option-item:hover{background-color:#7878611a}.no-options{padding:10px 15px;color:#787861;font-size:.9rem;font-style:italic}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: FormsModule }] });
1277
+ }
1278
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: InputSelectFilter, decorators: [{
1279
+ type: Component,
1280
+ args: [{ selector: 'app-input-select-filter', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule], providers: [
1281
+ {
1282
+ provide: NG_VALUE_ACCESSOR,
1283
+ useExisting: forwardRef(() => InputSelectFilter),
1284
+ multi: true,
1285
+ },
1286
+ ], template: "<div class=\"input-filter-container\">\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n <div class=\"chip\" [class.active]=\"activeFilterType() === item.value\" (click)=\"toggle(item.value)\">\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">\r\n {{ filterValues()[item.value] || item.placeholder || item.label }}\r\n </div>\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\r\n class=\"icon icon-tabler icons-tabler-outline icon-tabler-list\">\r\n <path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\" />\r\n <path d=\"M9 6l11 0\" />\r\n <path d=\"M9 12l11 0\" />\r\n <path d=\"M9 18l11 0\" />\r\n <path d=\"M5 6l0 .01\" />\r\n <path d=\"M5 12l0 .01\" />\r\n <path d=\"M5 18l0 .01\" />\r\n </svg>\r\n </div>\r\n </div>\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"options-list\">\r\n @for (option of options(); track option.value) {\r\n <div class=\"option-item\" (click)=\"selectOption(option)\">\r\n {{ option.label }}\r\n </div>\r\n }\r\n @if (options().length === 0) {\r\n <div class=\"no-options\">No hay opciones</div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>", styles: [".input-filter-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.dropdown{position:absolute;top:100%;left:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:200px;padding:0;overflow:hidden}.options-list{display:flex;flex-direction:column;max-height:200px;overflow-y:auto}.option-item{padding:10px 15px;cursor:pointer;color:#454733;font-size:.9rem;transition:background-color .2s}.option-item:hover{background-color:#7878611a}.no-options{padding:10px 15px;color:#787861;font-size:.9rem;font-style:italic}\n"] }]
1287
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], clearTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearTrigger", required: false }] }], filterSelected: [{ type: i0.Output, args: ["filterSelected"] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
1288
+
1289
+ class InputTextFilter {
1290
+ elementRef;
1291
+ ngZone;
1292
+ // Inputs
1293
+ filters = input([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
1294
+ clearTrigger = input(0, ...(ngDevMode ? [{ debugName: "clearTrigger" }] : [])); // Increment this to trigger a clear
1295
+ // Outputs
1296
+ filterSelected = output();
1297
+ valueChange = output();
1298
+ // Internal State
1299
+ activeFilterType = signal(null, ...(ngDevMode ? [{ debugName: "activeFilterType" }] : []));
1300
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
1301
+ inputValue = signal('', ...(ngDevMode ? [{ debugName: "inputValue" }] : []));
1302
+ filterValues = signal({}, ...(ngDevMode ? [{ debugName: "filterValues" }] : []));
1303
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
1304
+ isTouched = signal(false, ...(ngDevMode ? [{ debugName: "isTouched" }] : []));
1305
+ // Computed Properties based on active filter
1306
+ activeFilter = computed(() => {
1307
+ const type = this.activeFilterType();
1308
+ return this.filters().find(f => f.value === type) || null;
1309
+ }, ...(ngDevMode ? [{ debugName: "activeFilter" }] : []));
1310
+ placeholder = computed(() => this.activeFilter()?.placeholder || 'Escribir...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1311
+ filterValueKey = computed(() => this.activeFilter()?.value || '', ...(ngDevMode ? [{ debugName: "filterValueKey" }] : []));
1312
+ documentClickListener;
1313
+ onChange = () => { };
1314
+ onTouched = () => { };
1315
+ constructor(elementRef, ngZone) {
1316
+ this.elementRef = elementRef;
1317
+ this.ngZone = ngZone;
1318
+ // Watch for clear trigger changes
1319
+ effect(() => {
1320
+ const trigger = this.clearTrigger();
1321
+ if (trigger > 0) {
1322
+ this.resetAllFilters();
1323
+ }
1324
+ });
1325
+ }
1326
+ ngOnDestroy() {
1327
+ this.removeDocumentListener();
1328
+ }
1329
+ resetAllFilters() {
1330
+ this.filterValues.set({});
1331
+ this.inputValue.set('');
1332
+ this.activeFilterType.set(null);
1333
+ this.isOpen.set(false);
1334
+ }
1335
+ // ControlValueAccessor Implementation
1336
+ writeValue(value) {
1337
+ this.inputValue.set(value || '');
1338
+ }
1339
+ registerOnChange(fn) {
1340
+ this.onChange = fn;
1341
+ }
1342
+ registerOnTouched(fn) {
1343
+ this.onTouched = fn;
1344
+ }
1345
+ setDisabledState(isDisabled) {
1346
+ this.isDisabled.set(isDisabled);
1347
+ }
1348
+ // Public Methods
1349
+ toggle(filterValue) {
1350
+ if (this.isDisabled())
1351
+ return;
1352
+ if (this.activeFilterType() === filterValue && this.isOpen()) {
1353
+ this.close();
1354
+ }
1355
+ else {
1356
+ this.activeFilterType.set(filterValue);
1357
+ const currentValues = this.filterValues();
1358
+ this.inputValue.set(currentValues[filterValue] || '');
1359
+ this.open();
1360
+ }
1361
+ }
1362
+ open() {
1363
+ if (this.isDisabled())
1364
+ return;
1365
+ this.markAsTouched();
1366
+ this.isOpen.set(true);
1367
+ this.addDocumentListener();
1368
+ // Focus input after a short delay to allow rendering
1369
+ setTimeout(() => {
1370
+ const inputEl = this.elementRef.nativeElement.querySelector('input');
1371
+ if (inputEl)
1372
+ inputEl.focus();
1373
+ }, 50);
1374
+ }
1375
+ close() {
1376
+ this.isOpen.set(false);
1377
+ this.removeDocumentListener();
1378
+ this.markAsTouched();
1379
+ }
1380
+ onInputChange(event) {
1381
+ const value = event.target.value;
1382
+ this.inputValue.set(value);
1383
+ const type = this.activeFilterType();
1384
+ if (type) {
1385
+ this.filterValues.update(current => ({
1386
+ ...current,
1387
+ [type]: value
1388
+ }));
1389
+ }
1390
+ }
1391
+ applyFilter() {
1392
+ const value = this.inputValue();
1393
+ this.onChange(value);
1394
+ this.valueChange.emit(value);
1395
+ this.filterSelected.emit({
1396
+ filter: this.filterValueKey(),
1397
+ value: value,
1398
+ });
1399
+ this.close();
1400
+ }
1401
+ clear() {
1402
+ this.inputValue.set('');
1403
+ const type = this.activeFilterType();
1404
+ if (type) {
1405
+ this.filterValues.update(current => ({
1406
+ ...current,
1407
+ [type]: ''
1408
+ }));
1409
+ }
1410
+ this.onChange(null);
1411
+ this.valueChange.emit(null);
1412
+ this.close();
1413
+ }
1414
+ markAsTouched() {
1415
+ if (!this.isTouched()) {
1416
+ this.isTouched.set(true);
1417
+ this.onTouched();
1418
+ }
1419
+ }
1420
+ addDocumentListener() {
1421
+ if (this.documentClickListener)
1422
+ this.removeDocumentListener();
1423
+ this.ngZone.runOutsideAngular(() => {
1424
+ this.documentClickListener = (event) => {
1425
+ const target = event.target;
1426
+ if (!this.elementRef.nativeElement.contains(target)) {
1427
+ this.ngZone.run(() => {
1428
+ if (this.isOpen()) {
1429
+ this.markAsTouched();
1430
+ this.close();
1431
+ }
1432
+ });
1433
+ }
1434
+ };
1435
+ setTimeout(() => document.addEventListener('click', this.documentClickListener, true), 10);
1436
+ });
1437
+ }
1438
+ removeDocumentListener() {
1439
+ if (this.documentClickListener) {
1440
+ document.removeEventListener('click', this.documentClickListener, true);
1441
+ this.documentClickListener = undefined;
1442
+ }
1443
+ }
1444
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: InputTextFilter, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
1445
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: InputTextFilter, isStandalone: true, selector: "app-input-text-filter", inputs: { filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, clearTrigger: { classPropertyName: "clearTrigger", publicName: "clearTrigger", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterSelected: "filterSelected", valueChange: "valueChange" }, providers: [
1446
+ {
1447
+ provide: NG_VALUE_ACCESSOR,
1448
+ useExisting: forwardRef(() => InputTextFilter),
1449
+ multi: true,
1450
+ },
1451
+ ], ngImport: i0, template: "<div class=\"input-filter-container\">\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n <div\r\n class=\"chip\"\r\n [class.active]=\"activeFilterType() === item.value\"\r\n (click)=\"toggle(item.value)\"\r\n >\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">\r\n {{ filterValues()[item.value] || item.placeholder || item.label }}\r\n </div>\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"icon icon-tabler icons-tabler-outline icon-tabler-abc\"><path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M3 16v-6a2 2 0 1 1 4 0v6\" /><path d=\"M3 13h4\" /><path d=\"M10 8v6a2 2 0 1 0 4 0v-1a2 2 0 1 0 -4 0v1\" /><path d=\"M20.732 12a2 2 0 0 0 -3.732 1v1a2 2 0 0 0 3.726 1.01\" /></svg>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"input-content\">\r\n <input\r\n type=\"text\"\r\n [value]=\"inputValue()\"\r\n (input)=\"onInputChange($event)\"\r\n [placeholder]=\"placeholder()\"\r\n (keydown.enter)=\"applyFilter()\"\r\n class=\"filter-input\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".input-filter-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon.icon-tabler-abc{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.dropdown{position:absolute;top:100%;left:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:300px;padding:15px}.input-content{display:flex;flex-direction:column;gap:15px}.filter-input{width:100%;padding:10px;border:1px solid #787861;border-radius:5px;background-color:#fdfdf5;color:#454733;font-size:1rem}.filter-input:focus{outline:none;border-color:#454733;box-shadow:0 0 0 2px #45473333}.action-buttons{display:flex;justify-content:flex-end;gap:10px}.action-btn{padding:8px 16px;border:none;border-radius:3px;cursor:pointer;font-size:.9rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: FormsModule }] });
1452
+ }
1453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: InputTextFilter, decorators: [{
1454
+ type: Component,
1455
+ args: [{ selector: 'app-input-text-filter', standalone: true, imports: [CommonModule, ReactiveFormsModule, FormsModule], providers: [
1456
+ {
1457
+ provide: NG_VALUE_ACCESSOR,
1458
+ useExisting: forwardRef(() => InputTextFilter),
1459
+ multi: true,
1460
+ },
1461
+ ], template: "<div class=\"input-filter-container\">\r\n <div class=\"filter-chips\">\r\n @for (item of filters(); track item.value) {\r\n <div\r\n class=\"chip\"\r\n [class.active]=\"activeFilterType() === item.value\"\r\n (click)=\"toggle(item.value)\"\r\n >\r\n <div class=\"content-chip\">\r\n <div class=\"label-text\">\r\n {{ filterValues()[item.value] || item.placeholder || item.label }}\r\n </div>\r\n\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"icon icon-tabler icons-tabler-outline icon-tabler-abc\"><path stroke=\"none\" d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M3 16v-6a2 2 0 1 1 4 0v6\" /><path d=\"M3 13h4\" /><path d=\"M10 8v6a2 2 0 1 0 4 0v-1a2 2 0 1 0 -4 0v1\" /><path d=\"M20.732 12a2 2 0 0 0 -3.732 1v1a2 2 0 0 0 3.726 1.01\" /></svg>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <div class=\"input-content\">\r\n <input\r\n type=\"text\"\r\n [value]=\"inputValue()\"\r\n (input)=\"onInputChange($event)\"\r\n [placeholder]=\"placeholder()\"\r\n (keydown.enter)=\"applyFilter()\"\r\n class=\"filter-input\"\r\n />\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".input-filter-container{position:relative;width:100%}.filter-chips{display:flex;gap:10px;padding:8px;border-radius:12px;position:relative}.chip{border-radius:8px;border-style:solid;border-color:var(--schemes-outline-variant, #c0c7cd);border-width:1px;display:flex;flex-direction:row;align-items:center;justify-content:center;flex-shrink:0;height:32px;cursor:pointer;transition:.2s ease;position:relative}.chip:hover{background-color:#ececcf}.chip.active{color:var(--on-surface, #171c1f);border-color:#b6b69b}.content-chip{padding:6px 8px 6px 16px;display:flex;flex-direction:row;gap:8px;align-items:center;justify-items:center;height:32px}.label-text{color:var(--schemes-on-surface-variant, #454733);font-family:var( --theme-label-large-font-family, \"Roboto-Medium\", sans-serif );font-size:var(--theme-label-large-font-size, 14px);line-height:var(--theme-label-large-line-height, 20px);letter-spacing:var(--theme-label-large-letter-spacing, .1px);font-weight:var(--theme-label-large-font-weight, 500);padding-right:16px}.icon.icon-tabler-abc{width:18px;height:18px;color:var(--on-surface-variant, #40484c)}.dropdown{position:absolute;top:100%;left:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;z-index:1000;box-shadow:0 4px 12px #00000026;min-width:300px;padding:15px}.input-content{display:flex;flex-direction:column;gap:15px}.filter-input{width:100%;padding:10px;border:1px solid #787861;border-radius:5px;background-color:#fdfdf5;color:#454733;font-size:1rem}.filter-input:focus{outline:none;border-color:#454733;box-shadow:0 0 0 2px #45473333}.action-buttons{display:flex;justify-content:flex-end;gap:10px}.action-btn{padding:8px 16px;border:none;border-radius:3px;cursor:pointer;font-size:.9rem;transition:all .2s}.action-btn.secondary{background:transparent;color:#787861;border:1px solid #787861}.action-btn.secondary:hover{background:#7878611a;color:#454733}.action-btn.primary{background:#a9a97f;color:#e3e3d1;border:1px solid #a9a97f}.action-btn.primary:hover{background:#969669;border-color:#969669}\n"] }]
1462
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], clearTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearTrigger", required: false }] }], filterSelected: [{ type: i0.Output, args: ["filterSelected"] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
1463
+
1464
+ class SelectCustomSearch {
1465
+ elementRef;
1466
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
1467
+ placeholder = input('Seleccionar opción', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
1468
+ selectionChange = output();
1469
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
1470
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1471
+ selectedValue = signal(null, ...(ngDevMode ? [{ debugName: "selectedValue" }] : []));
1472
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
1473
+ selectedOption = computed(() => {
1474
+ const value = this.selectedValue();
1475
+ return this.options().find((option) => option.value === value) || null;
1476
+ }, ...(ngDevMode ? [{ debugName: "selectedOption" }] : []));
1477
+ filteredOptions = computed(() => {
1478
+ const term = this.searchTerm().toLowerCase().trim();
1479
+ const opts = this.options();
1480
+ if (!term)
1481
+ return opts;
1482
+ return opts.filter((option) => option.label.toLowerCase().includes(term) ||
1483
+ option.value.toString().toLowerCase().includes(term));
1484
+ }, ...(ngDevMode ? [{ debugName: "filteredOptions" }] : []));
1485
+ onChange = (value) => { };
1486
+ onTouched = () => { };
1487
+ constructor(elementRef) {
1488
+ this.elementRef = elementRef;
1489
+ }
1490
+ writeValue(value) {
1491
+ this.selectedValue.set(value);
1492
+ }
1493
+ registerOnChange(fn) {
1494
+ this.onChange = fn;
1495
+ }
1496
+ registerOnTouched(fn) {
1497
+ this.onTouched = fn;
1498
+ }
1499
+ setDisabledState(isDisabled) {
1500
+ this.isDisabled.set(isDisabled);
1501
+ }
1502
+ toggle() {
1503
+ if (this.isDisabled())
1504
+ return;
1505
+ if (this.isOpen()) {
1506
+ this.close();
1507
+ }
1508
+ else {
1509
+ this.open();
1510
+ }
1511
+ }
1512
+ open() {
1513
+ if (this.isDisabled())
1514
+ return;
1515
+ this.isOpen.set(true);
1516
+ this.searchTerm.set('');
1517
+ setTimeout(() => {
1518
+ const input = this.elementRef.nativeElement.querySelector('.search-input');
1519
+ if (input)
1520
+ input.focus();
1521
+ });
1522
+ }
1523
+ close() {
1524
+ this.isOpen.set(false);
1525
+ this.searchTerm.set('');
1526
+ }
1527
+ selectOption(option) {
1528
+ if (this.isDisabled())
1529
+ return;
1530
+ this.selectedValue.set(option.value);
1531
+ this.onChange(option.value);
1532
+ this.onTouched();
1533
+ this.selectionChange.emit(option);
1534
+ this.close();
1535
+ }
1536
+ onSearch(event) {
1537
+ if (this.isDisabled())
1538
+ return;
1539
+ const target = event.target;
1540
+ this.searchTerm.set(target.value);
1541
+ }
1542
+ onKeyDown(event) {
1543
+ if (this.isDisabled())
1544
+ return;
1545
+ if (event.key === 'Escape') {
1546
+ this.close();
1547
+ }
1548
+ else if (event.key === 'Enter') {
1549
+ const filtered = this.filteredOptions();
1550
+ if (filtered.length > 0) {
1551
+ this.selectOption(filtered[0]);
1552
+ }
1553
+ }
1554
+ }
1555
+ onDocumentClick(event) {
1556
+ if (!this.isDisabled() && !this.elementRef.nativeElement.contains(event.target)) {
1557
+ this.close();
1558
+ }
1559
+ }
1560
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: SelectCustomSearch, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1561
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: SelectCustomSearch, isStandalone: true, selector: "app-select-custom-search", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, providers: [
1562
+ {
1563
+ provide: NG_VALUE_ACCESSOR,
1564
+ useExisting: forwardRef(() => SelectCustomSearch),
1565
+ multi: true,
1566
+ },
1567
+ ], ngImport: i0, template: "<div class=\"select-container\">\r\n <div class=\"select-header\" [class.active]=\"isOpen()\" (click)=\"toggle()\">\r\n <span class=\"selected-text\" [class.placeholder]=\"!selectedOption()\">\r\n {{ selectedOption()?.label || placeholder() }}\r\n </span>\r\n\r\n <span class=\"arrow\" [class.open]=\"isOpen()\"></span>\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <input\r\n type=\"text\"\r\n class=\"search-input\"\r\n [value]=\"searchTerm()\"\r\n placeholder=\"Buscar...\"\r\n (input)=\"onSearch($event)\"\r\n (keydown)=\"onKeyDown($event)\"\r\n />\r\n\r\n @if (filteredOptions().length > 0) {\r\n <div class=\"options\">\r\n @for (option of filteredOptions(); track option.value) {\r\n <div\r\n class=\"option\"\r\n [class.selected]=\"option.value === selectedValue()\"\r\n (click)=\"selectOption(option)\"\r\n [attr.disabled]=\"isDisabled()\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <div class=\"no-results\">No se encontraron resultados</div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".select-container{position:relative;width:100%}.select-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto}.select-header:hover,.select-header:focus{outline:none;border-color:#a9a97f}.select-header.active{border-color:#a9a97f;outline:none}.selected-text{color:#454733;font-size:1.3rem}.selected-text.placeholder{color:#787861}.arrow{width:15px;height:15px;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:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{transform:rotate(180deg)}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;max-height:250px;overflow:hidden;z-index:1000;box-shadow:0 2px 8px #0000001a}.search-input{width:100%;border:none;border-bottom:1px solid #787861;border-radius:0;padding:15px;background-color:transparent;outline:none;font-size:inherit;color:#454733;font-size:1.3rem}.search-input::placeholder{color:#787861}.search-input:focus{outline:none;border-bottom-color:#a9a97f}.options{max-height:180px;overflow-y:auto;background:#e3e3d1}.option{padding:15px;cursor:pointer;border-bottom:1px solid rgba(120,120,97,.2);color:#454733;background:#e3e3d1;font-size:1.3rem}.option:hover{background-color:#a9a97f1a;color:#454733}.option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.option:last-child{border-bottom:none}.no-results{padding:15px;text-align:center;color:#787861;font-style:italic;background:#e3e3d1;font-size:1.3rem}.options::-webkit-scrollbar{width:6px}.options::-webkit-scrollbar-track{background:#7878611a}.options::-webkit-scrollbar-thumb{background:#787861;border-radius:3px}.options::-webkit-scrollbar-thumb:hover{background:#a9a97f}@media (max-width: 768px){.select-header,.search-input,.option{padding:12px 15px}}\n"] });
1568
+ }
1569
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: SelectCustomSearch, decorators: [{
1570
+ type: Component,
1571
+ args: [{ selector: 'app-select-custom-search', imports: [], providers: [
1572
+ {
1573
+ provide: NG_VALUE_ACCESSOR,
1574
+ useExisting: forwardRef(() => SelectCustomSearch),
1575
+ multi: true,
1576
+ },
1577
+ ], template: "<div class=\"select-container\">\r\n <div class=\"select-header\" [class.active]=\"isOpen()\" (click)=\"toggle()\">\r\n <span class=\"selected-text\" [class.placeholder]=\"!selectedOption()\">\r\n {{ selectedOption()?.label || placeholder() }}\r\n </span>\r\n\r\n <span class=\"arrow\" [class.open]=\"isOpen()\"></span>\r\n </div>\r\n\r\n @if (isOpen()) {\r\n <div class=\"dropdown\">\r\n <input\r\n type=\"text\"\r\n class=\"search-input\"\r\n [value]=\"searchTerm()\"\r\n placeholder=\"Buscar...\"\r\n (input)=\"onSearch($event)\"\r\n (keydown)=\"onKeyDown($event)\"\r\n />\r\n\r\n @if (filteredOptions().length > 0) {\r\n <div class=\"options\">\r\n @for (option of filteredOptions(); track option.value) {\r\n <div\r\n class=\"option\"\r\n [class.selected]=\"option.value === selectedValue()\"\r\n (click)=\"selectOption(option)\"\r\n [attr.disabled]=\"isDisabled()\"\r\n >\r\n {{ option.label }}\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <div class=\"no-results\">No se encontraron resultados</div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".select-container{position:relative;width:100%}.select-header{width:100%;border:1px solid #787861;border-radius:5px;padding:15px;background-color:transparent;cursor:pointer;display:flex;justify-content:space-between;align-items:center;min-height:auto}.select-header:hover,.select-header:focus{outline:none;border-color:#a9a97f}.select-header.active{border-color:#a9a97f;outline:none}.selected-text{color:#454733;font-size:1.3rem}.selected-text.placeholder{color:#787861}.arrow{width:15px;height:15px;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:center;color:#787861;flex-shrink:0;transition:transform .2s}.arrow.open{transform:rotate(180deg)}.dropdown{position:absolute;top:100%;left:0;right:0;background:#e3e3d1;border:1px solid #787861;border-top:none;border-radius:0 0 5px 5px;max-height:250px;overflow:hidden;z-index:1000;box-shadow:0 2px 8px #0000001a}.search-input{width:100%;border:none;border-bottom:1px solid #787861;border-radius:0;padding:15px;background-color:transparent;outline:none;font-size:inherit;color:#454733;font-size:1.3rem}.search-input::placeholder{color:#787861}.search-input:focus{outline:none;border-bottom-color:#a9a97f}.options{max-height:180px;overflow-y:auto;background:#e3e3d1}.option{padding:15px;cursor:pointer;border-bottom:1px solid rgba(120,120,97,.2);color:#454733;background:#e3e3d1;font-size:1.3rem}.option:hover{background-color:#a9a97f1a;color:#454733}.option.selected{background-color:#a9a97f;color:#e3e3d1;font-weight:500}.option:last-child{border-bottom:none}.no-results{padding:15px;text-align:center;color:#787861;font-style:italic;background:#e3e3d1;font-size:1.3rem}.options::-webkit-scrollbar{width:6px}.options::-webkit-scrollbar-track{background:#7878611a}.options::-webkit-scrollbar-thumb{background:#787861;border-radius:3px}.options::-webkit-scrollbar-thumb:hover{background:#a9a97f}@media (max-width: 768px){.select-header,.search-input,.option{padding:12px 15px}}\n"] }]
1578
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], onDocumentClick: [{
1579
+ type: HostListener,
1580
+ args: ['document:click', ['$event']]
1581
+ }] } });
1582
+
1583
+ class DynamicFormFields {
1584
+ form = input.required(...(ngDevMode ? [{ debugName: "form" }] : []));
1585
+ sections = input([], ...(ngDevMode ? [{ debugName: "sections" }] : []));
1586
+ copyFromSubscriptions = [];
1587
+ realTimeCopySubscriptions = [];
1588
+ ngOnInit() {
1589
+ setTimeout(() => {
1590
+ this.setupCopyFromLogic();
1591
+ }, 100);
1592
+ }
1593
+ getDatePickerMode(fieldType) {
1594
+ return fieldType === 'datetime-local' ? 'datetime' : 'date';
1595
+ }
1596
+ setupCopyFromLogic() {
1597
+ this.sections().forEach((section) => {
1598
+ section.fields.forEach((field) => {
1599
+ if (field.copyFrom) {
1600
+ this.setupFieldCopy(field);
1601
+ }
1602
+ });
1603
+ });
1604
+ }
1605
+ setupFieldCopy(field) {
1606
+ const targetControl = this.form().get(field.key);
1607
+ if (!targetControl)
1608
+ return;
1609
+ const sourceControl = this.form().get(field.copyFrom);
1610
+ if (!sourceControl)
1611
+ return;
1612
+ const triggerFieldKey = this.findTriggerField();
1613
+ if (!triggerFieldKey) {
1614
+ this.setupSimpleCopy(sourceControl, targetControl);
1615
+ return;
1616
+ }
1617
+ const triggerControl = this.form().get(triggerFieldKey);
1618
+ if (!triggerControl) {
1619
+ this.setupSimpleCopy(sourceControl, targetControl);
1620
+ return;
1621
+ }
1622
+ const subscription = triggerControl.valueChanges.subscribe((shouldCopy) => {
1623
+ if (shouldCopy) {
1624
+ targetControl.setValue(sourceControl.value);
1625
+ targetControl.disable();
1626
+ this.setupRealTimeCopy(sourceControl, targetControl, field.key);
1627
+ }
1628
+ else {
1629
+ targetControl.enable();
1630
+ targetControl.setValue('');
1631
+ this.removeRealTimeCopy(field.key);
1632
+ }
1633
+ });
1634
+ this.copyFromSubscriptions.push(subscription);
1635
+ if (triggerControl.value === true) {
1636
+ targetControl.setValue(sourceControl.value);
1637
+ targetControl.disable();
1638
+ this.setupRealTimeCopy(sourceControl, targetControl, field.key);
1639
+ }
1640
+ }
1641
+ findTriggerField() {
1642
+ for (const section of this.sections()) {
1643
+ const triggerField = section.fields.find((f) => f.type === 'radio' &&
1644
+ (f.key.includes('is_owner') || f.key.includes('infractor-owner')));
1645
+ if (triggerField)
1646
+ return triggerField.key;
1647
+ }
1648
+ return '';
1649
+ }
1650
+ setupSimpleCopy(sourceControl, targetControl) {
1651
+ const subscription = sourceControl.valueChanges.subscribe((value) => {
1652
+ targetControl.setValue(value);
1653
+ });
1654
+ this.copyFromSubscriptions.push(subscription);
1655
+ }
1656
+ setupRealTimeCopy(sourceControl, targetControl, fieldKey) {
1657
+ this.removeRealTimeCopy(fieldKey);
1658
+ const subscription = sourceControl.valueChanges.subscribe((value) => {
1659
+ targetControl.setValue(value);
1660
+ });
1661
+ this.realTimeCopySubscriptions.push({
1662
+ fieldKey,
1663
+ subscription,
1664
+ });
1665
+ }
1666
+ removeRealTimeCopy(fieldKey) {
1667
+ const index = this.realTimeCopySubscriptions.findIndex((item) => item.fieldKey === fieldKey);
1668
+ if (index >= 0) {
1669
+ this.realTimeCopySubscriptions[index].subscription.unsubscribe();
1670
+ this.realTimeCopySubscriptions.splice(index, 1);
1671
+ }
1672
+ }
1673
+ ctrl = (key) => this.form().get(key);
1674
+ getCheckboxArray(key) {
1675
+ return this.form().get(key);
1676
+ }
1677
+ onCheckboxChange(event, fieldKey, optionIndex) {
1678
+ const checkbox = event.target;
1679
+ const checkboxArray = this.getCheckboxArray(fieldKey);
1680
+ checkboxArray.at(optionIndex).setValue(checkbox.checked);
1681
+ }
1682
+ ngOnDestroy() {
1683
+ this.copyFromSubscriptions.forEach((sub) => sub.unsubscribe());
1684
+ this.realTimeCopySubscriptions.forEach((item) => item.subscription.unsubscribe());
1685
+ }
1686
+ onCancel() {
1687
+ }
1688
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: DynamicFormFields, deps: [], target: i0.ɵɵFactoryTarget.Component });
1689
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: DynamicFormFields, isStandalone: true, selector: "app-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 } }, ngImport: i0, template: "@if (form()) {\r\n<form class=\"form\" [formGroup]=\"form()\">\r\n @for (sec of sections(); track $index) {\r\n <section class=\"section\">\r\n @if (sec.title) {\r\n <h3 class=\"section-title\">{{ sec.title }}</h3>\r\n } @if (sec.description) {\r\n <p class=\"section-desc\">{{ sec.description }}</p>\r\n }\r\n\r\n <div class=\"grid\">\r\n @for (f of sec.fields; track f.key) {\r\n <div class=\"col\" [style.--col-span]=\"f.col || 6\">\r\n @if(f.label) {\r\n <label\r\n [class.label-radio]=\"f.type === 'radio'\"\r\n [class.label-checkbox]=\"f.type === 'checkbox'\"\r\n [class.label-disabled]=\"f.readonly\"\r\n class=\"label\"\r\n >\r\n {{ f.label }} </label\r\n >} @if (['text','number','email', 'password', 'time'].includes(f.type))\r\n {\r\n <input\r\n class=\"input\"\r\n [type]=\"f.type\"\r\n [placeholder]=\"f.placeholder\"\r\n [formControlName]=\"f.key\"\r\n [readonly]=\"f.readonly\"\r\n />\r\n } @if (['date', 'datetime-local'].includes(f.type)) {\r\n <app-date-time-picker\r\n [mode]=\"getDatePickerMode(f.type)\"\r\n [placeholder]=\"\r\n f.placeholder ||\r\n (f.type === 'date'\r\n ? 'Seleccionar fecha'\r\n : 'Seleccionar fecha y hora')\r\n \"\r\n [formControlName]=\"f.key\"\r\n [minDate]=\"f.minDate || null\"\r\n [maxDate]=\"f.maxDate || null\"\r\n />\r\n } @if (f.type === 'textarea') {\r\n <textarea\r\n class=\"input textarea\"\r\n [placeholder]=\"f.placeholder\"\r\n [formControlName]=\"f.key\"\r\n rows=\"6\"\r\n ></textarea>\r\n }@if (f.type === 'select') {\r\n\r\n <app-select-custom-search\r\n [options]=\"f.options ?? []\"\r\n [placeholder]=\"f.placeholder || 'Seleccionar...'\"\r\n [formControlName]=\"f.key\"\r\n />\r\n } @if (f.type === 'radio') {\r\n <div class=\"radio-group\">\r\n @for (o of f.options ?? []; track o.value) {\r\n <label class=\"radio\">\r\n {{ o.label }}\r\n <input type=\"radio\" [value]=\"o.value\" [formControlName]=\"f.key\" />\r\n </label>\r\n }\r\n </div>\r\n } @if (f.type === 'checkbox') {\r\n <div class=\"checkbox-group\">\r\n @for (o of f.options ?? []; track o.value; let i = $index) {\r\n <label class=\"checkbox\">\r\n {{ o.label }}\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"getCheckboxArray(f.key).at(i).value\"\r\n (change)=\"onCheckboxChange($event, f.key, i)\"\r\n />\r\n </label>\r\n }\r\n </div>\r\n } @if (f.type === 'disabled') {\r\n <label class=\"label\">{{ f.label }}</label>\r\n <input\r\n class=\"input input--disabled\"\r\n [placeholder]=\"f.placeholder || 'Autom\u00E1tico'\"\r\n disabled\r\n />\r\n } @if (ctrl(f.key)?.touched && ctrl(f.key)?.invalid) {\r\n <div class=\"error\">\r\n @if (ctrl(f.key)?.errors?.['required']) {\r\n <span>Campo requerido</span>\r\n } @if (ctrl(f.key)?.errors?.['email']) {\r\n <span>Correo inv\u00E1lido</span>\r\n } @if (ctrl(f.key)?.errors?.['pattern']) {\r\n <span>\r\n @switch (f.patternType) { @case ('numbers') { Solo se permiten\r\n n\u00FAmeros } @case ('phone') { Formato de tel\u00E9fono inv\u00E1lido } @case\r\n ('text') { Solo se permiten letras y espacios } @case ('username') {\r\n Solo se permiten letras, n\u00FAmeros, puntos y guiones bajos (no al\r\n inicio ni al final) } @case ('alphanumeric') { Solo se permiten\r\n letras y n\u00FAmeros } @default { Formato inv\u00E1lido } }\r\n </span>\r\n } @if (ctrl(f.key)?.errors?.['notMatching']) {\r\n <span>Las contrase\u00F1as no coinciden</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n }\r\n</form>\r\n}\r\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:25px}.col{grid-column:span var(--col-span, 6);min-width:0;width:100%;position:relative;padding-bottom:20px}.label{position:absolute;top:-8px;left:12px;font-size:1.2rem;color:#454733;background-color:var(--surface-container-high, #ebe8d6);padding:0 4px;font-weight:500;z-index:1}.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}.label-radio{font-size:1.4rem;position:static;padding-left:0}.radio-group{display:flex;gap:1rem;padding:.5rem 0}.radio{font-size:1.4rem;display:flex;align-items:center;gap:10px;color:#1c1c12;margin-top:20px}.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;line-height:1.5}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[readonly]{border:1px solid #a9a97f;color:#a9a97f}@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$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: SelectCustomSearch, selector: "app-select-custom-search", inputs: ["options", "placeholder"], outputs: ["selectionChange"] }, { kind: "component", type: DateTimePicker, selector: "app-date-time-picker", inputs: ["mode", "placeholder", "minDate", "maxDate"], outputs: ["dateChange"] }] });
1690
+ }
1691
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: DynamicFormFields, decorators: [{
1692
+ type: Component,
1693
+ args: [{ selector: 'app-dynamic-form-fields', standalone: true, imports: [ReactiveFormsModule, SelectCustomSearch, DateTimePicker], template: "@if (form()) {\r\n<form class=\"form\" [formGroup]=\"form()\">\r\n @for (sec of sections(); track $index) {\r\n <section class=\"section\">\r\n @if (sec.title) {\r\n <h3 class=\"section-title\">{{ sec.title }}</h3>\r\n } @if (sec.description) {\r\n <p class=\"section-desc\">{{ sec.description }}</p>\r\n }\r\n\r\n <div class=\"grid\">\r\n @for (f of sec.fields; track f.key) {\r\n <div class=\"col\" [style.--col-span]=\"f.col || 6\">\r\n @if(f.label) {\r\n <label\r\n [class.label-radio]=\"f.type === 'radio'\"\r\n [class.label-checkbox]=\"f.type === 'checkbox'\"\r\n [class.label-disabled]=\"f.readonly\"\r\n class=\"label\"\r\n >\r\n {{ f.label }} </label\r\n >} @if (['text','number','email', 'password', 'time'].includes(f.type))\r\n {\r\n <input\r\n class=\"input\"\r\n [type]=\"f.type\"\r\n [placeholder]=\"f.placeholder\"\r\n [formControlName]=\"f.key\"\r\n [readonly]=\"f.readonly\"\r\n />\r\n } @if (['date', 'datetime-local'].includes(f.type)) {\r\n <app-date-time-picker\r\n [mode]=\"getDatePickerMode(f.type)\"\r\n [placeholder]=\"\r\n f.placeholder ||\r\n (f.type === 'date'\r\n ? 'Seleccionar fecha'\r\n : 'Seleccionar fecha y hora')\r\n \"\r\n [formControlName]=\"f.key\"\r\n [minDate]=\"f.minDate || null\"\r\n [maxDate]=\"f.maxDate || null\"\r\n />\r\n } @if (f.type === 'textarea') {\r\n <textarea\r\n class=\"input textarea\"\r\n [placeholder]=\"f.placeholder\"\r\n [formControlName]=\"f.key\"\r\n rows=\"6\"\r\n ></textarea>\r\n }@if (f.type === 'select') {\r\n\r\n <app-select-custom-search\r\n [options]=\"f.options ?? []\"\r\n [placeholder]=\"f.placeholder || 'Seleccionar...'\"\r\n [formControlName]=\"f.key\"\r\n />\r\n } @if (f.type === 'radio') {\r\n <div class=\"radio-group\">\r\n @for (o of f.options ?? []; track o.value) {\r\n <label class=\"radio\">\r\n {{ o.label }}\r\n <input type=\"radio\" [value]=\"o.value\" [formControlName]=\"f.key\" />\r\n </label>\r\n }\r\n </div>\r\n } @if (f.type === 'checkbox') {\r\n <div class=\"checkbox-group\">\r\n @for (o of f.options ?? []; track o.value; let i = $index) {\r\n <label class=\"checkbox\">\r\n {{ o.label }}\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"getCheckboxArray(f.key).at(i).value\"\r\n (change)=\"onCheckboxChange($event, f.key, i)\"\r\n />\r\n </label>\r\n }\r\n </div>\r\n } @if (f.type === 'disabled') {\r\n <label class=\"label\">{{ f.label }}</label>\r\n <input\r\n class=\"input input--disabled\"\r\n [placeholder]=\"f.placeholder || 'Autom\u00E1tico'\"\r\n disabled\r\n />\r\n } @if (ctrl(f.key)?.touched && ctrl(f.key)?.invalid) {\r\n <div class=\"error\">\r\n @if (ctrl(f.key)?.errors?.['required']) {\r\n <span>Campo requerido</span>\r\n } @if (ctrl(f.key)?.errors?.['email']) {\r\n <span>Correo inv\u00E1lido</span>\r\n } @if (ctrl(f.key)?.errors?.['pattern']) {\r\n <span>\r\n @switch (f.patternType) { @case ('numbers') { Solo se permiten\r\n n\u00FAmeros } @case ('phone') { Formato de tel\u00E9fono inv\u00E1lido } @case\r\n ('text') { Solo se permiten letras y espacios } @case ('username') {\r\n Solo se permiten letras, n\u00FAmeros, puntos y guiones bajos (no al\r\n inicio ni al final) } @case ('alphanumeric') { Solo se permiten\r\n letras y n\u00FAmeros } @default { Formato inv\u00E1lido } }\r\n </span>\r\n } @if (ctrl(f.key)?.errors?.['notMatching']) {\r\n <span>Las contrase\u00F1as no coinciden</span>\r\n }\r\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n }\r\n</form>\r\n}\r\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:25px}.col{grid-column:span var(--col-span, 6);min-width:0;width:100%;position:relative;padding-bottom:20px}.label{position:absolute;top:-8px;left:12px;font-size:1.2rem;color:#454733;background-color:var(--surface-container-high, #ebe8d6);padding:0 4px;font-weight:500;z-index:1}.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}.label-radio{font-size:1.4rem;position:static;padding-left:0}.radio-group{display:flex;gap:1rem;padding:.5rem 0}.radio{font-size:1.4rem;display:flex;align-items:center;gap:10px;color:#1c1c12;margin-top:20px}.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;line-height:1.5}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[readonly]{border:1px solid #a9a97f;color:#a9a97f}@media (max-width: 768px){.grid{grid-template-columns:1fr}.col{grid-column:span 1!important}}\n"] }]
1694
+ }], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }] } });
1695
+
1696
+ class WizardForm {
1697
+ form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : []));
1698
+ currentStep = input(1, ...(ngDevMode ? [{ debugName: "currentStep" }] : []));
1699
+ steps = input([], ...(ngDevMode ? [{ debugName: "steps" }] : []));
1700
+ canContinue = output();
1701
+ // Devuelve la key del paso
1702
+ currentStepKey = computed(() => {
1703
+ const index = this.currentStep() - 1;
1704
+ return this.steps()[index]?.key ?? null;
1705
+ }, ...(ngDevMode ? [{ debugName: "currentStepKey" }] : []));
1706
+ // Devuelve el subform correspondiente
1707
+ currentGroup = computed(() => {
1708
+ const key = this.currentStepKey();
1709
+ return key ? this.form()?.get(key) : null;
1710
+ }, ...(ngDevMode ? [{ debugName: "currentGroup" }] : []));
1711
+ constructor() {
1712
+ effect(() => {
1713
+ const group = this.currentGroup();
1714
+ if (!group) {
1715
+ this.canContinue.emit(false);
1716
+ return;
1717
+ }
1718
+ this.canContinue.emit(group.valid);
1719
+ group.statusChanges.subscribe(() => {
1720
+ this.canContinue.emit(group.valid);
1721
+ });
1722
+ group.valueChanges.subscribe(() => {
1723
+ this.canContinue.emit(group.valid);
1724
+ });
1725
+ });
1726
+ }
1727
+ currentGroupComponent = computed(() => {
1728
+ const index = this.currentStep() - 1;
1729
+ return this.steps()[index].component;
1730
+ }, ...(ngDevMode ? [{ debugName: "currentGroupComponent" }] : []));
1731
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: WizardForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
1732
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: WizardForm, isStandalone: true, selector: "lib-wizard-form", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, currentStep: { classPropertyName: "currentStep", publicName: "currentStep", isSignal: true, isRequired: false, transformFunction: null }, steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { canContinue: "canContinue" }, ngImport: i0, template: "<ng-container>\r\n\r\n @if (currentGroup()) {\r\n <ng-container *ngComponentOutlet=\"currentGroupComponent()\"></ng-container>\r\n }\r\n\r\n</ng-container>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }] });
1733
+ }
1734
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: WizardForm, decorators: [{
1735
+ type: Component,
1736
+ args: [{ selector: 'lib-wizard-form', standalone: true, imports: [CommonModule], template: "<ng-container>\r\n\r\n @if (currentGroup()) {\r\n <ng-container *ngComponentOutlet=\"currentGroupComponent()\"></ng-container>\r\n }\r\n\r\n</ng-container>" }]
1737
+ }], ctorParameters: () => [], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], currentStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentStep", required: false }] }], steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], canContinue: [{ type: i0.Output, args: ["canContinue"] }] } });
1738
+
1739
+ class ModalForm {
1740
+ title = input('Formulario', ...(ngDevMode ? [{ debugName: "title" }] : []));
1741
+ submitLabel = input('Guardar', ...(ngDevMode ? [{ debugName: "submitLabel" }] : []));
1742
+ form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : []));
1743
+ steps = input([], ...(ngDevMode ? [{ debugName: "steps" }] : []));
1744
+ currentStep = signal(1, ...(ngDevMode ? [{ debugName: "currentStep" }] : []));
1745
+ canContinue = signal(false, ...(ngDevMode ? [{ debugName: "canContinue" }] : []));
1746
+ onSubmit = output();
1747
+ onCancel = output();
1748
+ nextStep() {
1749
+ if (this.currentStep() < this.steps().length) {
1750
+ this.currentStep.update(v => v + 1);
1751
+ }
1752
+ }
1753
+ prevStep() {
1754
+ if (this.currentStep() > 1) {
1755
+ this.currentStep.update(v => v - 1);
1756
+ }
1757
+ }
1758
+ submitForm() {
1759
+ this.onSubmit.emit(this.form()?.value);
1760
+ }
1761
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ModalForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
1762
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.13", type: ModalForm, isStandalone: true, selector: "lib-modal-form", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, submitLabel: { classPropertyName: "submitLabel", publicName: "submitLabel", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSubmit: "onSubmit", onCancel: "onCancel" }, ngImport: i0, template: "<div class=\"modal-form-container\">\r\n\r\n <div class=\"header-content\">\r\n <h1>{{ title() }}</h1>\r\n </div>\r\n\r\n <div class=\"steps\">\r\n @for (step of steps(); track $index) {\r\n <div\r\n class=\"step\"\r\n [class.step-active]=\"currentStep() === $index + 1\"\r\n [class.step-completed]=\"currentStep() > $index + 1\"\r\n >\r\n @if (currentStep() > $index + 1) {\r\n <span class=\"check-icon\">\u2714</span>\r\n }\r\n <span>Paso {{ $index + 1 }}: {{ step.label }}</span>\r\n </div>\r\n\r\n @if ($index < steps().length - 1) {\r\n <span class=\"separator\">\u203A</span>\r\n }\r\n }\r\n </div>\r\n\r\n <div class=\"divider\"></div>\r\n\r\n <!-- Render din\u00E1mico del paso actual -->\r\n <lib-wizard-form\r\n [form]=\"form()\"\r\n [currentStep]=\"currentStep()\"\r\n [steps]=\"steps()\"\r\n (canContinue)=\"canContinue.set($event)\"\r\n ></lib-wizard-form>\r\n\r\n <div class=\"actions\">\r\n @if (currentStep() === 1) {\r\n <button type=\"button\" class=\"btn--ghost\" (click)=\"onCancel.emit()\">Cancelar</button>\r\n } @else {\r\n <button type=\"button\" class=\"btn--ghost\" (click)=\"prevStep()\">Volver</button>\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"btn\"\r\n (click)=\"currentStep() === steps().length ? submitForm() : nextStep()\"\r\n [disabled]=\"!canContinue()\"\r\n >\r\n {{ currentStep() === steps().length ? submitLabel() : 'Continuar' }}\r\n </button>\r\n </div>\r\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: WizardForm, selector: "lib-wizard-form", inputs: ["form", "currentStep", "steps"], outputs: ["canContinue"] }] });
1763
+ }
1764
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImport: i0, type: ModalForm, decorators: [{
1765
+ type: Component,
1766
+ args: [{ selector: 'lib-modal-form', standalone: true, imports: [CommonModule, WizardForm], template: "<div class=\"modal-form-container\">\r\n\r\n <div class=\"header-content\">\r\n <h1>{{ title() }}</h1>\r\n </div>\r\n\r\n <div class=\"steps\">\r\n @for (step of steps(); track $index) {\r\n <div\r\n class=\"step\"\r\n [class.step-active]=\"currentStep() === $index + 1\"\r\n [class.step-completed]=\"currentStep() > $index + 1\"\r\n >\r\n @if (currentStep() > $index + 1) {\r\n <span class=\"check-icon\">\u2714</span>\r\n }\r\n <span>Paso {{ $index + 1 }}: {{ step.label }}</span>\r\n </div>\r\n\r\n @if ($index < steps().length - 1) {\r\n <span class=\"separator\">\u203A</span>\r\n }\r\n }\r\n </div>\r\n\r\n <div class=\"divider\"></div>\r\n\r\n <!-- Render din\u00E1mico del paso actual -->\r\n <lib-wizard-form\r\n [form]=\"form()\"\r\n [currentStep]=\"currentStep()\"\r\n [steps]=\"steps()\"\r\n (canContinue)=\"canContinue.set($event)\"\r\n ></lib-wizard-form>\r\n\r\n <div class=\"actions\">\r\n @if (currentStep() === 1) {\r\n <button type=\"button\" class=\"btn--ghost\" (click)=\"onCancel.emit()\">Cancelar</button>\r\n } @else {\r\n <button type=\"button\" class=\"btn--ghost\" (click)=\"prevStep()\">Volver</button>\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"btn\"\r\n (click)=\"currentStep() === steps().length ? submitForm() : nextStep()\"\r\n [disabled]=\"!canContinue()\"\r\n >\r\n {{ currentStep() === steps().length ? submitLabel() : 'Continuar' }}\r\n </button>\r\n </div>\r\n</div>" }]
1767
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], submitLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitLabel", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], onSubmit: [{ type: i0.Output, args: ["onSubmit"] }], onCancel: [{ type: i0.Output, args: ["onCancel"] }] } });
1768
+
537
1769
  // src/app/components/public-api.ts
538
1770
  // Exportar el componente
539
1771
 
@@ -541,5 +1773,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.13", ngImpo
541
1773
  * Generated bundle index. Do not edit.
542
1774
  */
543
1775
 
544
- export { DateTimeFilter, PaginationComponent, Table };
1776
+ export { DateTimeFilter, DateTimePicker, DynamicFormFields, InputNumberFilter, InputSelectFilter, InputTextFilter, ModalForm, PaginationComponent, SelectCustomSearch, Table, WizardForm };
545
1777
  //# sourceMappingURL=sapenlinea-components.mjs.map