tailjng 0.0.13 → 0.0.15

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.
Files changed (87) hide show
  1. package/cli/component-manager.js +45 -0
  2. package/cli/dependency-manager.js +52 -0
  3. package/cli/file-operations.js +88 -0
  4. package/cli/index.js +51 -0
  5. package/cli/settings/colors.js +17 -0
  6. package/cli/settings/components-list.js +87 -0
  7. package/cli/settings/header-generator.js +42 -0
  8. package/cli/settings/path-utils.js +50 -0
  9. package/cli/settings/prompt-utils.js +37 -0
  10. package/cli/settings/tailwind-check.js +21 -0
  11. package/fesm2022/tailjng.mjs +903 -25
  12. package/fesm2022/tailjng.mjs.map +1 -1
  13. package/lib/config/tailjng-config.token.d.ts +3 -0
  14. package/lib/interfaces/alert/dialog-alert.interface.d.ts +52 -0
  15. package/lib/interfaces/alert/toast-alert.interface.d.ts +52 -0
  16. package/lib/interfaces/config.interface.d.ts +5 -0
  17. package/lib/interfaces/crud/api-response.d.ts +29 -0
  18. package/lib/interfaces/crud/crud.interface.d.ts +103 -0
  19. package/lib/services/alert/dialog-alert.service.d.ts +24 -0
  20. package/lib/services/alert/toast-alert.service.d.ts +26 -0
  21. package/lib/services/crud/converter-crud.service.d.ts +41 -0
  22. package/lib/services/crud/generic-crud.service.d.ts +81 -0
  23. package/lib/services/http/error-handler-http.service.d.ts +26 -0
  24. package/lib/services/http/params-http.service.d.ts +13 -0
  25. package/lib/services/static/icons.service.d.ts +31 -0
  26. package/lib/services/transformer/calendar.service.d.ts +71 -0
  27. package/package.json +5 -3
  28. package/public-api.d.ts +10 -3
  29. package/src/lib/components/alert/dialog-alert/dialog-alert.component.css +0 -0
  30. package/src/lib/components/alert/dialog-alert/dialog-alert.component.html +72 -0
  31. package/src/lib/components/alert/dialog-alert/dialog-alert.component.ts +66 -0
  32. package/src/lib/components/alert/toast-alert/toast-alert.component.css +5 -0
  33. package/src/lib/components/alert/toast-alert/toast-alert.component.html +76 -0
  34. package/src/lib/components/alert/toast-alert/toast-alert.component.ts +87 -0
  35. package/src/lib/components/button/button.component.css +0 -0
  36. package/src/lib/components/button/button.component.html +36 -0
  37. package/src/lib/components/button/button.component.ts +95 -0
  38. package/src/lib/components/checkbox/input-checkbox/input-checkbox.component.css +0 -0
  39. package/src/lib/components/checkbox/input-checkbox/input-checkbox.component.html +23 -0
  40. package/src/lib/components/checkbox/input-checkbox/input-checkbox.component.ts +44 -0
  41. package/src/lib/components/checkbox/switch-checkbox/switch-checkbox.component.css +0 -0
  42. package/src/lib/components/checkbox/switch-checkbox/switch-checkbox.component.html +26 -0
  43. package/src/lib/components/checkbox/switch-checkbox/switch-checkbox.component.ts +29 -0
  44. package/src/lib/components/color/colors.service.ts +109 -0
  45. package/src/lib/components/dialog/dialog.component.css +8 -0
  46. package/src/lib/components/dialog/dialog.component.html +57 -0
  47. package/src/lib/components/dialog/dialog.component.ts +179 -0
  48. package/src/lib/components/image/viewer-image/viewer-image.component.css +4 -0
  49. package/src/lib/components/image/viewer-image/viewer-image.component.html +75 -0
  50. package/src/lib/components/image/viewer-image/viewer-image.component.ts +131 -0
  51. package/src/lib/components/input/file-input/file-input.component.css +0 -0
  52. package/src/lib/components/input/file-input/file-input.component.html +49 -0
  53. package/src/lib/components/input/file-input/file-input.component.ts +218 -0
  54. package/src/lib/components/input/input/input.component.css +0 -0
  55. package/src/lib/components/input/input/input.component.html +24 -0
  56. package/src/lib/components/input/input/input.component.ts +78 -0
  57. package/src/lib/components/input/range-input/range-input.component.css +0 -0
  58. package/src/lib/components/input/range-input/range-input.component.html +64 -0
  59. package/src/lib/components/input/range-input/range-input.component.ts +78 -0
  60. package/src/lib/components/input/textarea-input/textarea-input.component.css +0 -0
  61. package/src/lib/components/input/textarea-input/textarea-input.component.html +21 -0
  62. package/src/lib/components/input/textarea-input/textarea-input.component.ts +75 -0
  63. package/src/lib/components/label/label.component.html +1 -1
  64. package/src/lib/components/label/label.component.ts +1 -1
  65. package/src/lib/components/mode-toggle/mode-toggle.component.css +0 -0
  66. package/src/lib/components/mode-toggle/mode-toggle.component.html +8 -0
  67. package/src/lib/components/mode-toggle/mode-toggle.component.ts +61 -0
  68. package/src/lib/components/progress-bar/progress-bar.component.css +0 -0
  69. package/src/lib/components/progress-bar/progress-bar.component.html +22 -0
  70. package/src/lib/components/progress-bar/progress-bar.component.ts +20 -0
  71. package/src/lib/components/select/dropdown/dropdown.component.css +0 -0
  72. package/src/lib/components/select/dropdown/dropdown.component.html +95 -0
  73. package/src/lib/components/select/dropdown/dropdown.component.ts +562 -0
  74. package/src/lib/components/select/multi-dropdown/multi-dropdown.component.css +0 -0
  75. package/src/lib/components/select/multi-dropdown/multi-dropdown.component.html +87 -0
  76. package/src/lib/components/select/multi-dropdown/multi-dropdown.component.ts +315 -0
  77. package/src/lib/components/select/multi-table/multi-table.component.css +0 -0
  78. package/src/lib/components/select/multi-table/multi-table.component.html +83 -0
  79. package/src/lib/components/select/multi-table/multi-table.component.ts +230 -0
  80. package/src/lib/components/toggle-radio/toggle-radio.component.css +0 -0
  81. package/src/lib/components/toggle-radio/toggle-radio.component.html +51 -0
  82. package/src/lib/components/toggle-radio/toggle-radio.component.ts +203 -0
  83. package/src/styles.css +126 -0
  84. package/cli/tailjng.js +0 -105
  85. package/lib/services/icons.service.d.ts +0 -9
  86. package/lib/tailjng.component.d.ts +0 -5
  87. package/lib/tailjng.service.d.ts +0 -6
@@ -0,0 +1,87 @@
1
+ <div class="relative w-full h-full">
2
+ <!-- Botón para multi-dropdown -->
3
+ <div class="w-auto" #selectButton>
4
+ <button type="button"
5
+ [disabled]="disabled || isLoading"
6
+ (click)="toggleDropdown()"
7
+ class="flex w-full h-[40px] items-center justify-between px-3 py-2 text-sm bg-background dark:bg-dark-background border border-border dark:border-dark-border rounded focus:outline-none focus:ring-2 focus:ring-primary select-none"
8
+ [ngClass]="{ 'opacity-50 cursor-not-allowed pointer-events-none': disabled || isLoading }">
9
+ <span class="truncate text-black dark:text-white"
10
+ [ngClass]="{ 'opacity-50': selectedValues.length === 0 }">
11
+ {{ displayLabel }}
12
+ </span>
13
+ <div class="flex items-center">
14
+ @if (showClear && selectedValues.length > 0) {
15
+ <button type="button"
16
+ (click)="clearSelection($event)"
17
+ class="pr-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none cursor-pointer">
18
+ <lucide-icon [name]="icons.x" size="14"></lucide-icon>
19
+ </button>
20
+ }
21
+ @if (!isLoading) {
22
+ <lucide-icon [name]="icons.chevronDown"
23
+ size="16"
24
+ class="transition duration-300 ease-in-out text-gray-400"
25
+ [ngClass]="{ 'rotate-180': isDropdownOpen }"></lucide-icon>
26
+ } @else {
27
+ <lucide-icon [name]="icons.loading"
28
+ size="16"
29
+ class="text-gray-400 animate-spin"></lucide-icon>
30
+ }
31
+ </div>
32
+ </button>
33
+ </div>
34
+ </div>
35
+
36
+ <!-- Dropdown positioned outside the flow -->
37
+ @if (isDropdownOpen) {
38
+ <div @modalTransition
39
+ class="absolute z-[100] min-w-[250px] mt-1 bg-background dark:bg-dark-background rounded-lg shadow-lg border border-border border-dark-border"
40
+ [style.width.px]="dropdownWidth"
41
+ [style.top.px]="dropdownTop"
42
+ [style.left.px]="dropdownLeft">
43
+ <div class="pt-1 pl-3 pr-3 pb-3">
44
+ <div class="text-[10px] font-medium text-gray-500 dark:text-gray-500 mb-1">{{title}}</div>
45
+
46
+ <!-- Multi-Dropdown Options -->
47
+ <div class="max-h-60 overflow-auto flex flex-col gap-1 scroll-element">
48
+ <!-- Select All Option -->
49
+
50
+ @if (processedOptions.length > 0) {
51
+ @if (enableSelectAll) {
52
+ <div (click)="toggleSelectAll()"
53
+ class="flex gap-5 items-center border border-accent dark:border-dark-accent/50 px-4 py-2 cursor-pointer group rounded-md transition-all hover:bg-accent dark:hover:bg-dark-accent/50"
54
+ [ngClass]="{'bg-accent dark:bg-dark-accent/50': isAllSelected()}">
55
+ <input type="checkbox" class="hidden" [checked]="isAllSelected()" />
56
+ @if (isAllSelected()) {
57
+ <lucide-icon [name]="icons.check" size="15" class="transition text-black dark:text-white" />
58
+ } @else {
59
+ <lucide-icon [name]="icons.squareDashedMousePointer" size="15" class="transition text-black dark:text-white opacity-40" />
60
+ }
61
+ <label class="text-black dark:text-white text-sm font-medium w-full">{{ labelSelectAll }}</label>
62
+ </div>
63
+ }
64
+
65
+ <!-- Individual Options -->
66
+ @for (option of processedOptions; track option.value) {
67
+ <div (click)="toggleOption(option)"
68
+ class="flex gap-5 items-center border border-accent dark:border-dark-accent/50 px-4 py-2 cursor-pointer group rounded-md transition-all hover:bg-accent dark:hover:bg-dark-accent/50"
69
+ [ngClass]="{'bg-accent dark:bg-dark-accent/50' : selectedValues.includes(option.value)}">
70
+ <input type="checkbox" class="hidden" [checked]="selectedValues.includes(option.value)" />
71
+ @if (selectedValues.includes(option.value)) {
72
+ <lucide-icon [name]="icons.check" size="15" class="transition text-black dark:text-white" />
73
+ } @else {
74
+ <lucide-icon [name]="icons.squareDashedMousePointer" size="15" class="transition text-black dark:text-white opacity-40" />
75
+ }
76
+ <label class="text-black dark:text-white text-sm font-medium w-full">{{ option.text }}</label>
77
+ </div>
78
+ }
79
+ }
80
+
81
+ @if (processedOptions.length === 0) {
82
+ <div class="px-3 py-2 text-sm text-gray-500">No hay opciones disponibles</div>
83
+ }
84
+ </div>
85
+ </div>
86
+ </div>
87
+ }
@@ -0,0 +1,315 @@
1
+ import { Component, Input, Output, EventEmitter, ElementRef, ViewChild, OnDestroy, ChangeDetectorRef, AfterViewInit, OnInit, SimpleChanges, OnChanges, } from "@angular/core"
2
+ import { FormsModule, type ControlValueAccessor, ReactiveFormsModule, NG_VALUE_ACCESSOR } from "@angular/forms"
3
+ import { CommonModule } from "@angular/common"
4
+ import { X, LucideAngularModule, ChevronDown, Check, Loader2, SquareDashedMousePointer } from "lucide-angular"
5
+ import { animate, style, transition, trigger } from "@angular/animations"
6
+
7
+ interface ProcessedOption {
8
+ value: any
9
+ text: string
10
+ original?: any
11
+ }
12
+
13
+ @Component({
14
+ selector: "JMultiDropdownSelect",
15
+ imports: [LucideAngularModule, CommonModule, FormsModule, ReactiveFormsModule],
16
+ templateUrl: "./multi-dropdown.component.html",
17
+ styleUrl: "./multi-dropdown.component.css",
18
+ animations: [
19
+ trigger("modalTransition", [
20
+ transition(":enter", [
21
+ style({ transform: "translateX(1rem)", opacity: 0 }),
22
+ animate("300ms ease-out", style({ transform: "translateY(0)", opacity: 1 })),
23
+ ]),
24
+ transition(":leave", [animate("150ms ease-in", style({ transform: "translateX(1rem)", opacity: 0 }))]),
25
+ ]),
26
+ ],
27
+ providers: [
28
+ {
29
+ provide: NG_VALUE_ACCESSOR,
30
+ useExisting: JMultiDropdownSelectComponent,
31
+ multi: true,
32
+ },
33
+ ],
34
+ })
35
+ export class JMultiDropdownSelectComponent implements ControlValueAccessor, AfterViewInit, OnInit, OnChanges, OnDestroy {
36
+ // Lucide icons
37
+ icons = {
38
+ chevronDown: ChevronDown,
39
+ x: X,
40
+ check: Check,
41
+ loading: Loader2,
42
+ squareDashedMousePointer: SquareDashedMousePointer,
43
+ }
44
+
45
+ @Input() title = "Seleccionar opciones"
46
+ @Input() placeholder = "Seleccione opciones"
47
+ @Input() showClear = true
48
+ @Input() disabled = false
49
+ @Input() isLoading = false
50
+
51
+ // Opciones y configuración
52
+ @Input() options: any[] = []
53
+ @Input() optionLabel: string | string[] = "text"
54
+ @Input() optionValue = "value"
55
+ @Input() labelSeparator = " "
56
+
57
+ // Multi-selection específico
58
+ @Input() enableSelectAll = true
59
+ @Input() labelSelectAll = "TODOS"
60
+ @Input() multipleSeparator = ", "
61
+ @Input() maxDisplayItems = 3 // Máximo de items a mostrar antes de "X más"
62
+
63
+ @Output() selectionChange = new EventEmitter<any[]>()
64
+
65
+ @ViewChild("selectButton") selectButton!: ElementRef
66
+
67
+ // Estado del componente
68
+ isDropdownOpen = false
69
+ selectedValues: any[] = []
70
+ processedOptions: ProcessedOption[] = []
71
+ displayLabel = ""
72
+
73
+ // Dropdown positioning
74
+ dropdownTop = 0
75
+ dropdownLeft = 0
76
+ dropdownWidth = 0
77
+
78
+ // ControlValueAccessor
79
+ private onChange: any = () => { }
80
+ private onTouched: any = () => { }
81
+
82
+ // Click outside listener
83
+ private clickOutsideListener: any
84
+
85
+ constructor(
86
+ private readonly cdr: ChangeDetectorRef,
87
+ private readonly elementRef: ElementRef,
88
+ ) { }
89
+
90
+ ngOnInit() {
91
+ this.updateDisplayLabel()
92
+ }
93
+
94
+ ngAfterViewInit() {
95
+ this.setupClickOutsideListener()
96
+ setTimeout(() => {
97
+ this.processOptions()
98
+ })
99
+ }
100
+
101
+ ngOnChanges(changes: SimpleChanges): void {
102
+ if (changes["options"]) {
103
+ this.processOptions()
104
+ }
105
+ }
106
+
107
+ ngOnDestroy() {
108
+ if (this.clickOutsideListener) {
109
+ document.removeEventListener("click", this.clickOutsideListener)
110
+ }
111
+ }
112
+
113
+ // ======================================================
114
+ // Procesamiento de opciones
115
+ // ======================================================
116
+
117
+ processOptions() {
118
+ this.processedOptions = []
119
+
120
+ if (this.options && this.options.length > 0 && typeof this.options[0] !== "object") {
121
+ // Opciones simples (string, number)
122
+ this.processedOptions = this.options.map((option) => ({
123
+ value: option,
124
+ text: option.toString(),
125
+ }))
126
+ } else if (this.options && this.options.length > 0) {
127
+ // Opciones complejas (objetos)
128
+ this.processedOptions = this.options.map((option) => {
129
+ const text = Array.isArray(this.optionLabel)
130
+ ? this.optionLabel.map((k) => this.getNestedValue(option, k)).join(this.labelSeparator)
131
+ : this.getNestedValue(option, this.optionLabel)
132
+ return {
133
+ value: option[this.optionValue],
134
+ text,
135
+ original: option,
136
+ }
137
+ })
138
+ }
139
+
140
+ this.updateDisplayLabel()
141
+ this.cdr.detectChanges()
142
+ }
143
+
144
+ // ======================================================
145
+ // Manejo de selección múltiple
146
+ // ======================================================
147
+
148
+ toggleOption(option: ProcessedOption) {
149
+ const index = this.selectedValues.findIndex((v) => v === option.value)
150
+
151
+ if (index > -1) {
152
+ // Deseleccionar
153
+ this.selectedValues.splice(index, 1)
154
+ } else {
155
+ // Seleccionar
156
+ this.selectedValues.push(option.value)
157
+ }
158
+
159
+ this.updateDisplayLabel()
160
+ this.onChange(this.selectedValues)
161
+ this.selectionChange.emit(this.selectedValues)
162
+ }
163
+
164
+ toggleSelectAll() {
165
+ const allValues = this.processedOptions.map((opt) => opt.value)
166
+
167
+ if (this.isAllSelected()) {
168
+ // Deseleccionar todos
169
+ this.selectedValues = []
170
+ } else {
171
+ // Seleccionar todos
172
+ this.selectedValues = [...allValues]
173
+ }
174
+
175
+ this.updateDisplayLabel()
176
+ this.onChange(this.selectedValues)
177
+ this.selectionChange.emit(this.selectedValues)
178
+ }
179
+
180
+ isAllSelected(): boolean {
181
+ const allValues = this.processedOptions.map((opt) => opt.value)
182
+ return allValues.length > 0 && allValues.every((v) => this.selectedValues.includes(v))
183
+ }
184
+
185
+ clearSelection(event: Event) {
186
+ event.stopPropagation()
187
+ this.selectedValues = []
188
+ this.updateDisplayLabel()
189
+ this.onChange(this.selectedValues)
190
+ this.selectionChange.emit(this.selectedValues)
191
+ }
192
+
193
+ // ======================================================
194
+ // Display y UI
195
+ // ======================================================
196
+
197
+ updateDisplayLabel() {
198
+ if (this.selectedValues.length === 0) {
199
+ this.displayLabel = this.placeholder
200
+ return
201
+ }
202
+
203
+ // Verificar si todos están seleccionados
204
+ if (this.enableSelectAll && this.isAllSelected()) {
205
+ this.displayLabel = this.labelSelectAll
206
+ return
207
+ }
208
+
209
+ // Obtener textos de las opciones seleccionadas
210
+ const selectedTexts = this.processedOptions
211
+ .filter((opt) => this.selectedValues.includes(opt.value))
212
+ .map((opt) => opt.text)
213
+
214
+ if (selectedTexts.length <= this.maxDisplayItems) {
215
+ // Mostrar todos los elementos
216
+ this.displayLabel = selectedTexts.join(this.multipleSeparator)
217
+ } else {
218
+ // Mostrar algunos elementos + "X más"
219
+ const visibleItems = selectedTexts.slice(0, this.maxDisplayItems)
220
+ const remainingCount = selectedTexts.length - this.maxDisplayItems
221
+ this.displayLabel = `${visibleItems.join(this.multipleSeparator)} y ${remainingCount} más`
222
+ }
223
+ }
224
+
225
+ toggleDropdown() {
226
+ if (this.disabled || this.isLoading) return
227
+
228
+ this.isDropdownOpen = !this.isDropdownOpen
229
+ if (this.isDropdownOpen) {
230
+ this.onTouched()
231
+ this.updateDropdownPosition()
232
+ }
233
+ }
234
+
235
+ // ======================================================
236
+ // Utilidades
237
+ // ======================================================
238
+
239
+ getNestedValue(obj: any, path: string): any {
240
+ return path.split(".").reduce((acc, part) => acc && acc[part], obj) ?? ""
241
+ }
242
+
243
+ setupClickOutsideListener() {
244
+ this.clickOutsideListener = (event: MouseEvent) => {
245
+ const clickedElement = event.target as HTMLElement
246
+ const isOutsideDropdown = !this.elementRef.nativeElement.contains(clickedElement)
247
+ if (this.isDropdownOpen && isOutsideDropdown) {
248
+ this.isDropdownOpen = false
249
+ this.cdr.detectChanges()
250
+ }
251
+ }
252
+ document.addEventListener("click", this.clickOutsideListener)
253
+ }
254
+
255
+ updateDropdownPosition() {
256
+ setTimeout(() => {
257
+ if (!this.selectButton) return
258
+
259
+ const button = this.selectButton.nativeElement
260
+ const buttonRect = button.getBoundingClientRect()
261
+
262
+ // Posición básica
263
+ this.dropdownTop = buttonRect.bottom + window.scrollY
264
+ this.dropdownLeft = buttonRect.left + window.scrollX
265
+ this.dropdownWidth = buttonRect.width
266
+
267
+ this.cdr.detectChanges()
268
+
269
+ // Ajustar posición si es necesario
270
+ setTimeout(() => {
271
+ const dropdown = this.elementRef.nativeElement.querySelector(".absolute.z-\\[100\\]")
272
+ if (!dropdown) return
273
+
274
+ const dropdownRect = dropdown.getBoundingClientRect()
275
+ const viewportHeight = window.innerHeight
276
+ const viewportWidth = window.innerWidth
277
+
278
+ // Ajustar si se sale por abajo
279
+ if (buttonRect.bottom + dropdownRect.height > viewportHeight) {
280
+ this.dropdownTop = buttonRect.top + window.scrollY - dropdownRect.height
281
+ }
282
+
283
+ // Ajustar si se sale por la derecha
284
+ if (buttonRect.left + dropdownRect.width > viewportWidth) {
285
+ this.dropdownLeft = buttonRect.right + window.scrollX - dropdownRect.width
286
+ }
287
+
288
+ this.cdr.detectChanges()
289
+ }, 0)
290
+ })
291
+ }
292
+
293
+ // ======================================================
294
+ // ControlValueAccessor Implementation
295
+ // ======================================================
296
+
297
+ writeValue(value: any[]): void {
298
+ this.selectedValues = Array.isArray(value) ? value : []
299
+ this.updateDisplayLabel()
300
+ this.cdr.markForCheck()
301
+ }
302
+
303
+ registerOnChange(fn: any): void {
304
+ this.onChange = fn
305
+ }
306
+
307
+ registerOnTouched(fn: any): void {
308
+ this.onTouched = fn
309
+ }
310
+
311
+ setDisabledState(isDisabled: boolean): void {
312
+ this.disabled = isDisabled
313
+ this.cdr.markForCheck()
314
+ }
315
+ }
@@ -0,0 +1,83 @@
1
+ <div class="relative w-full h-full">
2
+ <!-- Botón para multi-table -->
3
+ <div #selectButton class="min-w-[40px]">
4
+ <JButton
5
+ (clicked)="toggleColumnSelector()"
6
+ classes="secondary"
7
+ [icon]="btnIcon"
8
+ [disabled]="disabled">
9
+ {{btnText}}
10
+ </JButton>
11
+ </div>
12
+ </div>
13
+
14
+ <!-- Dropdown positioned outside the flow -->
15
+ @if (isColumnSelectorOpen) {
16
+ <div @modalTransition
17
+ class="absolute z-[100] min-w-[250px] max-w-[250px] mt-1 bg-background dark:bg-dark-background rounded-lg shadow-lg border border-border border-dark-border"
18
+ [style.width.px]="dropdownWidth"
19
+ [style.top.px]="dropdownTop"
20
+ [style.left.px]="dropdownLeft">
21
+ <div class="pt-1 pl-3 pr-3 pb-3">
22
+ <div class="text-[10px] font-medium text-gray-500 dark:text-gray-500 mb-1">{{title}}</div>
23
+
24
+ <!-- Multi-checkbox para columnas -->
25
+ <div class="max-h-60 overflow-auto flex flex-col gap-1 scroll-element">
26
+ @for (column of columns; track column.key) {
27
+ <div onKeyDown
28
+ class="flex gap-5 items-center border border-accent dark:border-dark-accent/50 px-4 py-2 cursor-pointer group rounded-md transition-all hover:bg-accent dark:hover:bg-dark-accent/50"
29
+ [ngClass]="{'bg-accent dark:bg-dark-accent/50' : column.visible}"
30
+ (click)="toggleColumnVisibility(column)">
31
+ <!-- Checkbox oculto -->
32
+ <input type="checkbox"
33
+ [id]="'col-' + column.key"
34
+ [(ngModel)]="column.visible"
35
+ class="hidden">
36
+
37
+ <!-- Ícono Check Visible Solo al Seleccionar -->
38
+ @if (column.visible) {
39
+ <lucide-icon [name]="icons.check"
40
+ size="15"
41
+ class="transition text-black dark:text-white" />
42
+ } @else {
43
+ <lucide-icon [name]="icons.squareDashedMousePointer"
44
+ size="15"
45
+ class="transition text-black dark:text-white opacity-40" />
46
+ }
47
+
48
+ <!-- Etiqueta con Efectos de Hover e Iluminación -->
49
+ <label [for]="'col-' + column.key"
50
+ class="text-black dark:text-white text-sm font-medium w-full cursor-pointer">
51
+ {{ column.label }}
52
+ </label>
53
+ </div>
54
+ }
55
+
56
+ @if (columns.length === 0) {
57
+ <div class="px-3 py-2 text-sm text-gray-500">No hay columnas disponibles</div>
58
+ }
59
+ </div>
60
+
61
+ <!-- Acciones adicionales -->
62
+ @if (showActions) {
63
+ <div class="mt-3 pt-2 border-t border-border dark:border-dark-border flex gap-2">
64
+ <button type="button"
65
+ (click)="selectAllColumns()"
66
+ class="px-3 py-1 text-xs bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors">
67
+ Mostrar todas
68
+ </button>
69
+ <button type="button"
70
+ (click)="deselectAllColumns()"
71
+ class="px-3 py-1 text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/90 transition-colors">
72
+ Ocultar todas
73
+ </button>
74
+ <button type="button"
75
+ (click)="resetToDefault()"
76
+ class="px-3 py-1 text-xs bg-outline text-foreground rounded hover:bg-accent transition-colors">
77
+ Restablecer
78
+ </button>
79
+ </div>
80
+ }
81
+ </div>
82
+ </div>
83
+ }