tailjng 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +27 -5
  2. package/cli/execute/init-app.js +5 -2
  3. package/cli/execute/sync-app.js +14 -2
  4. package/cli/settings/colors-config-utils.js +69 -11
  5. package/cli/settings/icons-config-utils.js +62 -0
  6. package/cli/settings/path-utils.js +32 -2
  7. package/cli/settings/project-utils.js +7 -1
  8. package/cli/templates/app.generator.js +8 -5
  9. package/fesm2022/tailjng.mjs +247 -80
  10. package/fesm2022/tailjng.mjs.map +1 -1
  11. package/lib/services/static/theme.service.d.ts +39 -1
  12. package/lib/utils/theme/theme-variables.util.d.ts +31 -0
  13. package/package.json +1 -1
  14. package/public-api.d.ts +2 -1
  15. package/registry/components.json +41 -18
  16. package/src/colors.safelist.css +2 -2
  17. package/src/lib/components/.config/README.md +11 -0
  18. package/src/lib/components/.config/colors/README.md +40 -0
  19. package/src/lib/components/{colors-config → .config/colors}/colors.config.ts +5 -5
  20. package/src/lib/components/{colors-config → .config/colors}/colors.safelist.css +2 -2
  21. package/src/lib/components/.config/icons/README.md +26 -0
  22. package/src/lib/components/.config/icons/icons.lucide.ts +134 -0
  23. package/src/lib/components/.config/input/README.md +24 -0
  24. package/src/lib/components/.config/input/input.classes.ts +119 -0
  25. package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +177 -6
  26. package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +24 -37
  27. package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +66 -56
  28. package/src/lib/components/alert/alert-dialog/dialog-alert.types.ts +19 -0
  29. package/src/lib/components/alert/alert-toast/toast-alert.component.css +494 -14
  30. package/src/lib/components/alert/alert-toast/toast-alert.component.html +106 -102
  31. package/src/lib/components/alert/alert-toast/toast-alert.component.ts +485 -128
  32. package/src/lib/components/alert/alert-toast/toast-alert.types.ts +25 -0
  33. package/src/lib/components/badge/badge.component.html +34 -21
  34. package/src/lib/components/badge/badge.component.ts +140 -31
  35. package/src/lib/components/button/button.component.html +16 -10
  36. package/src/lib/components/button/button.component.ts +162 -22
  37. package/src/lib/components/card/card-complete/complete-card.component.html +2 -2
  38. package/src/lib/components/card/card-complete/complete-card.component.ts +26 -16
  39. package/src/lib/components/card/card-crud-complete/complete-crud-card.component.html +2 -2
  40. package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +26 -16
  41. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.css +30 -0
  42. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.html +58 -46
  43. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.ts +135 -64
  44. package/src/lib/components/checkbox/checkbox-input/input-checkbox.types.ts +3 -0
  45. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.css +62 -0
  46. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +39 -25
  47. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.ts +74 -15
  48. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.types.ts +1 -0
  49. package/src/lib/components/coach-mark/coach-mark.component.html +6 -24
  50. package/src/lib/components/coach-mark/coach-mark.component.scss +1 -7
  51. package/src/lib/components/coach-mark/coach-mark.component.ts +51 -18
  52. package/src/lib/components/coach-mark/coach-mark.directive.ts +133 -78
  53. package/src/lib/components/coach-mark/coach-mark.types.ts +12 -0
  54. package/src/lib/components/dialog/dialog.component.css +1 -1
  55. package/src/lib/components/dialog/dialog.component.html +56 -65
  56. package/src/lib/components/dialog/dialog.component.ts +136 -110
  57. package/src/lib/components/dialog/dialog.types.ts +19 -0
  58. package/src/lib/components/filter/filter-complete/complete-filter.component.html +17 -20
  59. package/src/lib/components/filter/filter-complete/complete-filter.component.scss +25 -0
  60. package/src/lib/components/filter/filter-complete/complete-filter.component.ts +58 -34
  61. package/src/lib/components/filter/filter-complete/complete-filter.types.ts +7 -0
  62. package/src/lib/components/filter/filter-complete/complete-filter.util.ts +16 -0
  63. package/src/lib/components/form/form-container/container-form.component.css +4 -0
  64. package/src/lib/components/form/form-container/container-form.component.html +2 -2
  65. package/src/lib/components/form/form-container/container-form.component.ts +72 -16
  66. package/src/lib/components/form/form-container/container-form.types.ts +42 -0
  67. package/src/lib/components/form/form-container/form-col-span.directive.ts +25 -0
  68. package/src/lib/components/form/form-sidebar/sidebar-form.component.css +45 -0
  69. package/src/lib/components/form/form-sidebar/sidebar-form.component.html +128 -124
  70. package/src/lib/components/form/form-sidebar/sidebar-form.component.ts +114 -34
  71. package/src/lib/components/form/form-sidebar/sidebar-form.types.ts +3 -0
  72. package/src/lib/components/{toggle-radio/toggle-radio.component.css → form/form-validation/validation-form.component.css} +0 -1
  73. package/src/lib/components/form/form-validation/validation-form.component.html +10 -6
  74. package/src/lib/components/form/form-validation/validation-form.component.ts +99 -12
  75. package/src/lib/components/form/form-validation/validation-form.types.ts +33 -0
  76. package/src/lib/components/icon/icon.component.html +8 -5
  77. package/src/lib/components/icon/icon.component.ts +111 -9
  78. package/src/lib/components/input/input/input.component.css +0 -14
  79. package/src/lib/components/input/input/input.component.html +19 -16
  80. package/src/lib/components/input/input/input.component.ts +130 -53
  81. package/src/lib/components/input/input/input.types.ts +8 -0
  82. package/src/lib/components/input/input-file/file-input.component.html +65 -56
  83. package/src/lib/components/input/input-file/file-input.component.ts +276 -173
  84. package/src/lib/components/input/input-file/file-input.types.ts +2 -0
  85. package/src/lib/components/input/input-range/range-input.component.css +67 -0
  86. package/src/lib/components/input/input-range/range-input.component.html +50 -58
  87. package/src/lib/components/input/input-range/range-input.component.ts +148 -60
  88. package/src/lib/components/input/input-range/range-input.types.ts +7 -0
  89. package/src/lib/components/input/input-textarea/textarea-input.component.html +16 -7
  90. package/src/lib/components/input/input-textarea/textarea-input.component.ts +140 -50
  91. package/src/lib/components/input/input-textarea/textarea-input.types.ts +2 -0
  92. package/src/lib/components/label/label.component.html +17 -16
  93. package/src/lib/components/label/label.component.ts +94 -16
  94. package/src/lib/components/label/label.types.ts +2 -0
  95. package/src/lib/components/menu/menu-options-table/menu-options-defaults.ts +34 -0
  96. package/src/lib/components/menu/menu-options-table/options-table-menu.component.html +34 -20
  97. package/src/lib/components/menu/menu-options-table/options-table-menu.component.ts +211 -58
  98. package/src/lib/components/menu/menu-options-table/options-table-menu.types.ts +38 -0
  99. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +49 -52
  100. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.ts +112 -24
  101. package/src/lib/components/menu/options-coach-menu/options-coach-menu.types.ts +9 -0
  102. package/src/lib/components/mode-toggle/mode-toggle.component.html +11 -16
  103. package/src/lib/components/mode-toggle/mode-toggle.component.ts +69 -33
  104. package/src/lib/components/paginator/paginator-complete/complete-paginator.component.html +4 -4
  105. package/src/lib/components/paginator/paginator-complete/complete-paginator.component.ts +31 -7
  106. package/src/lib/components/paginator/paginator-complete/complete-paginator.types.ts +12 -0
  107. package/src/lib/components/paginator/paginator-complete/complete-paginator.util.ts +36 -0
  108. package/src/lib/components/progress-bar/progress-bar.component.html +41 -40
  109. package/src/lib/components/progress-bar/progress-bar.component.ts +95 -11
  110. package/src/lib/components/progress-bar/progress-bar.types.ts +2 -0
  111. package/src/lib/components/select/select-dropdown/dropdown-select.component.html +56 -46
  112. package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +450 -509
  113. package/src/lib/components/select/select-dropdown/dropdown-select.types.ts +43 -0
  114. package/src/lib/components/select/select-dropdown/dropdown-select.util.ts +179 -0
  115. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +131 -42
  116. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +491 -475
  117. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.types.ts +22 -0
  118. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.util.ts +20 -0
  119. package/src/lib/components/select/select-multi-table/multi-table-select.component.css +4 -0
  120. package/src/lib/components/select/select-multi-table/multi-table-select.component.html +76 -60
  121. package/src/lib/components/select/select-multi-table/multi-table-select.component.ts +250 -313
  122. package/src/lib/components/select/select-multi-table/multi-table-select.types.ts +10 -0
  123. package/src/lib/components/select/select-multi-table/multi-table-select.util.ts +5 -0
  124. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.css +155 -0
  125. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.html +72 -53
  126. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.ts +84 -27
  127. package/src/lib/components/sidebar/sidebar-static/static-sidebar.types.ts +2 -0
  128. package/src/lib/components/table/table-complete/complete-table.component.html +21 -23
  129. package/src/lib/components/table/table-complete/complete-table.component.ts +190 -338
  130. package/src/lib/components/table/table-complete/complete-table.types.ts +28 -0
  131. package/src/lib/components/table/table-complete/complete-table.util.ts +236 -0
  132. package/src/lib/components/table/table-complete/index.ts +2 -0
  133. package/src/lib/components/table/table-crud-complete/complete-crud-table.animations.ts +34 -0
  134. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +87 -142
  135. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.scss +0 -63
  136. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +544 -831
  137. package/src/lib/components/table/table-crud-complete/complete-crud-table.types.ts +57 -0
  138. package/src/lib/components/table/table-crud-complete/complete-crud-table.util.ts +723 -0
  139. package/src/lib/components/table/table-crud-complete/index.ts +3 -0
  140. package/src/lib/components/theme-generator/theme-generator.component.css +21 -0
  141. package/src/lib/components/theme-generator/theme-generator.component.html +141 -116
  142. package/src/lib/components/theme-generator/theme-generator.component.ts +44 -24
  143. package/src/lib/components/toggle-radio/shared/toggle-options.types.ts +8 -0
  144. package/src/lib/components/toggle-radio/shared/toggle-options.util.ts +62 -0
  145. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.css +22 -0
  146. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.html +65 -0
  147. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.ts +192 -0
  148. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.types.ts +1 -0
  149. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.css +34 -0
  150. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.html +47 -0
  151. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.ts +187 -0
  152. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.types.ts +1 -0
  153. package/src/lib/components/tooltip/tooltip.directive.ts +12 -9
  154. package/src/lib/components/tooltip/tooltip.service.ts +331 -133
  155. package/src/lib/components/tooltip/tooltip.types.ts +9 -0
  156. package/src/lib/components/viewer/viewer-image/image-viewer.component.css +14 -4
  157. package/src/lib/components/viewer/viewer-image/image-viewer.component.html +61 -95
  158. package/src/lib/components/viewer/viewer-image/image-viewer.component.ts +182 -177
  159. package/src/lib/components/viewer/viewer-image/image-viewer.types.ts +3 -0
  160. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.css +25 -0
  161. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.html +95 -24
  162. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.ts +168 -15
  163. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.types.ts +1 -0
  164. package/src/styles.css +2 -2
  165. package/lib/services/static/icons.service.d.ts +0 -65
  166. package/src/lib/components/colors-config/README.md +0 -38
  167. package/src/lib/components/form/form-sidebar/sidebar-form.component.scss +0 -0
  168. package/src/lib/components/form/form-validation/validation-form.component.scss +0 -0
  169. package/src/lib/components/menu/menu-options-table/options-table-menu.component.scss +0 -0
  170. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +0 -12
  171. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.scss +0 -0
  172. package/src/lib/components/toggle-radio/toggle-radio.component.html +0 -51
  173. package/src/lib/components/toggle-radio/toggle-radio.component.ts +0 -222
  174. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.scss +0 -0
  175. package/tailjng-0.1.6.tgz +0 -0
@@ -1,163 +1,152 @@
1
- import { Component, Input, TemplateRef, CUSTOM_ELEMENTS_SCHEMA, Output, EventEmitter, HostListener, OnChanges, SimpleChanges } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
1
+ import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common';
2
+ import {
3
+ Component,
4
+ EventEmitter,
5
+ HostListener,
6
+ Input,
7
+ OnChanges,
8
+ Output,
9
+ SimpleChanges,
10
+ TemplateRef,
11
+ } from '@angular/core';
3
12
  import { trigger, transition, style, animate } from '@angular/animations';
4
- import { LucideAngularModule } from 'lucide-angular';
5
- import { JIconsService } from 'tailjng';
6
-
13
+ import { Icons } from '../.config/icons/icons.lucide';
14
+ import { JIconComponent } from '../icon/icon.component';
15
+ import { DialogOffset, DialogPosition, DialogSize } from './dialog.types';
16
+
17
+ export type { DialogOffset, DialogPosition, DialogSize } from './dialog.types';
18
+
19
+ const POSITION_CLASS_MAP: Record<DialogPosition, string> = {
20
+ center: 'justify-center items-center',
21
+ leftCenter: 'justify-start items-center',
22
+ rightCenter: 'justify-end items-center',
23
+ topCenter: 'justify-center items-start',
24
+ bottomCenter: 'justify-center items-end',
25
+ leftTop: 'justify-start items-start',
26
+ leftBottom: 'justify-start items-end',
27
+ rightTop: 'justify-end items-start',
28
+ rightBottom: 'justify-end items-end',
29
+ };
30
+
31
+ /**
32
+ * Modal dialog with optional overlay, positioning, drag handle, and template content.
33
+ *
34
+ * Install: `npx tailjng add dialog`
35
+ */
7
36
  @Component({
8
37
  selector: 'JDialog',
9
- imports: [LucideAngularModule, CommonModule],
38
+ imports: [NgClass, NgStyle, NgTemplateOutlet, JIconComponent],
10
39
  templateUrl: './dialog.component.html',
11
40
  styleUrl: './dialog.component.css',
12
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
13
41
  animations: [
14
42
  trigger('modalTransition', [
15
43
  transition(':enter', [
16
44
  style({ transform: 'translateY(1rem)', opacity: 0 }),
17
- animate('300ms ease-out', style({ transform: 'translateY(0)', opacity: 1 }))
45
+ animate('300ms ease-out', style({ transform: 'translateY(0)', opacity: 1 })),
18
46
  ]),
19
47
  transition(':leave', [
20
- animate('150ms ease-in', style({ transform: 'translateY(1rem)', opacity: 0 }))
21
- ])
22
- ])
23
- ]
48
+ animate('150ms ease-in', style({ transform: 'translateY(1rem)', opacity: 0 })),
49
+ ]),
50
+ ]),
51
+ ],
24
52
  })
25
53
  export class JDialogComponent implements OnChanges {
54
+ readonly Icons = Icons;
26
55
 
27
- @Input() position:
28
- | 'center'
29
- | 'leftCenter'
30
- | 'rightCenter'
31
- | 'topCenter'
32
- | 'bottomCenter'
33
- | 'leftTop'
34
- | 'leftBottom'
35
- | 'rightTop'
36
- | 'rightBottom' = 'center';
37
-
38
- @Input() offset: { top?: number, bottom?: number, left?: number, right?: number } = {};
56
+ @Input() position: DialogPosition = 'center';
57
+ @Input() offset: DialogOffset = {};
39
58
 
40
59
  @Input() openModal = false;
41
60
  @Output() closeModal = new EventEmitter<void>();
42
61
 
43
62
  @Input() title = 'Dialog Title';
44
- @Input() dialogTemplate!: TemplateRef<any>;
63
+ @Input() dialogTemplate!: TemplateRef<unknown>;
64
+
65
+ @Input() width: DialogSize = 500;
66
+ @Input() height: DialogSize = 300;
45
67
 
46
- @Input() width: number | 'auto' | 'full' = 500;
47
- @Input() height: number | 'auto' | 'full' = 300;
68
+ @Input() overlay = true;
69
+ @Input() draggable = false;
70
+ @Input() closeOnEscape = true;
48
71
 
49
- @Input() overlay: boolean = true;
50
- @Input() draggable: boolean = false;
72
+ /** Extra conditional classes on the dialog panel. */
73
+ @Input() ngClasses: Record<string, boolean> = {};
51
74
 
52
75
  private isDragging = false;
53
76
  private hasMoved = false;
54
77
  private dragOffset = { x: 0, y: 0 };
55
78
 
56
- constructor(
57
- public readonly iconsService: JIconsService
58
- ) { }
59
-
60
- ngOnChanges(changes: SimpleChanges) {
79
+ ngOnChanges(changes: SimpleChanges): void {
61
80
  if (changes['openModal']?.currentValue === true) {
62
81
  this.hasMoved = false;
63
82
  }
64
83
  }
65
84
 
66
-
67
85
  /**
68
- * Opens the dialog.
86
+ * Opens the dialog programmatically (e.g. when provided via `providers`).
69
87
  */
70
- onOpen() {
88
+ onOpen(): void {
71
89
  this.hasMoved = false;
72
90
  this.openModal = true;
73
91
  }
74
92
 
75
-
76
-
77
93
  /**
78
- * Closes the dialog.
94
+ * Emits `closeModal` so the parent can hide the dialog.
79
95
  */
80
- onClose() {
96
+ onClose(): void {
81
97
  this.closeModal.emit();
82
98
  }
83
99
 
84
-
85
-
86
- /**
87
- * Gets the width of the modal.
88
- * @returns The width of the modal.
89
- */
90
- getModalWidth(): string {
91
- if (this.width === 'auto') return 'auto';
92
- if (typeof this.width === 'number') return `${this.width}px`;
93
- if (this.width === 'full') return '90vw';
94
- return `${this.width || 100}px`;
95
- }
96
-
97
-
98
-
99
100
  /**
100
- * Gets the height of the modal.
101
- * @returns The height of the modal.
101
+ * Whether width or height is set to full viewport.
102
102
  */
103
- getModalHeight(): string {
104
- if (this.height === 'auto') return 'auto';
105
- if (typeof this.height === 'number') return `${this.height}px`;
106
- if (this.height === 'full') return '90vh';
107
- return `${this.height || 40}px`;
103
+ get isFullScreen(): boolean {
104
+ return this.width === 'full' || this.height === 'full';
108
105
  }
109
106
 
110
-
111
-
112
107
  /**
113
- * Checks if the modal is in full-screen mode.
114
- * @returns True if the modal is full-screen, false otherwise.
108
+ * Resolved panel width for inline styles.
115
109
  */
116
- isFullScreen(): boolean {
117
- return this.width === 'full' || this.height === 'full';
110
+ get modalWidth(): string {
111
+ if (this.width === 'auto') {
112
+ return 'auto';
113
+ }
114
+ if (typeof this.width === 'number') {
115
+ return `${this.width}px`;
116
+ }
117
+ if (this.width === 'full') {
118
+ return '90vw';
119
+ }
120
+ return '500px';
118
121
  }
119
122
 
120
-
121
-
122
123
  /**
123
- * Handles the escape key press event.
124
- * @param event The keyboard event.
124
+ * Resolved panel height for inline styles.
125
125
  */
126
- @HostListener('document:keydown.escape')
127
- handleEscape() {
128
- if (this.openModal) {
129
- this.onClose();
126
+ get modalHeight(): string {
127
+ if (this.height === 'auto') {
128
+ return 'auto';
129
+ }
130
+ if (typeof this.height === 'number') {
131
+ return `${this.height}px`;
130
132
  }
133
+ if (this.height === 'full') {
134
+ return '90vh';
135
+ }
136
+ return '300px';
131
137
  }
132
138
 
133
-
134
-
135
139
  /**
136
- * Gets the position class for the modal.
137
- * @returns The position class for the modal.
140
+ * Flex alignment classes for the dialog shell.
138
141
  */
139
- getPositionClass(): string {
140
- switch (this.position) {
141
- case 'leftCenter': return 'justify-start items-center';
142
- case 'rightCenter': return 'justify-end items-center';
143
- case 'topCenter': return 'justify-center items-start';
144
- case 'bottomCenter': return 'justify-center items-end';
145
- case 'leftTop': return 'justify-start items-start';
146
- case 'leftBottom': return 'justify-start items-end';
147
- case 'rightTop': return 'justify-end items-start';
148
- case 'rightBottom': return 'justify-end items-end';
149
- case 'center':
150
- default: return 'justify-center items-center';
151
- }
142
+ get positionClass(): string {
143
+ return POSITION_CLASS_MAP[this.position] ?? POSITION_CLASS_MAP.center;
152
144
  }
153
145
 
154
-
155
-
156
146
  /**
157
- * Gets the offset styles for the modal.
158
- * @returns The offset styles for the modal.
147
+ * Margin offsets applied to the panel.
159
148
  */
160
- getOffsetStyles(): { [key: string]: string } {
149
+ get offsetStyles(): Record<string, string> {
161
150
  return {
162
151
  marginTop: this.offset.top !== undefined ? `${this.offset.top}px` : '',
163
152
  marginBottom: this.offset.bottom !== undefined ? `${this.offset.bottom}px` : '',
@@ -166,28 +155,64 @@ export class JDialogComponent implements OnChanges {
166
155
  };
167
156
  }
168
157
 
158
+ /**
159
+ * Class map for the dialog panel shell.
160
+ */
161
+ get panelClasses(): Record<string, boolean> {
162
+ return {
163
+ 'pointer-events-auto': true,
164
+ 'overflow-hidden': true,
165
+ 'bg-background': true,
166
+ 'dark:bg-dark-background': true,
167
+ 'rounded-xl': true,
168
+ 'shadow-2xl': true,
169
+ 'border': true,
170
+ 'border-border': true,
171
+ 'dark:border-dark-border': true,
172
+ ...this.ngClasses,
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Class map for the scrollable content region.
178
+ */
179
+ get contentClasses(): Record<string, boolean> {
180
+ return {
181
+ 'jdialog-full': this.isFullScreen,
182
+ };
183
+ }
169
184
 
185
+ @HostListener('document:keydown.escape')
186
+ handleEscape(): void {
187
+ if (this.openModal && this.closeOnEscape) {
188
+ this.onClose();
189
+ }
190
+ }
170
191
 
171
192
  /**
172
- * Starts the drag operation.
173
- * @param event The mouse event.
174
- * @returns
193
+ * Starts dragging when `draggable` is enabled.
175
194
  */
176
- startDrag(event: MouseEvent) {
177
- if (!this.draggable) return;
195
+ startDrag(event: MouseEvent): void {
196
+ if (!this.draggable) {
197
+ return;
198
+ }
178
199
 
200
+ event.stopPropagation();
179
201
  this.isDragging = true;
180
202
 
181
- const dialogElement = (event.currentTarget as HTMLElement).closest('[data-draggable-dialog]') as HTMLElement;
182
- if (!dialogElement) return;
203
+ const dialogElement = (event.currentTarget as HTMLElement).closest(
204
+ '[data-draggable-dialog]',
205
+ ) as HTMLElement | null;
206
+ if (!dialogElement) {
207
+ return;
208
+ }
183
209
 
184
210
  const rect = dialogElement.getBoundingClientRect();
185
211
  this.dragOffset = {
186
212
  x: event.clientX - rect.left,
187
- y: event.clientY - rect.top
213
+ y: event.clientY - rect.top,
188
214
  };
189
215
 
190
- // Only set position if it hasn't been moved before (respects initial offset)
191
216
  if (!this.hasMoved) {
192
217
  const computedTop = this.offset.top ?? rect.top;
193
218
  const computedLeft = rect.left;
@@ -203,7 +228,9 @@ export class JDialogComponent implements OnChanges {
203
228
  }
204
229
 
205
230
  const mouseMoveHandler = (moveEvent: MouseEvent) => {
206
- if (!this.isDragging) return;
231
+ if (!this.isDragging) {
232
+ return;
233
+ }
207
234
 
208
235
  const newLeft = moveEvent.clientX - this.dragOffset.x;
209
236
  const newTop = moveEvent.clientY - this.dragOffset.y;
@@ -224,5 +251,4 @@ export class JDialogComponent implements OnChanges {
224
251
  document.addEventListener('mousemove', mouseMoveHandler);
225
252
  document.addEventListener('mouseup', mouseUpHandler);
226
253
  }
227
-
228
254
  }
@@ -0,0 +1,19 @@
1
+ export type DialogPosition =
2
+ | 'center'
3
+ | 'leftCenter'
4
+ | 'rightCenter'
5
+ | 'topCenter'
6
+ | 'bottomCenter'
7
+ | 'leftTop'
8
+ | 'leftBottom'
9
+ | 'rightTop'
10
+ | 'rightBottom';
11
+
12
+ export type DialogSize = number | 'auto' | 'full';
13
+
14
+ export type DialogOffset = {
15
+ top?: number;
16
+ bottom?: number;
17
+ left?: number;
18
+ right?: number;
19
+ };
@@ -19,11 +19,9 @@
19
19
  class="absolute right-3 top-1/2 transform -translate-y-1/2 flex items-center gap-2"
20
20
  >
21
21
  @if (isLoadingSearch) {
22
- <lucide-icon
23
- [name]="iconsService.icons.loading"
22
+ <JIcon [icon]="Icons.Loader2"
24
23
  size="18"
25
- class="text-gray-400 animate-spin"
26
- ></lucide-icon>
24
+ iconClass="text-gray-400 animate-spin" [ariaHidden]="true" />
27
25
  }
28
26
 
29
27
  @if (searchQuery && !isLoadingSearch) {
@@ -32,19 +30,15 @@
32
30
  class="pr-1 mr-1 text-gray-400 hover:text-gray-600 focus:outline-none cursor-pointer"
33
31
  aria-label="Limpiar búsqueda"
34
32
  >
35
- <lucide-icon
36
- [name]="iconsService.icons.close"
37
- size="16"
38
- ></lucide-icon>
33
+ <JIcon [icon]="Icons.X"
34
+ size="16" [ariaHidden]="true" />
39
35
  </button>
40
36
  }
41
37
 
42
38
  @if (!isLoadingSearch && !searchQuery) {
43
- <lucide-icon
44
- [name]="iconsService.icons.search"
39
+ <JIcon [icon]="Icons.Search"
45
40
  size="16"
46
- class="text-gray-400"
47
- ></lucide-icon>
41
+ iconClass="text-gray-400" [ariaHidden]="true" />
48
42
  }
49
43
  </div>
50
44
  </div>
@@ -81,7 +75,7 @@
81
75
  <div
82
76
  class="flex gap-3 text-sm w-full md:w-auto md:justify-center justify-between"
83
77
  >
84
- <div class="flex flex-row flex-wrap gap-2">
78
+ <div class="flex flex-row flex-wrap gap-2 items-center">
85
79
  @for (button of filtersButton; track $index) {
86
80
  @if (getIsVisible(button)) {
87
81
  <JButton
@@ -124,28 +118,29 @@
124
118
  </div>
125
119
 
126
120
  <!-- Content Filters -->
127
- <div class="select-none">
121
+ <div class="select-none jfilter-filters-dialog-host">
128
122
  <JDialog
129
123
  [openModal]="dialogOpen"
130
124
  (closeModal)="dialogOpen = false"
131
125
  [dialogTemplate]="templateDialog"
132
126
  position="leftCenter"
133
- title="Filtros"
127
+ title="FILTROS"
134
128
  [width]="220"
135
129
  height="auto"
136
130
  [overlay]="false"
137
131
  [draggable]="true"
132
+ [ngClasses]="{ 'jfilter-filters-dialog': true }"
138
133
  />
139
134
  </div>
140
135
 
141
136
  <ng-template #templateDialog>
142
- <div class="m-0 pt-1">
137
+ <div class="m-0">
143
138
  @if (filtersSelect.length > 0) {
144
139
  <div class="flex flex-col gap-2">
145
140
  @for (filter of filtersSelect; track $index) {
146
141
  @if (filter.isVisible ?? true) {
147
142
  @if (filter.type === "dropdown") {
148
- <div>
143
+ <div class="relative">
149
144
  <JDropdownSelect
150
145
  [type]="'dropdown'"
151
146
  [(ngModel)]="filter.selected"
@@ -160,13 +155,14 @@
160
155
  [options]="filter.options"
161
156
  [sort]="filter.sort ?? 'ASC'"
162
157
  [showAllOption]="filter.showAllOption ?? false"
158
+ [isFilterSelect]="true"
163
159
  />
164
160
  </div>
165
161
  }
166
162
 
167
163
  <!-- Searchable -->
168
164
  @if (filter.type === "searchable") {
169
- <div>
165
+ <div class="relative">
170
166
  <JDropdownSelect
171
167
  [type]="'searchable'"
172
168
  [(ngModel)]="filter.selected"
@@ -180,15 +176,16 @@
180
176
  [loadOnInit]="filter.loadOnInit ?? false"
181
177
  [isSearch]="filter.isSearch ?? true"
182
178
  [searchFields]="filter.searchFields || []"
183
- [defaultFilters]="filter.defaultFilters || []"
179
+ [defaultFilters]="filter.defaultFilters || {}"
184
180
  [sort]="filter.sort ?? 'ASC'"
185
181
  [showAllOption]="filter.showAllOption ?? false"
182
+ [isFilterSelect]="true"
186
183
  />
187
184
  </div>
188
185
  }
189
186
 
190
187
  @if (filter.type === "multi-table") {
191
- <div>
188
+ <div class="relative">
192
189
  <JMultiTableSelect
193
190
  [(ngModel)]="filter.selected"
194
191
  (selectionChange)="
@@ -0,0 +1,25 @@
1
+ :host ::ng-deep .jfilter-filters-dialog-host .jfilter-filters-dialog.jdialog-panel {
2
+ overflow: hidden;
3
+ border-radius: 0.75rem;
4
+ }
5
+
6
+ :host ::ng-deep .jfilter-filters-dialog-host .jfilter-filters-dialog .jdialog-header {
7
+ min-height: 2.125rem;
8
+ padding: 0.3125rem 0.5rem 0.3125rem 0.75rem;
9
+ gap: 0.5rem;
10
+ }
11
+
12
+ :host ::ng-deep .jfilter-filters-dialog-host .jfilter-filters-dialog .jdialog-title {
13
+ font-size: 0.8125rem;
14
+ line-height: 1.2;
15
+ }
16
+
17
+ :host ::ng-deep .jfilter-filters-dialog-host .jfilter-filters-dialog .jdialog-close {
18
+ width: 1.625rem;
19
+ height: 1.625rem;
20
+ flex: 0 0 1.625rem;
21
+ }
22
+
23
+ :host ::ng-deep .jfilter-filters-dialog-host .jfilter-filters-dialog .jdialog-content {
24
+ padding: 0.5rem 0.625rem 0.625rem;
25
+ }
@@ -1,30 +1,69 @@
1
1
  import { Component, EventEmitter, Input, Output, OnDestroy, OnInit, ViewChild, ElementRef } from '@angular/core';
2
2
  import { NgClass } from '@angular/common';
3
3
  import { FormsModule } from '@angular/forms';
4
- import { LucideAngularModule } from 'lucide-angular';
5
4
  import { Subject, Subscription, debounceTime, filter, forkJoin } from 'rxjs';
6
- import { TableColumn, FilterButton, FilterSelect, JUploadFilterService, JAlertDialogService, JAlertToastService, JGenericCrudService, JIconsService, LoadingState, JExcelService, JExcelFilterService } from 'tailjng';
5
+ import { TableColumn, FilterButton, FilterSelect, JUploadFilterService, JAlertDialogService, JAlertToastService, JGenericCrudService, LoadingState, JExcelService, JExcelFilterService } from 'tailjng';
7
6
  import { JDropdownSelectComponent } from '../../select/select-dropdown/dropdown-select.component';
8
7
  import { JMultiTableSelectComponent } from '../../select/select-multi-table/multi-table-select.component';
9
8
  import { JSwitchCheckboxComponent } from '../../checkbox/checkbox-switch/switch-checkbox.component';
10
9
  import { JDialogComponent } from '../../dialog/dialog.component';
11
10
  import { JButtonComponent } from '../../button/button.component';
12
-
11
+ import type { CompleteFilterAdditionalButtonLoading } from './complete-filter.types';
12
+ import { hasActiveFilters } from './complete-filter.util';
13
+ import { Icons } from '../../.config/icons/icons.lucide';
14
+ import { JIconComponent } from '../../icon/icon.component';
15
+
16
+ export type {
17
+ CompleteFilterAdditionalButtonLoading,
18
+ CompleteFilterClearReason} from './complete-filter.types';
19
+ export { hasActiveFilters } from './complete-filter.util';
20
+
21
+ /**
22
+ * Filter bar with debounced search, items-per-page, column picker, filter dialog and action buttons.
23
+ *
24
+ * Install: `npx tailjng add filter-complete`
25
+ *
26
+ * ```html
27
+ * <JFilter
28
+ * [endpoint]="endpoint"
29
+ * [mainEndpoint]="mainEndpoint"
30
+ * [columns]="columns"
31
+ * [data]="data"
32
+ * [(searchQuery)]="searchQuery"
33
+ * (search)="onSearch()"
34
+ * [(itemsPerPage)]="itemsPerPage"
35
+ * [itemsPerPageOptions]="itemsPerPageOptions"
36
+ * [filtersButton]="filtersButton"
37
+ * [filtersSelect]="filtersSelect"
38
+ * />
39
+ * ```
40
+ */
13
41
  @Component({
14
42
  selector: 'JFilter',
15
43
  standalone: true,
16
- imports: [LucideAngularModule, JDropdownSelectComponent, JMultiTableSelectComponent, JButtonComponent, JDialogComponent, FormsModule, JSwitchCheckboxComponent, NgClass],
44
+ imports: [
45
+ JIconComponent,
46
+ JDropdownSelectComponent,
47
+ JMultiTableSelectComponent,
48
+ JButtonComponent,
49
+ JDialogComponent,
50
+ FormsModule,
51
+ JSwitchCheckboxComponent,
52
+ NgClass,
53
+ ],
17
54
  templateUrl: './complete-filter.component.html',
18
55
  styleUrls: ['./complete-filter.component.scss'],
19
56
  providers: [JDialogComponent]
20
57
  })
21
58
  export class JCompleteFilterComponent implements OnInit, OnDestroy {
59
+ readonly Icons = Icons;
60
+
22
61
 
23
62
  @Output() afterUpload = new EventEmitter<void>();
24
63
 
25
64
  @Input() isLoadingSearch: boolean = false;
26
65
  @Input() isLoadingPerPage: boolean = false;
27
- @Input() isLoadingAditionalButtons: { [key: string]: LoadingState } = {};
66
+ @Input() isLoadingAditionalButtons: CompleteFilterAdditionalButtonLoading = {};
28
67
 
29
68
  @Input() searchPlaceholder: string = 'Buscar...';
30
69
 
@@ -90,7 +129,6 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
90
129
  @Output() checkActiveInactive = new EventEmitter<boolean>();
91
130
 
92
131
  constructor(
93
- public readonly iconsService: JIconsService,
94
132
  private readonly alertToastService: JAlertToastService,
95
133
  private readonly alertDialogService: JAlertDialogService,
96
134
  private readonly uploadFilterService: JUploadFilterService,
@@ -184,8 +222,8 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
184
222
  if (button.type === 'filter') {
185
223
  return {
186
224
  ...button,
187
- icon: this.iconsService.icons.filter,
188
- iconChange: this.iconsService.icons.filterList,
225
+ icon: Icons.Filter,
226
+ iconChange: Icons.ArrowDownWideNarrow,
189
227
  isChangeIcon: () => this.dialogOpen,
190
228
  tooltip: 'Filtros',
191
229
  clicked: () => {
@@ -199,18 +237,11 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
199
237
  if (button.type === 'clear') {
200
238
  return {
201
239
  ...button,
202
- icon: this.iconsService.icons.eraser,
203
- iconChange: this.iconsService.icons.transh,
240
+ icon: Icons.Eraser,
241
+ iconChange: Icons.Trash2,
204
242
  tooltip: 'Limpiar filtros',
205
243
  clicked: () => {
206
- const hasActiveSearch = this.searchQuery?.trim().length > 0;
207
- const hasSelectedFilters = this.filtersSelect.some(f => {
208
- const actual = f.selected;
209
- const inicial = f.initSelected ?? null;
210
- return JSON.stringify(actual) !== JSON.stringify(inicial);
211
- });
212
-
213
- if (!hasActiveSearch && !hasSelectedFilters) {
244
+ if (!hasActiveFilters(this.searchQuery, this.filtersSelect)) {
214
245
  this.alertToastService.AlertToast({
215
246
  type: 'info',
216
247
  title: 'No se ha filtrado nada...',
@@ -221,8 +252,10 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
221
252
  return;
222
253
  }
223
254
 
255
+ const hadActiveSearch = this.searchQuery?.trim().length > 0;
256
+
224
257
  // Limpiar búsqueda
225
- if (hasActiveSearch) {
258
+ if (hadActiveSearch) {
226
259
  this.clearSearch();
227
260
  }
228
261
 
@@ -244,15 +277,7 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
244
277
  }
245
278
  this.clearFilters.emit('clear');
246
279
  },
247
- isChangeIcon: () => {
248
- const hasActiveSearch = this.searchQuery?.trim().length > 0;
249
- const hasSelectedFilters = this.filtersSelect.some(f => {
250
- const actual = f.selected;
251
- const inicial = f.initSelected ?? null;
252
- return JSON.stringify(actual) !== JSON.stringify(inicial);
253
- });
254
- return !(!hasActiveSearch && !hasSelectedFilters);
255
- },
280
+ isChangeIcon: () => hasActiveFilters(this.searchQuery, this.filtersSelect),
256
281
  classes: 'bg-[#164666] hover:bg-[#133d5a] text-white'
257
282
  };
258
283
  }
@@ -261,7 +286,7 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
261
286
  if (button.type === 'excel') {
262
287
  return {
263
288
  ...button,
264
- icon: this.iconsService.icons.fileSpreadsheet,
289
+ icon: Icons.FileSpreadsheet,
265
290
  tooltip: 'Exportar a Excel',
266
291
  clicked: () => {
267
292
 
@@ -276,7 +301,7 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
276
301
  // Activar el estado de carga
277
302
  this.isLoadingAditionalButtons['excel'] = 'loading';
278
303
 
279
- const params = { ...this.params, limit: "1000000000", }
304
+ const params = { ...this.params, limit: "1000000000"}
280
305
 
281
306
  this.genericService.findAll<any>({ endpoint: this.endpoint, params }).subscribe({
282
307
  next: (response) => {
@@ -336,8 +361,8 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
336
361
  if (button.type === 'upload') {
337
362
  return {
338
363
  ...button,
339
- icon: this.iconsService.icons.fileUp,
340
- iconChange: this.iconsService.icons.listRestart,
364
+ icon: Icons.FileUp,
365
+ iconChange: Icons.ListRestart,
341
366
  isChangeIcon: () => this.dialogOpenCharge,
342
367
  tooltip: 'Cargar datos',
343
368
  clicked: () => {
@@ -382,8 +407,7 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
382
407
  error: reject
383
408
  });
384
409
 
385
- }),
386
- })
410
+ })})
387
411
 
388
412
  },
389
413
  classes: 'bg-[#1565c0] hover:bg-[#0d47a1] text-white'