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,56 +1,53 @@
1
- <div class="relative isolate">
2
- <JButton
3
- onKeyPress
4
- jCoachMark
5
- [contentTemplate]="customTemplateActions"
6
- [isDescriptionFormatted]="true"
7
- coachPosition="bottom-left"
8
- coachTrigger="click"
9
- [coachMaxWidth]="'170px'"
10
- [coachWidth]="'170px'"
11
- [coachSpotlight]="false"
12
- [icon]="iconsService.icons.ellipsisVertical"
13
- [iconSize]="20"
14
- classes="secondary w-[35px] h-[35px] rounded-full"
15
- (clicked)="handleClickGeneral()"
16
- />
1
+ <JButton
2
+ jCoachMark
3
+ [contentTemplate]="actionsTemplate"
4
+ [isDescriptionFormatted]="true"
5
+ coachPosition="bottom-left"
6
+ coachTrigger="click"
7
+ coachMaxWidth="170px"
8
+ coachWidth="170px"
9
+ [coachSpotlight]="false"
10
+ size="icon"
11
+ [icon]="Icons.EllipsisVertical"
12
+ [iconSize]="18"
13
+ [ariaLabel]="'Acciones de fila'"
14
+ classes="secondary rounded-full"
15
+ (clicked)="closeCoachOverlay()"
16
+ />
17
17
 
18
- <ng-template #customTemplateActions>
19
- <div class="menu-options-list flex flex-col gap-2 w-full min-w-full">
20
- @if (optionsTable.length > 0) {
21
- @for (option of optionsTable; track $index) {
22
- @if (getIsVisible(option, group)) {
23
- <div
24
- class="menu-option-item w-full hover:bg-primary/10 dark:hover:bg-dark-primary/10 text-[10px]"
18
+ <ng-template #actionsTemplate>
19
+ <div class="flex w-full min-w-0 flex-col gap-2">
20
+ @if (optionsTable.length > 0) {
21
+ @for (option of optionsTable; track option.type ?? $index) {
22
+ @if (getIsVisible(option, group)) {
23
+ <div
24
+ class="flex w-full min-w-0 rounded-lg transition-colors hover:bg-primary/10 dark:hover:bg-dark-primary/10"
25
+ role="none"
26
+ >
27
+ <JButton
28
+ size="sm"
29
+ [icon]="getIcon(option.icon, group)"
30
+ [iconSize]="14"
31
+ (clicked)="handleClick(option, group)"
32
+ [tooltip]="getTooltip(option.tooltip ?? '', group)"
33
+ [tooltipPosition]="option.tooltipPosition ?? 'top'"
34
+ [disabled]="getDisabled(option, group)"
35
+ [isLoading]="isOptionLoading(option)"
36
+ [classes]="optionButtonClasses(option)"
37
+ [ngClasses]="optionColorClasses(option, group)"
25
38
  >
26
- <JButton
27
- onKeyPress
28
- size="sm"
29
- [icon]="getIcon(option.icon, group)"
30
- [iconSize]="16"
31
- (clicked)="handleClick(option, group)"
32
- [tooltip]="getTooltip(option.tooltip ?? '', group)"
33
- [tooltipPosition]="option.tooltipPosition ?? 'top'"
34
- [disabled]="getDisabled(option, group)"
35
- [isLoading]="false"
36
- [classes]="
37
- 'w-full min-w-full text-left justify-start h-[30px] ' +
38
- (option.classes ?? '')
39
- "
40
- [ngClasses]="mergeNgClasses(option.ngClass, group)"
41
- >
42
- {{ getTooltip(option.text ?? '', group) }}
43
- </JButton>
44
- </div>
45
- }
39
+ {{ resolveOptionText(option, group) }}
40
+ </JButton>
41
+ </div>
46
42
  }
47
- } @else {
48
- <div
49
- class="text-center text-gray-500 dark:text-dark-text-secondary text-xs"
50
- >
51
- No hay opciones disponibles
52
- </div>
53
43
  }
54
- </div>
55
- </ng-template>
56
- </div>
44
+ } @else {
45
+ <div
46
+ class="py-2 px-1.5 text-center text-xs leading-5 text-muted-foreground dark:text-dark-muted-foreground"
47
+ role="status"
48
+ >
49
+ No hay opciones disponibles
50
+ </div>
51
+ }
52
+ </div>
53
+ </ng-template>
@@ -1,41 +1,129 @@
1
- import { Component, Input, Output, EventEmitter, ElementRef, OnDestroy, ViewChild, AfterViewInit, Renderer2 } from '@angular/core';
2
- import { LucideAngularModule } from 'lucide-angular';
3
- import { JButtonComponent } from '../../button/button.component';
4
- import { JIconsService } from 'tailjng';
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { OptionsTable } from 'tailjng';
3
+ import { Icons } from '../../.config/icons/icons.lucide';
5
4
  import { JCoachMarkDirective } from '../../coach-mark/coach-mark.directive';
5
+ import { JButtonComponent } from '../../button/button.component';
6
+ import {
7
+ defaultMenuIconResolver,
8
+ defaultMenuNgClassesResolver} from '../menu-options-table/menu-options-defaults';
9
+ import {
10
+ OptionsCoachMenuClassesResolver,
11
+ OptionsCoachMenuClickEvent,
12
+ OptionsCoachMenuDisabledResolver,
13
+ OptionsCoachMenuIconResolver,
14
+ OptionsCoachMenuLoadingResolver,
15
+ OptionsCoachMenuTextResolver,
16
+ OptionsCoachMenuVisibleResolver} from './options-coach-menu.types';
17
+
18
+ export type { OptionsCoachMenuClickEvent } from './options-coach-menu.types';
6
19
 
20
+ /**
21
+ * Row actions menu rendered inside a coach-mark popover (guided UX).
22
+ *
23
+ * Install: `npx tailjng add options-coach-menu`
24
+ */
7
25
  @Component({
8
26
  selector: 'JOptionsCoachMenu',
9
- imports: [LucideAngularModule, JButtonComponent, JCoachMarkDirective],
27
+ standalone: true,
28
+ imports: [JButtonComponent, JCoachMarkDirective],
10
29
  templateUrl: './options-coach-menu.component.html',
11
- styleUrl: './options-coach-menu.component.scss'
12
- })
30
+ host: { class: 'inline-flex relative isolate' }})
13
31
  export class JOptionsCoachMenuComponent {
32
+ readonly Icons = Icons;
14
33
 
15
- @Input() optionsTable: any[] = [];
16
- @Input() group: any;
34
+ private static readonly BLOCKED_NG_CLASSES = new Set([
35
+ 'min-w-auto',
36
+ 'p-[1px]!',
37
+ 'pl-[5px]!',
38
+ 'pr-[5px]!',
39
+ ]);
40
+
41
+ @Input() optionsTable: OptionsTable[] = [];
42
+ @Input() group: unknown;
17
43
  @Input() rowId: string | number | null = null;
18
44
 
19
- @Input() getIcon: (icon: any, group: any) => any = () => null;
20
- @Input() getTooltip: (tooltip: string, group: any) => string = () => '';
21
- @Input() getDisabled: (option: any, group: any) => boolean = () => false;
22
- @Input() getIsVisible: (option: any, group: any) => boolean = () => true;
23
- @Input() mergeNgClasses: (ngClass: any, group: any) => any = () => '';
24
- @Input() isAditionalButtonLoading: (type: string, id: any) => boolean = () => false;
45
+ @Input() getIcon: OptionsCoachMenuIconResolver = defaultMenuIconResolver;
46
+ @Input() getTooltip: OptionsCoachMenuTextResolver = () => '';
47
+ @Input() getDisabled: OptionsCoachMenuDisabledResolver = () => false;
48
+ @Input() getIsVisible: OptionsCoachMenuVisibleResolver = () => true;
49
+ @Input() mergeNgClasses: OptionsCoachMenuClassesResolver = defaultMenuNgClassesResolver;
50
+ @Input() isAditionalButtonLoading: OptionsCoachMenuLoadingResolver = () => false;
51
+
52
+ @Output() clicked = new EventEmitter<OptionsCoachMenuClickEvent>();
53
+
54
+ constructor() {}
55
+
56
+ optionButtonClasses(option: OptionsTable): string {
57
+ return [
58
+ 'w-full',
59
+ 'min-w-0',
60
+ 'h-[30px]',
61
+ 'min-h-[30px]',
62
+ 'text-left',
63
+ 'justify-start',
64
+ 'text-[14px]',
65
+ 'leading-tight',
66
+ 'font-medium',
67
+ option.classes ?? '',
68
+ ]
69
+ .filter(Boolean)
70
+ .join(' ');
71
+ }
72
+
73
+ optionColorClasses(option: OptionsTable, group: unknown): Record<string, boolean> {
74
+ const merged = this.mergeNgClasses(option.ngClass, group);
75
+ const normalized: Record<string, boolean> = {
76
+ 'w-full': true,
77
+ 'min-w-0': true};
78
+
79
+ for (const [className, active] of Object.entries(merged)) {
80
+ if (
81
+ !active ||
82
+ className === 'text-destructive' ||
83
+ JOptionsCoachMenuComponent.BLOCKED_NG_CLASSES.has(className)
84
+ ) {
85
+ continue;
86
+ }
87
+ normalized[className] = true;
88
+ }
25
89
 
26
- @Output() clicked = new EventEmitter<{ option: any; group: any }>();
90
+ if (merged['text-destructive']) {
91
+ normalized['text-red-600'] = true;
92
+ normalized['dark:text-red-300'] = true;
93
+ }
27
94
 
28
-
29
- constructor(
30
- public readonly iconsService: JIconsService,
31
- ) { }
95
+ return normalized;
96
+ }
97
+
98
+ resolveOptionText(option: OptionsTable, group: unknown): string {
99
+ const { text, tooltip } = option;
100
+
101
+ if (typeof text === 'function') {
102
+ return text(group);
103
+ }
104
+
105
+ if (typeof text === 'string' && text.length > 0) {
106
+ return text;
107
+ }
108
+
109
+ return this.getTooltip(tooltip ?? '', group);
110
+ }
111
+
112
+ isOptionLoading(option: OptionsTable): boolean {
113
+ if (this.rowId == null) {
114
+ return this.isAditionalButtonLoading(String(option.type ?? ''));
115
+ }
32
116
 
33
- handleClick(option: any, group: any): void {
117
+ return this.isAditionalButtonLoading(String(option.type ?? ''), this.rowId);
118
+ }
119
+
120
+ handleClick(option: OptionsTable, group: unknown): void {
34
121
  this.clicked.emit({ option, group });
35
- this.handleClickGeneral();
122
+ this.closeCoachOverlay();
36
123
  }
37
-
38
- handleClickGeneral(): void {
124
+
125
+ /** Simulates an outside click so the coach-mark directive closes the popover. */
126
+ closeCoachOverlay(): void {
39
127
  document.body.click();
40
128
  }
41
129
  }
@@ -0,0 +1,9 @@
1
+ export type {
2
+ OptionsTableMenuClassesResolver as OptionsCoachMenuClassesResolver,
3
+ OptionsTableMenuClickEvent as OptionsCoachMenuClickEvent,
4
+ OptionsTableMenuDisabledResolver as OptionsCoachMenuDisabledResolver,
5
+ OptionsTableMenuIconResolver as OptionsCoachMenuIconResolver,
6
+ OptionsTableMenuLoadingResolver as OptionsCoachMenuLoadingResolver,
7
+ OptionsTableMenuTextResolver as OptionsCoachMenuTextResolver,
8
+ OptionsTableMenuVisibleResolver as OptionsCoachMenuVisibleResolver,
9
+ } from '../menu-options-table/options-table-menu.types';
@@ -3,20 +3,15 @@
3
3
  (click)="toggleDarkMode()"
4
4
  [style.width.px]="size"
5
5
  [style.height.px]="size"
6
- [class]="
7
- 'inline-flex items-center justify-center shrink-0 rounded-full cursor-pointer bg-gray-300 dark:bg-gray-700 text-black dark:text-white leading-none p-0 border border-border/50 dark:border-dark-border/50 ' +
8
- classes
9
- "
6
+ [attr.aria-pressed]="themeService.themeSignal() === 'dark'"
7
+ [attr.aria-label]="resolvedAriaLabel"
8
+ [ngClass]="computedClasses"
9
+ [class]="classes"
10
10
  >
11
- @if (theme === "dark") {
12
- <lucide-icon
13
- [name]="iconsService.icons.moon"
14
- [size]="iconSize"
15
- ></lucide-icon>
16
- } @else {
17
- <lucide-icon
18
- [name]="iconsService.icons.sun"
19
- [size]="iconSize"
20
- ></lucide-icon>
21
- }
22
- </button>
11
+ <JIcon
12
+ [icon]="themeService.themeSignal() === 'dark' ? Icons.Moon : Icons.Sun"
13
+ [size]="resolvedIconSize"
14
+ [inheritParentColor]="true"
15
+ [ariaHidden]="true"
16
+ />
17
+ </button>
@@ -1,48 +1,84 @@
1
- import { Component, Inject, Input, PLATFORM_ID } from '@angular/core';
2
- import { isPlatformBrowser } from '@angular/common';
3
- import { LucideAngularModule } from 'lucide-angular';
4
- import { JIconsService } from 'tailjng';
1
+ import { NgClass } from '@angular/common';
2
+ import { Component, Input } from '@angular/core';
3
+ import { JThemeService, ThemeMode } from 'tailjng';
4
+ import { Icons } from '../.config/icons/icons.lucide';
5
+ import { JIconComponent } from '../icon/icon.component';
5
6
 
7
+ export type { ThemeMode } from 'tailjng';
8
+
9
+ /**
10
+ * Light / dark mode toggle for Tailwind `dark` class on `document.documentElement`.
11
+ *
12
+ * Uses {@link JThemeService} (localStorage + `prefers-color-scheme`).
13
+ *
14
+ * Install: `npx tailjng add mode-toggle`
15
+ */
6
16
  @Component({
7
17
  selector: 'JModeToggle',
8
- imports: [LucideAngularModule],
18
+ imports: [JIconComponent, NgClass],
9
19
  templateUrl: './mode-toggle.component.html',
10
- styleUrl: './mode-toggle.component.css'
20
+ styleUrl: './mode-toggle.component.css',
11
21
  })
12
22
  export class JModeToggleComponent {
13
- title = 'frontend';
14
- theme: string = 'light';
15
-
16
- @Input() size: number = 40;
17
- @Input() iconSize: number = 20;
18
- @Input() classes: string = '';
19
-
20
- constructor(
21
- @Inject(PLATFORM_ID) private readonly platformId: object,
22
- public readonly iconsService: JIconsService
23
- ) {
24
- this.loadTheme();
25
- }
23
+ // Project Icons registry — template usage: Icons.Sun, Icons.Moon
24
+ readonly Icons = Icons;
25
+
26
+ // Button diameter in px
27
+ @Input() size = 40;
28
+
29
+ // Icon size in px; 0 = derived from `size`
30
+ @Input() iconSize = 0;
31
+
32
+ // Extra Tailwind classes on the button
33
+ @Input() classes = '';
26
34
 
27
- setTheme(theme: 'light' | 'dark') {
28
- if (isPlatformBrowser(this.platformId)) {
29
- localStorage.setItem('theme', theme);
35
+ // Extra conditional classes (same role as [ngClass] on the host)
36
+ @Input() ngClasses: Record<string, boolean> = {};
37
+
38
+ // Override default toggle aria-label
39
+ @Input() ariaLabel?: string;
40
+
41
+ constructor(readonly themeService: JThemeService) {}
42
+
43
+ /**
44
+ * Final icon size in px.
45
+ * @returns `iconSize` when greater than 0; otherwise derived from `size`.
46
+ */
47
+ get resolvedIconSize(): number {
48
+ if (this.iconSize > 0) {
49
+ return this.iconSize;
30
50
  }
51
+ return Math.max(14, Math.round(this.size * 0.5));
52
+ }
31
53
 
32
- this.theme = theme;
33
- document.documentElement.classList.toggle('dark', theme === 'dark');
54
+ /**
55
+ * Host class map for `[ngClass]`.
56
+ * @returns Base toggle styles merged with `ngClasses`.
57
+ */
58
+ get computedClasses(): Record<string, boolean> {
59
+ return {
60
+ 'inline-flex items-center justify-center shrink-0 rounded-full cursor-pointer bg-gray-300 dark:bg-gray-700 text-black dark:text-white leading-none p-0 border border-border/50 dark:border-dark-border/50 transition-colors duration-300 select-none': true,
61
+ ...this.ngClasses,
62
+ };
34
63
  }
35
64
 
36
- loadTheme() {
37
- if (isPlatformBrowser(this.platformId)) {
38
- const storedTheme = localStorage.getItem('theme');
39
- this.theme = storedTheme ?? 'light';
40
- document.documentElement.classList.toggle('dark', this.theme === 'dark');
65
+ /**
66
+ * Accessible label describing the action on click.
67
+ * @returns Custom `ariaLabel` or a default switch label.
68
+ */
69
+ get resolvedAriaLabel(): string {
70
+ if (this.ariaLabel) {
71
+ return this.ariaLabel;
41
72
  }
73
+ return this.themeService.themeSignal() === 'dark'
74
+ ? 'Switch to light mode'
75
+ : 'Switch to dark mode';
42
76
  }
43
77
 
44
- toggleDarkMode() {
45
- const newTheme = this.theme === 'dark' ? 'light' : 'dark';
46
- this.setTheme(newTheme);
78
+ /**
79
+ * Toggles between light and dark mode via {@link JThemeService}.
80
+ */
81
+ toggleDarkMode(): void {
82
+ this.themeService.toggleTheme();
47
83
  }
48
- }
84
+ }
@@ -9,7 +9,7 @@
9
9
 
10
10
  <!-- First page -->
11
11
  <JButton onKeyPress
12
- [icon]="iconsService.icons.firstPage"
12
+ [icon]="Icons.ChevronsLeft"
13
13
  [iconSize]="16"
14
14
  (click)="goToFirstPage()"
15
15
  [disabled]="currentPage === 1 || isLoading"
@@ -22,7 +22,7 @@
22
22
 
23
23
  <!-- Previous page -->
24
24
  <JButton onKeyPress
25
- [icon]="iconsService.icons.prevPage"
25
+ [icon]="Icons.ChevronLeft"
26
26
  [iconSize]="16"
27
27
  (click)="goToPreviousPage()"
28
28
  [disabled]="currentPage === 1 || isLoading"
@@ -52,7 +52,7 @@
52
52
 
53
53
  <!-- Next page -->
54
54
  <JButton onKeyPress
55
- [icon]="iconsService.icons.nextPage"
55
+ [icon]="Icons.ChevronRight"
56
56
  [iconSize]="16"
57
57
  (click)="goToNextPage()"
58
58
  [disabled]="currentPage === totalPages || isLoading"
@@ -65,7 +65,7 @@
65
65
 
66
66
  <!-- Last page -->
67
67
  <JButton onKeyPress
68
- [icon]="iconsService.icons.lastPage"
68
+ [icon]="Icons.ChevronsRight"
69
69
  [iconSize]="16"
70
70
  (click)="goToLastPage()"
71
71
  [disabled]="currentPage === totalPages || isLoading"
@@ -1,19 +1,43 @@
1
1
  import { Component, Input, Output, EventEmitter } from '@angular/core';
2
- import { LucideAngularModule } from 'lucide-angular';
3
2
  import { JButtonComponent } from '../../button/button.component';
4
- import { JIconsService } from 'tailjng';
5
-
3
+ import type { CompletePaginatorLoadingButton, CompletePaginatorNavButton } from './complete-paginator.types';
4
+
5
+ import { Icons } from '../../.config/icons/icons.lucide';
6
+
7
+ export type {
8
+ CompletePaginatorLoadingButton,
9
+ CompletePaginatorNavButton,
10
+ GeneratePaginationPagesOptions} from './complete-paginator.types';
11
+ export { generatePaginationPages, generatePaginationPagesFromOptions } from './complete-paginator.util';
12
+
13
+ /**
14
+ * Paginator with record range, first/prev/next/last controls and a page-number window.
15
+ *
16
+ * Install: `npx tailjng add paginator-complete`
17
+ *
18
+ * ```html
19
+ * <JCompletePaginator
20
+ * [currentPage]="currentPage"
21
+ * [itemsPerPage]="itemsPerPage"
22
+ * [totalItems]="totalItems"
23
+ * [pages]="pages"
24
+ * [isLoading]="isLoading('pagination')"
25
+ * (pageChange)="handlePageChange($event)"
26
+ * />
27
+ * ```
28
+ */
6
29
  @Component({
7
30
  selector: 'JCompletePaginator',
8
- imports: [LucideAngularModule, JButtonComponent],
31
+ imports: [ JButtonComponent],
9
32
  templateUrl: './complete-paginator.component.html',
10
33
  styleUrl: './complete-paginator.component.scss'
11
34
  })
12
35
  export class JCompletePaginatorComponent {
36
+ readonly Icons = Icons;
13
37
 
14
38
  Math = Math;
15
39
 
16
- loadingButton: 'first' | 'prev' | 'next' | 'last' | number | null = null;
40
+ loadingButton: CompletePaginatorLoadingButton = null;
17
41
 
18
42
  @Input() pages: number[] = [];
19
43
  @Output() pageChange = new EventEmitter<number>();
@@ -40,7 +64,7 @@ export class JCompletePaginatorComponent {
40
64
  return Math.ceil(this.totalItems / this.itemsPerPage);
41
65
  }
42
66
 
43
- constructor(public readonly iconsService: JIconsService) { }
67
+ constructor() {}
44
68
 
45
69
 
46
70
  /**
@@ -110,7 +134,7 @@ export class JCompletePaginatorComponent {
110
134
  * @param button The button to check
111
135
  * @returns True if the button is loading, false otherwise
112
136
  */
113
- isButtonLoading(button: 'first' | 'prev' | 'next' | 'last' | number): boolean {
137
+ isButtonLoading(button: CompletePaginatorNavButton | number): boolean {
114
138
  return this.isLoading && this.loadingButton === button;
115
139
  }
116
140
 
@@ -0,0 +1,12 @@
1
+ /** Navigation control that can show a loading spinner while data is fetched. */
2
+ export type CompletePaginatorNavButton = 'first' | 'prev' | 'next' | 'last';
3
+
4
+ /** Active loading target while {@link JCompletePaginatorComponent.isLoading} is true. */
5
+ export type CompletePaginatorLoadingButton = CompletePaginatorNavButton | number | null;
6
+
7
+ export type GeneratePaginationPagesOptions = {
8
+ currentPage: number;
9
+ totalPages: number;
10
+ /** Number of page buttons visible at once. Default: 3 (same as table/card CRUD). */
11
+ maxPagesToShow?: number;
12
+ };
@@ -0,0 +1,36 @@
1
+ import type { GeneratePaginationPagesOptions } from './complete-paginator.types';
2
+
3
+ /**
4
+ * Builds the sliding window of page numbers shown by {@link JCompletePaginatorComponent}.
5
+ * Same algorithm used by complete table/card components.
6
+ */
7
+ export function generatePaginationPages(
8
+ currentPage: number,
9
+ totalPages: number,
10
+ maxPagesToShow = 3,
11
+ ): number[] {
12
+ if (totalPages <= 0) {
13
+ return [];
14
+ }
15
+
16
+ const safeCurrentPage = Math.min(Math.max(currentPage, 1), totalPages);
17
+ let startPage = Math.max(1, safeCurrentPage - Math.floor(maxPagesToShow / 2));
18
+ let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
19
+
20
+ if (endPage - startPage + 1 < maxPagesToShow) {
21
+ startPage = Math.max(1, endPage - maxPagesToShow + 1);
22
+ }
23
+
24
+ return Array.from({ length: endPage - startPage + 1 }, (_, index) => startPage + index);
25
+ }
26
+
27
+ /** Object-style overload for callers that prefer a single options bag. */
28
+ export function generatePaginationPagesFromOptions(
29
+ options: GeneratePaginationPagesOptions,
30
+ ): number[] {
31
+ return generatePaginationPages(
32
+ options.currentPage,
33
+ options.totalPages,
34
+ options.maxPagesToShow,
35
+ );
36
+ }
@@ -0,0 +1,11 @@
1
+ .j-progress-bar__label {
2
+ z-index: 1;
3
+ }
4
+
5
+ .j-progress-bar__label--fill {
6
+ z-index: 2;
7
+ }
8
+
9
+ .j-progress-bar__fill {
10
+ min-width: 0;
11
+ }