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.
Files changed (176) hide show
  1. package/README.md +12 -4
  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 +43 -8
  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 +2 -2
  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 +38 -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 +244 -2
  26. package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +25 -38
  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 +630 -12
  30. package/src/lib/components/alert/alert-toast/toast-alert.component.html +103 -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 +97 -0
  42. package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.html +54 -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 +112 -0
  46. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +28 -25
  47. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.ts +67 -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 +4 -22
  50. package/src/lib/components/coach-mark/coach-mark.component.scss +1 -1
  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 +103 -1
  55. package/src/lib/components/dialog/dialog.component.html +46 -66
  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 +16 -19
  59. package/src/lib/components/filter/filter-complete/complete-filter.component.scss +35 -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 +276 -0
  69. package/src/lib/components/form/form-sidebar/sidebar-form.component.html +117 -125
  70. package/src/lib/components/form/form-sidebar/sidebar-form.component.ts +109 -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.html +19 -16
  79. package/src/lib/components/input/input/input.component.ts +130 -53
  80. package/src/lib/components/input/input/input.types.ts +8 -0
  81. package/src/lib/components/input/input-file/file-input.component.html +65 -56
  82. package/src/lib/components/input/input-file/file-input.component.ts +276 -173
  83. package/src/lib/components/input/input-file/file-input.types.ts +2 -0
  84. package/src/lib/components/input/input-range/range-input.component.css +67 -0
  85. package/src/lib/components/input/input-range/range-input.component.html +50 -58
  86. package/src/lib/components/input/input-range/range-input.component.ts +148 -60
  87. package/src/lib/components/input/input-range/range-input.types.ts +7 -0
  88. package/src/lib/components/input/input-textarea/textarea-input.component.html +16 -7
  89. package/src/lib/components/input/input-textarea/textarea-input.component.ts +140 -50
  90. package/src/lib/components/input/input-textarea/textarea-input.types.ts +2 -0
  91. package/src/lib/components/label/label.component.html +17 -16
  92. package/src/lib/components/label/label.component.ts +70 -16
  93. package/src/lib/components/label/label.types.ts +2 -0
  94. package/src/lib/components/menu/menu-options-table/menu-options-defaults.ts +34 -0
  95. package/src/lib/components/menu/menu-options-table/options-table-menu.component.html +34 -20
  96. package/src/lib/components/menu/menu-options-table/options-table-menu.component.ts +211 -58
  97. package/src/lib/components/menu/menu-options-table/options-table-menu.types.ts +38 -0
  98. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +49 -52
  99. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.ts +112 -24
  100. package/src/lib/components/menu/options-coach-menu/options-coach-menu.types.ts +9 -0
  101. package/src/lib/components/mode-toggle/mode-toggle.component.html +11 -16
  102. package/src/lib/components/mode-toggle/mode-toggle.component.ts +69 -33
  103. package/src/lib/components/paginator/paginator-complete/complete-paginator.component.html +4 -4
  104. package/src/lib/components/paginator/paginator-complete/complete-paginator.component.ts +31 -7
  105. package/src/lib/components/paginator/paginator-complete/complete-paginator.types.ts +12 -0
  106. package/src/lib/components/paginator/paginator-complete/complete-paginator.util.ts +36 -0
  107. package/src/lib/components/progress-bar/progress-bar.component.css +11 -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.css +6 -0
  112. package/src/lib/components/select/select-dropdown/dropdown-select.component.html +54 -44
  113. package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +450 -509
  114. package/src/lib/components/select/select-dropdown/dropdown-select.types.ts +43 -0
  115. package/src/lib/components/select/select-dropdown/dropdown-select.util.ts +179 -0
  116. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +6 -0
  117. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +131 -42
  118. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +491 -475
  119. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.types.ts +22 -0
  120. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.util.ts +20 -0
  121. package/src/lib/components/select/select-multi-table/multi-table-select.component.css +10 -0
  122. package/src/lib/components/select/select-multi-table/multi-table-select.component.html +76 -60
  123. package/src/lib/components/select/select-multi-table/multi-table-select.component.ts +250 -313
  124. package/src/lib/components/select/select-multi-table/multi-table-select.types.ts +10 -0
  125. package/src/lib/components/select/select-multi-table/multi-table-select.util.ts +5 -0
  126. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.css +212 -0
  127. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.html +62 -53
  128. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.ts +84 -27
  129. package/src/lib/components/sidebar/sidebar-static/static-sidebar.types.ts +2 -0
  130. package/src/lib/components/table/table-complete/complete-table.component.html +15 -17
  131. package/src/lib/components/table/table-complete/complete-table.component.ts +190 -338
  132. package/src/lib/components/table/table-complete/complete-table.types.ts +28 -0
  133. package/src/lib/components/table/table-complete/complete-table.util.ts +236 -0
  134. package/src/lib/components/table/table-complete/index.ts +2 -0
  135. package/src/lib/components/table/table-crud-complete/complete-crud-table.animations.ts +34 -0
  136. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +73 -128
  137. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +542 -829
  138. package/src/lib/components/table/table-crud-complete/complete-crud-table.types.ts +57 -0
  139. package/src/lib/components/table/table-crud-complete/complete-crud-table.util.ts +723 -0
  140. package/src/lib/components/table/table-crud-complete/index.ts +3 -0
  141. package/src/lib/components/theme-generator/theme-generator.component.css +21 -0
  142. package/src/lib/components/theme-generator/theme-generator.component.html +146 -116
  143. package/src/lib/components/theme-generator/theme-generator.component.ts +44 -24
  144. package/src/lib/components/toggle-radio/shared/toggle-options.types.ts +8 -0
  145. package/src/lib/components/toggle-radio/shared/toggle-options.util.ts +44 -0
  146. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.css +135 -0
  147. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.html +52 -0
  148. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.ts +198 -0
  149. package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.types.ts +1 -0
  150. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.css +108 -0
  151. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.html +37 -0
  152. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.ts +193 -0
  153. package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.types.ts +1 -0
  154. package/src/lib/components/tooltip/tooltip.directive.ts +12 -9
  155. package/src/lib/components/tooltip/tooltip.service.ts +331 -133
  156. package/src/lib/components/tooltip/tooltip.types.ts +9 -0
  157. package/src/lib/components/viewer/viewer-image/image-viewer.component.css +90 -4
  158. package/src/lib/components/viewer/viewer-image/image-viewer.component.html +52 -103
  159. package/src/lib/components/viewer/viewer-image/image-viewer.component.ts +182 -177
  160. package/src/lib/components/viewer/viewer-image/image-viewer.types.ts +3 -0
  161. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.css +177 -0
  162. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.html +74 -24
  163. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.ts +168 -15
  164. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.types.ts +1 -0
  165. package/src/styles.css +2 -2
  166. package/lib/services/static/icons.service.d.ts +0 -65
  167. package/src/lib/components/colors-config/README.md +0 -38
  168. package/src/lib/components/form/form-sidebar/sidebar-form.component.scss +0 -0
  169. package/src/lib/components/form/form-validation/validation-form.component.scss +0 -0
  170. package/src/lib/components/menu/menu-options-table/options-table-menu.component.scss +0 -0
  171. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +0 -12
  172. package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.scss +0 -0
  173. package/src/lib/components/toggle-radio/toggle-radio.component.html +0 -51
  174. package/src/lib/components/toggle-radio/toggle-radio.component.ts +0 -222
  175. package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.scss +0 -0
  176. package/tailjng-0.1.6.tgz +0 -0
@@ -1,3 +1,6 @@
1
+ export * from './complete-crud-table.animations';
2
+ export * from './complete-crud-table.types';
3
+ export * from './complete-crud-table.util';
1
4
  export * from './expand-grid/table-expand-grid.builder';
2
5
  export * from './expand-grid/table-expand-grid.helper';
3
6
  export * from './expand-grid/table-expand-grid.types';
@@ -0,0 +1,21 @@
1
+ @keyframes theme-save-pop {
2
+ 0% {
3
+ transform: scale(1);
4
+ box-shadow: none;
5
+ }
6
+
7
+ 35% {
8
+ transform: scale(1.35);
9
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-primary) 40%, transparent);
10
+ }
11
+
12
+ 100% {
13
+ transform: scale(1);
14
+ box-shadow: none;
15
+ }
16
+ }
17
+
18
+ :host ::ng-deep button.theme-save-applied {
19
+ animation: theme-save-pop 0.65s cubic-bezier(0.34, 1.56, 0.64, 1);
20
+ color: var(--color-primary);
21
+ }
@@ -1,123 +1,153 @@
1
1
  <main class="container mx-auto">
2
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
3
-
4
- <!-- Controls Panel -->
5
- <div class="space-y-8 select-none">
6
-
7
- <div>
8
- <h2 class="text-sm font-bold mb-1 opacity-60">Selector de color</h2>
9
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
10
-
11
- <!-- Color Picker -->
12
- <div class="relative h-64 w-full overflow-hidden rounded-lg border border-border dark:border-dark-border">
13
- <div class="absolute inset-0 bg-gradient-to-br from-white to-transparent" style="z-index: 1;"></div>
14
- <div class="absolute inset-0 bg-gradient-to-b from-transparent to-black" style="z-index: 2;"></div>
15
- <div class="absolute inset-0" [style.background]="'linear-gradient(to right, hsl(' + huePosition + ', 0%, 50%), hsl(' + huePosition + ', 100%, 50%))'" style="z-index: 0;"></div>
16
- <div class="absolute w-6 h-6 rounded-full border-2 border-white transform -translate-x-1/2 -translate-y-1/2 cursor-pointer" [style.left.%]="colorPickerPosition.x" [style.top.%]="colorPickerPosition.y" style="z-index: 3;"></div>
17
- <div class="absolute inset-0 cursor-pointer" (mousedown)="startColorPicking($event)" (mousemove)="updateColorPicking($event)" (mouseup)="stopColorPicking()" (mouseleave)="stopColorPicking()" style="z-index: 4;"></div>
18
- </div>
19
-
20
- <!-- Color Spectrum -->
21
- <div class="space-y-4">
22
- <div class="relative">
23
-
24
- <!-- Picker bar -->
25
- <div class="h-8 w-full rounded-lg overflow-hidden border border-border dark:border-dark-border relative">
26
- <div class="absolute inset-0" [style.background]="hueGradient"></div>
27
-
28
- <input
29
- type="range"
30
- min="0"
31
- max="360"
32
- [value]="huePosition"
33
- (input)="updateHueFromInput($event)"
34
- class="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
35
- />
36
- </div>
37
-
38
- <!-- Ball outside the flow to stand out -->
39
- <div class="absolute top-1/2 transform -translate-y-1/2 -translate-x-1/2 w-9 h-9 rounded-full border-2 border-white z-10 pointer-events-none"
40
- [style.left.%]="(huePosition / 360) * 100"
41
- [style.backgroundColor]="hslToHex(huePosition, 100, 50)">
42
- </div>
43
-
44
- </div>
45
-
46
-
47
- <!-- Hex Input -->
48
- <JFormContainer>
49
- <JFormContainer>
50
- <JLabel for="baseColor" classes="opacity-60">Ingresa un valor Hex</JLabel>
51
-
52
- <JFormContainer [rows]="true">
53
-
54
- <JFormContainer class="flex-1">
55
- <JInput
56
- id="baseColor"
57
- type="text"
58
- [(ngModel)]="baseColor"
59
- (input)="updateFromHexInput()"
60
- placeholder="#000000"
61
- />
62
- </JFormContainer>
63
-
64
- <!-- Color Preview -->
65
- <div class="h-[35px] w-[60px] rounded shadow-md" [style.background-color]="baseColor"></div>
66
-
67
- </JFormContainer>
68
- </JFormContainer>
69
-
70
- <!-- Saturation Slider -->
71
- <JRangeInput
72
- [min]="0"
73
- [max]="100"
74
- [(ngModel)]="saturation"
75
- (input)="generateTheme()"
76
- placeholder="Saturación"
77
- [isLabel]="true"
78
- simbol="%"
79
- />
80
-
81
- <!-- Lightness Slider -->
82
- <JRangeInput
83
- [min]="0"
84
- [max]="100"
85
- [(ngModel)]="lightness"
86
- (input)="generateTheme()"
87
- placeholder="Luminosidad"
88
- [isLabel]="true"
89
- simbol="%"
90
- />
91
-
92
- </JFormContainer>
93
-
94
- </div>
95
- </div>
96
-
97
- </div>
98
- </div>
99
-
100
- <!-- Code Preview -->
101
- <div class="relative">
2
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
3
+ <!-- Controls Panel -->
4
+ <div class="space-y-8 select-none">
5
+ <div>
6
+ <h2 class="text-sm font-bold mb-1 opacity-60">Selector de color</h2>
7
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
8
+ <!-- Color Picker -->
9
+ <div
10
+ class="relative h-64 w-full overflow-hidden rounded-lg border border-border dark:border-dark-border"
11
+ >
102
12
  <div
103
- class="h-70 bg-background dark:bg-dark-background text-black dark:text-white overflow-auto rounded-lg font-mono text-sm whitespace-pre-wrap border border-border dark:border-dark-border scroll-element">
104
- <div class="p-4">
105
- <span>{{ themeCode }}</span>
106
- </div>
107
- </div>
108
-
109
- <div class="absolute top-4 right-4">
110
- <JButton
111
- (clicked)="copyThemeToClipboard()"
112
- [icon]="iconsService.icons.copy"
113
- [iconChange]="iconsService.icons.check"
114
- [isChangeIcon]="copied"
115
- [iconSize]="15"
116
- tooltip="Copiar"
117
- classes="w-[25px] h-[25px]"
13
+ class="absolute inset-0 bg-gradient-to-br from-white to-transparent"
14
+ style="z-index: 1"
15
+ ></div>
16
+ <div
17
+ class="absolute inset-0 bg-gradient-to-b from-transparent to-black"
18
+ style="z-index: 2"
19
+ ></div>
20
+ <div
21
+ class="absolute inset-0"
22
+ [style.background]="
23
+ 'linear-gradient(to right, hsl(' +
24
+ huePosition +
25
+ ', 0%, 50%), hsl(' +
26
+ huePosition +
27
+ ', 100%, 50%))'
28
+ "
29
+ style="z-index: 0"
30
+ ></div>
31
+ <div
32
+ class="absolute w-6 h-6 rounded-full border-2 border-white transform -translate-x-1/2 -translate-y-1/2 cursor-pointer"
33
+ [style.left.%]="colorPickerPosition.x"
34
+ [style.top.%]="colorPickerPosition.y"
35
+ style="z-index: 3"
36
+ ></div>
37
+ <div
38
+ class="absolute inset-0 cursor-pointer"
39
+ (mousedown)="startColorPicking($event)"
40
+ (mousemove)="updateColorPicking($event)"
41
+ (mouseup)="stopColorPicking()"
42
+ (mouseleave)="stopColorPicking()"
43
+ style="z-index: 4"
44
+ ></div>
45
+ </div>
46
+
47
+ <!-- Color Spectrum -->
48
+ <div class="space-y-4">
49
+ <div class="relative">
50
+ <!-- Picker bar -->
51
+ <div
52
+ class="h-8 w-full rounded-lg overflow-hidden border border-border dark:border-dark-border relative"
53
+ >
54
+ <div
55
+ class="absolute inset-0"
56
+ [style.background]="hueGradient"
57
+ ></div>
58
+
59
+ <input
60
+ type="range"
61
+ min="0"
62
+ max="360"
63
+ [value]="huePosition"
64
+ (input)="updateHueFromInput($event)"
65
+ class="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
118
66
  />
67
+ </div>
68
+
69
+ <!-- Ball outside the flow to stand out -->
70
+ <div
71
+ class="absolute top-1/2 transform -translate-y-1/2 -translate-x-1/2 w-9 h-9 rounded-full border-2 border-white z-10 pointer-events-none"
72
+ [style.left.%]="(huePosition / 360) * 100"
73
+ [style.backgroundColor]="hslToHex(huePosition, 100, 50)"
74
+ ></div>
119
75
  </div>
76
+
77
+ <!-- Hex Input -->
78
+ <JFormContainer>
79
+ <JFormContainer>
80
+ <JLabel for="baseColor" classes="opacity-60"
81
+ >Ingresa un valor Hex</JLabel
82
+ >
83
+
84
+ <JFormContainer [rows]="true" [gap]="3">
85
+ <JFormContainer class="flex-1">
86
+ <JInput
87
+ id="baseColor"
88
+ type="text"
89
+ [(ngModel)]="baseColor"
90
+ (input)="updateFromHexInput()"
91
+ placeholder="#000000"
92
+ />
93
+ </JFormContainer>
94
+
95
+ <!-- Color Preview -->
96
+ <div
97
+ class="h-[35px] w-[60px] rounded shadow-md"
98
+ [style.background-color]="baseColor"
99
+ ></div>
100
+ </JFormContainer>
101
+ </JFormContainer>
102
+
103
+ <!-- Saturation Slider -->
104
+ <JRangeInput
105
+ [min]="0"
106
+ [max]="100"
107
+ [(ngModel)]="saturation"
108
+ (input)="generateTheme()"
109
+ placeholder="Saturación"
110
+ [isLabel]="true"
111
+ simbol="%"
112
+ />
113
+
114
+ <!-- Lightness Slider -->
115
+ <JRangeInput
116
+ [min]="0"
117
+ [max]="100"
118
+ [(ngModel)]="lightness"
119
+ (input)="generateTheme()"
120
+ placeholder="Luminosidad"
121
+ [isLabel]="true"
122
+ simbol="%"
123
+ />
124
+ </JFormContainer>
125
+ </div>
120
126
  </div>
127
+ </div>
128
+ </div>
121
129
 
130
+ <!-- Code Preview -->
131
+ <div class="relative">
132
+ <div
133
+ class="h-70 bg-background dark:bg-dark-background text-black dark:text-white overflow-auto rounded-lg font-mono text-sm whitespace-pre-wrap border border-border dark:border-dark-border scroll-element"
134
+ >
135
+ <div class="p-4">
136
+ <span>{{ themeCode }}</span>
137
+ </div>
138
+ </div>
139
+
140
+ <div class="absolute top-4 right-4 flex gap-1">
141
+ <JButton
142
+ (clicked)="copyThemeToClipboard()"
143
+ [icon]="Icons.Copy"
144
+ [iconChange]="Icons.Check"
145
+ [isChangeIcon]="copied"
146
+ [iconSize]="15"
147
+ tooltip="Copiar"
148
+ classes="w-[25px] h-[25px]"
149
+ />
150
+ </div>
122
151
  </div>
123
- </main>
152
+ </div>
153
+ </main>
@@ -1,20 +1,20 @@
1
- import { Component } from '@angular/core';
1
+ import { Component, OnDestroy, OnInit, effect } from '@angular/core';
2
2
  import { FormsModule } from '@angular/forms';
3
- import { LucideAngularModule } from 'lucide-angular';
4
3
  import { JInputComponent } from '../input/input/input.component';
5
4
  import { JRangeInputComponent } from '../input/input-range/range-input.component';
6
5
  import { JButtonComponent } from '../button/button.component';
7
6
  import { JLabelComponent } from '../label/label.component';
8
- import { ColorPosition, GeneratedColors, JIconsService, JThemeService, ThemeMode } from 'tailjng';
7
+ import { ColorPosition, GeneratedColors, JThemeService, ThemeMode, applyGeneratedColorsToDocument, clearGeneratedColorsFromDocument } from 'tailjng';
9
8
  import { JFormContainerComponent } from '../form/form-container/container-form.component';
9
+ import { Icons } from '../.config/icons/icons.lucide';
10
10
 
11
11
  @Component({
12
12
  selector: 'JThemeGenerator',
13
- imports: [LucideAngularModule, JInputComponent, JRangeInputComponent, JButtonComponent, FormsModule, JLabelComponent, JFormContainerComponent],
13
+ imports: [JInputComponent, JRangeInputComponent, JButtonComponent, FormsModule, JLabelComponent, JFormContainerComponent],
14
14
  templateUrl: './theme-generator.component.html',
15
- styleUrl: './theme-generator.component.css'
16
15
  })
17
- export class JThemeGeneratorComponent {
16
+ export class JThemeGeneratorComponent implements OnInit, OnDestroy {
17
+ readonly Icons = Icons;
18
18
 
19
19
  // Color picker state
20
20
  baseColor: string = '#415884';
@@ -48,22 +48,27 @@ export class JThemeGeneratorComponent {
48
48
  }
49
49
 
50
50
  constructor(
51
- public readonly iconsService: JIconsService,
52
51
  public readonly themeService: JThemeService
53
- ) { }
52
+ ) {
53
+ effect(() => {
54
+ this.themeService.themeSignal();
55
+ if (this.generatedColors) {
56
+ this.applyColorsToApp(this.generatedColors);
57
+ }
58
+ });
59
+ }
54
60
 
55
- ngOnInit() {
56
- this.syncFromHSL();
61
+ ngOnDestroy(): void {
62
+ if (typeof this.themeService.clearPreviewColors === 'function') {
63
+ this.themeService.clearPreviewColors();
64
+ return;
65
+ }
57
66
 
58
- // Asynchronously check for theme changes every 300ms
59
- setInterval(() => {
60
- const current = localStorage.getItem('theme') as ThemeMode;
61
- if (current && current !== this.lastTheme) {
62
- this.previewMode = current;
63
- this.lastTheme = current;
64
- this.generateTheme();
65
- }
66
- }, 300);
67
+ clearGeneratedColorsFromDocument();
68
+ }
69
+
70
+ ngOnInit() {
71
+ this.syncFromHSL(false);
67
72
  }
68
73
 
69
74
 
@@ -179,9 +184,9 @@ export class JThemeGeneratorComponent {
179
184
  /**
180
185
  * Sync the internal state from HSL values.
181
186
  */
182
- syncFromHSL(): void {
187
+ syncFromHSL(applyToApp = true): void {
183
188
  this.baseColor = this.hslToHex(this.huePosition, this.saturation, this.lightness);
184
- this.generateTheme();
189
+ this.generateTheme(applyToApp);
185
190
  }
186
191
 
187
192
 
@@ -251,7 +256,7 @@ export class JThemeGeneratorComponent {
251
256
  /**
252
257
  * Generate the theme colors.
253
258
  */
254
- generateTheme(): void {
259
+ generateTheme(applyToApp = true): void {
255
260
  this.baseColor = this.hslToHex(this.huePosition, this.saturation * 0.9, this.lightness);
256
261
 
257
262
  const vars = this.generatedColors = {
@@ -298,8 +303,7 @@ export class JThemeGeneratorComponent {
298
303
  darkDestructiveForeground: this.hslToHex(this.huePosition, this.saturation * 0.2, 90),
299
304
  darkBorder: this.hslToHex(this.huePosition, this.saturation * 0.5, 45),
300
305
  darkInput: this.hslToHex(this.huePosition, this.saturation * 0.5, 45),
301
- darkRing: this.hslToHex(this.huePosition, this.saturation * 0.7, 28),
302
- };
306
+ darkRing: this.hslToHex(this.huePosition, this.saturation * 0.7, 28)};
303
307
 
304
308
  const toCssVar = (key: string, value: string) =>
305
309
  ` --color-${key.replace(/[A-Z]/g, m => '-' + m.toLowerCase())}: ${value};`;
@@ -320,8 +324,24 @@ export class JThemeGeneratorComponent {
320
324
  lines.push(` --color-dark-radius: 0.5rem;`);
321
325
 
322
326
  this.themeCode = `@theme {\n${lines.join('\n')}\n}`;
327
+
328
+ if (applyToApp) {
329
+ this.applyColorsToApp(vars);
330
+ }
323
331
  }
324
332
 
333
+ /**
334
+ * Applies palette CSS variables to the whole app.
335
+ * @param colors Generated palette.
336
+ */
337
+ private applyColorsToApp(colors: GeneratedColors): void {
338
+ if (typeof this.themeService.applyPreviewColors === 'function') {
339
+ this.themeService.applyPreviewColors(colors);
340
+ return;
341
+ }
342
+
343
+ applyGeneratedColorsToDocument(colors);
344
+ }
325
345
 
326
346
  copyThemeToClipboard(): void {
327
347
  navigator.clipboard.writeText(this.themeCode);
@@ -0,0 +1,8 @@
1
+ export type ToggleOption<T = unknown> = {
2
+ value: T;
3
+ label: string;
4
+ };
5
+
6
+ export type ToggleSortOrder = 'ASC' | 'DESC';
7
+
8
+ export type ToggleRadioLayout = 'vertical' | 'horizontal';
@@ -0,0 +1,44 @@
1
+ import { ToggleOption } from './toggle-options.types';
2
+
3
+ /**
4
+ * Reads a nested property using dot notation.
5
+ */
6
+ export function getNestedToggleValue(obj: unknown, path: string): string {
7
+ if (!obj || typeof obj !== 'object') {
8
+ return '';
9
+ }
10
+
11
+ const value = path.split('.').reduce<unknown>((acc, part) => {
12
+ if (acc && typeof acc === 'object' && part in (acc as Record<string, unknown>)) {
13
+ return (acc as Record<string, unknown>)[part];
14
+ }
15
+ return undefined;
16
+ }, obj);
17
+
18
+ return value == null ? '' : String(value);
19
+ }
20
+
21
+ /**
22
+ * Normalizes static options into `{ value, label }` pairs.
23
+ */
24
+ export function normalizeToggleOptions(
25
+ options: unknown[],
26
+ optionLabel: string,
27
+ optionValue: string,
28
+ ): ToggleOption[] {
29
+ if (!options.length) {
30
+ return [];
31
+ }
32
+
33
+ if (typeof options[0] === 'object' && options[0] !== null) {
34
+ return options.map((opt) => ({
35
+ value: (opt as Record<string, unknown>)[optionValue],
36
+ label: getNestedToggleValue(opt, optionLabel),
37
+ }));
38
+ }
39
+
40
+ return options.map((opt) => ({
41
+ value: opt,
42
+ label: String(opt),
43
+ }));
44
+ }
@@ -0,0 +1,135 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ }
5
+
6
+ .j-radio-group {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 0.625rem;
10
+ }
11
+
12
+ .j-radio-group--horizontal {
13
+ flex-flow: row wrap;
14
+ gap: 1rem 1.25rem;
15
+ }
16
+
17
+ .j-radio-group--disabled {
18
+ opacity: 0.55;
19
+ }
20
+
21
+ .j-radio-option {
22
+ display: inline-flex;
23
+ align-items: center;
24
+ gap: 0.625rem;
25
+ cursor: pointer;
26
+ user-select: none;
27
+ }
28
+
29
+ .j-radio-option--disabled {
30
+ cursor: not-allowed;
31
+ }
32
+
33
+ .j-radio-input {
34
+ position: absolute;
35
+ opacity: 0;
36
+ width: 0;
37
+ height: 0;
38
+ margin: 0;
39
+ pointer-events: none;
40
+ }
41
+
42
+ .j-radio-input:focus-visible + .j-radio-indicator {
43
+ outline: 2px solid var(--color-ring);
44
+ outline-offset: 2px;
45
+ }
46
+
47
+ .j-radio-indicator {
48
+ display: inline-flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ width: 1.125rem;
52
+ height: 1.125rem;
53
+ flex-shrink: 0;
54
+ border: 1.5px solid var(--color-border);
55
+ border-radius: 9999px;
56
+ background-color: var(--color-background);
57
+ transition:
58
+ border-color 0.15s ease,
59
+ background-color 0.15s ease;
60
+ }
61
+
62
+ :host-context(.dark) .j-radio-indicator,
63
+ :host-context(html.dark) .j-radio-indicator {
64
+ border-color: var(--color-dark-border);
65
+ background-color: var(--color-dark-background);
66
+ }
67
+
68
+ .j-radio-dot {
69
+ width: 0.4375rem;
70
+ height: 0.4375rem;
71
+ border-radius: 9999px;
72
+ background-color: var(--color-primary);
73
+ transform: scale(0);
74
+ transition: transform 0.15s ease;
75
+ }
76
+
77
+ :host-context(.dark) .j-radio-dot,
78
+ :host-context(html.dark) .j-radio-dot {
79
+ background-color: var(--color-dark-primary);
80
+ }
81
+
82
+ .j-radio-option--selected .j-radio-indicator {
83
+ border-color: var(--color-primary);
84
+ }
85
+
86
+ :host-context(.dark) .j-radio-option--selected .j-radio-indicator,
87
+ :host-context(html.dark) .j-radio-option--selected .j-radio-indicator {
88
+ border-color: var(--color-dark-primary);
89
+ }
90
+
91
+ .j-radio-option--selected .j-radio-dot {
92
+ transform: scale(1);
93
+ }
94
+
95
+ .j-radio-label {
96
+ font-size: 0.875rem;
97
+ line-height: 1.35;
98
+ color: var(--color-foreground);
99
+ }
100
+
101
+ :host-context(.dark) .j-radio-label,
102
+ :host-context(html.dark) .j-radio-label {
103
+ color: var(--color-dark-foreground);
104
+ }
105
+
106
+ .j-radio-state {
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: flex-start;
110
+ gap: 0.5rem;
111
+ min-height: 2rem;
112
+ color: var(--color-muted-foreground);
113
+ font-size: 0.875rem;
114
+ }
115
+
116
+ :host-context(.dark) .j-radio-state,
117
+ :host-context(html.dark) .j-radio-state {
118
+ color: var(--color-dark-muted-foreground);
119
+ }
120
+
121
+ .j-radio-clear {
122
+ margin-top: 0.625rem;
123
+ padding: 0;
124
+ border: 0;
125
+ background: transparent;
126
+ color: var(--color-destructive);
127
+ font-size: 0.75rem;
128
+ text-decoration: underline;
129
+ cursor: pointer;
130
+ }
131
+
132
+ .j-radio-clear:disabled {
133
+ opacity: 0.5;
134
+ cursor: not-allowed;
135
+ }
@@ -0,0 +1,52 @@
1
+ @if (internalOptions.length > 0) {
2
+ <div [ngClass]="groupClasses" role="radiogroup">
3
+ @for (opt of internalOptions; track $index) {
4
+ <label
5
+ class="j-radio-option"
6
+ [class.j-radio-option--selected]="isSelected(opt.value)"
7
+ [class.j-radio-option--disabled]="isInteractionDisabled"
8
+ [for]="optionId(opt.value, $index)"
9
+ >
10
+ <input
11
+ class="j-radio-input"
12
+ type="radio"
13
+ [id]="optionId(opt.value, $index)"
14
+ [name]="name"
15
+ [value]="opt.value"
16
+ [checked]="isSelected(opt.value)"
17
+ [disabled]="isInteractionDisabled"
18
+ (change)="select(opt.value)"
19
+ />
20
+
21
+ <span class="j-radio-indicator" aria-hidden="true">
22
+ <span class="j-radio-dot"></span>
23
+ </span>
24
+
25
+ <span class="j-radio-label">{{ opt.label }}</span>
26
+ </label>
27
+ }
28
+ </div>
29
+ } @else if (isLoading) {
30
+ <div class="j-radio-state">
31
+ <JIcon
32
+ [icon]="Icons.Loader2"
33
+ [size]="16"
34
+ iconClass="animate-spin text-muted-foreground"
35
+ [ariaHidden]="true"
36
+ />
37
+ <span>Cargando...</span>
38
+ </div>
39
+ } @else {
40
+ <div class="j-radio-state">No hay opciones</div>
41
+ }
42
+
43
+ @if (showClear && selectedValue !== null) {
44
+ <button
45
+ type="button"
46
+ class="j-radio-clear"
47
+ (click)="clear()"
48
+ [disabled]="isInteractionDisabled"
49
+ >
50
+ Limpiar selección
51
+ </button>
52
+ }