tailjng 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/cli/execute/init-app.js +5 -2
- package/cli/execute/sync-app.js +14 -2
- package/cli/settings/colors-config-utils.js +43 -8
- package/cli/settings/icons-config-utils.js +62 -0
- package/cli/settings/path-utils.js +32 -2
- package/cli/settings/project-utils.js +7 -1
- package/cli/templates/app.generator.js +2 -2
- package/fesm2022/tailjng.mjs +247 -80
- package/fesm2022/tailjng.mjs.map +1 -1
- package/lib/services/static/theme.service.d.ts +39 -1
- package/lib/utils/theme/theme-variables.util.d.ts +31 -0
- package/package.json +1 -1
- package/public-api.d.ts +2 -1
- package/registry/components.json +41 -18
- package/src/colors.safelist.css +2 -2
- package/src/lib/components/.config/README.md +11 -0
- package/src/lib/components/.config/colors/README.md +38 -0
- package/src/lib/components/{colors-config → .config/colors}/colors.config.ts +5 -5
- package/src/lib/components/{colors-config → .config/colors}/colors.safelist.css +2 -2
- package/src/lib/components/.config/icons/README.md +26 -0
- package/src/lib/components/.config/icons/icons.lucide.ts +134 -0
- package/src/lib/components/.config/input/README.md +24 -0
- package/src/lib/components/.config/input/input.classes.ts +119 -0
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +244 -2
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +25 -38
- package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +66 -56
- package/src/lib/components/alert/alert-dialog/dialog-alert.types.ts +19 -0
- package/src/lib/components/alert/alert-toast/toast-alert.component.css +630 -12
- package/src/lib/components/alert/alert-toast/toast-alert.component.html +103 -102
- package/src/lib/components/alert/alert-toast/toast-alert.component.ts +485 -128
- package/src/lib/components/alert/alert-toast/toast-alert.types.ts +25 -0
- package/src/lib/components/badge/badge.component.html +34 -21
- package/src/lib/components/badge/badge.component.ts +140 -31
- package/src/lib/components/button/button.component.html +16 -10
- package/src/lib/components/button/button.component.ts +162 -22
- package/src/lib/components/card/card-complete/complete-card.component.html +2 -2
- package/src/lib/components/card/card-complete/complete-card.component.ts +26 -16
- package/src/lib/components/card/card-crud-complete/complete-crud-card.component.html +2 -2
- package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +26 -16
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.css +97 -0
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.html +54 -46
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.component.ts +135 -64
- package/src/lib/components/checkbox/checkbox-input/input-checkbox.types.ts +3 -0
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.css +112 -0
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +28 -25
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.ts +67 -15
- package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.types.ts +1 -0
- package/src/lib/components/coach-mark/coach-mark.component.html +4 -22
- package/src/lib/components/coach-mark/coach-mark.component.scss +1 -1
- package/src/lib/components/coach-mark/coach-mark.component.ts +51 -18
- package/src/lib/components/coach-mark/coach-mark.directive.ts +133 -78
- package/src/lib/components/coach-mark/coach-mark.types.ts +12 -0
- package/src/lib/components/dialog/dialog.component.css +103 -1
- package/src/lib/components/dialog/dialog.component.html +46 -66
- package/src/lib/components/dialog/dialog.component.ts +136 -110
- package/src/lib/components/dialog/dialog.types.ts +19 -0
- package/src/lib/components/filter/filter-complete/complete-filter.component.html +16 -19
- package/src/lib/components/filter/filter-complete/complete-filter.component.scss +35 -0
- package/src/lib/components/filter/filter-complete/complete-filter.component.ts +58 -34
- package/src/lib/components/filter/filter-complete/complete-filter.types.ts +7 -0
- package/src/lib/components/filter/filter-complete/complete-filter.util.ts +16 -0
- package/src/lib/components/form/form-container/container-form.component.css +4 -0
- package/src/lib/components/form/form-container/container-form.component.html +2 -2
- package/src/lib/components/form/form-container/container-form.component.ts +72 -16
- package/src/lib/components/form/form-container/container-form.types.ts +42 -0
- package/src/lib/components/form/form-container/form-col-span.directive.ts +25 -0
- package/src/lib/components/form/form-sidebar/sidebar-form.component.css +276 -0
- package/src/lib/components/form/form-sidebar/sidebar-form.component.html +117 -125
- package/src/lib/components/form/form-sidebar/sidebar-form.component.ts +109 -34
- package/src/lib/components/form/form-sidebar/sidebar-form.types.ts +3 -0
- package/src/lib/components/{toggle-radio/toggle-radio.component.css → form/form-validation/validation-form.component.css} +0 -1
- package/src/lib/components/form/form-validation/validation-form.component.html +10 -6
- package/src/lib/components/form/form-validation/validation-form.component.ts +99 -12
- package/src/lib/components/form/form-validation/validation-form.types.ts +33 -0
- package/src/lib/components/icon/icon.component.html +8 -5
- package/src/lib/components/icon/icon.component.ts +111 -9
- package/src/lib/components/input/input/input.component.html +19 -16
- package/src/lib/components/input/input/input.component.ts +130 -53
- package/src/lib/components/input/input/input.types.ts +8 -0
- package/src/lib/components/input/input-file/file-input.component.html +65 -56
- package/src/lib/components/input/input-file/file-input.component.ts +276 -173
- package/src/lib/components/input/input-file/file-input.types.ts +2 -0
- package/src/lib/components/input/input-range/range-input.component.css +67 -0
- package/src/lib/components/input/input-range/range-input.component.html +50 -58
- package/src/lib/components/input/input-range/range-input.component.ts +148 -60
- package/src/lib/components/input/input-range/range-input.types.ts +7 -0
- package/src/lib/components/input/input-textarea/textarea-input.component.html +16 -7
- package/src/lib/components/input/input-textarea/textarea-input.component.ts +140 -50
- package/src/lib/components/input/input-textarea/textarea-input.types.ts +2 -0
- package/src/lib/components/label/label.component.html +17 -16
- package/src/lib/components/label/label.component.ts +70 -16
- package/src/lib/components/label/label.types.ts +2 -0
- package/src/lib/components/menu/menu-options-table/menu-options-defaults.ts +34 -0
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.html +34 -20
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.ts +211 -58
- package/src/lib/components/menu/menu-options-table/options-table-menu.types.ts +38 -0
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +49 -52
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.ts +112 -24
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.types.ts +9 -0
- package/src/lib/components/mode-toggle/mode-toggle.component.html +11 -16
- package/src/lib/components/mode-toggle/mode-toggle.component.ts +69 -33
- package/src/lib/components/paginator/paginator-complete/complete-paginator.component.html +4 -4
- package/src/lib/components/paginator/paginator-complete/complete-paginator.component.ts +31 -7
- package/src/lib/components/paginator/paginator-complete/complete-paginator.types.ts +12 -0
- package/src/lib/components/paginator/paginator-complete/complete-paginator.util.ts +36 -0
- package/src/lib/components/progress-bar/progress-bar.component.css +11 -0
- package/src/lib/components/progress-bar/progress-bar.component.html +41 -40
- package/src/lib/components/progress-bar/progress-bar.component.ts +95 -11
- package/src/lib/components/progress-bar/progress-bar.types.ts +2 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.component.css +6 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.component.html +54 -44
- package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +450 -509
- package/src/lib/components/select/select-dropdown/dropdown-select.types.ts +43 -0
- package/src/lib/components/select/select-dropdown/dropdown-select.util.ts +179 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +6 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +131 -42
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +491 -475
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.types.ts +22 -0
- package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.util.ts +20 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.component.css +10 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.component.html +76 -60
- package/src/lib/components/select/select-multi-table/multi-table-select.component.ts +250 -313
- package/src/lib/components/select/select-multi-table/multi-table-select.types.ts +10 -0
- package/src/lib/components/select/select-multi-table/multi-table-select.util.ts +5 -0
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.css +212 -0
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.html +62 -53
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.ts +84 -27
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.types.ts +2 -0
- package/src/lib/components/table/table-complete/complete-table.component.html +15 -17
- package/src/lib/components/table/table-complete/complete-table.component.ts +190 -338
- package/src/lib/components/table/table-complete/complete-table.types.ts +28 -0
- package/src/lib/components/table/table-complete/complete-table.util.ts +236 -0
- package/src/lib/components/table/table-complete/index.ts +2 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.animations.ts +34 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +73 -128
- package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +542 -829
- package/src/lib/components/table/table-crud-complete/complete-crud-table.types.ts +57 -0
- package/src/lib/components/table/table-crud-complete/complete-crud-table.util.ts +723 -0
- package/src/lib/components/table/table-crud-complete/index.ts +3 -0
- package/src/lib/components/theme-generator/theme-generator.component.css +21 -0
- package/src/lib/components/theme-generator/theme-generator.component.html +146 -116
- package/src/lib/components/theme-generator/theme-generator.component.ts +44 -24
- package/src/lib/components/toggle-radio/shared/toggle-options.types.ts +8 -0
- package/src/lib/components/toggle-radio/shared/toggle-options.util.ts +44 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.css +135 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.html +52 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.component.ts +198 -0
- package/src/lib/components/toggle-radio/toggle-radio/toggle-radio.types.ts +1 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.css +108 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.html +37 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.component.ts +193 -0
- package/src/lib/components/toggle-radio/toggle-segment/segment-toggle.types.ts +1 -0
- package/src/lib/components/tooltip/tooltip.directive.ts +12 -9
- package/src/lib/components/tooltip/tooltip.service.ts +331 -133
- package/src/lib/components/tooltip/tooltip.types.ts +9 -0
- package/src/lib/components/viewer/viewer-image/image-viewer.component.css +90 -4
- package/src/lib/components/viewer/viewer-image/image-viewer.component.html +52 -103
- package/src/lib/components/viewer/viewer-image/image-viewer.component.ts +182 -177
- package/src/lib/components/viewer/viewer-image/image-viewer.types.ts +3 -0
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.css +177 -0
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.html +74 -24
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.ts +168 -15
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.types.ts +1 -0
- package/src/styles.css +2 -2
- package/lib/services/static/icons.service.d.ts +0 -65
- package/src/lib/components/colors-config/README.md +0 -38
- package/src/lib/components/form/form-sidebar/sidebar-form.component.scss +0 -0
- package/src/lib/components/form/form-validation/validation-form.component.scss +0 -0
- package/src/lib/components/menu/menu-options-table/options-table-menu.component.scss +0 -0
- package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +0 -12
- package/src/lib/components/sidebar/sidebar-static/static-sidebar.component.scss +0 -0
- package/src/lib/components/toggle-radio/toggle-radio.component.html +0 -51
- package/src/lib/components/toggle-radio/toggle-radio.component.ts +0 -222
- package/src/lib/components/viewer/viewer-pdf/pdf-viewer.component.scss +0 -0
- package/tailjng-0.1.6.tgz +0 -0
|
@@ -1,56 +1,53 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
-
|
|
27
|
+
standalone: true,
|
|
28
|
+
imports: [JButtonComponent, JCoachMarkDirective],
|
|
10
29
|
templateUrl: './options-coach-menu.component.html',
|
|
11
|
-
|
|
12
|
-
})
|
|
30
|
+
host: { class: 'inline-flex relative isolate' }})
|
|
13
31
|
export class JOptionsCoachMenuComponent {
|
|
32
|
+
readonly Icons = Icons;
|
|
14
33
|
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
20
|
-
@Input() getTooltip:
|
|
21
|
-
@Input() getDisabled:
|
|
22
|
-
@Input() getIsVisible:
|
|
23
|
-
@Input() mergeNgClasses:
|
|
24
|
-
@Input() isAditionalButtonLoading:
|
|
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
|
-
|
|
90
|
+
if (merged['text-destructive']) {
|
|
91
|
+
normalized['text-red-600'] = true;
|
|
92
|
+
normalized['dark:text-red-300'] = true;
|
|
93
|
+
}
|
|
27
94
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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.
|
|
122
|
+
this.closeCoachOverlay();
|
|
36
123
|
}
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
[
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
6
|
+
[attr.aria-pressed]="themeService.themeSignal() === 'dark'"
|
|
7
|
+
[attr.aria-label]="resolvedAriaLabel"
|
|
8
|
+
[ngClass]="computedClasses"
|
|
9
|
+
[class]="classes"
|
|
10
10
|
>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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: [
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@Input()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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]="
|
|
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 {
|
|
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: [
|
|
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:
|
|
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(
|
|
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:
|
|
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
|
+
}
|