tailjng 0.1.6 → 0.1.7
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/README.md +12 -4
- package/cli/execute/init-app.js +5 -2
- package/cli/execute/sync-app.js +14 -2
- package/cli/settings/colors-config-utils.js +43 -8
- package/cli/settings/icons-config-utils.js +62 -0
- package/cli/settings/path-utils.js +32 -2
- package/cli/settings/project-utils.js +7 -1
- package/cli/templates/app.generator.js +2 -2
- package/fesm2022/tailjng.mjs +247 -80
- package/fesm2022/tailjng.mjs.map +1 -1
- package/lib/services/static/theme.service.d.ts +39 -1
- package/lib/utils/theme/theme-variables.util.d.ts +31 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -1
- package/registry/components.json +41 -18
- package/src/colors.safelist.css +2 -2
- package/src/lib/components/.config/README.md +11 -0
- package/src/lib/components/.config/colors/README.md +38 -0
- package/src/lib/components/{colors-config → .config/colors}/colors.config.ts +5 -5
- package/src/lib/components/{colors-config → .config/colors}/colors.safelist.css +2 -2
- package/src/lib/components/.config/icons/README.md +26 -0
- package/src/lib/components/.config/icons/icons.lucide.ts +134 -0
- package/src/lib/components/.config/input/README.md +24 -0
- package/src/lib/components/.config/input/input.classes.ts +119 -0
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +244 -2
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +25 -38
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +66 -56
- package/src/lib/components/alert/alert-dialog/dialog-alert.types.ts +19 -0
- package/src/lib/components/alert/alert-toast/toast-alert.component.css +630 -12
- package/src/lib/components/alert/alert-toast/toast-alert.component.html +103 -102
- package/src/lib/components/alert/alert-toast/toast-alert.component.ts +485 -128
- package/src/lib/components/alert/alert-toast/toast-alert.types.ts +25 -0
- package/src/lib/components/badge/badge.component.html +34 -21
- package/src/lib/components/badge/badge.component.ts +140 -31
- package/src/lib/components/button/button.component.html +16 -10
- package/src/lib/components/button/button.component.ts +162 -22
- package/src/lib/components/card/card-complete/complete-card.component.html +2 -2
- package/src/lib/components/card/card-complete/complete-card.component.ts +26 -16
- package/src/lib/components/card/card-crud-complete/complete-crud-card.component.html +2 -2
- package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +26 -16
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.css +97 -0
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.html +54 -46
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.ts +135 -64
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.types.ts +3 -0
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.css +112 -0
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +28 -25
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.ts +67 -15
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.types.ts +1 -0
- package/src/lib/components/coach-mark/coach-mark.component.html +4 -22
- package/src/lib/components/coach-mark/coach-mark.component.scss +1 -1
- package/src/lib/components/coach-mark/coach-mark.component.ts +51 -18
- package/src/lib/components/coach-mark/coach-mark.directive.ts +133 -78
- package/src/lib/components/coach-mark/coach-mark.types.ts +12 -0
- package/src/lib/components/dialog/dialog.component.css +103 -1
- package/src/lib/components/dialog/dialog.component.html +46 -66
- package/src/lib/components/dialog/dialog.component.ts +136 -110
- package/src/lib/components/dialog/dialog.types.ts +19 -0
- package/src/lib/components/filter/filter-complete/complete-filter.component.html +16 -19
- package/src/lib/components/filter/filter-complete/complete-filter.component.scss +35 -0
- package/src/lib/components/filter/filter-complete/complete-filter.component.ts +58 -34
- package/src/lib/components/filter/filter-complete/complete-filter.types.ts +7 -0
- package/src/lib/components/filter/filter-complete/complete-filter.util.ts +16 -0
- package/src/lib/components/form/form-container/container-form.component.css +4 -0
- package/src/lib/components/form/form-container/container-form.component.html +2 -2
- package/src/lib/components/form/form-container/container-form.component.ts +72 -16
- package/src/lib/components/form/form-container/container-form.types.ts +42 -0
- package/src/lib/components/form/form-container/form-col-span.directive.ts +25 -0
- package/src/lib/components/form/form-sidebar/sidebar-form.component.css +276 -0
- package/src/lib/components/form/form-sidebar/sidebar-form.component.html +117 -125
- package/src/lib/components/form/form-sidebar/sidebar-form.component.ts +109 -34
- package/src/lib/components/form/form-sidebar/sidebar-form.types.ts +3 -0
- package/src/lib/components/{toggle-radio/toggle-radio.component.css → form/form-validation/validation-form.component.css} +0 -1
- package/src/lib/components/form/form-validation/validation-form.component.html +10 -6
- package/src/lib/components/form/form-validation/validation-form.component.ts +99 -12
- package/src/lib/components/form/form-validation/validation-form.types.ts +33 -0
- package/src/lib/components/icon/icon.component.html +8 -5
- package/src/lib/components/icon/icon.component.ts +111 -9
- package/src/lib/components/input/input/input.component.html +19 -16
- package/src/lib/components/input/input/input.component.ts +130 -53
- package/src/lib/components/input/input/input.types.ts +8 -0
- package/src/lib/components/input/input-file/file-input.component.html +65 -56
- package/src/lib/components/input/input-file/file-input.component.ts +276 -173
- package/src/lib/components/input/input-file/file-input.types.ts +2 -0
- package/src/lib/components/input/input-range/range-input.component.css +67 -0
- package/src/lib/components/input/input-range/range-input.component.html +50 -58
- package/src/lib/components/input/input-range/range-input.component.ts +148 -60
- package/src/lib/components/input/input-range/range-input.types.ts +7 -0
- package/src/lib/components/input/input-textarea/textarea-input.component.html +16 -7
- package/src/lib/components/input/input-textarea/textarea-input.component.ts +140 -50
- package/src/lib/components/input/input-textarea/textarea-input.types.ts +2 -0
- package/src/lib/components/label/label.component.html +17 -16
- package/src/lib/components/label/label.component.ts +70 -16
- package/src/lib/components/label/label.types.ts +2 -0
- package/src/lib/components/menu/menu-options-table/menu-options-defaults.ts +34 -0
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.html +34 -20
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.ts +211 -58
- package/src/lib/components/menu/menu-options-table/options-table-menu.types.ts +38 -0
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +49 -52
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.ts +112 -24
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.types.ts +9 -0
- package/src/lib/components/mode-toggle/mode-toggle.component.html +11 -16
- package/src/lib/components/mode-toggle/mode-toggle.component.ts +69 -33
- package/src/lib/components/paginator/paginator-complete/complete-paginator.component.html +4 -4
- package/src/lib/components/paginator/paginator-complete/complete-paginator.component.ts +31 -7
- package/src/lib/components/paginator/paginator-complete/complete-paginator.types.ts +12 -0
- package/src/lib/components/paginator/paginator-complete/complete-paginator.util.ts +36 -0
- package/src/lib/components/progress-bar/progress-bar.component.css +11 -0
- package/src/lib/components/progress-bar/progress-bar.component.html +41 -40
- package/src/lib/components/progress-bar/progress-bar.component.ts +95 -11
- package/src/lib/components/progress-bar/progress-bar.types.ts +2 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.component.css +6 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.component.html +54 -44
- package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +450 -509
- package/src/lib/components/select/select-dropdown/dropdown-select.types.ts +43 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.util.ts +179 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +6 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +131 -42
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +491 -475
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.types.ts +22 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.util.ts +20 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.component.css +10 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.component.html +76 -60
- package/src/lib/components/select/select-multi-table/multi-table-select.component.ts +250 -313
- package/src/lib/components/select/select-multi-table/multi-table-select.types.ts +10 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.util.ts +5 -0
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.css +212 -0
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.html +62 -53
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.ts +84 -27
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.types.ts +2 -0
- package/src/lib/components/table/table-complete/complete-table.component.html +15 -17
- package/src/lib/components/table/table-complete/complete-table.component.ts +190 -338
- package/src/lib/components/table/table-complete/complete-table.types.ts +28 -0
- package/src/lib/components/table/table-complete/complete-table.util.ts +236 -0
- package/src/lib/components/table/table-complete/index.ts +2 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.animations.ts +34 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +73 -128
- package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +542 -829
- package/src/lib/components/table/table-crud-complete/complete-crud-table.types.ts +57 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.util.ts +723 -0
- package/src/lib/components/table/table-crud-complete/index.ts +3 -0
- package/src/lib/components/theme-generator/theme-generator.component.css +21 -0
- package/src/lib/components/theme-generator/theme-generator.component.html +146 -116
- package/src/lib/components/theme-generator/theme-generator.component.ts +44 -24
- package/src/lib/components/toggle-radio/shared/toggle-options.types.ts +8 -0
- package/src/lib/components/toggle-radio/shared/toggle-options.util.ts +44 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.css +135 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.html +52 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.ts +198 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.types.ts +1 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.css +108 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.html +37 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.ts +193 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.types.ts +1 -0
- package/src/lib/components/tooltip/tooltip.directive.ts +12 -9
- package/src/lib/components/tooltip/tooltip.service.ts +331 -133
- package/src/lib/components/tooltip/tooltip.types.ts +9 -0
- package/src/lib/components/viewer/viewer-image/image-viewer.component.css +90 -4
- package/src/lib/components/viewer/viewer-image/image-viewer.component.html +52 -103
- package/src/lib/components/viewer/viewer-image/image-viewer.component.ts +182 -177
- package/src/lib/components/viewer/viewer-image/image-viewer.types.ts +3 -0
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.css +177 -0
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.html +74 -24
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.ts +168 -15
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.types.ts +1 -0
- package/src/styles.css +2 -2
- package/lib/services/static/icons.service.d.ts +0 -65
- package/src/lib/components/colors-config/README.md +0 -38
- package/src/lib/components/form/form-sidebar/sidebar-form.component.scss +0 -0
- package/src/lib/components/form/form-validation/validation-form.component.scss +0 -0
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.scss +0 -0
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +0 -12
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.scss +0 -0
- package/src/lib/components/toggle-radio/toggle-radio.component.html +0 -51
- package/src/lib/components/toggle-radio/toggle-radio.component.ts +0 -222
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.scss +0 -0
- package/tailjng-0.1.6.tgz +0 -0
|
@@ -1,27 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { NgClass } from '@angular/common';
|
|
2
|
+
import {
|
|
3
|
+
Component,
|
|
4
|
+
HostListener,
|
|
5
|
+
Input,
|
|
6
|
+
OnChanges,
|
|
7
|
+
SimpleChanges,
|
|
8
|
+
} from '@angular/core';
|
|
3
9
|
import { SafeUrl } from '@angular/platform-browser';
|
|
4
|
-
import {
|
|
5
|
-
import { JIconsService } from 'tailjng';
|
|
10
|
+
import { Icons } from '../../.config/icons/icons.lucide';
|
|
6
11
|
import { JButtonComponent } from '../../button/button.component';
|
|
12
|
+
import { JIconComponent } from '../../icon/icon.component';
|
|
7
13
|
import { JTooltipDirective } from '../../tooltip/tooltip.directive';
|
|
8
|
-
|
|
14
|
+
import { ImageObjectFit, ImageViewerControlSize } from './image-viewer.types';
|
|
15
|
+
|
|
16
|
+
export type { ImageObjectFit, ImageViewerControlSize } from './image-viewer.types';
|
|
17
|
+
|
|
18
|
+
const ZOOM_MIN = 0.5;
|
|
19
|
+
const ZOOM_MAX = 3;
|
|
20
|
+
const ZOOM_STEP = 0.1;
|
|
21
|
+
|
|
22
|
+
const CONTROL_BUTTON_SIZES: Record<ImageViewerControlSize, string> = {
|
|
23
|
+
sm: 'w-[28px] h-[28px]',
|
|
24
|
+
md: 'w-[32px] h-[32px]',
|
|
25
|
+
lg: 'w-[38px] h-[38px]',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const CONTROL_ICON_SIZES: Record<ImageViewerControlSize, number> = {
|
|
29
|
+
sm: 15,
|
|
30
|
+
md: 18,
|
|
31
|
+
lg: 21,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Image viewer with zoom, rotate, pan, download, and fullscreen controls.
|
|
36
|
+
*
|
|
37
|
+
* Install: `npx tailjng add viewer-image`
|
|
38
|
+
*/
|
|
9
39
|
@Component({
|
|
10
40
|
selector: 'JImageViewer',
|
|
11
|
-
imports: [
|
|
41
|
+
imports: [NgClass, JButtonComponent, JTooltipDirective, JIconComponent],
|
|
12
42
|
templateUrl: './image-viewer.component.html',
|
|
13
|
-
styleUrl: './image-viewer.component.css'
|
|
43
|
+
styleUrl: './image-viewer.component.css',
|
|
14
44
|
})
|
|
15
45
|
export class JImageViewerComponent implements OnChanges {
|
|
46
|
+
readonly Icons = Icons;
|
|
16
47
|
|
|
17
48
|
@Input() src: string | SafeUrl | null = null;
|
|
18
|
-
@Input() alt
|
|
19
|
-
@Input() emptyMessage
|
|
20
|
-
|
|
21
|
-
@Input() objectFit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down' = 'contain';
|
|
22
|
-
|
|
49
|
+
@Input() alt = 'Imagen';
|
|
50
|
+
@Input() emptyMessage = 'La imagen no existe';
|
|
51
|
+
@Input() objectFit: ImageObjectFit = 'contain';
|
|
23
52
|
@Input() showControls = true;
|
|
24
|
-
|
|
25
53
|
@Input() showRotateLeft?: boolean;
|
|
26
54
|
@Input() showRotateRight?: boolean;
|
|
27
55
|
@Input() showReset?: boolean;
|
|
@@ -29,16 +57,16 @@ export class JImageViewerComponent implements OnChanges {
|
|
|
29
57
|
@Input() showFullscreen?: boolean;
|
|
30
58
|
@Input() showZoomIn?: boolean;
|
|
31
59
|
@Input() showZoomOut?: boolean;
|
|
32
|
-
|
|
33
|
-
@Input()
|
|
60
|
+
@Input() controlSize: ImageViewerControlSize = 'md';
|
|
61
|
+
@Input() ngClasses: Record<string, boolean> = {};
|
|
34
62
|
|
|
35
63
|
posX = 0;
|
|
36
64
|
posY = 0;
|
|
37
|
-
|
|
38
65
|
zoom = 1;
|
|
39
66
|
rotate = 0;
|
|
40
|
-
|
|
41
67
|
isFullscreen = false;
|
|
68
|
+
isDownloading = false;
|
|
69
|
+
|
|
42
70
|
internalLoading = true;
|
|
43
71
|
animateTransform = true;
|
|
44
72
|
hasError = false;
|
|
@@ -46,98 +74,159 @@ export class JImageViewerComponent implements OnChanges {
|
|
|
46
74
|
private dragging = false;
|
|
47
75
|
private dragStart = { x: 0, y: 0 };
|
|
48
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Whether the image is still loading.
|
|
79
|
+
*/
|
|
49
80
|
get loading(): boolean {
|
|
50
81
|
return this.internalLoading;
|
|
51
82
|
}
|
|
52
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Whether a source URL is provided.
|
|
86
|
+
*/
|
|
53
87
|
get hasImage(): boolean {
|
|
54
88
|
return !!this.src;
|
|
55
89
|
}
|
|
56
90
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Whether zoom out is at the minimum level.
|
|
93
|
+
*/
|
|
94
|
+
get isZoomMin(): boolean {
|
|
95
|
+
return this.zoom <= ZOOM_MIN;
|
|
96
|
+
}
|
|
62
97
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Whether zoom in is at the maximum level.
|
|
100
|
+
*/
|
|
101
|
+
get isZoomMax(): boolean {
|
|
102
|
+
return this.zoom >= ZOOM_MAX;
|
|
66
103
|
}
|
|
67
104
|
|
|
105
|
+
/**
|
|
106
|
+
* CSS transform applied to the image.
|
|
107
|
+
*/
|
|
108
|
+
get imageTransform(): string {
|
|
109
|
+
return `scale(${this.zoom}) rotate(${this.rotate}deg) translate(${this.posX}px, ${this.posY}px)`;
|
|
110
|
+
}
|
|
68
111
|
|
|
69
112
|
/**
|
|
70
|
-
*
|
|
113
|
+
* Tailwind size classes for toolbar buttons.
|
|
71
114
|
*/
|
|
72
|
-
|
|
73
|
-
this.
|
|
74
|
-
this.hasError = false;
|
|
115
|
+
get buttonSizeClass(): string {
|
|
116
|
+
return CONTROL_BUTTON_SIZES[this.controlSize];
|
|
75
117
|
}
|
|
76
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Icon size in px for toolbar buttons.
|
|
121
|
+
*/
|
|
122
|
+
get buttonIconSize(): number {
|
|
123
|
+
return CONTROL_ICON_SIZES[this.controlSize];
|
|
124
|
+
}
|
|
77
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Shared classes for floating control buttons.
|
|
128
|
+
*/
|
|
129
|
+
get controlButtonClasses(): string {
|
|
130
|
+
return `secondary rounded-lg shrink-0 opacity-30 hover:opacity-100 transition-opacity duration-200 ${this.buttonSizeClass}`;
|
|
131
|
+
}
|
|
78
132
|
|
|
79
133
|
/**
|
|
80
|
-
*
|
|
134
|
+
* Class map for the controls toolbar position.
|
|
81
135
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
136
|
+
get controlsPositionClasses(): Record<string, boolean> {
|
|
137
|
+
return {
|
|
138
|
+
'top-3 right-3': this.isFullscreen,
|
|
139
|
+
'top-2 right-2': !this.isFullscreen,
|
|
140
|
+
};
|
|
85
141
|
}
|
|
86
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Class map for the image element.
|
|
145
|
+
*/
|
|
146
|
+
get imageClasses(): Record<string, boolean> {
|
|
147
|
+
return {
|
|
148
|
+
'j-image-viewer__image': true,
|
|
149
|
+
'invisible': this.loading,
|
|
150
|
+
'transition-transform': this.animateTransform,
|
|
151
|
+
'duration-200': this.animateTransform,
|
|
152
|
+
'ease-in-out': this.animateTransform,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
87
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Whether at least one toolbar action is visible.
|
|
158
|
+
*/
|
|
159
|
+
get hasAnyControlVisible(): boolean {
|
|
160
|
+
return [
|
|
161
|
+
this.showRotateLeft,
|
|
162
|
+
this.showRotateRight,
|
|
163
|
+
this.showReset,
|
|
164
|
+
this.showZoomOut,
|
|
165
|
+
this.showZoomIn,
|
|
166
|
+
this.showDownload,
|
|
167
|
+
this.showFullscreen,
|
|
168
|
+
].some((value) => this.isControlVisible(value));
|
|
169
|
+
}
|
|
88
170
|
|
|
89
171
|
/**
|
|
90
|
-
*
|
|
91
|
-
* @param container
|
|
172
|
+
* Fullscreen toggle icon based on current state.
|
|
92
173
|
*/
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
this.
|
|
174
|
+
get fullscreenIcon() {
|
|
175
|
+
return this.isFullscreen ? Icons.Minimize2 : Icons.Scan;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
179
|
+
if (changes['src'] && changes['src'].currentValue !== changes['src'].previousValue) {
|
|
180
|
+
this.resetView();
|
|
181
|
+
this.hasError = false;
|
|
182
|
+
this.internalLoading = !!this.src;
|
|
100
183
|
}
|
|
101
184
|
}
|
|
102
185
|
|
|
186
|
+
@HostListener('document:fullscreenchange')
|
|
187
|
+
onFullscreenChange(): void {
|
|
188
|
+
this.isFullscreen = !!document.fullscreenElement;
|
|
189
|
+
}
|
|
103
190
|
|
|
104
191
|
/**
|
|
105
|
-
*
|
|
192
|
+
* Whether a specific toolbar action should render.
|
|
106
193
|
*/
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (!this.src) return '';
|
|
194
|
+
isControlVisible(value?: boolean): boolean {
|
|
195
|
+
return value ?? this.showControls;
|
|
196
|
+
}
|
|
111
197
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
198
|
+
onImageLoad(): void {
|
|
199
|
+
this.internalLoading = false;
|
|
200
|
+
this.hasError = false;
|
|
201
|
+
}
|
|
115
202
|
|
|
116
|
-
|
|
203
|
+
onImageError(): void {
|
|
204
|
+
this.internalLoading = false;
|
|
205
|
+
this.hasError = true;
|
|
206
|
+
}
|
|
117
207
|
|
|
118
|
-
|
|
119
|
-
|
|
208
|
+
toggleFullscreen(container: HTMLElement): void {
|
|
209
|
+
if (!document.fullscreenElement) {
|
|
210
|
+
container.requestFullscreen();
|
|
211
|
+
this.isFullscreen = true;
|
|
212
|
+
return;
|
|
120
213
|
}
|
|
121
214
|
|
|
122
|
-
|
|
215
|
+
document.exitFullscreen();
|
|
216
|
+
this.isFullscreen = false;
|
|
123
217
|
}
|
|
124
218
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Downloads the currently displayed image, handling both base64 and normal URLs.
|
|
129
|
-
* @returns
|
|
130
|
-
*/
|
|
131
|
-
download() {
|
|
219
|
+
download(): void {
|
|
132
220
|
try {
|
|
133
|
-
if (!this.src)
|
|
221
|
+
if (!this.src) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
134
224
|
|
|
135
225
|
this.isDownloading = true;
|
|
136
226
|
|
|
137
|
-
const imageUrl = this.
|
|
227
|
+
const imageUrl = this.resolveImageUrl();
|
|
138
228
|
const fileName = this.alt?.replace(/\s+/g, '') || 'imagen';
|
|
139
229
|
|
|
140
|
-
// Si es base64
|
|
141
230
|
if (imageUrl.startsWith('data:image')) {
|
|
142
231
|
const link = document.createElement('a');
|
|
143
232
|
link.href = imageUrl;
|
|
@@ -149,7 +238,6 @@ export class JImageViewerComponent implements OnChanges {
|
|
|
149
238
|
return;
|
|
150
239
|
}
|
|
151
240
|
|
|
152
|
-
// Si es URL normal
|
|
153
241
|
fetch(imageUrl)
|
|
154
242
|
.then((response) => {
|
|
155
243
|
if (!response.ok) {
|
|
@@ -175,172 +263,89 @@ export class JImageViewerComponent implements OnChanges {
|
|
|
175
263
|
.finally(() => {
|
|
176
264
|
this.isDownloading = false;
|
|
177
265
|
});
|
|
178
|
-
|
|
179
266
|
} catch (err) {
|
|
180
267
|
this.isDownloading = false;
|
|
181
268
|
console.error('Error general en download():', err);
|
|
182
269
|
}
|
|
183
270
|
}
|
|
184
271
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Zooms in the image.
|
|
190
|
-
*/
|
|
191
|
-
zoomIn() {
|
|
192
|
-
this.zoom = Math.min(this.zoom + 0.1, 3);
|
|
272
|
+
zoomIn(): void {
|
|
273
|
+
this.zoom = Math.min(this.zoom + ZOOM_STEP, ZOOM_MAX);
|
|
193
274
|
}
|
|
194
275
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Zooms out the image.
|
|
199
|
-
*/
|
|
200
|
-
zoomOut() {
|
|
201
|
-
this.zoom = Math.max(this.zoom - 0.1, 0.5);
|
|
276
|
+
zoomOut(): void {
|
|
277
|
+
this.zoom = Math.max(this.zoom - ZOOM_STEP, ZOOM_MIN);
|
|
202
278
|
}
|
|
203
279
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Rotates the image to the right.
|
|
208
|
-
*/
|
|
209
|
-
rotateRightImg() {
|
|
280
|
+
rotateRight(): void {
|
|
210
281
|
this.rotate += 90;
|
|
211
282
|
}
|
|
212
283
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Rotates the image to the left.
|
|
217
|
-
*/
|
|
218
|
-
rotateLeftImg() {
|
|
284
|
+
rotateLeft(): void {
|
|
219
285
|
this.rotate -= 90;
|
|
220
286
|
}
|
|
221
287
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Resets the image viewer to its initial state.
|
|
226
|
-
*/
|
|
227
|
-
reset() {
|
|
288
|
+
resetView(): void {
|
|
228
289
|
this.zoom = 1;
|
|
229
290
|
this.rotate = 0;
|
|
230
291
|
this.posX = 0;
|
|
231
292
|
this.posY = 0;
|
|
232
293
|
}
|
|
233
294
|
|
|
295
|
+
startDrag(event: MouseEvent): void {
|
|
296
|
+
if (this.zoom <= 1) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
234
299
|
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Starts dragging the image.
|
|
238
|
-
* @param event The mouse event.
|
|
239
|
-
* @returns
|
|
240
|
-
*/
|
|
241
|
-
startDrag(event: MouseEvent) {
|
|
242
|
-
if (this.zoom <= 1) return;
|
|
243
300
|
this.dragging = true;
|
|
244
301
|
this.animateTransform = false;
|
|
245
|
-
|
|
246
302
|
this.dragStart = {
|
|
247
303
|
x: event.clientX,
|
|
248
|
-
y: event.clientY
|
|
304
|
+
y: event.clientY,
|
|
249
305
|
};
|
|
250
306
|
|
|
251
307
|
document.addEventListener('mousemove', this.onDrag);
|
|
252
308
|
document.addEventListener('mouseup', this.endDrag);
|
|
253
309
|
}
|
|
254
310
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
* @param event The mouse event.
|
|
260
|
-
* @returns
|
|
261
|
-
*/
|
|
262
|
-
onDrag = (event: MouseEvent) => {
|
|
263
|
-
if (!this.dragging) return;
|
|
311
|
+
private onDrag = (event: MouseEvent): void => {
|
|
312
|
+
if (!this.dragging) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
264
315
|
|
|
265
316
|
const deltaX = event.clientX - this.dragStart.x;
|
|
266
317
|
const deltaY = event.clientY - this.dragStart.y;
|
|
267
|
-
|
|
268
318
|
const angleRad = (this.rotate % 360) * (Math.PI / 180);
|
|
269
|
-
|
|
270
319
|
const rotatedX = deltaX * Math.cos(angleRad) + deltaY * Math.sin(angleRad);
|
|
271
320
|
const rotatedY = deltaY * Math.cos(angleRad) - deltaX * Math.sin(angleRad);
|
|
272
321
|
|
|
273
322
|
this.posX += rotatedX;
|
|
274
323
|
this.posY += rotatedY;
|
|
275
|
-
|
|
276
324
|
this.dragStart.x = event.clientX;
|
|
277
325
|
this.dragStart.y = event.clientY;
|
|
278
326
|
};
|
|
279
327
|
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Ends dragging the image.
|
|
283
|
-
*/
|
|
284
|
-
endDrag = () => {
|
|
328
|
+
private endDrag = (): void => {
|
|
285
329
|
this.dragging = false;
|
|
286
330
|
this.animateTransform = true;
|
|
287
331
|
document.removeEventListener('mousemove', this.onDrag);
|
|
288
332
|
document.removeEventListener('mouseup', this.endDrag);
|
|
289
333
|
};
|
|
290
334
|
|
|
335
|
+
private resolveImageUrl(): string {
|
|
336
|
+
if (!this.src) {
|
|
337
|
+
return '';
|
|
338
|
+
}
|
|
291
339
|
|
|
340
|
+
if (typeof this.src === 'string') {
|
|
341
|
+
return this.src;
|
|
342
|
+
}
|
|
292
343
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const sizes = {
|
|
298
|
-
sm: 'w-[28px] h-[28px]',
|
|
299
|
-
md: 'w-[32px] h-[32px]',
|
|
300
|
-
lg: 'w-[38px] h-[38px]',
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
return sizes[this.controlSize];
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Returns the icon size for the control buttons based on the selected size.
|
|
309
|
-
*/
|
|
310
|
-
get buttonIconSize(): number {
|
|
311
|
-
const sizes = {
|
|
312
|
-
sm: 15,
|
|
313
|
-
md: 18,
|
|
314
|
-
lg: 21,
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
return sizes[this.controlSize];
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Determines if a control button should be visible based on the input value and the general showControls flag.
|
|
323
|
-
* @param value The specific control visibility input value.
|
|
324
|
-
* @returns True if the control should be visible, false otherwise.
|
|
325
|
-
*/
|
|
326
|
-
isControlVisible(value?: boolean): boolean {
|
|
327
|
-
return value ?? this.showControls;
|
|
328
|
-
}
|
|
329
|
-
|
|
344
|
+
const safe = this.src as SafeUrl & { changingThisBreaksApplicationSecurity?: string };
|
|
345
|
+
if (safe.changingThisBreaksApplicationSecurity) {
|
|
346
|
+
return safe.changingThisBreaksApplicationSecurity;
|
|
347
|
+
}
|
|
330
348
|
|
|
331
|
-
|
|
332
|
-
* Determines if any control button is visible, used to conditionally display the controls container.
|
|
333
|
-
* @returns True if at least one control is visible, false otherwise.
|
|
334
|
-
*/
|
|
335
|
-
get hasAnyControlVisible(): boolean {
|
|
336
|
-
return [
|
|
337
|
-
this.showRotateLeft,
|
|
338
|
-
this.showRotateRight,
|
|
339
|
-
this.showReset,
|
|
340
|
-
this.showZoomOut,
|
|
341
|
-
this.showZoomIn,
|
|
342
|
-
this.showDownload,
|
|
343
|
-
this.showFullscreen,
|
|
344
|
-
].some(value => this.isControlVisible(value));
|
|
349
|
+
return this.src.toString();
|
|
345
350
|
}
|
|
346
351
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
width: 100%;
|
|
4
|
+
height: 100%;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.j-pdf-viewer {
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
width: 100%;
|
|
11
|
+
height: 100%;
|
|
12
|
+
min-height: 18.75rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.j-pdf-viewer__frame {
|
|
16
|
+
display: block;
|
|
17
|
+
width: 100%;
|
|
18
|
+
height: 100%;
|
|
19
|
+
min-height: 18.75rem;
|
|
20
|
+
border: 1px solid var(--color-border);
|
|
21
|
+
border-radius: 0.75rem;
|
|
22
|
+
background-color: var(--color-background);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:host-context(.dark) .j-pdf-viewer__frame,
|
|
26
|
+
:host-context(html.dark) .j-pdf-viewer__frame {
|
|
27
|
+
border-color: var(--color-dark-border);
|
|
28
|
+
background-color: var(--color-dark-background);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.j-pdf-viewer__frame--mobile {
|
|
32
|
+
min-height: 60vh;
|
|
33
|
+
border-radius: 0.75rem 0.75rem 0 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.j-pdf-viewer__fallback,
|
|
37
|
+
.j-pdf-viewer__empty {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
gap: 0.75rem;
|
|
43
|
+
padding: 1.5rem;
|
|
44
|
+
text-align: center;
|
|
45
|
+
border: 1px dashed var(--color-border);
|
|
46
|
+
border-radius: 0.75rem;
|
|
47
|
+
min-height: 18.75rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:host-context(.dark) .j-pdf-viewer__fallback,
|
|
51
|
+
:host-context(.dark) .j-pdf-viewer__empty,
|
|
52
|
+
:host-context(html.dark) .j-pdf-viewer__fallback,
|
|
53
|
+
:host-context(html.dark) .j-pdf-viewer__empty {
|
|
54
|
+
border-color: var(--color-dark-border);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.j-pdf-viewer__fallback {
|
|
58
|
+
display: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.j-pdf-viewer__fallback-title,
|
|
62
|
+
.j-pdf-viewer__mobile-title {
|
|
63
|
+
margin: 0;
|
|
64
|
+
font-size: 0.9375rem;
|
|
65
|
+
font-weight: 600;
|
|
66
|
+
color: var(--color-foreground);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
:host-context(.dark) .j-pdf-viewer__fallback-title,
|
|
70
|
+
:host-context(.dark) .j-pdf-viewer__mobile-title,
|
|
71
|
+
:host-context(html.dark) .j-pdf-viewer__fallback-title,
|
|
72
|
+
:host-context(html.dark) .j-pdf-viewer__mobile-title {
|
|
73
|
+
color: var(--color-dark-foreground);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.j-pdf-viewer__fallback-text,
|
|
77
|
+
.j-pdf-viewer__mobile-text,
|
|
78
|
+
.j-pdf-viewer__message {
|
|
79
|
+
margin: 0;
|
|
80
|
+
font-size: 0.8125rem;
|
|
81
|
+
line-height: 1.45;
|
|
82
|
+
color: var(--color-muted-foreground);
|
|
83
|
+
max-width: 20rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
:host-context(.dark) .j-pdf-viewer__fallback-text,
|
|
87
|
+
:host-context(.dark) .j-pdf-viewer__mobile-text,
|
|
88
|
+
:host-context(.dark) .j-pdf-viewer__message,
|
|
89
|
+
:host-context(html.dark) .j-pdf-viewer__fallback-text,
|
|
90
|
+
:host-context(html.dark) .j-pdf-viewer__mobile-text,
|
|
91
|
+
:host-context(html.dark) .j-pdf-viewer__message {
|
|
92
|
+
color: var(--color-dark-muted-foreground);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.j-pdf-viewer__actions {
|
|
96
|
+
display: flex;
|
|
97
|
+
flex-wrap: wrap;
|
|
98
|
+
align-items: center;
|
|
99
|
+
justify-content: center;
|
|
100
|
+
gap: 0.5rem;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.j-pdf-viewer__actions--stacked {
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
width: 100%;
|
|
106
|
+
max-width: 16rem;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.j-pdf-viewer__mobile {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
gap: 0;
|
|
113
|
+
width: 100%;
|
|
114
|
+
height: 100%;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.j-pdf-viewer__mobile-card {
|
|
118
|
+
display: flex;
|
|
119
|
+
flex: 1;
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
align-items: center;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
gap: 0.875rem;
|
|
124
|
+
padding: 1.25rem;
|
|
125
|
+
text-align: center;
|
|
126
|
+
border: 1px solid var(--color-border);
|
|
127
|
+
border-radius: 0.75rem;
|
|
128
|
+
background: linear-gradient(
|
|
129
|
+
180deg,
|
|
130
|
+
color-mix(in srgb, var(--color-muted) 22%, var(--color-background)) 0%,
|
|
131
|
+
var(--color-background) 100%
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
:host-context(.dark) .j-pdf-viewer__mobile-card,
|
|
136
|
+
:host-context(html.dark) .j-pdf-viewer__mobile-card {
|
|
137
|
+
border-color: var(--color-dark-border);
|
|
138
|
+
background: linear-gradient(
|
|
139
|
+
180deg,
|
|
140
|
+
color-mix(in srgb, var(--color-dark-muted) 30%, var(--color-dark-background)) 0%,
|
|
141
|
+
var(--color-dark-background) 100%
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.j-pdf-viewer__mobile-copy {
|
|
146
|
+
display: flex;
|
|
147
|
+
flex-direction: column;
|
|
148
|
+
gap: 0.375rem;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.j-pdf-viewer__mobile-bar {
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
gap: 0.5rem;
|
|
156
|
+
padding: 0.625rem 0.75rem;
|
|
157
|
+
border: 1px solid var(--color-border);
|
|
158
|
+
border-top: 0;
|
|
159
|
+
border-radius: 0 0 0.75rem 0.75rem;
|
|
160
|
+
background-color: var(--color-background);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
:host-context(.dark) .j-pdf-viewer__mobile-bar,
|
|
164
|
+
:host-context(html.dark) .j-pdf-viewer__mobile-bar {
|
|
165
|
+
border-color: var(--color-dark-border);
|
|
166
|
+
background-color: var(--color-dark-background);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@media (max-width: 767px) {
|
|
170
|
+
.j-pdf-viewer {
|
|
171
|
+
min-height: 14rem;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.j-pdf-viewer__frame {
|
|
175
|
+
min-height: 14rem;
|
|
176
|
+
}
|
|
177
|
+
}
|