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.
- package/cli/component-manager.js +45 -0
- package/cli/dependency-manager.js +52 -0
- package/cli/file-operations.js +88 -0
- package/cli/index.js +51 -0
- package/cli/settings/colors.js +17 -0
- package/cli/settings/components-list.js +87 -0
- package/cli/settings/header-generator.js +42 -0
- package/cli/settings/path-utils.js +50 -0
- package/cli/settings/prompt-utils.js +37 -0
- package/cli/settings/tailwind-check.js +21 -0
- package/fesm2022/tailjng.mjs +903 -25
- package/fesm2022/tailjng.mjs.map +1 -1
- package/lib/config/tailjng-config.token.d.ts +3 -0
- package/lib/interfaces/alert/dialog-alert.interface.d.ts +52 -0
- package/lib/interfaces/alert/toast-alert.interface.d.ts +52 -0
- package/lib/interfaces/config.interface.d.ts +5 -0
- package/lib/interfaces/crud/api-response.d.ts +29 -0
- package/lib/interfaces/crud/crud.interface.d.ts +103 -0
- package/lib/services/alert/dialog-alert.service.d.ts +24 -0
- package/lib/services/alert/toast-alert.service.d.ts +26 -0
- package/lib/services/crud/converter-crud.service.d.ts +41 -0
- package/lib/services/crud/generic-crud.service.d.ts +81 -0
- package/lib/services/http/error-handler-http.service.d.ts +26 -0
- package/lib/services/http/params-http.service.d.ts +13 -0
- package/lib/services/static/icons.service.d.ts +31 -0
- package/lib/services/transformer/calendar.service.d.ts +71 -0
- package/package.json +5 -3
- package/public-api.d.ts +10 -3
- package/src/lib/components/alert/dialog-alert/dialog-alert.component.css +0 -0
- package/src/lib/components/alert/dialog-alert/dialog-alert.component.html +72 -0
- package/src/lib/components/alert/dialog-alert/dialog-alert.component.ts +66 -0
- package/src/lib/components/alert/toast-alert/toast-alert.component.css +5 -0
- package/src/lib/components/alert/toast-alert/toast-alert.component.html +76 -0
- package/src/lib/components/alert/toast-alert/toast-alert.component.ts +87 -0
- package/src/lib/components/button/button.component.css +0 -0
- package/src/lib/components/button/button.component.html +36 -0
- package/src/lib/components/button/button.component.ts +95 -0
- package/src/lib/components/checkbox/input-checkbox/input-checkbox.component.css +0 -0
- package/src/lib/components/checkbox/input-checkbox/input-checkbox.component.html +23 -0
- package/src/lib/components/checkbox/input-checkbox/input-checkbox.component.ts +44 -0
- package/src/lib/components/checkbox/switch-checkbox/switch-checkbox.component.css +0 -0
- package/src/lib/components/checkbox/switch-checkbox/switch-checkbox.component.html +26 -0
- package/src/lib/components/checkbox/switch-checkbox/switch-checkbox.component.ts +29 -0
- package/src/lib/components/color/colors.service.ts +109 -0
- package/src/lib/components/dialog/dialog.component.css +8 -0
- package/src/lib/components/dialog/dialog.component.html +57 -0
- package/src/lib/components/dialog/dialog.component.ts +179 -0
- package/src/lib/components/image/viewer-image/viewer-image.component.css +4 -0
- package/src/lib/components/image/viewer-image/viewer-image.component.html +75 -0
- package/src/lib/components/image/viewer-image/viewer-image.component.ts +131 -0
- package/src/lib/components/input/file-input/file-input.component.css +0 -0
- package/src/lib/components/input/file-input/file-input.component.html +49 -0
- package/src/lib/components/input/file-input/file-input.component.ts +218 -0
- package/src/lib/components/input/input/input.component.css +0 -0
- package/src/lib/components/input/input/input.component.html +24 -0
- package/src/lib/components/input/input/input.component.ts +78 -0
- package/src/lib/components/input/range-input/range-input.component.css +0 -0
- package/src/lib/components/input/range-input/range-input.component.html +64 -0
- package/src/lib/components/input/range-input/range-input.component.ts +78 -0
- package/src/lib/components/input/textarea-input/textarea-input.component.css +0 -0
- package/src/lib/components/input/textarea-input/textarea-input.component.html +21 -0
- package/src/lib/components/input/textarea-input/textarea-input.component.ts +75 -0
- package/src/lib/components/label/label.component.html +1 -1
- package/src/lib/components/label/label.component.ts +1 -1
- package/src/lib/components/mode-toggle/mode-toggle.component.css +0 -0
- package/src/lib/components/mode-toggle/mode-toggle.component.html +8 -0
- package/src/lib/components/mode-toggle/mode-toggle.component.ts +61 -0
- package/src/lib/components/progress-bar/progress-bar.component.css +0 -0
- package/src/lib/components/progress-bar/progress-bar.component.html +22 -0
- package/src/lib/components/progress-bar/progress-bar.component.ts +20 -0
- package/src/lib/components/select/dropdown/dropdown.component.css +0 -0
- package/src/lib/components/select/dropdown/dropdown.component.html +95 -0
- package/src/lib/components/select/dropdown/dropdown.component.ts +562 -0
- package/src/lib/components/select/multi-dropdown/multi-dropdown.component.css +0 -0
- package/src/lib/components/select/multi-dropdown/multi-dropdown.component.html +87 -0
- package/src/lib/components/select/multi-dropdown/multi-dropdown.component.ts +315 -0
- package/src/lib/components/select/multi-table/multi-table.component.css +0 -0
- package/src/lib/components/select/multi-table/multi-table.component.html +83 -0
- package/src/lib/components/select/multi-table/multi-table.component.ts +230 -0
- package/src/lib/components/toggle-radio/toggle-radio.component.css +0 -0
- package/src/lib/components/toggle-radio/toggle-radio.component.html +51 -0
- package/src/lib/components/toggle-radio/toggle-radio.component.ts +203 -0
- package/src/styles.css +126 -0
- package/cli/tailjng.js +0 -105
- package/lib/services/icons.service.d.ts +0 -9
- package/lib/tailjng.component.d.ts +0 -5
- package/lib/tailjng.service.d.ts +0 -6
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ElementRef, ViewChild, OnDestroy, ChangeDetectorRef, AfterViewInit, OnInit, SimpleChanges, OnChanges } from "@angular/core";
|
|
2
|
+
import { FormsModule } from "@angular/forms";
|
|
3
|
+
import { CommonModule } from "@angular/common";
|
|
4
|
+
import { Eye, LucideAngularModule, Check, SquareDashedMousePointer } from "lucide-angular";
|
|
5
|
+
import { animate, style, transition, trigger } from "@angular/animations";
|
|
6
|
+
import { JButtonComponent } from "../../button/button.component";
|
|
7
|
+
import { TableColumn } from 'tailjng';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: "JMultiTableSelect",
|
|
11
|
+
imports: [LucideAngularModule, JButtonComponent, CommonModule, FormsModule],
|
|
12
|
+
templateUrl: "./multi-table.component.html",
|
|
13
|
+
styleUrls: ["./multi-table.component.css"],
|
|
14
|
+
animations: [
|
|
15
|
+
trigger("modalTransition", [
|
|
16
|
+
transition(":enter", [
|
|
17
|
+
style({ transform: "translateX(1rem)", opacity: 0 }),
|
|
18
|
+
animate("300ms ease-out", style({ transform: "translateY(0)", opacity: 1 })),
|
|
19
|
+
]),
|
|
20
|
+
transition(":leave", [animate("150ms ease-in", style({ transform: "translateX(1rem)", opacity: 0 }))]),
|
|
21
|
+
]),
|
|
22
|
+
],
|
|
23
|
+
})
|
|
24
|
+
export class JMultiTableSelectComponent implements AfterViewInit, OnInit, OnChanges, OnDestroy {
|
|
25
|
+
// Definición de iconos
|
|
26
|
+
icons = {
|
|
27
|
+
view: Eye,
|
|
28
|
+
check: Check,
|
|
29
|
+
squareDashedMousePointer: SquareDashedMousePointer,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
@Input() btnIcon = this.icons.view; // Icono del botón
|
|
33
|
+
@Input() btnText = ""; // Texto del botón
|
|
34
|
+
@Input() title = "Columnas visibles"; // Título del dropdown
|
|
35
|
+
@Input() disabled = false; // Estado deshabilitado
|
|
36
|
+
@Input() showActions = true; // Mostrar botones de acción (mostrar todas, ocultar todas, etc.)
|
|
37
|
+
|
|
38
|
+
// Columnas de la tabla
|
|
39
|
+
@Input() columns: TableColumn<any>[] = []; // Usar 'any' para el tipo genérico
|
|
40
|
+
|
|
41
|
+
// Eventos
|
|
42
|
+
@Output() visibilityChange = new EventEmitter<TableColumn<any>[]>();
|
|
43
|
+
@Output() columnToggle = new EventEmitter<{ column: TableColumn<any>; visible: boolean }>();
|
|
44
|
+
|
|
45
|
+
@ViewChild("selectButton") selectButton!: ElementRef;
|
|
46
|
+
|
|
47
|
+
// Estado del componente
|
|
48
|
+
isColumnSelectorOpen = false;
|
|
49
|
+
private originalColumns: TableColumn<any>[] = []; // Para restablecer valores por defecto
|
|
50
|
+
|
|
51
|
+
// Posicionamiento del dropdown
|
|
52
|
+
dropdownTop = 0;
|
|
53
|
+
dropdownLeft = 0;
|
|
54
|
+
dropdownWidth = 0;
|
|
55
|
+
|
|
56
|
+
// Click fuera del componente
|
|
57
|
+
private clickOutsideListener: any;
|
|
58
|
+
|
|
59
|
+
constructor(private readonly cdr: ChangeDetectorRef, private readonly elementRef: ElementRef) {}
|
|
60
|
+
|
|
61
|
+
ngOnInit() {
|
|
62
|
+
// Guardar el estado original de las columnas
|
|
63
|
+
this.saveOriginalState();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ngAfterViewInit() {
|
|
67
|
+
this.setupClickOutsideListener();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
71
|
+
if (changes["columns"]) {
|
|
72
|
+
this.saveOriginalState();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ngOnDestroy() {
|
|
77
|
+
if (this.clickOutsideListener) {
|
|
78
|
+
document.removeEventListener("click", this.clickOutsideListener);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ======================================================
|
|
83
|
+
// Manejo de columnas
|
|
84
|
+
// ======================================================
|
|
85
|
+
|
|
86
|
+
toggleColumnVisibility(column: TableColumn<any>) {
|
|
87
|
+
// No permitir ocultar columnas si `hidden` es true
|
|
88
|
+
if (column.hidden) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
column.visible = !column.visible;
|
|
93
|
+
this.emitChanges(column);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
selectAllColumns() {
|
|
97
|
+
this.columns.forEach((column) => {
|
|
98
|
+
column.visible = true;
|
|
99
|
+
});
|
|
100
|
+
this.emitVisibilityChange();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
deselectAllColumns() {
|
|
104
|
+
this.columns.forEach((column) => {
|
|
105
|
+
// No ocultar columnas que estén marcadas como `hidden`
|
|
106
|
+
if (!column.hidden) {
|
|
107
|
+
column.visible = false;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
this.emitVisibilityChange();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
resetToDefault() {
|
|
114
|
+
this.columns.forEach((column, index) => {
|
|
115
|
+
const original = this.originalColumns[index];
|
|
116
|
+
if (original) {
|
|
117
|
+
column.visible = original.visible ?? false; // Si `visible` es undefined, asignar false
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
this.emitVisibilityChange();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ======================================================
|
|
124
|
+
// Métodos de utilidad
|
|
125
|
+
// ======================================================
|
|
126
|
+
|
|
127
|
+
private saveOriginalState() {
|
|
128
|
+
this.originalColumns = this.columns.map((col) => ({
|
|
129
|
+
...col,
|
|
130
|
+
visible: col.visible ?? false, // Asegúrate de que `visible` siempre tenga un valor booleano
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private emitChanges(column: TableColumn<any>) {
|
|
135
|
+
this.columnToggle.emit({ column, visible: column.visible ?? false });
|
|
136
|
+
this.emitVisibilityChange();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private emitVisibilityChange() {
|
|
140
|
+
this.visibilityChange.emit([...this.columns]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ======================================================
|
|
144
|
+
// UI y posicionamiento
|
|
145
|
+
// ======================================================
|
|
146
|
+
|
|
147
|
+
toggleColumnSelector() {
|
|
148
|
+
if (this.disabled) return;
|
|
149
|
+
|
|
150
|
+
this.isColumnSelectorOpen = !this.isColumnSelectorOpen;
|
|
151
|
+
if (this.isColumnSelectorOpen) {
|
|
152
|
+
this.updateDropdownPosition();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
setupClickOutsideListener() {
|
|
157
|
+
this.clickOutsideListener = (event: MouseEvent) => {
|
|
158
|
+
const clickedElement = event.target as HTMLElement;
|
|
159
|
+
const isOutsideDropdown = !this.elementRef.nativeElement.contains(clickedElement);
|
|
160
|
+
if (this.isColumnSelectorOpen && isOutsideDropdown) {
|
|
161
|
+
this.isColumnSelectorOpen = false;
|
|
162
|
+
this.cdr.detectChanges();
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
document.addEventListener("click", this.clickOutsideListener);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
updateDropdownPosition() {
|
|
169
|
+
setTimeout(() => {
|
|
170
|
+
if (!this.selectButton) return;
|
|
171
|
+
|
|
172
|
+
const button = this.selectButton.nativeElement;
|
|
173
|
+
const buttonRect = button.getBoundingClientRect();
|
|
174
|
+
|
|
175
|
+
// Posición básica
|
|
176
|
+
this.dropdownTop = buttonRect.bottom + window.scrollY;
|
|
177
|
+
this.dropdownLeft = buttonRect.left + window.scrollX;
|
|
178
|
+
this.dropdownWidth = Math.max(buttonRect.width, 250); // Mínimo 250px
|
|
179
|
+
|
|
180
|
+
this.cdr.detectChanges();
|
|
181
|
+
|
|
182
|
+
// Ajustar posición si es necesario
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
const dropdown = this.elementRef.nativeElement.querySelector(".absolute.z-\\[100\\]");
|
|
185
|
+
if (!dropdown) return;
|
|
186
|
+
|
|
187
|
+
const dropdownRect = dropdown.getBoundingClientRect();
|
|
188
|
+
const viewportHeight = window.innerHeight;
|
|
189
|
+
const viewportWidth = window.innerWidth;
|
|
190
|
+
|
|
191
|
+
// Ajustar si se sale por abajo
|
|
192
|
+
if (buttonRect.bottom + dropdownRect.height > viewportHeight) {
|
|
193
|
+
this.dropdownTop = buttonRect.top + window.scrollY - dropdownRect.height;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Ajustar si se sale por la derecha
|
|
197
|
+
if (buttonRect.left + dropdownRect.width > viewportWidth) {
|
|
198
|
+
this.dropdownLeft = buttonRect.right + window.scrollX - dropdownRect.width;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.cdr.detectChanges();
|
|
202
|
+
}, 0);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ======================================================
|
|
207
|
+
// Métodos públicos para uso externo
|
|
208
|
+
// ======================================================
|
|
209
|
+
|
|
210
|
+
getVisibleColumns(): TableColumn<any>[] {
|
|
211
|
+
return this.columns.filter((col) => col.visible);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getHiddenColumns(): TableColumn<any>[] {
|
|
215
|
+
return this.columns.filter((col) => !col.visible);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
setColumnVisibility(key: string, visible: boolean) {
|
|
219
|
+
const column = this.columns.find((col) => col.key === key);
|
|
220
|
+
if (column && !column.hidden) { // Solo cambiar visibilidad si no está oculto
|
|
221
|
+
column.visible = visible;
|
|
222
|
+
this.emitChanges(column);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getColumnVisibility(key: string): boolean {
|
|
227
|
+
const column = this.columns.find((col) => col.key === key);
|
|
228
|
+
return column?.visible ?? false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
@if (internalOptions.length > 0) {
|
|
2
|
+
<div class="flex rounded-md overflow-hidden border border-border dark:border-dark-border text-sm min-h-[40px] select-none" [ngClass]="{ 'opacity-50': disabled || isComponentDisabled }" [class]="classes">
|
|
3
|
+
|
|
4
|
+
<ng-container>
|
|
5
|
+
@for (opt of internalOptions; track $index) {
|
|
6
|
+
<button
|
|
7
|
+
type="button"
|
|
8
|
+
[disabled]="disabled || isComponentDisabled"
|
|
9
|
+
(click)="select(opt.value)"
|
|
10
|
+
[ngClass]="{
|
|
11
|
+
'bg-primary dark:bg-dark-primary text-white': selectedValue === opt.value,
|
|
12
|
+
'bg-background dark:bg-dark-background hover:bg-muted/80 hover:dark:bg-dark-muted/30 text-gray-700 dark:text-white': selectedValue !== opt.value,
|
|
13
|
+
'cursor-not-allowed pointer-events-none': disabled || isComponentDisabled
|
|
14
|
+
}"
|
|
15
|
+
class="w-full py-2 px-4 font-medium border-r border-border last:border-none focus:outline-none transition-colors duration-200 ease-in-out cursor-pointer"
|
|
16
|
+
[class]="classesElement"
|
|
17
|
+
>
|
|
18
|
+
{{ opt.label }}
|
|
19
|
+
</button>
|
|
20
|
+
}
|
|
21
|
+
</ng-container>
|
|
22
|
+
|
|
23
|
+
</div>
|
|
24
|
+
} @else if (isLoading) {
|
|
25
|
+
|
|
26
|
+
<div class="flex text-black dark:text-white gap-2 items-center justify-center rounded-md overflow-hidden border border-border dark:border-dark-border h-[40px]">
|
|
27
|
+
<lucide-icon [name]="iconsService.icons.loading" [size]="15" class="text-gray-400 animate-spin" />
|
|
28
|
+
<span>Cargando...</span>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
} @else {
|
|
32
|
+
|
|
33
|
+
<div class="flex text-black dark:text-white items-center justify-center rounded-md overflow-hidden border border-border dark:border-dark-border h-[40px]">
|
|
34
|
+
No hay opciones
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
<!-- Clear button -->
|
|
40
|
+
@if (showClear && selectedValue !== null) {
|
|
41
|
+
<div class="mt-2">
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
(click)="clear()"
|
|
45
|
+
[disabled]="disabled || isComponentDisabled"
|
|
46
|
+
class="text-xs text-red-500 underline disabled:opacity-50 disabled:cursor-not-allowed">
|
|
47
|
+
Limpiar selección
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
}
|
|
51
|
+
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
|
|
3
|
+
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
|
+
import { LucideAngularModule } from 'lucide-angular';
|
|
5
|
+
import { JIconsService, JGenericCrudService } from 'tailjng';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'JToggleRadio',
|
|
9
|
+
imports: [LucideAngularModule, CommonModule],
|
|
10
|
+
templateUrl: './toggle-radio.component.html',
|
|
11
|
+
styleUrl: './toggle-radio.component.css',
|
|
12
|
+
providers: [
|
|
13
|
+
{
|
|
14
|
+
provide: NG_VALUE_ACCESSOR,
|
|
15
|
+
useExisting: forwardRef(() => JToggleRadioComponent),
|
|
16
|
+
multi: true,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
})
|
|
20
|
+
export class JToggleRadioComponent implements OnInit, ControlValueAccessor {
|
|
21
|
+
|
|
22
|
+
@Input() endpoint: string = '';
|
|
23
|
+
|
|
24
|
+
@Input() options: any[] = [];
|
|
25
|
+
@Input() optionLabel = 'label';
|
|
26
|
+
@Input() optionValue = 'value';
|
|
27
|
+
|
|
28
|
+
@Input() sort: 'ASC' | 'DESC' = 'ASC';
|
|
29
|
+
@Input() defaultFilters: { [key: string]: any } = {};
|
|
30
|
+
|
|
31
|
+
@Input() loadOnInit = false;
|
|
32
|
+
@Input() selectFirstOnLoad: boolean = false;
|
|
33
|
+
|
|
34
|
+
@Input() disabled: boolean = false;
|
|
35
|
+
@Input() showClear = false;
|
|
36
|
+
|
|
37
|
+
@Input() classes: string = '';
|
|
38
|
+
@Input() classesElement: string = '';
|
|
39
|
+
|
|
40
|
+
@Output() selectionChange = new EventEmitter<any>();
|
|
41
|
+
|
|
42
|
+
internalOptions: { value: any; label: string }[] = [];
|
|
43
|
+
selectedValue: any = null;
|
|
44
|
+
isLoading = false;
|
|
45
|
+
|
|
46
|
+
isComponentDisabled = false;
|
|
47
|
+
|
|
48
|
+
// ControlValueAccessor
|
|
49
|
+
onChange: any = () => { };
|
|
50
|
+
onTouched: any = () => { };
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
public readonly iconsService: JIconsService,
|
|
54
|
+
private readonly genericService: JGenericCrudService
|
|
55
|
+
) { }
|
|
56
|
+
|
|
57
|
+
ngOnInit(): void {
|
|
58
|
+
if (this.endpoint && this.loadOnInit) {
|
|
59
|
+
this.loadOptionsFromApi();
|
|
60
|
+
} else {
|
|
61
|
+
this.processOptions();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Set the disabled state of the component.
|
|
68
|
+
* This method is part of the ControlValueAccessor interface.
|
|
69
|
+
* @param isDisabled
|
|
70
|
+
*/
|
|
71
|
+
setDisabledState(isDisabled: boolean): void {
|
|
72
|
+
this.isComponentDisabled = isDisabled;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Load options from the API based on the endpoint and default filters.
|
|
78
|
+
* This method fetches data from the API and processes it to set the internal options.
|
|
79
|
+
*/
|
|
80
|
+
loadOptionsFromApi() {
|
|
81
|
+
this.isLoading = true;
|
|
82
|
+
|
|
83
|
+
const params: any = {};
|
|
84
|
+
params['sortOrder'] = this.sort;
|
|
85
|
+
|
|
86
|
+
Object.keys(this.defaultFilters).forEach((key) => {
|
|
87
|
+
params[`filter[${key}]`] = this.defaultFilters[key];
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.genericService.getAll<any>(this.endpoint, params).subscribe({
|
|
91
|
+
next: (res) => {
|
|
92
|
+
const data = res.data[this.endpoint] || [];
|
|
93
|
+
this.options = data;
|
|
94
|
+
this.processOptions();
|
|
95
|
+
this.isLoading = false;
|
|
96
|
+
|
|
97
|
+
// If selectFirstOnLoad is true, select the first element
|
|
98
|
+
if (this.selectFirstOnLoad && this.internalOptions.length > 0) {
|
|
99
|
+
this.selectedValue = this.internalOptions[0].value;
|
|
100
|
+
this.onChange(this.selectedValue);
|
|
101
|
+
this.selectionChange.emit(this.selectedValue);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
error: () => (this.isLoading = false),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get a nested value from an object using a dot-separated path.
|
|
111
|
+
* @param obj The object to search.
|
|
112
|
+
* @param path The dot-separated path to the desired value.
|
|
113
|
+
* @returns The value found at the specified path, or an empty string if not found.
|
|
114
|
+
*/
|
|
115
|
+
getNestedValue(obj: any, path: string): any {
|
|
116
|
+
return path.split('.').reduce((acc, part) => acc && acc[part], obj) ?? '';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Process the options to extract value and label.
|
|
122
|
+
* This method checks if the options are objects and extracts the specified value and label.
|
|
123
|
+
*/
|
|
124
|
+
processOptions() {
|
|
125
|
+
if (this.options.length > 0 && typeof this.options[0] === 'object') {
|
|
126
|
+
|
|
127
|
+
this.internalOptions = this.options.map((opt) => ({
|
|
128
|
+
value: opt[this.optionValue],
|
|
129
|
+
label: this.getNestedValue(opt, this.optionLabel),
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
} else {
|
|
133
|
+
this.internalOptions = this.options.map((opt) => ({
|
|
134
|
+
value: opt,
|
|
135
|
+
label: opt.toString(),
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Select an option.
|
|
143
|
+
* @param value The value of the option to select.
|
|
144
|
+
* @returns
|
|
145
|
+
*/
|
|
146
|
+
select(value: any) {
|
|
147
|
+
if (this.disabled || this.isComponentDisabled) return;
|
|
148
|
+
this.selectedValue = value;
|
|
149
|
+
this.onChange(this.selectedValue);
|
|
150
|
+
this.selectionChange.emit(this.selectedValue);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Clear the selected value.
|
|
156
|
+
*/
|
|
157
|
+
clear() {
|
|
158
|
+
this.selectedValue = null;
|
|
159
|
+
this.onChange(null);
|
|
160
|
+
this.selectionChange.emit(null);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Write the value to the component.
|
|
166
|
+
* This method is part of the ControlValueAccessor interface.
|
|
167
|
+
* It updates the selected value and notifies the change.
|
|
168
|
+
* @param value The value to write.
|
|
169
|
+
*/
|
|
170
|
+
writeValue(value: any): void {
|
|
171
|
+
this.selectedValue = value;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Register a callback function to be called when the value changes.
|
|
177
|
+
* This method is part of the ControlValueAccessor interface.
|
|
178
|
+
* @param fn
|
|
179
|
+
*/
|
|
180
|
+
registerOnChange(fn: any): void {
|
|
181
|
+
this.onChange = fn;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Register a callback function to be called when the control is touched.
|
|
187
|
+
* This method is part of the ControlValueAccessor interface.
|
|
188
|
+
* @param fn The callback function to register.
|
|
189
|
+
*/
|
|
190
|
+
registerOnTouched(fn: any): void {
|
|
191
|
+
this.onTouched = fn;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Reload the options based on the current filters.
|
|
197
|
+
* This method can be called to refresh the options displayed in the toggle radio component.
|
|
198
|
+
*/
|
|
199
|
+
reloadOptions() {
|
|
200
|
+
this.loadOptionsFromApi(); // Reload options with the new filters
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
}
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/* Scrolls */
|
|
2
|
+
|
|
3
|
+
.scroll-element {
|
|
4
|
+
padding-right: 5px
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.scroll-element::-webkit-scrollbar {
|
|
8
|
+
width: 5px;
|
|
9
|
+
height: 5px
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.scroll-element::-webkit-scrollbar-track {
|
|
13
|
+
border-radius: 10px
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.scroll-element::-webkit-scrollbar-thumb {
|
|
17
|
+
border-radius: 10px;
|
|
18
|
+
background: var(--color-primary);
|
|
19
|
+
-webkit-transition: .5s;
|
|
20
|
+
transition: .5s
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.scroll-element::-webkit-scrollbar-thumb:hover {
|
|
24
|
+
background: var(--color-dark-primary)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.scroll-element::-webkit-scrollbar-button {
|
|
28
|
+
width: 0px;
|
|
29
|
+
height: 0px
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.scroll-screen {
|
|
33
|
+
padding-right: 5px
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.scroll-screen::-webkit-scrollbar {
|
|
37
|
+
width: 10px;
|
|
38
|
+
height: 10px
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.scroll-screen::-webkit-scrollbar-thumb {
|
|
42
|
+
background: var(--color-dark-primary);
|
|
43
|
+
border: 3px solid var(--color-primary);
|
|
44
|
+
-webkit-transition: .5s;
|
|
45
|
+
transition: .5s
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.scroll-screen::-webkit-scrollbar-thumb:hover {
|
|
49
|
+
background: var(--color-primary)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.scroll-screen::-webkit-scrollbar-button {
|
|
53
|
+
width: 0px;
|
|
54
|
+
height: 0px
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.dark .scroll-screen::-webkit-scrollbar-thumb {
|
|
58
|
+
background: var(--color-dark-primary);
|
|
59
|
+
border: 3px solid var(--color-primary);
|
|
60
|
+
-webkit-transition: .5s;
|
|
61
|
+
transition: .5s
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.dark .scroll-screen::-webkit-scrollbar-thumb:hover {
|
|
65
|
+
background: var(--color-primary)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
/* Inputs */
|
|
72
|
+
|
|
73
|
+
.input::placeholder {
|
|
74
|
+
color: rgba(0, 0, 0, 0.5) !important;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.input:-webkit-autofill {
|
|
78
|
+
-webkit-text-fill-color: black !important;
|
|
79
|
+
-webkit-box-shadow: 0 0 0px 1000px color-mix(in srgb, var(--color-primary) 15%, white) inset !important;
|
|
80
|
+
caret-color: var(--color-primary) !important
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.input:-webkit-autofill:hover,
|
|
84
|
+
.input:-webkit-autofill:focus,
|
|
85
|
+
.input:-webkit-autofill:active {
|
|
86
|
+
-webkit-text-fill-color: black !important;
|
|
87
|
+
-webkit-box-shadow: 0 0 0px 1000px color-mix(in srgb, var(--color-primary) 15%, white) inset !important;
|
|
88
|
+
caret-color: var(--color-primary) !important
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.input[type=date]::-webkit-calendar-picker-indicator,
|
|
92
|
+
.input[type=datetime-local]::-webkit-calendar-picker-indicator {
|
|
93
|
+
filter: none !important;
|
|
94
|
+
opacity: 1 !important;
|
|
95
|
+
color: #000 !important;
|
|
96
|
+
background-color: transparent !important;
|
|
97
|
+
-webkit-filter: brightness(0) invert(0) !important
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
.dark .input::placeholder {
|
|
102
|
+
color: rgba(255, 255, 255, 0.5) !important;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.dark .input:-webkit-autofill {
|
|
106
|
+
-webkit-text-fill-color: white !important;
|
|
107
|
+
-webkit-box-shadow: 0 0 0px 1000px color-mix(in srgb, var(--color-background) 15%, black) inset !important;
|
|
108
|
+
caret-color: var(--color-dark-primary) !important
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.dark .input .input:-webkit-autofill:hover,
|
|
112
|
+
.dark .input .input:-webkit-autofill:focus,
|
|
113
|
+
.dark .input .input:-webkit-autofill:active {
|
|
114
|
+
-webkit-text-fill-color: white !important;
|
|
115
|
+
-webkit-box-shadow: 0 0 0px 1000px color-mix(in srgb, var(--color-background) 15%, black) inset !important;
|
|
116
|
+
caret-color: var(--color-primary) !important
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.dark .input[type=date]::-webkit-calendar-picker-indicator,
|
|
120
|
+
.dark .input[type=datetime-local]::-webkit-calendar-picker-indicator {
|
|
121
|
+
filter: none !important;
|
|
122
|
+
opacity: 1 !important;
|
|
123
|
+
color: #fff !important;
|
|
124
|
+
background-color: transparent !important;
|
|
125
|
+
-webkit-filter: brightness(0) invert(1) !important
|
|
126
|
+
}
|