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,13 @@
|
|
|
1
|
+
import { HttpParams } from '@angular/common/http';
|
|
2
|
+
import { Params } from '@angular/router';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class JParamsHttpService {
|
|
5
|
+
/**
|
|
6
|
+
* Transform Params to HttpParams
|
|
7
|
+
* @param params
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
resParams(params: Params): HttpParams;
|
|
11
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<JParamsHttpService, never>;
|
|
12
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<JParamsHttpService>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class JIconsService {
|
|
3
|
+
icons: {
|
|
4
|
+
info: import("lucide-angular").LucideIconData;
|
|
5
|
+
success: import("lucide-angular").LucideIconData;
|
|
6
|
+
error: import("lucide-angular").LucideIconData;
|
|
7
|
+
warning: import("lucide-angular").LucideIconData;
|
|
8
|
+
question: import("lucide-angular").LucideIconData;
|
|
9
|
+
close: import("lucide-angular").LucideIconData;
|
|
10
|
+
check: import("lucide-angular").LucideIconData;
|
|
11
|
+
zoomIn: import("lucide-angular").LucideIconData;
|
|
12
|
+
zoomOut: import("lucide-angular").LucideIconData;
|
|
13
|
+
rotateRight: import("lucide-angular").LucideIconData;
|
|
14
|
+
rotateLeft: import("lucide-angular").LucideIconData;
|
|
15
|
+
reset: import("lucide-angular").LucideIconData;
|
|
16
|
+
fullscreen: import("lucide-angular").LucideIconData;
|
|
17
|
+
exitFullscreen: import("lucide-angular").LucideIconData;
|
|
18
|
+
imageOff: import("lucide-angular").LucideIconData;
|
|
19
|
+
upload: import("lucide-angular").LucideIconData;
|
|
20
|
+
view: import("lucide-angular").LucideIconData;
|
|
21
|
+
chevronDown: import("lucide-angular").LucideIconData;
|
|
22
|
+
squareDashedMousePointer: import("lucide-angular").LucideIconData;
|
|
23
|
+
search: import("lucide-angular").LucideIconData;
|
|
24
|
+
sun: import("lucide-angular").LucideIconData;
|
|
25
|
+
moon: import("lucide-angular").LucideIconData;
|
|
26
|
+
loading: import("lucide-angular").LucideIconData;
|
|
27
|
+
};
|
|
28
|
+
constructor();
|
|
29
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<JIconsService, never>;
|
|
30
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<JIconsService>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
export declare class JCalendarService {
|
|
3
|
+
nameDaysAb: string[];
|
|
4
|
+
nameDays: string[];
|
|
5
|
+
nameMontsAb: string[];
|
|
6
|
+
nameMonts: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Get month from date
|
|
9
|
+
* @param date The date to extract the month from, can be a Date object or a string in 'YYYY-MM-DD' format.
|
|
10
|
+
* @returns The name of the month in Spanish.
|
|
11
|
+
*/
|
|
12
|
+
getMonthFromDate(date: Date | string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Calculate the age of a person based on their birth date.
|
|
15
|
+
* @param birth The birth date as a Date object or a string in 'YYYY-MM-DD' format.
|
|
16
|
+
* @returns The age in years, or null if the birth date is invalid.
|
|
17
|
+
*/
|
|
18
|
+
calculateAge(birth: Date | string): number | null;
|
|
19
|
+
/**
|
|
20
|
+
* Calculate the complete age of a person based on their birth date.
|
|
21
|
+
* @param birth The birth date as a Date object or a string in 'YYYY-MM-DD' format.
|
|
22
|
+
* @returns An object containing years, months, and days of age.
|
|
23
|
+
*/
|
|
24
|
+
calculateAgeComplete(birth: Date | string): {
|
|
25
|
+
years: number;
|
|
26
|
+
months: number;
|
|
27
|
+
days: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Format a date string to a more readable format.
|
|
31
|
+
* @param date The date string in 'YYYY-MM-DD' format.
|
|
32
|
+
* @param month Optional parameter to add to the month name.
|
|
33
|
+
* @returns A formatted date string in the format 'DD de MMMM del YYYY'.
|
|
34
|
+
*/
|
|
35
|
+
formatearFechaString(date: string, month?: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Format a Date object to a string in the format 'DD de MMMM del YYYY'.
|
|
38
|
+
* @param date The Date object to format.
|
|
39
|
+
* @param month Optional parameter to add to the month name.
|
|
40
|
+
* @returns A formatted date string.
|
|
41
|
+
*/
|
|
42
|
+
formatearFechaDate(date: Date, month?: number): string;
|
|
43
|
+
/**
|
|
44
|
+
* Parse a date string in the format "dd de MMMM del yyyy" to "yyyy-MM-dd".
|
|
45
|
+
* @param fechaStr The date string to parse.
|
|
46
|
+
* @returns The parsed date string in "yyyy-MM-dd" format or null if invalid.
|
|
47
|
+
*/
|
|
48
|
+
parseFechaString(fechaStr: string): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Format a date to "MMM YY" format.
|
|
51
|
+
* @param date The date to format, can be a Date object.
|
|
52
|
+
* @returns The formatted date string.
|
|
53
|
+
*/
|
|
54
|
+
formatMonthYear(date: Date): string;
|
|
55
|
+
/**
|
|
56
|
+
* Format a date to the Bogotá timezone (UTC-5).
|
|
57
|
+
* @param date The date to format, can be a Date object.
|
|
58
|
+
* @returns The formatted date string in 'yyyy-MM-ddTHH:mm:ss' format.
|
|
59
|
+
*/
|
|
60
|
+
formatDateToBogota(date: Date): string;
|
|
61
|
+
/**
|
|
62
|
+
* Format a date to a relative time string.
|
|
63
|
+
* This method uses date-fns to format the date relative to now.
|
|
64
|
+
* @param date The date to format, can be a string in 'YYYY-MM-DD' format.
|
|
65
|
+
* @returns A formatted relative time string.
|
|
66
|
+
* @example "hace 2 días", "hace 1 hora", etc
|
|
67
|
+
*/
|
|
68
|
+
formatRelativeDate(date: string): string;
|
|
69
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<JCalendarService, never>;
|
|
70
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<JCalendarService>;
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailjng",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": "^19.2.0",
|
|
6
6
|
"@angular/core": "^19.2.0",
|
|
7
|
+
"@angular/animations": "^19.2.0",
|
|
7
8
|
"@ng-icons/lucide": ">=29.0.0",
|
|
8
9
|
"lucide-angular": ">=0.477.0",
|
|
9
|
-
"tailwindcss": ">=4.0.9"
|
|
10
|
+
"tailwindcss": ">=4.0.9",
|
|
11
|
+
"date-fns": "^4.1.0"
|
|
10
12
|
},
|
|
11
13
|
"bin": {
|
|
12
|
-
"tailjng": "./cli/
|
|
14
|
+
"tailjng": "./cli/index.js"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
17
|
"tslib": "^2.3.0"
|
package/public-api.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
export * from './lib/tailjng.
|
|
2
|
-
export * from './lib/
|
|
3
|
-
export * from './lib/services/
|
|
1
|
+
export * from './lib/config/tailjng-config.token';
|
|
2
|
+
export * from './lib/services/crud/converter-crud.service';
|
|
3
|
+
export * from './lib/services/crud/generic-crud.service';
|
|
4
|
+
export * from './lib/services/http/params-http.service';
|
|
5
|
+
export * from './lib/services/http/error-handler-http.service';
|
|
6
|
+
export * from './lib/services/transformer/calendar.service';
|
|
7
|
+
export * from './lib/services/static/icons.service';
|
|
8
|
+
export * from './lib/services/alert/dialog-alert.service';
|
|
9
|
+
export * from './lib/services/alert/toast-alert.service';
|
|
10
|
+
export * from './lib/interfaces/crud/crud.interface';
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
@for (dialog of dialogs(); track dialog.config.title) {
|
|
2
|
+
<div class="fixed inset-0 z-1001 flex items-center justify-center bg-black/50">
|
|
3
|
+
<div @modalTransition
|
|
4
|
+
class="relative w-100 border border-border p-4 rounded-lg shadow-5xl overflow-hidden"
|
|
5
|
+
[ngClass]="getDialogClass(dialog.config.type)">
|
|
6
|
+
|
|
7
|
+
<!-- Icon big in the bottom left corner -->
|
|
8
|
+
<div [ngClass]="{ 'animate-spin': dialog.config.type === 'loading'}"
|
|
9
|
+
class="absolute -bottom-5 opacity-25 dark:opacity-25 -left-5 text-black dark:text-white pointer-events-none">
|
|
10
|
+
<lucide-icon
|
|
11
|
+
[name]="getIcon(dialog.config.type)"
|
|
12
|
+
[size]="130"
|
|
13
|
+
[ngClass]="getIconClass(dialog.config.type)"
|
|
14
|
+
>
|
|
15
|
+
</lucide-icon>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Icon small in the top right corner -->
|
|
19
|
+
<div [ngClass]="{ 'animate-spin': dialog.config.type === 'loading'}"
|
|
20
|
+
class="absolute top-5 opacity-25 dark:opacity-25 right-5 text-black dark:text-white pointer-events-none">
|
|
21
|
+
<lucide-icon
|
|
22
|
+
[name]="getIcon(dialog.config.type)"
|
|
23
|
+
[size]="30"
|
|
24
|
+
[ngClass]="getIconClass(dialog.config.type)"
|
|
25
|
+
>
|
|
26
|
+
</lucide-icon>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<h3 class="text-lg font-semibold text-black dark:text-white pb-2">{{ dialog.config.title }}</h3>
|
|
30
|
+
<p class="text-black/80 dark:text-dark-muted-foreground" [innerHTML]="dialog.config.description"></p>
|
|
31
|
+
|
|
32
|
+
<div class="flex justify-end gap-2 mt-4">
|
|
33
|
+
|
|
34
|
+
<!-- Button Retry (Only if type is "error") -->
|
|
35
|
+
@if (dialog.config.type === 'error' && dialog.config.onRetry) {
|
|
36
|
+
<JButton
|
|
37
|
+
(clicked)="handleAction('retry')"
|
|
38
|
+
[disabled]="dialog.isRetryLoading || dialog.isCancelLoading || dialog.isConfirmLoading"
|
|
39
|
+
[isLoading]="dialog.isRetryLoading"
|
|
40
|
+
[ngClasses]="getButtonSecondaryClass(dialog.config.type)"
|
|
41
|
+
>
|
|
42
|
+
{{ dialog.config.titleBtnRetry || 'Reintentar' }}
|
|
43
|
+
</JButton>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<!-- Button Cancel (Not shown if type is "success") -->
|
|
47
|
+
@if (dialog.config.type !== 'success' && dialog.config.onCancel) {
|
|
48
|
+
<JButton
|
|
49
|
+
(clicked)="handleAction('cancel')"
|
|
50
|
+
[disabled]="dialog.isRetryLoading || dialog.isCancelLoading || dialog.isConfirmLoading"
|
|
51
|
+
[isLoading]="dialog.isCancelLoading"
|
|
52
|
+
[ngClasses]="getButtonSecondaryClass(dialog.config.type)"
|
|
53
|
+
>
|
|
54
|
+
{{ dialog.config.titleBtnCancel || 'Cancelar' }}
|
|
55
|
+
</JButton>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
<!-- Button Confirm (Not shown if type is "loading") -->
|
|
59
|
+
@if (dialog.config.type !== 'loading' && dialog?.config?.onConfirm) {
|
|
60
|
+
<JButton
|
|
61
|
+
(clicked)="handleAction('confirm')"
|
|
62
|
+
[disabled]="dialog.isRetryLoading || dialog.isCancelLoading || dialog.isConfirmLoading"
|
|
63
|
+
[isLoading]="dialog.isConfirmLoading"
|
|
64
|
+
[ngClasses]="getButtonClass(dialog.config.type)"
|
|
65
|
+
>
|
|
66
|
+
{{ dialog.config.titleBtnConfirm || 'Confirmar' }}
|
|
67
|
+
</JButton>
|
|
68
|
+
}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Component, inject, computed, Input } from "@angular/core";
|
|
2
|
+
import { NgClass } from "@angular/common";
|
|
3
|
+
import { trigger, transition, style, animate } from "@angular/animations";
|
|
4
|
+
import { JIconsService, JAlertDialogService } from "tailjng";
|
|
5
|
+
import { LucideAngularModule } from "lucide-angular";
|
|
6
|
+
import { JButtonComponent } from "../../button/button.component";
|
|
7
|
+
import { JColorsService } from "../../color/colors.service";
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'JAlertDialog',
|
|
11
|
+
imports: [LucideAngularModule, NgClass, JButtonComponent],
|
|
12
|
+
templateUrl: './dialog-alert.component.html',
|
|
13
|
+
styleUrl: './dialog-alert.component.css',
|
|
14
|
+
animations: [
|
|
15
|
+
trigger("modalTransition", [
|
|
16
|
+
transition(":enter", [
|
|
17
|
+
style({ transform: "translateY(1rem)", opacity: 0 }),
|
|
18
|
+
animate("300ms ease-out", style({ transform: "translateY(0)", opacity: 1 })),
|
|
19
|
+
]),
|
|
20
|
+
transition(":leave", [
|
|
21
|
+
animate("150ms ease-in", style({ transform: "translateY(1rem)", opacity: 0 })),
|
|
22
|
+
]),
|
|
23
|
+
]),
|
|
24
|
+
],
|
|
25
|
+
})
|
|
26
|
+
export class JAlertDialogComponent {
|
|
27
|
+
@Input() monocromatic: boolean = false;
|
|
28
|
+
private readonly alertDialogService = inject(JAlertDialogService);
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private readonly colorsService: JColorsService,
|
|
32
|
+
private readonly iconsService: JIconsService
|
|
33
|
+
) { }
|
|
34
|
+
|
|
35
|
+
// Single computed property for dialogs
|
|
36
|
+
dialogs = computed(() => this.alertDialogService.dialogs());
|
|
37
|
+
|
|
38
|
+
getIcon(type: string) {
|
|
39
|
+
return this.iconsService.icons[type as keyof typeof this.iconsService.icons] || this.iconsService.icons.info;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Method to execute the action based on the button clicked
|
|
43
|
+
handleAction(action: "confirm" | "cancel" | "retry") {
|
|
44
|
+
this.alertDialogService.executeAction(action);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get the class for the toast
|
|
48
|
+
getDialogClass(type: string) {
|
|
49
|
+
return this.colorsService.getAlertClass(type, this.monocromatic);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Get the class for the icon
|
|
53
|
+
getIconClass(type: string) {
|
|
54
|
+
return this.colorsService.getIconClass(type, this.monocromatic);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Get the class for the button
|
|
58
|
+
getButtonClass(type: string): { [key: string]: boolean } {
|
|
59
|
+
return this.colorsService.getButtonClass(type, this.monocromatic);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Get the class for the secondary button
|
|
63
|
+
getButtonSecondaryClass(type: string): { [key: string]: boolean } {
|
|
64
|
+
return this.colorsService.getButtonSecondaryClass(type, this.monocromatic);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<div [ngClass]="getPositionClass()">
|
|
2
|
+
@for (toast of toasts(); track toast.id) {
|
|
3
|
+
<div @toastTransition
|
|
4
|
+
class="j-dialog-toas relative ml-auto w-100 border border-border border-l-4 p-4 rounded-lg shadow-lg overflow-hidden flex gap-3"
|
|
5
|
+
[ngClass]="getToastClass(toast.config.type)">
|
|
6
|
+
|
|
7
|
+
<!-- Close button -->
|
|
8
|
+
<button
|
|
9
|
+
class="absolute p-1 top-2 right-2 cursor-pointer rounded transition duration-300 hover:bg-background dark:hover:bg-dark-background"
|
|
10
|
+
[ngClass]="getIconClass(toast.config.type)"
|
|
11
|
+
(click)="closeToast(toast.id)"
|
|
12
|
+
>
|
|
13
|
+
<lucide-icon [name]="iconsService.icons.close" [size]="16"></lucide-icon>
|
|
14
|
+
</button>
|
|
15
|
+
|
|
16
|
+
<!-- Big icon in the bottom left corner -->
|
|
17
|
+
<div [ngClass]="{ 'animate-spin': toast.config.type === 'loading'}"
|
|
18
|
+
class="absolute -bottom-5 opacity-15 dark:opacity-25 -left-5 text-black dark:text-white z-2 pointer-events-none">
|
|
19
|
+
<lucide-icon
|
|
20
|
+
[name]="getIcon(toast.config.type)"
|
|
21
|
+
[size]="100"
|
|
22
|
+
[ngClass]="getIconClass(toast.config.type)"
|
|
23
|
+
>
|
|
24
|
+
</lucide-icon>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Small icon in the top right corner -->
|
|
28
|
+
<div [ngClass]="{ 'animate-spin': toast.config.type === 'loading'}"
|
|
29
|
+
class="absolute top-5 opacity-15 dark:opacity-25 right-5 text-black dark:text-white z-2 pointer-events-none">
|
|
30
|
+
<lucide-icon
|
|
31
|
+
[name]="getIcon(toast.config.type)"
|
|
32
|
+
[size]="30"
|
|
33
|
+
[ngClass]="getIconClass(toast.config.type)"
|
|
34
|
+
>
|
|
35
|
+
</lucide-icon>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Content -->
|
|
39
|
+
<div class="flex-1">
|
|
40
|
+
<h3 class="text-sm font-semibold text-black dark:text-white pb-2">{{ toast.config.title }}</h3>
|
|
41
|
+
<p class="text-sm text-black/80 dark:text-dark-muted-foreground" [innerHTML]="toast.config.description"></p>
|
|
42
|
+
|
|
43
|
+
<!-- Action buttons -->
|
|
44
|
+
<div class="flex justify-end gap-2 mt-2">
|
|
45
|
+
|
|
46
|
+
<!-- Button Cancel -->
|
|
47
|
+
@if (toast.config.type !== 'success' && toast.onCancelCallback) {
|
|
48
|
+
<JButton
|
|
49
|
+
(clicked)="handleAction(toast.id, 'cancel')"
|
|
50
|
+
[disabled]="toast.isCancelLoading || toast.isActionLoading"
|
|
51
|
+
[isLoading]="toast.isCancelLoading"
|
|
52
|
+
classes="text-[10px] min-w-auto"
|
|
53
|
+
[ngClasses]="getButtonSecondaryClass(toast.config.type)"
|
|
54
|
+
>
|
|
55
|
+
{{toast.config.titleBtnCancel || 'Cancelar'}}
|
|
56
|
+
</JButton>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
<!-- Button Action -->
|
|
60
|
+
@if (toast.config.type !== 'loading' && toast.onActionCallback) {
|
|
61
|
+
<JButton
|
|
62
|
+
(clicked)="handleAction(toast.id, 'action')"
|
|
63
|
+
[disabled]="toast.isCancelLoading || toast.isActionLoading"
|
|
64
|
+
[isLoading]="toast.isActionLoading"
|
|
65
|
+
classes="text-[10px] min-w-auto"
|
|
66
|
+
[ngClasses]="getButtonClass(toast.config.type)"
|
|
67
|
+
>
|
|
68
|
+
{{toast.actionNameButton}}
|
|
69
|
+
</JButton>
|
|
70
|
+
}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
</div>
|
|
75
|
+
}
|
|
76
|
+
</div>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Component, computed, inject, Input } from '@angular/core';
|
|
2
|
+
import { trigger, transition, style, animate } from "@angular/animations";
|
|
3
|
+
import { NgClass } from '@angular/common';
|
|
4
|
+
import { LucideAngularModule } from 'lucide-angular';
|
|
5
|
+
import { JButtonComponent } from '../../button/button.component';
|
|
6
|
+
import { JColorsService } from '../../color/colors.service';
|
|
7
|
+
import { JIconsService, JAlertToastService } from 'tailjng';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'JAlertToast',
|
|
11
|
+
imports: [LucideAngularModule, NgClass, JButtonComponent],
|
|
12
|
+
templateUrl: './toast-alert.component.html',
|
|
13
|
+
styleUrl: './toast-alert.component.css',
|
|
14
|
+
animations: [
|
|
15
|
+
trigger("toastTransition", [
|
|
16
|
+
transition(":enter", [
|
|
17
|
+
style({ opacity: 0, transform: "translateY(10px)" }),
|
|
18
|
+
animate("300ms ease-out", style({ opacity: 1, transform: "translateY(0)" }))
|
|
19
|
+
]),
|
|
20
|
+
transition(":leave", [
|
|
21
|
+
animate("150ms ease-in", style({ opacity: 0, transform: "translateY(10px)" }))
|
|
22
|
+
])
|
|
23
|
+
])
|
|
24
|
+
]
|
|
25
|
+
})
|
|
26
|
+
export class JAlertToastComponent {
|
|
27
|
+
@Input() monocromatic: boolean = false;
|
|
28
|
+
@Input() position: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' = 'bottom-right';
|
|
29
|
+
|
|
30
|
+
private readonly alertToastService = inject(JAlertToastService);
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
private readonly colorsService: JColorsService,
|
|
34
|
+
public readonly iconsService: JIconsService
|
|
35
|
+
) { }
|
|
36
|
+
|
|
37
|
+
toasts = computed(() => this.alertToastService.toasts());
|
|
38
|
+
|
|
39
|
+
getIcon(type: string) {
|
|
40
|
+
return this.iconsService.icons[type as keyof typeof this.iconsService.icons] || this.iconsService.icons.info;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
handleAction(toastId: string, action: "action" | "cancel") {
|
|
44
|
+
this.alertToastService.executeToastAction(toastId, action);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
closeToast(toastId: string) {
|
|
48
|
+
this.alertToastService.closeToastById(toastId);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Get the class of the toast
|
|
52
|
+
getToastClass(type: string) {
|
|
53
|
+
return this.colorsService.getAlertClass(type, this.monocromatic);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Get the class of the icon
|
|
57
|
+
getIconClass(type: string) {
|
|
58
|
+
return this.colorsService.getIconClass(type, this.monocromatic);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get the class of the button
|
|
62
|
+
getButtonClass(type: string): { [key: string]: boolean } {
|
|
63
|
+
return this.colorsService.getButtonClass(type, this.monocromatic);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get the class of the secondary button
|
|
67
|
+
getButtonSecondaryClass(type: string): { [key: string]: boolean } {
|
|
68
|
+
return this.colorsService.getButtonSecondaryClass(type, this.monocromatic);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Assign toast position
|
|
72
|
+
getPositionClass(): string {
|
|
73
|
+
const base = 'w-full fixed z-1000 flex flex-col gap-2 max-w-md';
|
|
74
|
+
switch (this.position) {
|
|
75
|
+
case 'top-left':
|
|
76
|
+
return `${base} top-4 left-4`;
|
|
77
|
+
case 'top-right':
|
|
78
|
+
return `${base} top-20 right-4`;
|
|
79
|
+
case 'bottom-left':
|
|
80
|
+
return `${base} bottom-4 left-4`;
|
|
81
|
+
case 'bottom-right':
|
|
82
|
+
default:
|
|
83
|
+
return `${base} bottom-4 right-4`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<button
|
|
2
|
+
[attr.type]="type"
|
|
3
|
+
[jTooltip]="tooltip"
|
|
4
|
+
[jTooltipPosition]="tooltipPosition"
|
|
5
|
+
[disabled]="disabled || isLoading"
|
|
6
|
+
[ngClass]="computedClasses"
|
|
7
|
+
[class]="classes"
|
|
8
|
+
(click)="handleClick($event)"
|
|
9
|
+
>
|
|
10
|
+
<div class="flex items-center justify-center gap-2">
|
|
11
|
+
|
|
12
|
+
@if (isLoading) {
|
|
13
|
+
<div [ngClass]="{ 'pt-1 pb-1': icon }">
|
|
14
|
+
<lucide-icon [name]="iconsService.icons.loading" [size]="iconSize || 15" class="animate-spin" />
|
|
15
|
+
</div>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@if (!isLoading && icon) {
|
|
19
|
+
<div class="pt-1 pb-1">
|
|
20
|
+
|
|
21
|
+
@if (!isChangeIcon) {
|
|
22
|
+
<lucide-icon [name]="icon" [size]="iconSize" />
|
|
23
|
+
} @else {
|
|
24
|
+
<lucide-icon [name]="iconChange" [size]="iconSize" />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
</div>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@if (!isLoading && text) {
|
|
31
|
+
<span>{{ text }}</span>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
<ng-content></ng-content>
|
|
35
|
+
</div>
|
|
36
|
+
</button>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { NgClass } from "@angular/common"
|
|
2
|
+
import { Component, Input, Output, EventEmitter } from "@angular/core"
|
|
3
|
+
import { LucideAngularModule } from "lucide-angular"
|
|
4
|
+
import { JIconsService } from "tailjng";
|
|
5
|
+
import { JColorsService } from '../color/colors.service';
|
|
6
|
+
import { JTooltipDirective } from "../tooltip/tooltip.directive"
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'JButton',
|
|
10
|
+
imports: [NgClass, LucideAngularModule, JTooltipDirective],
|
|
11
|
+
templateUrl: './button.component.html',
|
|
12
|
+
styleUrl: './button.component.css'
|
|
13
|
+
})
|
|
14
|
+
export class JButtonComponent {
|
|
15
|
+
|
|
16
|
+
@Input() type: "button" | "submit" | "reset" = "button";
|
|
17
|
+
@Input() tooltipPosition: "top" | "right" | "bottom" | "left" = "top";
|
|
18
|
+
|
|
19
|
+
@Input() text!: string | number;
|
|
20
|
+
@Input() tooltip: string = "";
|
|
21
|
+
|
|
22
|
+
@Input() icon!: any;
|
|
23
|
+
@Input() iconSize: number = 15;
|
|
24
|
+
@Input() iconChange!: any;
|
|
25
|
+
@Input() isChangeIcon: boolean = false;
|
|
26
|
+
|
|
27
|
+
@Output() clicked = new EventEmitter<Event>();
|
|
28
|
+
|
|
29
|
+
@Input() disabled = false;
|
|
30
|
+
@Input() isLoading = false;
|
|
31
|
+
|
|
32
|
+
@Input() classes: string = "";
|
|
33
|
+
@Input() ngClasses: { [key: string]: boolean } = {};
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// Define classes based on button type (switch)
|
|
37
|
+
get variantClasses(): string {
|
|
38
|
+
return this.colorsService.variants[this.getActiveVariant()] || "min-w-[100px] text-black dark:text-white shadow-md"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Combine base classes with variants
|
|
42
|
+
get computedClasses() {
|
|
43
|
+
return {
|
|
44
|
+
"flex gap-3 items-center justify-center font-semibold border border-border dark:border-dark-border px-3 py-2 rounded transition duration-300 select-none": true,
|
|
45
|
+
[this.variantClasses]: true, // Apply variant classes based on switch
|
|
46
|
+
"cursor-pointer": !this.disabled && !this.isLoading, // Default cursor when active
|
|
47
|
+
"cursor-default opacity-50 pointer-events-none": this.disabled || this.isLoading, // Disabled cursor
|
|
48
|
+
...this.ngClasses, // Allows using dynamic validations with [ngClass]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
public readonly iconsService: JIconsService,
|
|
55
|
+
private readonly colorsService: JColorsService,
|
|
56
|
+
) { }
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Verify if a class is present in `classes` or `ngClasses`
|
|
61
|
+
* Split the class string by spaces to check each class individually
|
|
62
|
+
* @param className Name of the class to check
|
|
63
|
+
* @returns True if the class is present, false otherwise
|
|
64
|
+
*/
|
|
65
|
+
private hasClass(className: string): boolean {
|
|
66
|
+
const classArray = this.classes.split(" ")
|
|
67
|
+
return classArray.includes(className) || this.ngClasses[className]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the active variant based on the provided classes.
|
|
74
|
+
* It checks if the class exists in the `variants` object of `JColorsService
|
|
75
|
+
* @returns The active variant based on the provided classes.
|
|
76
|
+
*/
|
|
77
|
+
private getActiveVariant(): string {
|
|
78
|
+
const variant = Object.keys(this.colorsService.variants).find((variant) => this.hasClass(variant))
|
|
79
|
+
return variant ?? "default"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Handle click event on the button.
|
|
86
|
+
* Emits the clicked event if the button is not disabled and not loading.
|
|
87
|
+
* @param event The click event
|
|
88
|
+
*/
|
|
89
|
+
handleClick(event: Event) {
|
|
90
|
+
if (!this.disabled && !this.isLoading) {
|
|
91
|
+
this.clicked.emit(event)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="flex flex-col items-center justify-center gap-2">
|
|
2
|
+
@if (title) {
|
|
3
|
+
<span class="text-[8px] opacity-80">{{title}}</span>
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
<label class="relative inline-flex items-center justify-center">
|
|
7
|
+
<input
|
|
8
|
+
type="checkbox"
|
|
9
|
+
class="absolute opacity-0 cursor-pointer peer"
|
|
10
|
+
[checked]="getValue(item, column)"
|
|
11
|
+
(change)="onCheckboxChange(item, column)"
|
|
12
|
+
[disabled]="disabled"
|
|
13
|
+
>
|
|
14
|
+
<span
|
|
15
|
+
class="inline-flex items-center justify-center w-[25px] h-[25px] bg-white border border-primary dark:border-dark-border rounded transition-all duration-200 ease-in-out relative peer peer-checked:bg-primary peer-checked:border-primary dark:peer-checked:bg-dark-primary dark:peer-checked:border-dark-primary"
|
|
16
|
+
[class]="classes"
|
|
17
|
+
[ngClass]="{'opacity-50' : disabled, 'cursor-not-allowed': disabled, 'cursor-pointer': !disabled}"
|
|
18
|
+
>
|
|
19
|
+
<lucide-icon [name]="icon" [size]="iconSize" class="text-white"></lucide-icon>
|
|
20
|
+
</span>
|
|
21
|
+
</label>
|
|
22
|
+
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Component, forwardRef, Input } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { LucideAngularModule } from 'lucide-angular';
|
|
4
|
+
import { JIconsService } from 'tailjng';
|
|
5
|
+
import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'JInputCheckbox',
|
|
9
|
+
imports: [CommonModule, LucideAngularModule, FormsModule, ReactiveFormsModule],
|
|
10
|
+
templateUrl: './input-checkbox.component.html',
|
|
11
|
+
styleUrl: './input-checkbox.component.css',
|
|
12
|
+
providers: [
|
|
13
|
+
{
|
|
14
|
+
provide: NG_VALUE_ACCESSOR,
|
|
15
|
+
useExisting: forwardRef(() => JInputCheckboxComponent),
|
|
16
|
+
multi: true
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
})
|
|
20
|
+
export class JInputCheckboxComponent {
|
|
21
|
+
|
|
22
|
+
@Input() title!: string;
|
|
23
|
+
|
|
24
|
+
@Input() icon: any;
|
|
25
|
+
@Input() iconSize: number = 15;
|
|
26
|
+
|
|
27
|
+
@Input() disabled?: boolean;
|
|
28
|
+
@Input() isLoading?: boolean;
|
|
29
|
+
@Input() classes: string = '';
|
|
30
|
+
|
|
31
|
+
@Input() item: any;
|
|
32
|
+
@Input() column: any;
|
|
33
|
+
|
|
34
|
+
// Funciones
|
|
35
|
+
@Input() getValue: (item: any, column: any) => boolean = () => false;
|
|
36
|
+
@Input() onCheckboxChange: (item: any, column: any) => void = () => { };
|
|
37
|
+
|
|
38
|
+
constructor(
|
|
39
|
+
public readonly iconsService: JIconsService
|
|
40
|
+
) {
|
|
41
|
+
this.icon = this.iconsService.icons.check;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|