@highstacklabs2026/ui 1.1.1 → 1.2.0
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/fesm2022/highstacklabs2026-ui.mjs +2180 -19
- package/fesm2022/highstacklabs2026-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles.css +1520 -33
- package/types/highstacklabs2026-ui.d.ts +746 -5
|
@@ -1,5 +1,390 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input,
|
|
2
|
+
import { input, booleanAttribute, signal, Component, inject, computed, output, contentChildren, effect, model, forwardRef, ElementRef, HostListener, Directive, numberAttribute, TemplateRef, viewChild, afterNextRender, ApplicationRef, EnvironmentInjector, createComponent, Injectable, Renderer2, APP_INITIALIZER } from '@angular/core';
|
|
3
|
+
import { RouterLink } from '@angular/router';
|
|
4
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
|
+
import { NgTemplateOutlet, DOCUMENT } from '@angular/common';
|
|
6
|
+
|
|
7
|
+
let nextId$6 = 0;
|
|
8
|
+
/**
|
|
9
|
+
* Acordeón compositional. `multiple` permite varios paneles abiertos; por
|
|
10
|
+
* defecto solo uno (al abrir otro se cierra el anterior).
|
|
11
|
+
*/
|
|
12
|
+
class AccordionComponent {
|
|
13
|
+
multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
14
|
+
/** Ids de los items abiertos. */
|
|
15
|
+
openIds = signal(new Set(), /* @ts-ignore */
|
|
16
|
+
...(ngDevMode ? [{ debugName: "openIds" }] : /* istanbul ignore next */ []));
|
|
17
|
+
isOpen(id) {
|
|
18
|
+
return this.openIds().has(id);
|
|
19
|
+
}
|
|
20
|
+
toggle(id) {
|
|
21
|
+
this.openIds.update((set) => {
|
|
22
|
+
const next = new Set(this.multiple() ? set : []);
|
|
23
|
+
if (set.has(id))
|
|
24
|
+
next.delete(id);
|
|
25
|
+
else
|
|
26
|
+
next.add(id);
|
|
27
|
+
return next;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
31
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: AccordionComponent, isStandalone: true, selector: "ui-accordion", inputs: { multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block divide-y divide-[var(--color-border)] border-y border-[var(--color-border)]" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
32
|
+
}
|
|
33
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AccordionComponent, decorators: [{
|
|
34
|
+
type: Component,
|
|
35
|
+
args: [{
|
|
36
|
+
selector: 'ui-accordion',
|
|
37
|
+
template: `<ng-content />`,
|
|
38
|
+
host: { class: 'block divide-y divide-[var(--color-border)] border-y border-[var(--color-border)]' },
|
|
39
|
+
}]
|
|
40
|
+
}], propDecorators: { multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }] } });
|
|
41
|
+
/** Un panel del acordeón. */
|
|
42
|
+
class AccordionItemComponent {
|
|
43
|
+
title = input('', /* @ts-ignore */
|
|
44
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
45
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
46
|
+
accordion = inject(AccordionComponent);
|
|
47
|
+
id = nextId$6++;
|
|
48
|
+
contentId = `ui-accordion-panel-${this.id}`;
|
|
49
|
+
open = computed(() => this.accordion.isOpen(this.id), /* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
51
|
+
toggle() {
|
|
52
|
+
if (!this.disabled())
|
|
53
|
+
this.accordion.toggle(this.id);
|
|
54
|
+
}
|
|
55
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AccordionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
56
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: AccordionItemComponent, isStandalone: true, selector: "ui-accordion-item", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<h3>\n <button\n type=\"button\"\n (click)=\"toggle()\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"contentId\"\n class=\"flex w-full items-center justify-between gap-3 py-4 text-left text-sm font-medium text-[var(--color-foreground)] transition-colors hover:text-[var(--color-foreground)]/80 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring)] rounded-sm\"\n >\n <span>{{ title() }}</span>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"shrink-0 text-[var(--color-muted-foreground)] transition-transform duration-200\"\n [class.rotate-180]=\"open()\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n</h3>\n\n<div\n [id]=\"contentId\"\n role=\"region\"\n class=\"grid transition-all duration-200\"\n [class.grid-rows-[1fr]]=\"open()\"\n [class.grid-rows-[0fr]]=\"!open()\"\n [class.opacity-100]=\"open()\"\n [class.opacity-0]=\"!open()\"\n>\n <div class=\"overflow-hidden\">\n <div class=\"pb-4 text-sm text-[var(--color-muted-foreground)]\">\n <ng-content />\n </div>\n </div>\n</div>\n" });
|
|
57
|
+
}
|
|
58
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AccordionItemComponent, decorators: [{
|
|
59
|
+
type: Component,
|
|
60
|
+
args: [{ selector: 'ui-accordion-item', template: "<h3>\n <button\n type=\"button\"\n (click)=\"toggle()\"\n [disabled]=\"disabled()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"contentId\"\n class=\"flex w-full items-center justify-between gap-3 py-4 text-left text-sm font-medium text-[var(--color-foreground)] transition-colors hover:text-[var(--color-foreground)]/80 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring)] rounded-sm\"\n >\n <span>{{ title() }}</span>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"shrink-0 text-[var(--color-muted-foreground)] transition-transform duration-200\"\n [class.rotate-180]=\"open()\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n</h3>\n\n<div\n [id]=\"contentId\"\n role=\"region\"\n class=\"grid transition-all duration-200\"\n [class.grid-rows-[1fr]]=\"open()\"\n [class.grid-rows-[0fr]]=\"!open()\"\n [class.opacity-100]=\"open()\"\n [class.opacity-0]=\"!open()\"\n>\n <div class=\"overflow-hidden\">\n <div class=\"pb-4 text-sm text-[var(--color-muted-foreground)]\">\n <ng-content />\n </div>\n </div>\n</div>\n" }]
|
|
61
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Mensaje contextual con color e ícono según el tipo. El cuerpo se proyecta;
|
|
65
|
+
* `title` opcional en negrita. Cerrable con el output (close).
|
|
66
|
+
*/
|
|
67
|
+
class AlertComponent {
|
|
68
|
+
type = input('info', /* @ts-ignore */
|
|
69
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
70
|
+
variant = input('soft', /* @ts-ignore */
|
|
71
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
72
|
+
title = input('', /* @ts-ignore */
|
|
73
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
74
|
+
closable = input(false, { ...(ngDevMode ? { debugName: "closable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
75
|
+
close = output();
|
|
76
|
+
hostClasses = computed(() => {
|
|
77
|
+
const base = 'flex items-start gap-3 rounded-[var(--radius)] px-4 py-3 text-sm';
|
|
78
|
+
const softMap = {
|
|
79
|
+
info: 'bg-[var(--color-primary)]/8 text-[var(--color-foreground)] border border-[var(--color-primary)]/20',
|
|
80
|
+
success: 'bg-emerald-500/10 text-[var(--color-foreground)] border border-emerald-500/25',
|
|
81
|
+
warning: 'bg-amber-500/10 text-[var(--color-foreground)] border border-amber-500/30',
|
|
82
|
+
error: 'bg-[var(--color-destructive)]/10 text-[var(--color-foreground)] border border-[var(--color-destructive)]/25',
|
|
83
|
+
};
|
|
84
|
+
const solidMap = {
|
|
85
|
+
info: 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]',
|
|
86
|
+
success: 'bg-emerald-500 text-white',
|
|
87
|
+
warning: 'bg-amber-500 text-white',
|
|
88
|
+
error: 'bg-[var(--color-destructive)] text-[var(--color-destructive-foreground)]',
|
|
89
|
+
};
|
|
90
|
+
const tone = this.variant() === 'solid' ? solidMap[this.type()] : softMap[this.type()];
|
|
91
|
+
return [base, tone].join(' ');
|
|
92
|
+
}, /* @ts-ignore */
|
|
93
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
94
|
+
/** Color del ícono (en soft toma el color del tipo; en solid hereda currentColor). */
|
|
95
|
+
iconColor = computed(() => {
|
|
96
|
+
if (this.variant() === 'solid')
|
|
97
|
+
return '';
|
|
98
|
+
const map = {
|
|
99
|
+
info: 'text-[var(--color-primary)]',
|
|
100
|
+
success: 'text-emerald-600',
|
|
101
|
+
warning: 'text-amber-600',
|
|
102
|
+
error: 'text-[var(--color-destructive)]',
|
|
103
|
+
};
|
|
104
|
+
return map[this.type()];
|
|
105
|
+
}, /* @ts-ignore */
|
|
106
|
+
...(ngDevMode ? [{ debugName: "iconColor" }] : /* istanbul ignore next */ []));
|
|
107
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
108
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: AlertComponent, isStandalone: true, selector: "ui-alert", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close" }, host: { attributes: { "role": "alert" }, properties: { "class": "hostClasses()" } }, ngImport: i0, template: "<span class=\"shrink-0 mt-0.5\" [class]=\"iconColor()\">\n @switch (type()) {\n @case ('success') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\" /><polyline points=\"22 4 12 14.01 9 11.01\" /></svg>\n }\n @case ('warning') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" /></svg>\n }\n @case ('error') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" /></svg>\n }\n @default {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" /></svg>\n }\n }\n</span>\n\n<div class=\"flex-1 min-w-0\">\n @if (title()) {\n <p class=\"font-semibold leading-tight\">{{ title() }}</p>\n }\n <div [class.mt-1]=\"title()\" [class.text-[var(--color-muted-foreground)]]=\"variant() === 'soft'\">\n <ng-content />\n </div>\n</div>\n\n@if (closable()) {\n <button\n type=\"button\"\n (click)=\"close.emit()\"\n aria-label=\"Cerrar\"\n class=\"shrink-0 -mr-1 -mt-0.5 flex items-center justify-center size-6 rounded-md opacity-70 hover:opacity-100 transition-opacity cursor-pointer\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n </button>\n}\n" });
|
|
109
|
+
}
|
|
110
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AlertComponent, decorators: [{
|
|
111
|
+
type: Component,
|
|
112
|
+
args: [{ selector: 'ui-alert', host: { role: 'alert', '[class]': 'hostClasses()' }, template: "<span class=\"shrink-0 mt-0.5\" [class]=\"iconColor()\">\n @switch (type()) {\n @case ('success') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\" /><polyline points=\"22 4 12 14.01 9 11.01\" /></svg>\n }\n @case ('warning') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" /></svg>\n }\n @case ('error') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" /></svg>\n }\n @default {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" /></svg>\n }\n }\n</span>\n\n<div class=\"flex-1 min-w-0\">\n @if (title()) {\n <p class=\"font-semibold leading-tight\">{{ title() }}</p>\n }\n <div [class.mt-1]=\"title()\" [class.text-[var(--color-muted-foreground)]]=\"variant() === 'soft'\">\n <ng-content />\n </div>\n</div>\n\n@if (closable()) {\n <button\n type=\"button\"\n (click)=\"close.emit()\"\n aria-label=\"Cerrar\"\n class=\"shrink-0 -mr-1 -mt-0.5 flex items-center justify-center size-6 rounded-md opacity-70 hover:opacity-100 transition-opacity cursor-pointer\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n </button>\n}\n" }]
|
|
113
|
+
}], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], close: [{ type: i0.Output, args: ["close"] }] } });
|
|
114
|
+
|
|
115
|
+
const SIZE_BOX = {
|
|
116
|
+
xs: 'size-6 text-[10px]',
|
|
117
|
+
sm: 'size-8 text-xs',
|
|
118
|
+
md: 'size-10 text-sm',
|
|
119
|
+
lg: 'size-12 text-base',
|
|
120
|
+
xl: 'size-16 text-lg',
|
|
121
|
+
};
|
|
122
|
+
const STATUS_DOT = {
|
|
123
|
+
xs: 'size-1.5',
|
|
124
|
+
sm: 'size-2',
|
|
125
|
+
md: 'size-2.5',
|
|
126
|
+
lg: 'size-3',
|
|
127
|
+
xl: 'size-3.5',
|
|
128
|
+
};
|
|
129
|
+
const STATUS_COLOR = {
|
|
130
|
+
online: 'bg-emerald-500',
|
|
131
|
+
offline: 'bg-zinc-400',
|
|
132
|
+
away: 'bg-amber-500',
|
|
133
|
+
busy: 'bg-[var(--color-destructive)]',
|
|
134
|
+
};
|
|
135
|
+
/** Avatar: imagen con fallback a iniciales, tamaños, forma e indicador de estado. */
|
|
136
|
+
class AvatarComponent {
|
|
137
|
+
src = input('', /* @ts-ignore */
|
|
138
|
+
...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
139
|
+
name = input('', /* @ts-ignore */
|
|
140
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
141
|
+
alt = input('', /* @ts-ignore */
|
|
142
|
+
...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
|
|
143
|
+
size = input('md', /* @ts-ignore */
|
|
144
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
145
|
+
shape = input('circle', /* @ts-ignore */
|
|
146
|
+
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
147
|
+
status = input(null, /* @ts-ignore */
|
|
148
|
+
...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
149
|
+
/** Lo controla ui-avatar-group para ocultar los que exceden `max`. */
|
|
150
|
+
hidden = signal(false, /* @ts-ignore */
|
|
151
|
+
...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
|
|
152
|
+
imgError = signal(false, /* @ts-ignore */
|
|
153
|
+
...(ngDevMode ? [{ debugName: "imgError" }] : /* istanbul ignore next */ []));
|
|
154
|
+
showImage = computed(() => !!this.src() && !this.imgError(), /* @ts-ignore */
|
|
155
|
+
...(ngDevMode ? [{ debugName: "showImage" }] : /* istanbul ignore next */ []));
|
|
156
|
+
initials = computed(() => {
|
|
157
|
+
const n = this.name().trim();
|
|
158
|
+
if (!n)
|
|
159
|
+
return '';
|
|
160
|
+
const parts = n.split(/\s+/);
|
|
161
|
+
const first = parts[0]?.[0] ?? '';
|
|
162
|
+
const last = parts.length > 1 ? (parts[parts.length - 1][0] ?? '') : '';
|
|
163
|
+
return (first + last).toUpperCase();
|
|
164
|
+
}, /* @ts-ignore */
|
|
165
|
+
...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
|
|
166
|
+
statusDotClasses = computed(() => {
|
|
167
|
+
const s = this.status();
|
|
168
|
+
if (!s)
|
|
169
|
+
return '';
|
|
170
|
+
return [
|
|
171
|
+
'absolute bottom-0 right-0 rounded-full ring-2 ring-[var(--color-background)]',
|
|
172
|
+
STATUS_DOT[this.size()],
|
|
173
|
+
STATUS_COLOR[s],
|
|
174
|
+
].join(' ');
|
|
175
|
+
}, /* @ts-ignore */
|
|
176
|
+
...(ngDevMode ? [{ debugName: "statusDotClasses" }] : /* istanbul ignore next */ []));
|
|
177
|
+
innerClasses = computed(() => {
|
|
178
|
+
const base = 'inline-flex items-center justify-center size-full overflow-hidden select-none font-medium bg-[var(--color-muted)] text-[var(--color-muted-foreground)]';
|
|
179
|
+
const shape = this.shape() === 'square' ? 'rounded-[var(--radius)]' : 'rounded-full';
|
|
180
|
+
return [base, shape].join(' ');
|
|
181
|
+
}, /* @ts-ignore */
|
|
182
|
+
...(ngDevMode ? [{ debugName: "innerClasses" }] : /* istanbul ignore next */ []));
|
|
183
|
+
hostClasses = computed(() => {
|
|
184
|
+
const base = 'relative inline-flex shrink-0';
|
|
185
|
+
const shape = this.shape() === 'square' ? 'rounded-[var(--radius)]' : 'rounded-full';
|
|
186
|
+
const hidden = this.hidden() ? 'hidden' : '';
|
|
187
|
+
// shape en el host también, para que el ring del apilado siga la forma
|
|
188
|
+
return [base, SIZE_BOX[this.size()], shape, hidden].join(' ');
|
|
189
|
+
}, /* @ts-ignore */
|
|
190
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
191
|
+
onImgError() {
|
|
192
|
+
this.imgError.set(true);
|
|
193
|
+
}
|
|
194
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
195
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: AvatarComponent, isStandalone: true, selector: "ui-avatar", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: "<span [class]=\"innerClasses()\">\n @if (showImage()) {\n <img\n [src]=\"src()\"\n [alt]=\"alt() || name()\"\n (error)=\"onImgError()\"\n class=\"size-full object-cover\"\n />\n } @else if (initials()) {\n <span>{{ initials() }}</span>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"size-3/5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\" />\n <circle cx=\"12\" cy=\"7\" r=\"4\" />\n </svg>\n }\n</span>\n\n@if (status()) {\n <span [class]=\"statusDotClasses()\"></span>\n}\n" });
|
|
196
|
+
}
|
|
197
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AvatarComponent, decorators: [{
|
|
198
|
+
type: Component,
|
|
199
|
+
args: [{ selector: 'ui-avatar', host: { '[class]': 'hostClasses()' }, template: "<span [class]=\"innerClasses()\">\n @if (showImage()) {\n <img\n [src]=\"src()\"\n [alt]=\"alt() || name()\"\n (error)=\"onImgError()\"\n class=\"size-full object-cover\"\n />\n } @else if (initials()) {\n <span>{{ initials() }}</span>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"size-3/5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\" />\n <circle cx=\"12\" cy=\"7\" r=\"4\" />\n </svg>\n }\n</span>\n\n@if (status()) {\n <span [class]=\"statusDotClasses()\"></span>\n}\n" }]
|
|
200
|
+
}], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }] } });
|
|
201
|
+
/** Grupo de avatares apilados, con "+N" cuando se supera `max`. */
|
|
202
|
+
class AvatarGroupComponent {
|
|
203
|
+
max = input(null, /* @ts-ignore */
|
|
204
|
+
...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
205
|
+
size = input('md', /* @ts-ignore */
|
|
206
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
207
|
+
avatars = contentChildren(AvatarComponent, /* @ts-ignore */
|
|
208
|
+
...(ngDevMode ? [{ debugName: "avatars" }] : /* istanbul ignore next */ []));
|
|
209
|
+
overflow = computed(() => {
|
|
210
|
+
const m = this.max();
|
|
211
|
+
const n = this.avatars().length;
|
|
212
|
+
return m != null && n > m ? n - m : 0;
|
|
213
|
+
}, /* @ts-ignore */
|
|
214
|
+
...(ngDevMode ? [{ debugName: "overflow" }] : /* istanbul ignore next */ []));
|
|
215
|
+
moreClasses = computed(() => [
|
|
216
|
+
'relative inline-flex items-center justify-center shrink-0 rounded-full font-medium -ml-2',
|
|
217
|
+
'ring-2 ring-[var(--color-background)] bg-[var(--color-muted)] text-[var(--color-muted-foreground)]',
|
|
218
|
+
SIZE_BOX[this.size()],
|
|
219
|
+
].join(' '), /* @ts-ignore */
|
|
220
|
+
...(ngDevMode ? [{ debugName: "moreClasses" }] : /* istanbul ignore next */ []));
|
|
221
|
+
constructor() {
|
|
222
|
+
effect(() => {
|
|
223
|
+
const m = this.max();
|
|
224
|
+
this.avatars().forEach((a, i) => a.hidden.set(m != null && i >= m));
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AvatarGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
228
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: AvatarGroupComponent, isStandalone: true, selector: "ui-avatar-group", inputs: { max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex items-center [&>ui-avatar]:ring-2 [&>ui-avatar]:ring-[var(--color-background)] [&>ui-avatar:not(:first-child)]:-ml-2" }, queries: [{ propertyName: "avatars", predicate: AvatarComponent, isSignal: true }], ngImport: i0, template: `
|
|
229
|
+
<ng-content />
|
|
230
|
+
@if (overflow() > 0) {
|
|
231
|
+
<span
|
|
232
|
+
[class]="moreClasses()"
|
|
233
|
+
aria-hidden="true"
|
|
234
|
+
>+{{ overflow() }}</span>
|
|
235
|
+
}
|
|
236
|
+
`, isInline: true });
|
|
237
|
+
}
|
|
238
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: AvatarGroupComponent, decorators: [{
|
|
239
|
+
type: Component,
|
|
240
|
+
args: [{
|
|
241
|
+
selector: 'ui-avatar-group',
|
|
242
|
+
template: `
|
|
243
|
+
<ng-content />
|
|
244
|
+
@if (overflow() > 0) {
|
|
245
|
+
<span
|
|
246
|
+
[class]="moreClasses()"
|
|
247
|
+
aria-hidden="true"
|
|
248
|
+
>+{{ overflow() }}</span>
|
|
249
|
+
}
|
|
250
|
+
`,
|
|
251
|
+
host: {
|
|
252
|
+
class: 'flex items-center [&>ui-avatar]:ring-2 [&>ui-avatar]:ring-[var(--color-background)] [&>ui-avatar:not(:first-child)]:-ml-2',
|
|
253
|
+
},
|
|
254
|
+
}]
|
|
255
|
+
}], ctorParameters: () => [], propDecorators: { max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], avatars: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => AvatarComponent), { isSignal: true }] }] } });
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Etiqueta compacta para estados, tags y contadores.
|
|
259
|
+
* Dos ejes: variant (solid/soft/outline) × color, más dot, ícono y removible.
|
|
260
|
+
*/
|
|
261
|
+
class BadgeComponent {
|
|
262
|
+
variant = input('soft', /* @ts-ignore */
|
|
263
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
264
|
+
color = input('primary', /* @ts-ignore */
|
|
265
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
266
|
+
size = input('md', /* @ts-ignore */
|
|
267
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
268
|
+
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
269
|
+
removable = input(false, { ...(ngDevMode ? { debugName: "removable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
270
|
+
/** Se emite al pulsar la × (solo si removable). */
|
|
271
|
+
remove = output();
|
|
272
|
+
hostClasses = computed(() => {
|
|
273
|
+
const base = 'inline-flex items-center gap-1.5 rounded-full font-medium whitespace-nowrap align-middle';
|
|
274
|
+
const sizeMap = {
|
|
275
|
+
sm: 'h-5 px-2 text-[11px]',
|
|
276
|
+
md: 'h-6 px-2.5 text-xs',
|
|
277
|
+
};
|
|
278
|
+
// color × variant. solid: fondo lleno · soft: tinte suave · outline: solo borde ·
|
|
279
|
+
// glass: translúcido con desenfoque (luce sobre imágenes/fondos con color).
|
|
280
|
+
const styleMap = {
|
|
281
|
+
primary: {
|
|
282
|
+
solid: 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)]',
|
|
283
|
+
soft: 'bg-[var(--color-primary)]/12 text-[var(--color-primary)]',
|
|
284
|
+
outline: 'border border-[var(--color-primary)]/40 text-[var(--color-primary)]',
|
|
285
|
+
glass: 'backdrop-blur-md bg-[var(--color-primary)]/20 border border-[var(--color-primary)]/30 text-[var(--color-primary)] shadow-sm',
|
|
286
|
+
},
|
|
287
|
+
secondary: {
|
|
288
|
+
solid: 'bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)]',
|
|
289
|
+
soft: 'bg-[var(--color-muted)] text-[var(--color-muted-foreground)]',
|
|
290
|
+
outline: 'border border-[var(--color-border)] text-[var(--color-muted-foreground)]',
|
|
291
|
+
glass: 'backdrop-blur-md bg-white/15 border border-white/25 text-white shadow-sm',
|
|
292
|
+
},
|
|
293
|
+
success: {
|
|
294
|
+
solid: 'bg-emerald-500 text-white',
|
|
295
|
+
soft: 'bg-emerald-500/12 text-emerald-600',
|
|
296
|
+
outline: 'border border-emerald-500/40 text-emerald-600',
|
|
297
|
+
glass: 'backdrop-blur-md bg-emerald-400/25 border border-emerald-200/40 text-emerald-50 shadow-sm',
|
|
298
|
+
},
|
|
299
|
+
warning: {
|
|
300
|
+
solid: 'bg-amber-500 text-white',
|
|
301
|
+
soft: 'bg-amber-500/15 text-amber-600',
|
|
302
|
+
outline: 'border border-amber-500/40 text-amber-600',
|
|
303
|
+
glass: 'backdrop-blur-md bg-amber-400/25 border border-amber-200/40 text-amber-50 shadow-sm',
|
|
304
|
+
},
|
|
305
|
+
destructive: {
|
|
306
|
+
solid: 'bg-[var(--color-destructive)] text-[var(--color-destructive-foreground)]',
|
|
307
|
+
soft: 'bg-[var(--color-destructive)]/12 text-[var(--color-destructive)]',
|
|
308
|
+
outline: 'border border-[var(--color-destructive)]/40 text-[var(--color-destructive)]',
|
|
309
|
+
glass: 'backdrop-blur-md bg-[var(--color-destructive)]/25 border border-white/30 text-white shadow-sm',
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
return [base, sizeMap[this.size()], styleMap[this.color()][this.variant()]].join(' ');
|
|
313
|
+
}, /* @ts-ignore */
|
|
314
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
315
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
316
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BadgeComponent, isStandalone: true, selector: "ui-badge", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { remove: "remove" }, host: { classAttribute: "inline-flex" }, ngImport: i0, template: "<span [class]=\"hostClasses()\">\n @if (dot()) {\n <span class=\"size-1.5 rounded-full bg-current\"></span>\n }\n\n <ng-content select=\"[slot=icon]\" />\n\n <ng-content />\n\n @if (removable()) {\n <button\n type=\"button\"\n (click)=\"remove.emit()\"\n aria-label=\"Quitar\"\n class=\"-mr-0.5 flex items-center justify-center rounded-full opacity-70 hover:opacity-100 transition-opacity cursor-pointer\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n </button>\n }\n</span>\n" });
|
|
317
|
+
}
|
|
318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BadgeComponent, decorators: [{
|
|
319
|
+
type: Component,
|
|
320
|
+
args: [{ selector: 'ui-badge', host: { class: 'inline-flex' }, template: "<span [class]=\"hostClasses()\">\n @if (dot()) {\n <span class=\"size-1.5 rounded-full bg-current\"></span>\n }\n\n <ng-content select=\"[slot=icon]\" />\n\n <ng-content />\n\n @if (removable()) {\n <button\n type=\"button\"\n (click)=\"remove.emit()\"\n aria-label=\"Quitar\"\n class=\"-mr-0.5 flex items-center justify-center rounded-full opacity-70 hover:opacity-100 transition-opacity cursor-pointer\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n </button>\n }\n</span>\n" }]
|
|
321
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], remove: [{ type: i0.Output, args: ["remove"] }] } });
|
|
322
|
+
|
|
323
|
+
/** Ruta de navegación jerárquica. Inserta un separador entre ítems. */
|
|
324
|
+
class BreadcrumbComponent {
|
|
325
|
+
items = contentChildren(BreadcrumbItemComponent, /* @ts-ignore */
|
|
326
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
327
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
328
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.2", type: BreadcrumbComponent, isStandalone: true, selector: "ui-breadcrumb", host: { attributes: { "aria-label": "Breadcrumb" } }, queries: [{ propertyName: "items", predicate: BreadcrumbItemComponent, isSignal: true }], ngImport: i0, template: "<ol class=\"flex flex-wrap items-center text-sm\">\n <ng-content />\n</ol>\n" });
|
|
329
|
+
}
|
|
330
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BreadcrumbComponent, decorators: [{
|
|
331
|
+
type: Component,
|
|
332
|
+
args: [{ selector: 'ui-breadcrumb', host: { 'aria-label': 'Breadcrumb' }, template: "<ol class=\"flex flex-wrap items-center text-sm\">\n <ng-content />\n</ol>\n" }]
|
|
333
|
+
}], propDecorators: { items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => BreadcrumbItemComponent), { isSignal: true }] }] } });
|
|
334
|
+
/** Un ítem del breadcrumb. Con `link` es navegable; sin él, es el actual. */
|
|
335
|
+
class BreadcrumbItemComponent {
|
|
336
|
+
/** Destino de routerLink; si se omite, el ítem es el actual (no navegable). */
|
|
337
|
+
link = input(null, /* @ts-ignore */
|
|
338
|
+
...(ngDevMode ? [{ debugName: "link" }] : /* istanbul ignore next */ []));
|
|
339
|
+
parent = inject(BreadcrumbComponent);
|
|
340
|
+
/** Es el último ítem (no muestra separador después). */
|
|
341
|
+
isLast = computed(() => {
|
|
342
|
+
const list = this.parent.items();
|
|
343
|
+
return list.length > 0 && list[list.length - 1] === this;
|
|
344
|
+
}, /* @ts-ignore */
|
|
345
|
+
...(ngDevMode ? [{ debugName: "isLast" }] : /* istanbul ignore next */ []));
|
|
346
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BreadcrumbItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
347
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BreadcrumbItemComponent, isStandalone: true, selector: "ui-breadcrumb-item", inputs: { link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "inline-flex items-center" }, ngImport: i0, template: `
|
|
348
|
+
<a
|
|
349
|
+
[routerLink]="link() || null"
|
|
350
|
+
[attr.aria-current]="link() ? null : 'page'"
|
|
351
|
+
[class]="
|
|
352
|
+
link()
|
|
353
|
+
? 'text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] transition-colors cursor-pointer'
|
|
354
|
+
: 'font-medium text-[var(--color-foreground)]'
|
|
355
|
+
"
|
|
356
|
+
>
|
|
357
|
+
<ng-content />
|
|
358
|
+
</a>
|
|
359
|
+
@if (!isLast()) {
|
|
360
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mx-1.5 text-[var(--color-muted-foreground)]/60"><path d="m9 18 6-6-6-6" /></svg>
|
|
361
|
+
}
|
|
362
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }] });
|
|
363
|
+
}
|
|
364
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BreadcrumbItemComponent, decorators: [{
|
|
365
|
+
type: Component,
|
|
366
|
+
args: [{
|
|
367
|
+
selector: 'ui-breadcrumb-item',
|
|
368
|
+
template: `
|
|
369
|
+
<a
|
|
370
|
+
[routerLink]="link() || null"
|
|
371
|
+
[attr.aria-current]="link() ? null : 'page'"
|
|
372
|
+
[class]="
|
|
373
|
+
link()
|
|
374
|
+
? 'text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] transition-colors cursor-pointer'
|
|
375
|
+
: 'font-medium text-[var(--color-foreground)]'
|
|
376
|
+
"
|
|
377
|
+
>
|
|
378
|
+
<ng-content />
|
|
379
|
+
</a>
|
|
380
|
+
@if (!isLast()) {
|
|
381
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mx-1.5 text-[var(--color-muted-foreground)]/60"><path d="m9 18 6-6-6-6" /></svg>
|
|
382
|
+
}
|
|
383
|
+
`,
|
|
384
|
+
imports: [RouterLink],
|
|
385
|
+
host: { class: 'inline-flex items-center' },
|
|
386
|
+
}]
|
|
387
|
+
}], propDecorators: { link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }] } });
|
|
3
388
|
|
|
4
389
|
class ButtonComponent {
|
|
5
390
|
variant = input('default', /* @ts-ignore */
|
|
@@ -12,42 +397,1818 @@ class ButtonComponent {
|
|
|
12
397
|
...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
13
398
|
type = input('button', /* @ts-ignore */
|
|
14
399
|
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
400
|
+
pill = input(false, /* @ts-ignore */
|
|
401
|
+
...(ngDevMode ? [{ debugName: "pill" }] : /* istanbul ignore next */ []));
|
|
402
|
+
full = input(false, /* @ts-ignore */
|
|
403
|
+
...(ngDevMode ? [{ debugName: "full" }] : /* istanbul ignore next */ []));
|
|
15
404
|
isDisabled = computed(() => this.disabled() || this.loading(), /* @ts-ignore */
|
|
16
405
|
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
17
406
|
hostClasses = computed(() => {
|
|
18
407
|
const base = [
|
|
19
|
-
'inline-flex items-center justify-center gap-2 whitespace-nowrap
|
|
20
|
-
'
|
|
408
|
+
'inline-flex items-center justify-center gap-2 whitespace-nowrap',
|
|
409
|
+
this.pill() ? 'rounded-full' : '',
|
|
410
|
+
this.full() ? 'w-full' : '',
|
|
411
|
+
'font-medium transition-all duration-150',
|
|
21
412
|
'outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--color-ring)]/50',
|
|
22
|
-
'disabled:pointer-events-none disabled:opacity-50',
|
|
413
|
+
'disabled:pointer-events-none disabled:opacity-50 disabled:transform-none disabled:shadow-none',
|
|
23
414
|
'cursor-pointer',
|
|
24
|
-
].join(' ');
|
|
415
|
+
].filter(Boolean).join(' ');
|
|
25
416
|
const variantMap = {
|
|
26
|
-
default: 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)] shadow-
|
|
27
|
-
destructive: 'bg-[var(--color-destructive)] text-[var(--color-destructive-foreground)] shadow-
|
|
28
|
-
outline: 'border border-[var(--color-input)] bg-[var(--color-background)] shadow-
|
|
29
|
-
secondary: 'bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)] shadow-
|
|
30
|
-
ghost: 'hover:bg-[var(--color-accent)] hover:text-[var(--color-accent-foreground)]',
|
|
417
|
+
default: 'bg-[var(--color-primary)] text-[var(--color-primary-foreground)] shadow-sm hover:bg-[var(--color-primary)]/95 active:translate-y-[1px] active:scale-[0.98] active:shadow-sm',
|
|
418
|
+
destructive: 'bg-[var(--color-destructive)] text-[var(--color-destructive-foreground)] shadow-sm hover:bg-[var(--color-destructive)]/95 active:translate-y-[1px] active:scale-[0.98] active:shadow-sm focus-visible:ring-[var(--color-destructive)]/20',
|
|
419
|
+
outline: 'border border-[var(--color-input)] bg-[var(--color-background)] shadow-sm hover:bg-[var(--color-accent)] hover:text-[var(--color-accent-foreground)] active:translate-y-[1px] active:scale-[0.98] active:shadow-sm',
|
|
420
|
+
secondary: 'bg-[var(--color-secondary)] text-[var(--color-secondary-foreground)] shadow-sm hover:bg-[var(--color-secondary)]/90 active:translate-y-[1px] active:scale-[0.98] active:shadow-sm',
|
|
421
|
+
ghost: 'hover:bg-[var(--color-accent)] hover:text-[var(--color-accent-foreground)] active:scale-[0.98]',
|
|
31
422
|
link: 'text-[var(--color-primary)] underline-offset-4 hover:underline',
|
|
32
|
-
gradient: '
|
|
33
|
-
|
|
423
|
+
gradient: 'text-[var(--color-primary-foreground)] ' +
|
|
424
|
+
'bg-[linear-gradient(135deg,var(--color-primary)_0%,var(--color-primary-gradient)_50%,var(--color-primary)_100%)] ' +
|
|
425
|
+
'bg-[length:200%_100%] bg-left ' +
|
|
426
|
+
'shadow-[0_1px_2px_0_rgb(0_0_0/0.10),inset_0_1px_0_0_rgb(255_255_255/0.22)] ' +
|
|
427
|
+
'transition-[background-position,transform,box-shadow] duration-300 ease-out ' +
|
|
428
|
+
'hover:bg-right ' +
|
|
429
|
+
'active:translate-y-[1px] active:scale-[0.98] active:shadow-sm',
|
|
430
|
+
glass: 'backdrop-blur-2xl bg-white/10 border border-white/15 text-white/90 shadow-sm hover:bg-white/[0.18] active:translate-y-[1px] active:scale-[0.98] active:shadow-sm',
|
|
431
|
+
success: 'bg-emerald-500 text-white shadow-sm hover:bg-emerald-600 active:translate-y-[1px] active:scale-[0.98] active:shadow-sm focus-visible:ring-emerald-500/20',
|
|
432
|
+
warning: 'bg-amber-500 text-white shadow-sm hover:bg-amber-600 active:translate-y-[1px] active:scale-[0.98] active:shadow-sm focus-visible:ring-amber-500/20',
|
|
34
433
|
};
|
|
434
|
+
// Radio ligado al tamaño: menos alto → menos redondez (estilo shadcn).
|
|
435
|
+
const pill = this.pill();
|
|
35
436
|
const sizeMap = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
437
|
+
xs: `h-6 px-2 gap-1 text-xs ${pill ? '' : 'rounded-lg'}`,
|
|
438
|
+
sm: `h-8 px-3 gap-1.5 text-xs ${pill ? '' : 'rounded-lg'}`,
|
|
439
|
+
md: `h-9 px-4 gap-2 text-sm ${pill ? '' : 'rounded-[10px]'}`,
|
|
440
|
+
lg: `h-10 px-5 gap-2 text-sm ${pill ? '' : 'rounded-[10px]'}`,
|
|
441
|
+
icon: `size-9 ${pill ? '' : 'rounded-[10px]'}`,
|
|
40
442
|
};
|
|
41
443
|
return [base, variantMap[this.variant()], sizeMap[this.size()]].join(' ');
|
|
42
444
|
}, /* @ts-ignore */
|
|
43
445
|
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
44
446
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
45
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ButtonComponent, isStandalone: true, selector: "ui-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<button\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [class]=\"hostClasses()\"\n>\n @if (loading()) {\n <svg\n class=\"h-4 w-4 animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n ></path>\n </svg>\n }\n <ng-content />\n</button>\n" });
|
|
447
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ButtonComponent, isStandalone: true, selector: "ui-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, pill: { classPropertyName: "pill", publicName: "pill", isSignal: true, isRequired: false, transformFunction: null }, full: { classPropertyName: "full", publicName: "full", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<button\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [class]=\"hostClasses()\"\n>\n @if (loading()) {\n <svg\n class=\"h-4 w-4 animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n ></path>\n </svg>\n }\n <ng-content />\n</button>\n" });
|
|
46
448
|
}
|
|
47
449
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
48
450
|
type: Component,
|
|
49
451
|
args: [{ selector: 'ui-button', template: "<button\n [type]=\"type()\"\n [disabled]=\"isDisabled()\"\n [class]=\"hostClasses()\"\n>\n @if (loading()) {\n <svg\n class=\"h-4 w-4 animate-spin\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n ></path>\n </svg>\n }\n <ng-content />\n</button>\n" }]
|
|
50
|
-
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
|
|
452
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], pill: [{ type: i0.Input, args: [{ isSignal: true, alias: "pill", required: false }] }], full: [{ type: i0.Input, args: [{ isSignal: true, alias: "full", required: false }] }] } });
|
|
453
|
+
|
|
454
|
+
/** Contenedor principal. Proyecta header/content/footer (u contenido libre). */
|
|
455
|
+
class CardComponent {
|
|
456
|
+
variant = input('elevated', /* @ts-ignore */
|
|
457
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
458
|
+
hostClasses = computed(() => {
|
|
459
|
+
const base = 'flex flex-col gap-6 py-6 rounded-[var(--radius)] bg-[var(--color-background)] text-[var(--color-foreground)]';
|
|
460
|
+
const variantMap = {
|
|
461
|
+
elevated: 'border border-[var(--color-border)] shadow-sm',
|
|
462
|
+
outline: 'border border-[var(--color-border)]',
|
|
463
|
+
soft: 'bg-[var(--color-muted)]/40',
|
|
464
|
+
interactive: 'border border-[var(--color-border)] shadow-sm transition-all hover:shadow-md hover:border-[var(--color-ring)]/40 active:scale-[0.995] cursor-pointer',
|
|
465
|
+
};
|
|
466
|
+
return `${base} ${variantMap[this.variant()]}`;
|
|
467
|
+
}, /* @ts-ignore */
|
|
468
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
469
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
470
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: CardComponent, isStandalone: true, selector: "ui-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
471
|
+
}
|
|
472
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardComponent, decorators: [{
|
|
473
|
+
type: Component,
|
|
474
|
+
args: [{
|
|
475
|
+
selector: 'ui-card',
|
|
476
|
+
template: `<ng-content />`,
|
|
477
|
+
host: { '[class]': 'hostClasses()' },
|
|
478
|
+
}]
|
|
479
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }] } });
|
|
480
|
+
/** Cabecera: padding + columna con separación. */
|
|
481
|
+
class CardHeaderComponent {
|
|
482
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
483
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: CardHeaderComponent, isStandalone: true, selector: "ui-card-header", host: { classAttribute: "flex flex-col gap-1.5 px-6" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
484
|
+
}
|
|
485
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardHeaderComponent, decorators: [{
|
|
486
|
+
type: Component,
|
|
487
|
+
args: [{
|
|
488
|
+
selector: 'ui-card-header',
|
|
489
|
+
template: `<ng-content />`,
|
|
490
|
+
host: { class: 'flex flex-col gap-1.5 px-6' },
|
|
491
|
+
}]
|
|
492
|
+
}] });
|
|
493
|
+
/** Título de la tarjeta. */
|
|
494
|
+
class CardTitleComponent {
|
|
495
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardTitleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
496
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: CardTitleComponent, isStandalone: true, selector: "ui-card-title", host: { classAttribute: "block text-base font-semibold tracking-tight text-[var(--color-foreground)]" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
497
|
+
}
|
|
498
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardTitleComponent, decorators: [{
|
|
499
|
+
type: Component,
|
|
500
|
+
args: [{
|
|
501
|
+
selector: 'ui-card-title',
|
|
502
|
+
template: `<ng-content />`,
|
|
503
|
+
host: { class: 'block text-base font-semibold tracking-tight text-[var(--color-foreground)]' },
|
|
504
|
+
}]
|
|
505
|
+
}] });
|
|
506
|
+
/** Descripción / subtítulo. */
|
|
507
|
+
class CardDescriptionComponent {
|
|
508
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardDescriptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
509
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: CardDescriptionComponent, isStandalone: true, selector: "ui-card-description", host: { classAttribute: "block text-sm text-[var(--color-muted-foreground)]" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
510
|
+
}
|
|
511
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardDescriptionComponent, decorators: [{
|
|
512
|
+
type: Component,
|
|
513
|
+
args: [{
|
|
514
|
+
selector: 'ui-card-description',
|
|
515
|
+
template: `<ng-content />`,
|
|
516
|
+
host: { class: 'block text-sm text-[var(--color-muted-foreground)]' },
|
|
517
|
+
}]
|
|
518
|
+
}] });
|
|
519
|
+
/** Cuerpo de la tarjeta. */
|
|
520
|
+
class CardContentComponent {
|
|
521
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
522
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: CardContentComponent, isStandalone: true, selector: "ui-card-content", host: { classAttribute: "block px-6" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
523
|
+
}
|
|
524
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardContentComponent, decorators: [{
|
|
525
|
+
type: Component,
|
|
526
|
+
args: [{
|
|
527
|
+
selector: 'ui-card-content',
|
|
528
|
+
template: `<ng-content />`,
|
|
529
|
+
host: { class: 'block px-6' },
|
|
530
|
+
}]
|
|
531
|
+
}] });
|
|
532
|
+
/** Pie: fila para acciones (botones). */
|
|
533
|
+
class CardFooterComponent {
|
|
534
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
535
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: CardFooterComponent, isStandalone: true, selector: "ui-card-footer", host: { classAttribute: "flex items-center gap-3 px-6" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
536
|
+
}
|
|
537
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CardFooterComponent, decorators: [{
|
|
538
|
+
type: Component,
|
|
539
|
+
args: [{
|
|
540
|
+
selector: 'ui-card-footer',
|
|
541
|
+
template: `<ng-content />`,
|
|
542
|
+
host: { class: 'flex items-center gap-3 px-6' },
|
|
543
|
+
}]
|
|
544
|
+
}] });
|
|
545
|
+
|
|
546
|
+
let nextId$5 = 0;
|
|
547
|
+
/**
|
|
548
|
+
* Casilla de verificación. Funciona con Signal Forms (`[formField]`, contrato
|
|
549
|
+
* FormCheckboxControl), ControlValueAccessor (ngModel/Reactive) y two-way `[(checked)]`.
|
|
550
|
+
*/
|
|
551
|
+
class CheckboxComponent {
|
|
552
|
+
/** Fuente única de verdad (Signal Forms usa `checked` para checkboxes). */
|
|
553
|
+
checked = model(false, /* @ts-ignore */
|
|
554
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
555
|
+
label = input('', /* @ts-ignore */
|
|
556
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
557
|
+
description = input('', /* @ts-ignore */
|
|
558
|
+
...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
|
|
559
|
+
size = input('md', /* @ts-ignore */
|
|
560
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
561
|
+
id = input(`ui-checkbox-${nextId$5++}`, /* @ts-ignore */
|
|
562
|
+
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
563
|
+
name = input('', /* @ts-ignore */
|
|
564
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
565
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
566
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
567
|
+
indeterminate = input(false, { ...(ngDevMode ? { debugName: "indeterminate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
568
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
569
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
570
|
+
errors = input([], /* @ts-ignore */
|
|
571
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
572
|
+
cvaDisabled = signal(false, /* @ts-ignore */
|
|
573
|
+
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
574
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
575
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
576
|
+
errorMessage = computed(() => {
|
|
577
|
+
if (this.touched()) {
|
|
578
|
+
const first = this.errors()[0];
|
|
579
|
+
if (first)
|
|
580
|
+
return first.message ?? first.kind ?? 'Campo inválido';
|
|
581
|
+
}
|
|
582
|
+
return '';
|
|
583
|
+
}, /* @ts-ignore */
|
|
584
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
585
|
+
hasError = computed(() => !!this.errorMessage() || (this.invalid() && this.touched()), /* @ts-ignore */
|
|
586
|
+
...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
587
|
+
boxClasses = computed(() => {
|
|
588
|
+
const sizeMap = {
|
|
589
|
+
sm: 'size-4',
|
|
590
|
+
md: 'size-5',
|
|
591
|
+
};
|
|
592
|
+
const on = this.checked() || this.indeterminate();
|
|
593
|
+
const base = 'shrink-0 flex items-center justify-center rounded-[6px] border transition-all outline-none';
|
|
594
|
+
const state = on
|
|
595
|
+
? 'bg-[var(--color-primary)] border-[var(--color-primary)] text-[var(--color-primary-foreground)]'
|
|
596
|
+
: this.hasError()
|
|
597
|
+
? 'border-[var(--color-destructive)] bg-[var(--color-background)]'
|
|
598
|
+
: 'border-[var(--color-input)] bg-[var(--color-background)]';
|
|
599
|
+
const focus = 'peer-focus-visible:ring-[3px] peer-focus-visible:ring-[var(--color-ring)]/40';
|
|
600
|
+
const disabled = this.isDisabled() ? 'opacity-50' : '';
|
|
601
|
+
return [base, sizeMap[this.size()], state, focus, disabled].join(' ');
|
|
602
|
+
}, /* @ts-ignore */
|
|
603
|
+
...(ngDevMode ? [{ debugName: "boxClasses" }] : /* istanbul ignore next */ []));
|
|
604
|
+
// --- ControlValueAccessor ---
|
|
605
|
+
onChange = () => { };
|
|
606
|
+
onTouched = () => { };
|
|
607
|
+
writeValue(value) {
|
|
608
|
+
this.checked.set(!!value);
|
|
609
|
+
}
|
|
610
|
+
registerOnChange(fn) {
|
|
611
|
+
this.onChange = fn;
|
|
612
|
+
}
|
|
613
|
+
registerOnTouched(fn) {
|
|
614
|
+
this.onTouched = fn;
|
|
615
|
+
}
|
|
616
|
+
setDisabledState(isDisabled) {
|
|
617
|
+
this.cvaDisabled.set(isDisabled);
|
|
618
|
+
}
|
|
619
|
+
toggle(event) {
|
|
620
|
+
const next = event.target.checked;
|
|
621
|
+
this.checked.set(next);
|
|
622
|
+
this.onChange(next);
|
|
623
|
+
this.onTouched();
|
|
624
|
+
}
|
|
625
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
626
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: CheckboxComponent, isStandalone: true, selector: "ui-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { classAttribute: "block" }, providers: [
|
|
627
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CheckboxComponent), multi: true },
|
|
628
|
+
], ngImport: i0, template: "<label\n [for]=\"id()\"\n class=\"flex items-start gap-2.5\"\n [class.cursor-pointer]=\"!isDisabled()\"\n [class.cursor-not-allowed]=\"isDisabled()\"\n>\n <span class=\"relative inline-flex items-center\">\n <input\n type=\"checkbox\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.aria-invalid]=\"hasError() || null\"\n (change)=\"toggle($event)\"\n class=\"peer absolute inset-0 size-full opacity-0 m-0 cursor-pointer disabled:cursor-not-allowed\"\n />\n <span [class]=\"boxClasses()\">\n @if (indeterminate()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" [attr.width]=\"size() === 'sm' ? 10 : 12\" [attr.height]=\"size() === 'sm' ? 10 : 12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" /></svg>\n } @else if (checked()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" [attr.width]=\"size() === 'sm' ? 10 : 12\" [attr.height]=\"size() === 'sm' ? 10 : 12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\" /></svg>\n }\n </span>\n </span>\n\n @if (label() || description()) {\n <span class=\"flex flex-col gap-0.5 -mt-px\">\n @if (label()) {\n <span class=\"text-sm font-medium text-[var(--color-foreground)] leading-tight\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </span>\n }\n @if (description()) {\n <span class=\"text-xs text-[var(--color-muted-foreground)]\">{{ description() }}</span>\n }\n </span>\n }\n</label>\n\n@if (hasError()) {\n <p class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">{{ errorMessage() }}</p>\n}\n" });
|
|
629
|
+
}
|
|
630
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: CheckboxComponent, decorators: [{
|
|
631
|
+
type: Component,
|
|
632
|
+
args: [{ selector: 'ui-checkbox', host: { class: 'block' }, providers: [
|
|
633
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CheckboxComponent), multi: true },
|
|
634
|
+
], template: "<label\n [for]=\"id()\"\n class=\"flex items-start gap-2.5\"\n [class.cursor-pointer]=\"!isDisabled()\"\n [class.cursor-not-allowed]=\"isDisabled()\"\n>\n <span class=\"relative inline-flex items-center\">\n <input\n type=\"checkbox\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.aria-invalid]=\"hasError() || null\"\n (change)=\"toggle($event)\"\n class=\"peer absolute inset-0 size-full opacity-0 m-0 cursor-pointer disabled:cursor-not-allowed\"\n />\n <span [class]=\"boxClasses()\">\n @if (indeterminate()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" [attr.width]=\"size() === 'sm' ? 10 : 12\" [attr.height]=\"size() === 'sm' ? 10 : 12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" /></svg>\n } @else if (checked()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" [attr.width]=\"size() === 'sm' ? 10 : 12\" [attr.height]=\"size() === 'sm' ? 10 : 12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\" /></svg>\n }\n </span>\n </span>\n\n @if (label() || description()) {\n <span class=\"flex flex-col gap-0.5 -mt-px\">\n @if (label()) {\n <span class=\"text-sm font-medium text-[var(--color-foreground)] leading-tight\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </span>\n }\n @if (description()) {\n <span class=\"text-xs text-[var(--color-muted-foreground)]\">{{ description() }}</span>\n }\n </span>\n }\n</label>\n\n@if (hasError()) {\n <p class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">{{ errorMessage() }}</p>\n}\n" }]
|
|
635
|
+
}], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }] } });
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Menú desplegable compositional, sin dependencias. Maneja abrir/cerrar,
|
|
639
|
+
* posición, click-afuera y navegación por teclado.
|
|
640
|
+
*/
|
|
641
|
+
class DropdownComponent {
|
|
642
|
+
side = input('bottom', /* @ts-ignore */
|
|
643
|
+
...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
644
|
+
align = input('start', /* @ts-ignore */
|
|
645
|
+
...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
646
|
+
open = signal(false, /* @ts-ignore */
|
|
647
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
648
|
+
el = inject((ElementRef));
|
|
649
|
+
items = contentChildren(DropdownItemComponent, /* @ts-ignore */
|
|
650
|
+
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
651
|
+
panelClasses = computed(() => {
|
|
652
|
+
const base = 'absolute z-50 min-w-48 rounded-[var(--radius)] border border-[var(--color-border)] bg-[var(--color-background)] p-1 shadow-md text-[var(--color-foreground)]';
|
|
653
|
+
const side = this.side() === 'top' ? 'bottom-full mb-1.5' : 'top-full mt-1.5';
|
|
654
|
+
const align = this.align() === 'end' ? 'right-0' : 'left-0';
|
|
655
|
+
return [base, side, align].join(' ');
|
|
656
|
+
}, /* @ts-ignore */
|
|
657
|
+
...(ngDevMode ? [{ debugName: "panelClasses" }] : /* istanbul ignore next */ []));
|
|
658
|
+
toggle() {
|
|
659
|
+
this.open.update((o) => !o);
|
|
660
|
+
if (this.open())
|
|
661
|
+
this.focusItem(0);
|
|
662
|
+
}
|
|
663
|
+
close() {
|
|
664
|
+
this.open.set(false);
|
|
665
|
+
}
|
|
666
|
+
onDocClick(event) {
|
|
667
|
+
if (this.open() && !this.el.nativeElement.contains(event.target)) {
|
|
668
|
+
this.close();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
onKeydown(event) {
|
|
672
|
+
if (!this.open())
|
|
673
|
+
return;
|
|
674
|
+
const enabled = this.items().filter((i) => !i.disabled());
|
|
675
|
+
if (!enabled.length)
|
|
676
|
+
return;
|
|
677
|
+
if (event.key === 'Escape') {
|
|
678
|
+
event.preventDefault();
|
|
679
|
+
this.close();
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
683
|
+
event.preventDefault();
|
|
684
|
+
const active = document.activeElement;
|
|
685
|
+
const idx = enabled.findIndex((i) => i.isActive(active));
|
|
686
|
+
const next = event.key === 'ArrowDown'
|
|
687
|
+
? (idx + 1) % enabled.length
|
|
688
|
+
: (idx - 1 + enabled.length) % enabled.length;
|
|
689
|
+
enabled[next].focus();
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/** Enfoca el ítem habilitado en la posición dada (tras render). */
|
|
693
|
+
focusItem(index) {
|
|
694
|
+
setTimeout(() => {
|
|
695
|
+
const enabled = this.items().filter((i) => !i.disabled());
|
|
696
|
+
enabled[index]?.focus();
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
700
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: DropdownComponent, isStandalone: true, selector: "ui-dropdown", inputs: { side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:click": "onDocClick($event)", "keydown": "onKeydown($event)" }, classAttribute: "relative inline-block" }, queries: [{ propertyName: "items", predicate: DropdownItemComponent, isSignal: true }], ngImport: i0, template: "<ng-content select=\"[uiDropdownTrigger]\" />\n\n@if (open()) {\n <div role=\"menu\" [class]=\"panelClasses()\">\n <ng-content />\n </div>\n}\n" });
|
|
701
|
+
}
|
|
702
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownComponent, decorators: [{
|
|
703
|
+
type: Component,
|
|
704
|
+
args: [{ selector: 'ui-dropdown', host: { class: 'relative inline-block' }, template: "<ng-content select=\"[uiDropdownTrigger]\" />\n\n@if (open()) {\n <div role=\"menu\" [class]=\"panelClasses()\">\n <ng-content />\n </div>\n}\n" }]
|
|
705
|
+
}], propDecorators: { side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], items: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => DropdownItemComponent), { isSignal: true }] }], onDocClick: [{
|
|
706
|
+
type: HostListener,
|
|
707
|
+
args: ['document:click', ['$event']]
|
|
708
|
+
}], onKeydown: [{
|
|
709
|
+
type: HostListener,
|
|
710
|
+
args: ['keydown', ['$event']]
|
|
711
|
+
}] } });
|
|
712
|
+
/** Marca el elemento disparador del dropdown. */
|
|
713
|
+
class DropdownTriggerDirective {
|
|
714
|
+
dd = inject(DropdownComponent);
|
|
715
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
716
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: DropdownTriggerDirective, isStandalone: true, selector: "[uiDropdownTrigger]", host: { listeners: { "click": "dd.toggle()" }, properties: { "attr.aria-haspopup": "\"menu\"", "attr.aria-expanded": "dd.open()" } }, ngImport: i0 });
|
|
717
|
+
}
|
|
718
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownTriggerDirective, decorators: [{
|
|
719
|
+
type: Directive,
|
|
720
|
+
args: [{
|
|
721
|
+
selector: '[uiDropdownTrigger]',
|
|
722
|
+
host: {
|
|
723
|
+
'(click)': 'dd.toggle()',
|
|
724
|
+
'[attr.aria-haspopup]': '"menu"',
|
|
725
|
+
'[attr.aria-expanded]': 'dd.open()',
|
|
726
|
+
},
|
|
727
|
+
}]
|
|
728
|
+
}] });
|
|
729
|
+
/** Ítem seleccionable del menú. */
|
|
730
|
+
class DropdownItemComponent {
|
|
731
|
+
destructive = input(false, { ...(ngDevMode ? { debugName: "destructive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
732
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
733
|
+
select = output();
|
|
734
|
+
dd = inject(DropdownComponent);
|
|
735
|
+
el = inject((ElementRef));
|
|
736
|
+
hostClasses = computed(() => {
|
|
737
|
+
const base = 'flex items-center gap-2 rounded-md px-2 py-1.5 text-sm cursor-pointer select-none outline-none transition-colors';
|
|
738
|
+
const tone = this.destructive()
|
|
739
|
+
? 'text-[var(--color-destructive)] hover:bg-[var(--color-destructive)]/10 focus:bg-[var(--color-destructive)]/10'
|
|
740
|
+
: 'text-[var(--color-foreground)] hover:bg-[var(--color-accent)] focus:bg-[var(--color-accent)]';
|
|
741
|
+
const disabled = this.disabled() ? 'opacity-50 pointer-events-none' : '';
|
|
742
|
+
return [base, tone, disabled].join(' ');
|
|
743
|
+
}, /* @ts-ignore */
|
|
744
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
745
|
+
focus() {
|
|
746
|
+
this.el.nativeElement.focus();
|
|
747
|
+
}
|
|
748
|
+
isActive(node) {
|
|
749
|
+
return node === this.el.nativeElement;
|
|
750
|
+
}
|
|
751
|
+
onClick() {
|
|
752
|
+
if (this.disabled())
|
|
753
|
+
return;
|
|
754
|
+
this.select.emit();
|
|
755
|
+
this.dd.close();
|
|
756
|
+
}
|
|
757
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
758
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: DropdownItemComponent, isStandalone: true, selector: "ui-dropdown-item", inputs: { destructive: { classPropertyName: "destructive", publicName: "destructive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select" }, host: { attributes: { "role": "menuitem", "tabindex": "-1" }, listeners: { "click": "onClick()" }, properties: { "attr.aria-disabled": "disabled() || null", "class": "hostClasses()" } }, ngImport: i0, template: `
|
|
759
|
+
<span class="shrink-0 flex items-center [&:empty]:hidden"><ng-content select="[slot=icon]" /></span>
|
|
760
|
+
<span class="flex-1 text-left"><ng-content /></span>
|
|
761
|
+
<span class="shrink-0 text-xs text-[var(--color-muted-foreground)] [&:empty]:hidden"><ng-content select="[slot=shortcut]" /></span>
|
|
762
|
+
`, isInline: true });
|
|
763
|
+
}
|
|
764
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownItemComponent, decorators: [{
|
|
765
|
+
type: Component,
|
|
766
|
+
args: [{
|
|
767
|
+
selector: 'ui-dropdown-item',
|
|
768
|
+
template: `
|
|
769
|
+
<span class="shrink-0 flex items-center [&:empty]:hidden"><ng-content select="[slot=icon]" /></span>
|
|
770
|
+
<span class="flex-1 text-left"><ng-content /></span>
|
|
771
|
+
<span class="shrink-0 text-xs text-[var(--color-muted-foreground)] [&:empty]:hidden"><ng-content select="[slot=shortcut]" /></span>
|
|
772
|
+
`,
|
|
773
|
+
host: {
|
|
774
|
+
role: 'menuitem',
|
|
775
|
+
tabindex: '-1',
|
|
776
|
+
'[attr.aria-disabled]': 'disabled() || null',
|
|
777
|
+
'(click)': 'onClick()',
|
|
778
|
+
'[class]': 'hostClasses()',
|
|
779
|
+
},
|
|
780
|
+
}]
|
|
781
|
+
}], propDecorators: { destructive: [{ type: i0.Input, args: [{ isSignal: true, alias: "destructive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], select: [{ type: i0.Output, args: ["select"] }] } });
|
|
782
|
+
/** Encabezado de sección. */
|
|
783
|
+
class DropdownLabelComponent {
|
|
784
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
785
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: DropdownLabelComponent, isStandalone: true, selector: "ui-dropdown-label", host: { classAttribute: "block px-2 py-1.5 text-[11px] font-semibold uppercase tracking-widest text-[var(--color-muted-foreground)]" }, ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
786
|
+
}
|
|
787
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownLabelComponent, decorators: [{
|
|
788
|
+
type: Component,
|
|
789
|
+
args: [{
|
|
790
|
+
selector: 'ui-dropdown-label',
|
|
791
|
+
template: `<ng-content />`,
|
|
792
|
+
host: {
|
|
793
|
+
class: 'block px-2 py-1.5 text-[11px] font-semibold uppercase tracking-widest text-[var(--color-muted-foreground)]',
|
|
794
|
+
},
|
|
795
|
+
}]
|
|
796
|
+
}] });
|
|
797
|
+
/** Línea divisoria. */
|
|
798
|
+
class DropdownSeparatorComponent {
|
|
799
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownSeparatorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
800
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.2", type: DropdownSeparatorComponent, isStandalone: true, selector: "ui-dropdown-separator", host: { classAttribute: "-mx-1 my-1 block h-px bg-[var(--color-border)]" }, ngImport: i0, template: '', isInline: true });
|
|
801
|
+
}
|
|
802
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: DropdownSeparatorComponent, decorators: [{
|
|
803
|
+
type: Component,
|
|
804
|
+
args: [{
|
|
805
|
+
selector: 'ui-dropdown-separator',
|
|
806
|
+
template: '',
|
|
807
|
+
host: { class: '-mx-1 my-1 block h-px bg-[var(--color-border)]' },
|
|
808
|
+
}]
|
|
809
|
+
}] });
|
|
810
|
+
|
|
811
|
+
let nextId$4 = 0;
|
|
812
|
+
/**
|
|
813
|
+
* Campo de texto. Funciona con:
|
|
814
|
+
* - Signal Forms (Angular 22) vía `[formField]` — implementa el contrato
|
|
815
|
+
* FormValueControl por duck typing (value = model + errors/disabled/... = input).
|
|
816
|
+
* - ControlValueAccessor — ngModel, Reactive Forms, formControlName.
|
|
817
|
+
* - Two-way simple `[(value)]`.
|
|
818
|
+
*/
|
|
819
|
+
class InputComponent {
|
|
820
|
+
/** Fuente única de verdad del valor (Signal Forms + two-way). */
|
|
821
|
+
value = model('', /* @ts-ignore */
|
|
822
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
823
|
+
type = input('text', /* @ts-ignore */
|
|
824
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
825
|
+
size = input('md', /* @ts-ignore */
|
|
826
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
827
|
+
label = input('', /* @ts-ignore */
|
|
828
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
829
|
+
hint = input('', /* @ts-ignore */
|
|
830
|
+
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
831
|
+
placeholder = input('', /* @ts-ignore */
|
|
832
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
833
|
+
name = input('', /* @ts-ignore */
|
|
834
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
835
|
+
id = input(`ui-input-${nextId$4++}`, /* @ts-ignore */
|
|
836
|
+
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
837
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
838
|
+
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
839
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
840
|
+
/** Muestra el botón de ojito para revelar/ocultar la contraseña (solo type="password"). */
|
|
841
|
+
passwordToggle = input(true, { ...(ngDevMode ? { debugName: "passwordToggle" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
842
|
+
/** Mensaje de error manual (tiene prioridad sobre los errors de Signal Forms). */
|
|
843
|
+
error = input('', /* @ts-ignore */
|
|
844
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
845
|
+
/** Estado de validación que cablea Signal Forms automáticamente. */
|
|
846
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
847
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
848
|
+
errors = input([], /* @ts-ignore */
|
|
849
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
850
|
+
/** disabled puede venir del input o de CVA (setDisabledState). */
|
|
851
|
+
cvaDisabled = signal(false, /* @ts-ignore */
|
|
852
|
+
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
853
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
854
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
855
|
+
/** Estado y tipo efectivo para el toggle de contraseña. */
|
|
856
|
+
showPassword = signal(false, /* @ts-ignore */
|
|
857
|
+
...(ngDevMode ? [{ debugName: "showPassword" }] : /* istanbul ignore next */ []));
|
|
858
|
+
hasPasswordToggle = computed(() => this.type() === 'password' && this.passwordToggle(), /* @ts-ignore */
|
|
859
|
+
...(ngDevMode ? [{ debugName: "hasPasswordToggle" }] : /* istanbul ignore next */ []));
|
|
860
|
+
resolvedType = computed(() => this.type() === 'password' && this.showPassword() ? 'text' : this.type(), /* @ts-ignore */
|
|
861
|
+
...(ngDevMode ? [{ debugName: "resolvedType" }] : /* istanbul ignore next */ []));
|
|
862
|
+
errorMessage = computed(() => {
|
|
863
|
+
if (this.error())
|
|
864
|
+
return this.error();
|
|
865
|
+
// Los errores de validación (Signal Forms) solo se muestran tras interactuar.
|
|
866
|
+
if (this.touched()) {
|
|
867
|
+
const first = this.errors()[0];
|
|
868
|
+
if (first)
|
|
869
|
+
return first.message ?? first.kind ?? 'Campo inválido';
|
|
870
|
+
}
|
|
871
|
+
return '';
|
|
872
|
+
}, /* @ts-ignore */
|
|
873
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
874
|
+
hasError = computed(() => !!this.error() || !!this.errorMessage() || (this.invalid() && this.touched()), /* @ts-ignore */
|
|
875
|
+
...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
876
|
+
describedById = computed(() => this.hasError() || this.hint() ? `${this.id()}-desc` : null, /* @ts-ignore */
|
|
877
|
+
...(ngDevMode ? [{ debugName: "describedById" }] : /* istanbul ignore next */ []));
|
|
878
|
+
wrapperClasses = computed(() => {
|
|
879
|
+
// Radio ligado al tamaño: menos alto -> menos redondez (estilo shadcn).
|
|
880
|
+
const sizeMap = {
|
|
881
|
+
sm: 'h-8 px-2.5 gap-2 text-xs rounded-lg',
|
|
882
|
+
md: 'h-9 px-3 gap-2 text-sm rounded-[10px]',
|
|
883
|
+
lg: 'h-10 px-3.5 gap-2 text-base rounded-[10px]',
|
|
884
|
+
};
|
|
885
|
+
const base = 'flex items-center border bg-[var(--color-background)] transition-all';
|
|
886
|
+
const state = this.hasError()
|
|
887
|
+
? 'border-[var(--color-destructive)] focus-within:ring-[3px] focus-within:ring-[var(--color-destructive)]/25'
|
|
888
|
+
: 'border-[var(--color-input)] focus-within:border-[var(--color-ring)] focus-within:ring-[3px] focus-within:ring-[var(--color-ring)]/40';
|
|
889
|
+
const disabled = this.isDisabled()
|
|
890
|
+
? 'opacity-50 cursor-not-allowed bg-[var(--color-muted)]/40'
|
|
891
|
+
: '';
|
|
892
|
+
return [base, sizeMap[this.size()], state, disabled].join(' ');
|
|
893
|
+
}, /* @ts-ignore */
|
|
894
|
+
...(ngDevMode ? [{ debugName: "wrapperClasses" }] : /* istanbul ignore next */ []));
|
|
895
|
+
// --- ControlValueAccessor ---
|
|
896
|
+
onChange = () => { };
|
|
897
|
+
onTouched = () => { };
|
|
898
|
+
writeValue(value) {
|
|
899
|
+
this.value.set(value ?? '');
|
|
900
|
+
}
|
|
901
|
+
registerOnChange(fn) {
|
|
902
|
+
this.onChange = fn;
|
|
903
|
+
}
|
|
904
|
+
registerOnTouched(fn) {
|
|
905
|
+
this.onTouched = fn;
|
|
906
|
+
}
|
|
907
|
+
setDisabledState(isDisabled) {
|
|
908
|
+
this.cvaDisabled.set(isDisabled);
|
|
909
|
+
}
|
|
910
|
+
onInput(event) {
|
|
911
|
+
const next = event.target.value;
|
|
912
|
+
this.value.set(next);
|
|
913
|
+
this.onChange(next);
|
|
914
|
+
}
|
|
915
|
+
onBlur() {
|
|
916
|
+
this.onTouched();
|
|
917
|
+
}
|
|
918
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
919
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: InputComponent, isStandalone: true, selector: "ui-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, passwordToggle: { classPropertyName: "passwordToggle", publicName: "passwordToggle", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, providers: [
|
|
920
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputComponent), multi: true },
|
|
921
|
+
], ngImport: i0, template: "@if (label()) {\n <label\n [for]=\"id()\"\n class=\"block mb-1.5 text-sm font-medium text-[var(--color-foreground)]\"\n >\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </label>\n}\n\n<div [class]=\"wrapperClasses()\">\n <span class=\"shrink-0 flex items-center text-[var(--color-muted-foreground)]\">\n <ng-content select=\"[slot=prefix]\" />\n </span>\n\n <input\n [id]=\"id()\"\n [type]=\"resolvedType()\"\n [name]=\"name()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"describedById()\"\n class=\"w-full min-w-0 bg-transparent border-0 p-0 outline-none text-[var(--color-foreground)] placeholder:text-[var(--color-muted-foreground)] disabled:cursor-not-allowed\"\n />\n\n <span class=\"shrink-0 flex items-center text-[var(--color-muted-foreground)]\">\n <ng-content select=\"[slot=suffix]\" />\n </span>\n\n @if (hasPasswordToggle()) {\n <button\n type=\"button\"\n (click)=\"showPassword.set(!showPassword())\"\n [attr.aria-label]=\"showPassword() ? 'Ocultar contrase\u00F1a' : 'Mostrar contrase\u00F1a'\"\n class=\"shrink-0 flex items-center text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] transition-colors cursor-pointer\"\n >\n @if (showPassword()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9.88 9.88a3 3 0 1 0 4.24 4.24\" /><path d=\"M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68\" /><path d=\"M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61\" /><line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" /></svg>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z\" /><circle cx=\"12\" cy=\"12\" r=\"3\" /></svg>\n }\n </button>\n }\n</div>\n\n@if (hasError()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">\n {{ errorMessage() }}\n </p>\n} @else if (hint()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-muted-foreground)]\">\n {{ hint() }}\n </p>\n}\n" });
|
|
922
|
+
}
|
|
923
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: InputComponent, decorators: [{
|
|
924
|
+
type: Component,
|
|
925
|
+
args: [{ selector: 'ui-input', host: { class: 'block' }, providers: [
|
|
926
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputComponent), multi: true },
|
|
927
|
+
], template: "@if (label()) {\n <label\n [for]=\"id()\"\n class=\"block mb-1.5 text-sm font-medium text-[var(--color-foreground)]\"\n >\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </label>\n}\n\n<div [class]=\"wrapperClasses()\">\n <span class=\"shrink-0 flex items-center text-[var(--color-muted-foreground)]\">\n <ng-content select=\"[slot=prefix]\" />\n </span>\n\n <input\n [id]=\"id()\"\n [type]=\"resolvedType()\"\n [name]=\"name()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"describedById()\"\n class=\"w-full min-w-0 bg-transparent border-0 p-0 outline-none text-[var(--color-foreground)] placeholder:text-[var(--color-muted-foreground)] disabled:cursor-not-allowed\"\n />\n\n <span class=\"shrink-0 flex items-center text-[var(--color-muted-foreground)]\">\n <ng-content select=\"[slot=suffix]\" />\n </span>\n\n @if (hasPasswordToggle()) {\n <button\n type=\"button\"\n (click)=\"showPassword.set(!showPassword())\"\n [attr.aria-label]=\"showPassword() ? 'Ocultar contrase\u00F1a' : 'Mostrar contrase\u00F1a'\"\n class=\"shrink-0 flex items-center text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] transition-colors cursor-pointer\"\n >\n @if (showPassword()) {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9.88 9.88a3 3 0 1 0 4.24 4.24\" /><path d=\"M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68\" /><path d=\"M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61\" /><line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" /></svg>\n } @else {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z\" /><circle cx=\"12\" cy=\"12\" r=\"3\" /></svg>\n }\n </button>\n }\n</div>\n\n@if (hasError()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">\n {{ errorMessage() }}\n </p>\n} @else if (hint()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-muted-foreground)]\">\n {{ hint() }}\n </p>\n}\n" }]
|
|
928
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], passwordToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "passwordToggle", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }] } });
|
|
929
|
+
|
|
930
|
+
/** Indicador giratorio. Hereda el color (currentColor). */
|
|
931
|
+
class SpinnerComponent {
|
|
932
|
+
size = input('md', /* @ts-ignore */
|
|
933
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
934
|
+
px = computed(() => ({ sm: 16, md: 24, lg: 36 })[this.size()], /* @ts-ignore */
|
|
935
|
+
...(ngDevMode ? [{ debugName: "px" }] : /* istanbul ignore next */ []));
|
|
936
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
937
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: SpinnerComponent, isStandalone: true, selector: "ui-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "inline-flex" }, ngImport: i0, template: `
|
|
938
|
+
<svg
|
|
939
|
+
[attr.width]="px()"
|
|
940
|
+
[attr.height]="px()"
|
|
941
|
+
class="animate-spin"
|
|
942
|
+
viewBox="0 0 24 24"
|
|
943
|
+
fill="none"
|
|
944
|
+
[attr.aria-label]="'Cargando'"
|
|
945
|
+
role="status"
|
|
946
|
+
>
|
|
947
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
948
|
+
<path class="opacity-90" fill="currentColor" d="M4 12a8 8 0 0 1 8-8V0C5.37 0 0 5.37 0 12h4z"></path>
|
|
949
|
+
</svg>
|
|
950
|
+
`, isInline: true });
|
|
951
|
+
}
|
|
952
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SpinnerComponent, decorators: [{
|
|
953
|
+
type: Component,
|
|
954
|
+
args: [{
|
|
955
|
+
selector: 'ui-spinner',
|
|
956
|
+
template: `
|
|
957
|
+
<svg
|
|
958
|
+
[attr.width]="px()"
|
|
959
|
+
[attr.height]="px()"
|
|
960
|
+
class="animate-spin"
|
|
961
|
+
viewBox="0 0 24 24"
|
|
962
|
+
fill="none"
|
|
963
|
+
[attr.aria-label]="'Cargando'"
|
|
964
|
+
role="status"
|
|
965
|
+
>
|
|
966
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
967
|
+
<path class="opacity-90" fill="currentColor" d="M4 12a8 8 0 0 1 8-8V0C5.37 0 0 5.37 0 12h4z"></path>
|
|
968
|
+
</svg>
|
|
969
|
+
`,
|
|
970
|
+
host: { class: 'inline-flex' },
|
|
971
|
+
}]
|
|
972
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
973
|
+
/** Placeholder con pulso mientras carga el contenido real. */
|
|
974
|
+
class SkeletonComponent {
|
|
975
|
+
width = input('100%', /* @ts-ignore */
|
|
976
|
+
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
977
|
+
height = input('1rem', /* @ts-ignore */
|
|
978
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
979
|
+
circle = input(false, { ...(ngDevMode ? { debugName: "circle" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
980
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
981
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: SkeletonComponent, isStandalone: true, selector: "ui-skeleton", inputs: { width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, circle: { classPropertyName: "circle", publicName: "circle", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.rounded-full": "circle()", "class.rounded-md": "!circle()", "style.width": "width()", "style.height": "height()" }, classAttribute: "block animate-pulse bg-[var(--color-muted)]" }, ngImport: i0, template: '', isInline: true });
|
|
982
|
+
}
|
|
983
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SkeletonComponent, decorators: [{
|
|
984
|
+
type: Component,
|
|
985
|
+
args: [{
|
|
986
|
+
selector: 'ui-skeleton',
|
|
987
|
+
template: '',
|
|
988
|
+
host: {
|
|
989
|
+
class: 'block animate-pulse bg-[var(--color-muted)]',
|
|
990
|
+
'[class.rounded-full]': 'circle()',
|
|
991
|
+
'[class.rounded-md]': '!circle()',
|
|
992
|
+
'[style.width]': 'width()',
|
|
993
|
+
'[style.height]': 'height()',
|
|
994
|
+
},
|
|
995
|
+
}]
|
|
996
|
+
}], propDecorators: { width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], circle: [{ type: i0.Input, args: [{ isSignal: true, alias: "circle", required: false }] }] } });
|
|
997
|
+
/** Barra de progreso (determinada con value 0-100, o indeterminada). */
|
|
998
|
+
class ProgressComponent {
|
|
999
|
+
value = input(0, { ...(ngDevMode ? { debugName: "value" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
1000
|
+
size = input('md', /* @ts-ignore */
|
|
1001
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1002
|
+
indeterminate = input(false, { ...(ngDevMode ? { debugName: "indeterminate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1003
|
+
clamped = computed(() => Math.max(0, Math.min(100, this.value())), /* @ts-ignore */
|
|
1004
|
+
...(ngDevMode ? [{ debugName: "clamped" }] : /* istanbul ignore next */ []));
|
|
1005
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ProgressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1006
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: ProgressComponent, isStandalone: true, selector: "ui-progress", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "progressbar" }, properties: { "class.h-1": "size() === 'sm'", "class.h-2": "size() === 'md'", "class.h-3": "size() === 'lg'", "attr.aria-valuenow": "indeterminate() ? null : clamped()", "attr.aria-valuemin": "0", "attr.aria-valuemax": "100" }, classAttribute: "block w-full overflow-hidden rounded-full bg-[var(--color-muted)]" }, ngImport: i0, template: `
|
|
1007
|
+
<div
|
|
1008
|
+
class="h-full rounded-full bg-[var(--color-primary)] transition-[width] duration-300"
|
|
1009
|
+
[class.animate-progress-indeterminate]="indeterminate()"
|
|
1010
|
+
[class.w-2/5]="indeterminate()"
|
|
1011
|
+
[style.width.%]="indeterminate() ? null : clamped()"
|
|
1012
|
+
></div>
|
|
1013
|
+
`, isInline: true });
|
|
1014
|
+
}
|
|
1015
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ProgressComponent, decorators: [{
|
|
1016
|
+
type: Component,
|
|
1017
|
+
args: [{
|
|
1018
|
+
selector: 'ui-progress',
|
|
1019
|
+
template: `
|
|
1020
|
+
<div
|
|
1021
|
+
class="h-full rounded-full bg-[var(--color-primary)] transition-[width] duration-300"
|
|
1022
|
+
[class.animate-progress-indeterminate]="indeterminate()"
|
|
1023
|
+
[class.w-2/5]="indeterminate()"
|
|
1024
|
+
[style.width.%]="indeterminate() ? null : clamped()"
|
|
1025
|
+
></div>
|
|
1026
|
+
`,
|
|
1027
|
+
host: {
|
|
1028
|
+
role: 'progressbar',
|
|
1029
|
+
class: 'block w-full overflow-hidden rounded-full bg-[var(--color-muted)]',
|
|
1030
|
+
'[class.h-1]': "size() === 'sm'",
|
|
1031
|
+
'[class.h-2]': "size() === 'md'",
|
|
1032
|
+
'[class.h-3]': "size() === 'lg'",
|
|
1033
|
+
'[attr.aria-valuenow]': 'indeterminate() ? null : clamped()',
|
|
1034
|
+
'[attr.aria-valuemin]': '0',
|
|
1035
|
+
'[attr.aria-valuemax]': '100',
|
|
1036
|
+
},
|
|
1037
|
+
}]
|
|
1038
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }] } });
|
|
1039
|
+
|
|
1040
|
+
let nextId$3 = 0;
|
|
1041
|
+
/**
|
|
1042
|
+
* Select compositional, sin dependencias. Combina el overlay del Dropdown con
|
|
1043
|
+
* la integración de formularios del Input: Signal Forms (`[formField]`),
|
|
1044
|
+
* ControlValueAccessor (ngModel/Reactive) y two-way `[(value)]`.
|
|
1045
|
+
*/
|
|
1046
|
+
class SelectComponent {
|
|
1047
|
+
value = model('', /* @ts-ignore */
|
|
1048
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1049
|
+
placeholder = input('Selecciona…', /* @ts-ignore */
|
|
1050
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
1051
|
+
label = input('', /* @ts-ignore */
|
|
1052
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1053
|
+
hint = input('', /* @ts-ignore */
|
|
1054
|
+
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
1055
|
+
error = input('', /* @ts-ignore */
|
|
1056
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
1057
|
+
size = input('md', /* @ts-ignore */
|
|
1058
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1059
|
+
id = input(`ui-select-${nextId$3++}`, /* @ts-ignore */
|
|
1060
|
+
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
1061
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1062
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1063
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1064
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1065
|
+
errors = input([], /* @ts-ignore */
|
|
1066
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
1067
|
+
open = signal(false, /* @ts-ignore */
|
|
1068
|
+
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
1069
|
+
el = inject((ElementRef));
|
|
1070
|
+
options = contentChildren(OptionComponent, /* @ts-ignore */
|
|
1071
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
1072
|
+
cvaDisabled = signal(false, /* @ts-ignore */
|
|
1073
|
+
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
1074
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
1075
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
1076
|
+
errorMessage = computed(() => {
|
|
1077
|
+
if (this.error())
|
|
1078
|
+
return this.error();
|
|
1079
|
+
if (this.touched()) {
|
|
1080
|
+
const first = this.errors()[0];
|
|
1081
|
+
if (first)
|
|
1082
|
+
return first.message ?? first.kind ?? 'Campo inválido';
|
|
1083
|
+
}
|
|
1084
|
+
return '';
|
|
1085
|
+
}, /* @ts-ignore */
|
|
1086
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
1087
|
+
hasError = computed(() => !!this.error() || !!this.errorMessage() || (this.invalid() && this.touched()), /* @ts-ignore */
|
|
1088
|
+
...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
1089
|
+
selectedLabel = computed(() => {
|
|
1090
|
+
const v = this.value();
|
|
1091
|
+
const opt = this.options().find((o) => o.value() === v);
|
|
1092
|
+
return opt ? opt.getLabel() : '';
|
|
1093
|
+
}, /* @ts-ignore */
|
|
1094
|
+
...(ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []));
|
|
1095
|
+
triggerClasses = computed(() => {
|
|
1096
|
+
const sizeMap = {
|
|
1097
|
+
sm: 'h-8 px-2.5 text-xs rounded-lg',
|
|
1098
|
+
md: 'h-9 px-3 text-sm rounded-[10px]',
|
|
1099
|
+
lg: 'h-10 px-3.5 text-base rounded-[10px]',
|
|
1100
|
+
};
|
|
1101
|
+
const base = 'flex w-full items-center justify-between gap-2 border bg-[var(--color-background)] transition-all outline-none cursor-pointer text-left';
|
|
1102
|
+
const state = this.hasError()
|
|
1103
|
+
? 'border-[var(--color-destructive)] focus-visible:ring-[3px] focus-visible:ring-[var(--color-destructive)]/25'
|
|
1104
|
+
: 'border-[var(--color-input)] focus-visible:border-[var(--color-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--color-ring)]/40';
|
|
1105
|
+
const disabled = this.isDisabled()
|
|
1106
|
+
? 'opacity-50 cursor-not-allowed bg-[var(--color-muted)]/40'
|
|
1107
|
+
: '';
|
|
1108
|
+
return [base, sizeMap[this.size()], state, disabled].join(' ');
|
|
1109
|
+
}, /* @ts-ignore */
|
|
1110
|
+
...(ngDevMode ? [{ debugName: "triggerClasses" }] : /* istanbul ignore next */ []));
|
|
1111
|
+
describedById = computed(() => this.hasError() || this.hint() ? `${this.id()}-desc` : null, /* @ts-ignore */
|
|
1112
|
+
...(ngDevMode ? [{ debugName: "describedById" }] : /* istanbul ignore next */ []));
|
|
1113
|
+
toggle() {
|
|
1114
|
+
if (this.isDisabled())
|
|
1115
|
+
return;
|
|
1116
|
+
this.open.update((o) => !o);
|
|
1117
|
+
if (this.open())
|
|
1118
|
+
this.focusActive();
|
|
1119
|
+
}
|
|
1120
|
+
close() {
|
|
1121
|
+
this.open.set(false);
|
|
1122
|
+
}
|
|
1123
|
+
/** Llamado por una ui-option al elegirse. */
|
|
1124
|
+
choose(v) {
|
|
1125
|
+
this.value.set(v);
|
|
1126
|
+
this.onChange(v);
|
|
1127
|
+
this.onTouched();
|
|
1128
|
+
this.close();
|
|
1129
|
+
const trigger = this.el.nativeElement.querySelector('[data-trigger]');
|
|
1130
|
+
trigger?.focus();
|
|
1131
|
+
}
|
|
1132
|
+
onDocClick(event) {
|
|
1133
|
+
if (this.open() && !this.el.nativeElement.contains(event.target))
|
|
1134
|
+
this.close();
|
|
1135
|
+
}
|
|
1136
|
+
onKeydown(event) {
|
|
1137
|
+
const enabled = this.options().filter((o) => !o.disabled());
|
|
1138
|
+
if (!this.open()) {
|
|
1139
|
+
if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {
|
|
1140
|
+
event.preventDefault();
|
|
1141
|
+
this.toggle();
|
|
1142
|
+
}
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
if (event.key === 'Escape') {
|
|
1146
|
+
event.preventDefault();
|
|
1147
|
+
this.close();
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
1151
|
+
event.preventDefault();
|
|
1152
|
+
if (!enabled.length)
|
|
1153
|
+
return;
|
|
1154
|
+
const active = document.activeElement;
|
|
1155
|
+
const idx = enabled.findIndex((o) => o.isActive(active));
|
|
1156
|
+
const next = event.key === 'ArrowDown'
|
|
1157
|
+
? (idx + 1) % enabled.length
|
|
1158
|
+
: (idx - 1 + enabled.length) % enabled.length;
|
|
1159
|
+
enabled[next].focus();
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
focusActive() {
|
|
1163
|
+
setTimeout(() => {
|
|
1164
|
+
const opts = this.options().filter((o) => !o.disabled());
|
|
1165
|
+
const sel = opts.find((o) => o.value() === this.value());
|
|
1166
|
+
(sel ?? opts[0])?.focus();
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
// --- ControlValueAccessor ---
|
|
1170
|
+
onChange = () => { };
|
|
1171
|
+
onTouched = () => { };
|
|
1172
|
+
writeValue(value) {
|
|
1173
|
+
this.value.set(value ?? '');
|
|
1174
|
+
}
|
|
1175
|
+
registerOnChange(fn) {
|
|
1176
|
+
this.onChange = fn;
|
|
1177
|
+
}
|
|
1178
|
+
registerOnTouched(fn) {
|
|
1179
|
+
this.onTouched = fn;
|
|
1180
|
+
}
|
|
1181
|
+
setDisabledState(isDisabled) {
|
|
1182
|
+
this.cvaDisabled.set(isDisabled);
|
|
1183
|
+
}
|
|
1184
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1185
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: SelectComponent, isStandalone: true, selector: "ui-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { listeners: { "document:click": "onDocClick($event)", "keydown": "onKeydown($event)" }, classAttribute: "block" }, providers: [
|
|
1186
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectComponent), multi: true },
|
|
1187
|
+
], queries: [{ propertyName: "options", predicate: OptionComponent, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <label\n [for]=\"id()\"\n class=\"block mb-1.5 text-sm font-medium text-[var(--color-foreground)]\"\n >\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </label>\n}\n\n<div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n data-trigger\n [class]=\"triggerClasses()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"describedById()\"\n (click)=\"toggle()\"\n >\n <span [class]=\"selectedLabel() ? 'text-[var(--color-foreground)]' : 'text-[var(--color-muted-foreground)]'\" class=\"truncate\">\n {{ selectedLabel() || placeholder() }}\n </span>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"shrink-0 text-[var(--color-muted-foreground)] transition-transform\"\n [class.rotate-180]=\"open()\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <div\n role=\"listbox\"\n class=\"absolute z-50 left-0 right-0 mt-1.5 max-h-60 overflow-y-auto rounded-[var(--radius)] border border-[var(--color-border)] bg-[var(--color-background)] p-1 shadow-md\"\n [class.hidden]=\"!open()\"\n >\n <ng-content />\n </div>\n</div>\n\n@if (hasError()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">{{ errorMessage() }}</p>\n} @else if (hint()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-muted-foreground)]\">{{ hint() }}</p>\n}\n" });
|
|
1188
|
+
}
|
|
1189
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SelectComponent, decorators: [{
|
|
1190
|
+
type: Component,
|
|
1191
|
+
args: [{ selector: 'ui-select', host: { class: 'block' }, providers: [
|
|
1192
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectComponent), multi: true },
|
|
1193
|
+
], template: "@if (label()) {\n <label\n [for]=\"id()\"\n class=\"block mb-1.5 text-sm font-medium text-[var(--color-foreground)]\"\n >\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </label>\n}\n\n<div class=\"relative\">\n <button\n type=\"button\"\n [id]=\"id()\"\n data-trigger\n [class]=\"triggerClasses()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"describedById()\"\n (click)=\"toggle()\"\n >\n <span [class]=\"selectedLabel() ? 'text-[var(--color-foreground)]' : 'text-[var(--color-muted-foreground)]'\" class=\"truncate\">\n {{ selectedLabel() || placeholder() }}\n </span>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=\"shrink-0 text-[var(--color-muted-foreground)] transition-transform\"\n [class.rotate-180]=\"open()\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <div\n role=\"listbox\"\n class=\"absolute z-50 left-0 right-0 mt-1.5 max-h-60 overflow-y-auto rounded-[var(--radius)] border border-[var(--color-border)] bg-[var(--color-background)] p-1 shadow-md\"\n [class.hidden]=\"!open()\"\n >\n <ng-content />\n </div>\n</div>\n\n@if (hasError()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">{{ errorMessage() }}</p>\n} @else if (hint()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-muted-foreground)]\">{{ hint() }}</p>\n}\n" }]
|
|
1194
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], options: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => OptionComponent), { isSignal: true }] }], onDocClick: [{
|
|
1195
|
+
type: HostListener,
|
|
1196
|
+
args: ['document:click', ['$event']]
|
|
1197
|
+
}], onKeydown: [{
|
|
1198
|
+
type: HostListener,
|
|
1199
|
+
args: ['keydown', ['$event']]
|
|
1200
|
+
}] } });
|
|
1201
|
+
/** Opción dentro de ui-select. */
|
|
1202
|
+
class OptionComponent {
|
|
1203
|
+
value = input.required(/* @ts-ignore */
|
|
1204
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1205
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1206
|
+
select = inject(SelectComponent);
|
|
1207
|
+
el = inject((ElementRef));
|
|
1208
|
+
selected = computed(() => this.select.value() === this.value(), /* @ts-ignore */
|
|
1209
|
+
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
1210
|
+
hostClasses = computed(() => {
|
|
1211
|
+
const base = 'flex items-center gap-2 rounded-md px-2 py-1.5 text-sm cursor-pointer select-none outline-none transition-colors text-[var(--color-foreground)]';
|
|
1212
|
+
const state = this.disabled()
|
|
1213
|
+
? 'opacity-50 pointer-events-none'
|
|
1214
|
+
: 'hover:bg-[var(--color-accent)] focus:bg-[var(--color-accent)]';
|
|
1215
|
+
return [base, state].join(' ');
|
|
1216
|
+
}, /* @ts-ignore */
|
|
1217
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
1218
|
+
getLabel() {
|
|
1219
|
+
return this.el.nativeElement.textContent?.trim() ?? '';
|
|
1220
|
+
}
|
|
1221
|
+
focus() {
|
|
1222
|
+
this.el.nativeElement.focus();
|
|
1223
|
+
}
|
|
1224
|
+
isActive(node) {
|
|
1225
|
+
return node === this.el.nativeElement;
|
|
1226
|
+
}
|
|
1227
|
+
onClick() {
|
|
1228
|
+
if (this.disabled())
|
|
1229
|
+
return;
|
|
1230
|
+
this.select.choose(this.value());
|
|
1231
|
+
}
|
|
1232
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: OptionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1233
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: OptionComponent, isStandalone: true, selector: "ui-option", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option", "tabindex": "-1" }, listeners: { "click": "onClick()" }, properties: { "attr.aria-selected": "selected()", "attr.aria-disabled": "disabled() || null", "class": "hostClasses()" } }, ngImport: i0, template: `
|
|
1234
|
+
<span class="flex-1"><ng-content /></span>
|
|
1235
|
+
@if (selected()) {
|
|
1236
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="text-[var(--color-primary)]"><polyline points="20 6 9 17 4 12" /></svg>
|
|
1237
|
+
}
|
|
1238
|
+
`, isInline: true });
|
|
1239
|
+
}
|
|
1240
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: OptionComponent, decorators: [{
|
|
1241
|
+
type: Component,
|
|
1242
|
+
args: [{
|
|
1243
|
+
selector: 'ui-option',
|
|
1244
|
+
template: `
|
|
1245
|
+
<span class="flex-1"><ng-content /></span>
|
|
1246
|
+
@if (selected()) {
|
|
1247
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="text-[var(--color-primary)]"><polyline points="20 6 9 17 4 12" /></svg>
|
|
1248
|
+
}
|
|
1249
|
+
`,
|
|
1250
|
+
host: {
|
|
1251
|
+
role: 'option',
|
|
1252
|
+
tabindex: '-1',
|
|
1253
|
+
'[attr.aria-selected]': 'selected()',
|
|
1254
|
+
'[attr.aria-disabled]': 'disabled() || null',
|
|
1255
|
+
'(click)': 'onClick()',
|
|
1256
|
+
'[class]': 'hostClasses()',
|
|
1257
|
+
},
|
|
1258
|
+
}]
|
|
1259
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
1260
|
+
|
|
1261
|
+
/** Token de elipsis en la lista de páginas. */
|
|
1262
|
+
const GAP$1 = '…';
|
|
1263
|
+
/**
|
|
1264
|
+
* Paginación. Two-way `[(page)]` (1-based). Modo 'numbers' (con elipsis) o
|
|
1265
|
+
* 'compact' ('Página X de Y'). Selector opcional de items por página.
|
|
1266
|
+
*/
|
|
1267
|
+
class PaginationComponent {
|
|
1268
|
+
page = model(1, /* @ts-ignore */
|
|
1269
|
+
...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
1270
|
+
totalPages = input(1, { ...(ngDevMode ? { debugName: "totalPages" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
1271
|
+
variant = input('numbers', /* @ts-ignore */
|
|
1272
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
1273
|
+
size = input('md', /* @ts-ignore */
|
|
1274
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1275
|
+
/** Si se pasan opciones, se muestra el selector de items por página. */
|
|
1276
|
+
pageSize = model(10, /* @ts-ignore */
|
|
1277
|
+
...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
|
|
1278
|
+
pageSizeOptions = input([], /* @ts-ignore */
|
|
1279
|
+
...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
|
|
1280
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1281
|
+
gap = GAP$1;
|
|
1282
|
+
canPrev = computed(() => this.page() > 1 && !this.disabled(), /* @ts-ignore */
|
|
1283
|
+
...(ngDevMode ? [{ debugName: "canPrev" }] : /* istanbul ignore next */ []));
|
|
1284
|
+
canNext = computed(() => this.page() < this.totalPages() && !this.disabled(), /* @ts-ignore */
|
|
1285
|
+
...(ngDevMode ? [{ debugName: "canNext" }] : /* istanbul ignore next */ []));
|
|
1286
|
+
/** Lista de páginas visibles con elipsis: 1 … 4 5 6 … 20 */
|
|
1287
|
+
pages = computed(() => {
|
|
1288
|
+
const total = this.totalPages();
|
|
1289
|
+
const current = this.page();
|
|
1290
|
+
if (total <= 7)
|
|
1291
|
+
return Array.from({ length: total }, (_, i) => i + 1);
|
|
1292
|
+
const result = [1];
|
|
1293
|
+
const start = Math.max(2, current - 1);
|
|
1294
|
+
const end = Math.min(total - 1, current + 1);
|
|
1295
|
+
if (start > 2)
|
|
1296
|
+
result.push(GAP$1);
|
|
1297
|
+
for (let i = start; i <= end; i++)
|
|
1298
|
+
result.push(i);
|
|
1299
|
+
if (end < total - 1)
|
|
1300
|
+
result.push(GAP$1);
|
|
1301
|
+
result.push(total);
|
|
1302
|
+
return result;
|
|
1303
|
+
}, /* @ts-ignore */
|
|
1304
|
+
...(ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []));
|
|
1305
|
+
btnSize = computed(() => (this.size() === 'sm' ? 'size-8 text-xs' : 'size-9 text-sm'), /* @ts-ignore */
|
|
1306
|
+
...(ngDevMode ? [{ debugName: "btnSize" }] : /* istanbul ignore next */ []));
|
|
1307
|
+
// pageSize como string para el ui-select (que usa value:string)
|
|
1308
|
+
pageSizeStr = computed(() => String(this.pageSize()), /* @ts-ignore */
|
|
1309
|
+
...(ngDevMode ? [{ debugName: "pageSizeStr" }] : /* istanbul ignore next */ []));
|
|
1310
|
+
go(p) {
|
|
1311
|
+
if (p < 1 || p > this.totalPages() || p === this.page() || this.disabled())
|
|
1312
|
+
return;
|
|
1313
|
+
this.page.set(p);
|
|
1314
|
+
}
|
|
1315
|
+
prev() {
|
|
1316
|
+
if (this.canPrev())
|
|
1317
|
+
this.page.set(this.page() - 1);
|
|
1318
|
+
}
|
|
1319
|
+
next() {
|
|
1320
|
+
if (this.canNext())
|
|
1321
|
+
this.page.set(this.page() + 1);
|
|
1322
|
+
}
|
|
1323
|
+
onPageSize(v) {
|
|
1324
|
+
this.pageSize.set(Number(v));
|
|
1325
|
+
this.page.set(1);
|
|
1326
|
+
}
|
|
1327
|
+
isGap(p) {
|
|
1328
|
+
return p === GAP$1;
|
|
1329
|
+
}
|
|
1330
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1331
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: PaginationComponent, isStandalone: true, selector: "ui-pagination", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, totalPages: { classPropertyName: "totalPages", publicName: "totalPages", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange", pageSize: "pageSizeChange" }, host: { attributes: { "role": "navigation", "aria-label": "Paginaci\u00F3n" }, classAttribute: "flex items-center gap-3 flex-wrap" }, ngImport: i0, template: "@let baseBtn =\n 'inline-flex items-center justify-center rounded-lg border border-[var(--color-border)] text-[var(--color-foreground)] transition-colors disabled:opacity-40 disabled:pointer-events-none cursor-pointer hover:bg-[var(--color-accent)]';\n\n<div class=\"flex items-center gap-1\">\n <!-- Prev -->\n <button\n type=\"button\"\n [class]=\"baseBtn + ' ' + btnSize()\"\n [disabled]=\"!canPrev()\"\n (click)=\"prev()\"\n aria-label=\"P\u00E1gina anterior\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\" /></svg>\n </button>\n\n @if (variant() === 'compact') {\n <span class=\"px-3 text-sm text-[var(--color-muted-foreground)]\">\n P\u00E1gina <span class=\"font-medium text-[var(--color-foreground)]\">{{ page() }}</span> de {{ totalPages() }}\n </span>\n } @else {\n @for (p of pages(); track $index) {\n @if (isGap(p)) {\n <span class=\"inline-flex items-center justify-center text-[var(--color-muted-foreground)]\" [class]=\"btnSize()\">{{ gap }}</span>\n } @else {\n <button\n type=\"button\"\n (click)=\"go(p)\"\n [attr.aria-current]=\"p === page() ? 'page' : null\"\n [class]=\"btnSize()\"\n class=\"inline-flex items-center justify-center rounded-lg border transition-colors cursor-pointer font-medium\"\n [class.bg-[var(--color-primary)]]=\"p === page()\"\n [class.text-[var(--color-primary-foreground)]]=\"p === page()\"\n [class.border-[var(--color-primary)]]=\"p === page()\"\n [class.border-[var(--color-border)]]=\"p !== page()\"\n [class.text-[var(--color-foreground)]]=\"p !== page()\"\n [class.hover:bg-[var(--color-accent)]]=\"p !== page()\"\n >\n {{ p }}\n </button>\n }\n }\n }\n\n <!-- Next -->\n <button\n type=\"button\"\n [class]=\"baseBtn + ' ' + btnSize()\"\n [disabled]=\"!canNext()\"\n (click)=\"next()\"\n aria-label=\"P\u00E1gina siguiente\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m9 18 6-6-6-6\" /></svg>\n </button>\n</div>\n\n@if (pageSizeOptions().length) {\n <div class=\"flex items-center gap-2 text-sm text-[var(--color-muted-foreground)]\">\n <span>Por p\u00E1gina:</span>\n <div class=\"w-20\">\n <ui-select [value]=\"pageSizeStr()\" (valueChange)=\"onPageSize($event)\" size=\"sm\">\n @for (opt of pageSizeOptions(); track opt) {\n <ui-option [value]=\"opt.toString()\">{{ opt }}</ui-option>\n }\n </ui-select>\n </div>\n </div>\n}\n", dependencies: [{ kind: "component", type: SelectComponent, selector: "ui-select", inputs: ["value", "placeholder", "label", "hint", "error", "size", "id", "disabled", "required", "invalid", "touched", "errors"], outputs: ["valueChange"] }, { kind: "component", type: OptionComponent, selector: "ui-option", inputs: ["value", "disabled"] }] });
|
|
1332
|
+
}
|
|
1333
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: PaginationComponent, decorators: [{
|
|
1334
|
+
type: Component,
|
|
1335
|
+
args: [{ selector: 'ui-pagination', imports: [SelectComponent, OptionComponent], host: { role: 'navigation', 'aria-label': 'Paginación', class: 'flex items-center gap-3 flex-wrap' }, template: "@let baseBtn =\n 'inline-flex items-center justify-center rounded-lg border border-[var(--color-border)] text-[var(--color-foreground)] transition-colors disabled:opacity-40 disabled:pointer-events-none cursor-pointer hover:bg-[var(--color-accent)]';\n\n<div class=\"flex items-center gap-1\">\n <!-- Prev -->\n <button\n type=\"button\"\n [class]=\"baseBtn + ' ' + btnSize()\"\n [disabled]=\"!canPrev()\"\n (click)=\"prev()\"\n aria-label=\"P\u00E1gina anterior\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m15 18-6-6 6-6\" /></svg>\n </button>\n\n @if (variant() === 'compact') {\n <span class=\"px-3 text-sm text-[var(--color-muted-foreground)]\">\n P\u00E1gina <span class=\"font-medium text-[var(--color-foreground)]\">{{ page() }}</span> de {{ totalPages() }}\n </span>\n } @else {\n @for (p of pages(); track $index) {\n @if (isGap(p)) {\n <span class=\"inline-flex items-center justify-center text-[var(--color-muted-foreground)]\" [class]=\"btnSize()\">{{ gap }}</span>\n } @else {\n <button\n type=\"button\"\n (click)=\"go(p)\"\n [attr.aria-current]=\"p === page() ? 'page' : null\"\n [class]=\"btnSize()\"\n class=\"inline-flex items-center justify-center rounded-lg border transition-colors cursor-pointer font-medium\"\n [class.bg-[var(--color-primary)]]=\"p === page()\"\n [class.text-[var(--color-primary-foreground)]]=\"p === page()\"\n [class.border-[var(--color-primary)]]=\"p === page()\"\n [class.border-[var(--color-border)]]=\"p !== page()\"\n [class.text-[var(--color-foreground)]]=\"p !== page()\"\n [class.hover:bg-[var(--color-accent)]]=\"p !== page()\"\n >\n {{ p }}\n </button>\n }\n }\n }\n\n <!-- Next -->\n <button\n type=\"button\"\n [class]=\"baseBtn + ' ' + btnSize()\"\n [disabled]=\"!canNext()\"\n (click)=\"next()\"\n aria-label=\"P\u00E1gina siguiente\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m9 18 6-6-6-6\" /></svg>\n </button>\n</div>\n\n@if (pageSizeOptions().length) {\n <div class=\"flex items-center gap-2 text-sm text-[var(--color-muted-foreground)]\">\n <span>Por p\u00E1gina:</span>\n <div class=\"w-20\">\n <ui-select [value]=\"pageSizeStr()\" (valueChange)=\"onPageSize($event)\" size=\"sm\">\n @for (opt of pageSizeOptions(); track opt) {\n <ui-option [value]=\"opt.toString()\">{{ opt }}</ui-option>\n }\n </ui-select>\n </div>\n </div>\n}\n" }]
|
|
1336
|
+
}], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], totalPages: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalPages", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
1337
|
+
|
|
1338
|
+
let nextId$2 = 0;
|
|
1339
|
+
/**
|
|
1340
|
+
* Grupo de selección única. El valor vive aquí. Funciona con Signal Forms
|
|
1341
|
+
* (`[formField]`, FormValueControl), ControlValueAccessor y two-way `[(value)]`.
|
|
1342
|
+
* Los ui-radio hijos lo inyectan por DI.
|
|
1343
|
+
*/
|
|
1344
|
+
class RadioGroupComponent {
|
|
1345
|
+
value = model('', /* @ts-ignore */
|
|
1346
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1347
|
+
size = input('md', /* @ts-ignore */
|
|
1348
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1349
|
+
orientation = input('vertical', /* @ts-ignore */
|
|
1350
|
+
...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
1351
|
+
appearance = input('default', /* @ts-ignore */
|
|
1352
|
+
...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
|
|
1353
|
+
name = input(`ui-radio-group-${nextId$2++}`, /* @ts-ignore */
|
|
1354
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
1355
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1356
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1357
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1358
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1359
|
+
errors = input([], /* @ts-ignore */
|
|
1360
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
1361
|
+
cvaDisabled = signal(false, /* @ts-ignore */
|
|
1362
|
+
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
1363
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
1364
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
1365
|
+
errorMessage = computed(() => {
|
|
1366
|
+
if (this.touched()) {
|
|
1367
|
+
const first = this.errors()[0];
|
|
1368
|
+
if (first)
|
|
1369
|
+
return first.message ?? first.kind ?? 'Campo inválido';
|
|
1370
|
+
}
|
|
1371
|
+
return '';
|
|
1372
|
+
}, /* @ts-ignore */
|
|
1373
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
1374
|
+
hasError = computed(() => !!this.errorMessage() || (this.invalid() && this.touched()), /* @ts-ignore */
|
|
1375
|
+
...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
1376
|
+
hostClasses = computed(() => {
|
|
1377
|
+
const dir = this.orientation() === 'horizontal' ? 'flex flex-row flex-wrap gap-3' : 'flex flex-col gap-3';
|
|
1378
|
+
return dir;
|
|
1379
|
+
}, /* @ts-ignore */
|
|
1380
|
+
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
1381
|
+
// --- ControlValueAccessor ---
|
|
1382
|
+
onChange = () => { };
|
|
1383
|
+
onTouched = () => { };
|
|
1384
|
+
writeValue(value) {
|
|
1385
|
+
this.value.set(value ?? '');
|
|
1386
|
+
}
|
|
1387
|
+
registerOnChange(fn) {
|
|
1388
|
+
this.onChange = fn;
|
|
1389
|
+
}
|
|
1390
|
+
registerOnTouched(fn) {
|
|
1391
|
+
this.onTouched = fn;
|
|
1392
|
+
}
|
|
1393
|
+
setDisabledState(isDisabled) {
|
|
1394
|
+
this.cvaDisabled.set(isDisabled);
|
|
1395
|
+
}
|
|
1396
|
+
/** Llamado por un ui-radio hijo al seleccionarse. */
|
|
1397
|
+
select(value) {
|
|
1398
|
+
this.value.set(value);
|
|
1399
|
+
this.onChange(value);
|
|
1400
|
+
this.onTouched();
|
|
1401
|
+
}
|
|
1402
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1403
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.2", type: RadioGroupComponent, isStandalone: true, selector: "ui-radio-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { attributes: { "role": "radiogroup" }, properties: { "class": "hostClasses()" } }, providers: [
|
|
1404
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true },
|
|
1405
|
+
], ngImport: i0, template: `<ng-content />`, isInline: true });
|
|
1406
|
+
}
|
|
1407
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RadioGroupComponent, decorators: [{
|
|
1408
|
+
type: Component,
|
|
1409
|
+
args: [{
|
|
1410
|
+
selector: 'ui-radio-group',
|
|
1411
|
+
template: `<ng-content />`,
|
|
1412
|
+
host: { role: 'radiogroup', '[class]': 'hostClasses()' },
|
|
1413
|
+
providers: [
|
|
1414
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true },
|
|
1415
|
+
],
|
|
1416
|
+
}]
|
|
1417
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }] } });
|
|
1418
|
+
/** Una opción dentro de ui-radio-group. */
|
|
1419
|
+
class RadioComponent {
|
|
1420
|
+
group = inject(RadioGroupComponent);
|
|
1421
|
+
value = input.required(/* @ts-ignore */
|
|
1422
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1423
|
+
label = input('', /* @ts-ignore */
|
|
1424
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1425
|
+
description = input('', /* @ts-ignore */
|
|
1426
|
+
...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
|
|
1427
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1428
|
+
id = `ui-radio-${nextId$2++}`;
|
|
1429
|
+
checked = computed(() => this.group.value() === this.value(), /* @ts-ignore */
|
|
1430
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
1431
|
+
isDisabled = computed(() => this.group.isDisabled() || this.disabled(), /* @ts-ignore */
|
|
1432
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
1433
|
+
circleClasses = computed(() => {
|
|
1434
|
+
const sizeMap = { sm: 'size-4', md: 'size-5' };
|
|
1435
|
+
const base = 'shrink-0 flex items-center justify-center rounded-full border transition-all peer-focus-visible:ring-[3px] peer-focus-visible:ring-[var(--color-ring)]/40';
|
|
1436
|
+
const state = this.checked()
|
|
1437
|
+
? 'border-[var(--color-primary)]'
|
|
1438
|
+
: this.group.hasError()
|
|
1439
|
+
? 'border-[var(--color-destructive)]'
|
|
1440
|
+
: 'border-[var(--color-input)]';
|
|
1441
|
+
const disabled = this.isDisabled() ? 'opacity-50' : '';
|
|
1442
|
+
return [base, sizeMap[this.group.size()], state, disabled].join(' ');
|
|
1443
|
+
}, /* @ts-ignore */
|
|
1444
|
+
...(ngDevMode ? [{ debugName: "circleClasses" }] : /* istanbul ignore next */ []));
|
|
1445
|
+
dotClasses = computed(() => {
|
|
1446
|
+
const sizeMap = { sm: 'size-1.5', md: 'size-2' };
|
|
1447
|
+
return `rounded-full bg-[var(--color-primary)] ${sizeMap[this.group.size()]}`;
|
|
1448
|
+
}, /* @ts-ignore */
|
|
1449
|
+
...(ngDevMode ? [{ debugName: "dotClasses" }] : /* istanbul ignore next */ []));
|
|
1450
|
+
cardClasses = computed(() => {
|
|
1451
|
+
const base = 'rounded-[var(--radius)] border p-4 transition-all';
|
|
1452
|
+
const state = this.checked()
|
|
1453
|
+
? 'border-[var(--color-primary)] ring-1 ring-[var(--color-primary)] bg-[var(--color-primary)]/5'
|
|
1454
|
+
: 'border-[var(--color-border)] hover:border-[var(--color-muted-foreground)]/40';
|
|
1455
|
+
const disabled = this.isDisabled() ? 'opacity-50' : '';
|
|
1456
|
+
return [base, state, disabled].join(' ');
|
|
1457
|
+
}, /* @ts-ignore */
|
|
1458
|
+
...(ngDevMode ? [{ debugName: "cardClasses" }] : /* istanbul ignore next */ []));
|
|
1459
|
+
onSelect() {
|
|
1460
|
+
if (!this.isDisabled())
|
|
1461
|
+
this.group.select(this.value());
|
|
1462
|
+
}
|
|
1463
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RadioComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1464
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: RadioComponent, isStandalone: true, selector: "ui-radio", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: "@let card = group.appearance() === 'card';\n\n<label\n [for]=\"id\"\n [class]=\"card ? cardClasses() : ''\"\n class=\"flex items-start gap-2.5\"\n [class.cursor-pointer]=\"!isDisabled()\"\n [class.cursor-not-allowed]=\"isDisabled()\"\n>\n <span class=\"relative inline-flex items-center\">\n <input\n type=\"radio\"\n [id]=\"id\"\n [name]=\"group.name()\"\n [value]=\"value()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n (change)=\"onSelect()\"\n class=\"peer absolute inset-0 size-full opacity-0 m-0 cursor-pointer disabled:cursor-not-allowed\"\n />\n <span [class]=\"circleClasses()\">\n @if (checked()) {\n <span [class]=\"dotClasses()\"></span>\n }\n </span>\n </span>\n\n @if (label() || description()) {\n <span class=\"flex flex-col gap-0.5 -mt-px\">\n @if (label()) {\n <span class=\"text-sm font-medium text-[var(--color-foreground)] leading-tight\">\n {{ label() }}\n </span>\n }\n @if (description()) {\n <span class=\"text-xs text-[var(--color-muted-foreground)]\">{{ description() }}</span>\n }\n </span>\n }\n</label>\n" });
|
|
1465
|
+
}
|
|
1466
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RadioComponent, decorators: [{
|
|
1467
|
+
type: Component,
|
|
1468
|
+
args: [{ selector: 'ui-radio', host: { class: 'block' }, template: "@let card = group.appearance() === 'card';\n\n<label\n [for]=\"id\"\n [class]=\"card ? cardClasses() : ''\"\n class=\"flex items-start gap-2.5\"\n [class.cursor-pointer]=\"!isDisabled()\"\n [class.cursor-not-allowed]=\"isDisabled()\"\n>\n <span class=\"relative inline-flex items-center\">\n <input\n type=\"radio\"\n [id]=\"id\"\n [name]=\"group.name()\"\n [value]=\"value()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n (change)=\"onSelect()\"\n class=\"peer absolute inset-0 size-full opacity-0 m-0 cursor-pointer disabled:cursor-not-allowed\"\n />\n <span [class]=\"circleClasses()\">\n @if (checked()) {\n <span [class]=\"dotClasses()\"></span>\n }\n </span>\n </span>\n\n @if (label() || description()) {\n <span class=\"flex flex-col gap-0.5 -mt-px\">\n @if (label()) {\n <span class=\"text-sm font-medium text-[var(--color-foreground)] leading-tight\">\n {{ label() }}\n </span>\n }\n @if (description()) {\n <span class=\"text-xs text-[var(--color-muted-foreground)]\">{{ description() }}</span>\n }\n </span>\n }\n</label>\n" }]
|
|
1469
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
1470
|
+
|
|
1471
|
+
/** Resuelve un path anidado: getByPath(row, 'direccion.ciudad'). */
|
|
1472
|
+
function getByPath(obj, path) {
|
|
1473
|
+
if (!obj || !path)
|
|
1474
|
+
return undefined;
|
|
1475
|
+
return path.split('.').reduce((acc, key) => {
|
|
1476
|
+
return acc && typeof acc === 'object' ? acc[key] : undefined;
|
|
1477
|
+
}, obj);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/** Marca un template de celda para un field: <ng-template tableCell="estado">. */
|
|
1481
|
+
class TableCellDirective {
|
|
1482
|
+
field = input.required({ ...(ngDevMode ? { debugName: "field" } : /* istanbul ignore next */ {}), alias: 'tableCell' });
|
|
1483
|
+
template = inject((TemplateRef));
|
|
1484
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableCellDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1485
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: TableCellDirective, isStandalone: true, selector: "[tableCell]", inputs: { field: { classPropertyName: "field", publicName: "tableCell", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
1486
|
+
}
|
|
1487
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableCellDirective, decorators: [{
|
|
1488
|
+
type: Directive,
|
|
1489
|
+
args: [{ selector: '[tableCell]' }]
|
|
1490
|
+
}], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableCell", required: true }] }] } });
|
|
1491
|
+
/**
|
|
1492
|
+
* Tabla data-driven. Pinta `columns` automáticamente (incluido anidado);
|
|
1493
|
+
* para render custom, declara <ng-template tableCell="<field>"> y se usa solo
|
|
1494
|
+
* en esa columna. Soporta sorting, selección, loading y estado vacío.
|
|
1495
|
+
*/
|
|
1496
|
+
class TableComponent {
|
|
1497
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1498
|
+
data = input([], /* @ts-ignore */
|
|
1499
|
+
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
1500
|
+
columns = input([], /* @ts-ignore */
|
|
1501
|
+
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
1502
|
+
loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1503
|
+
rowKey = input('', /* @ts-ignore */
|
|
1504
|
+
...(ngDevMode ? [{ debugName: "rowKey" }] : /* istanbul ignore next */ []));
|
|
1505
|
+
selectable = input(false, { ...(ngDevMode ? { debugName: "selectable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1506
|
+
emptyMessage = input('No hay resultados', /* @ts-ignore */
|
|
1507
|
+
...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
|
|
1508
|
+
sortChange = output();
|
|
1509
|
+
selectionChange = output();
|
|
1510
|
+
cellTemplates = contentChildren(TableCellDirective, /* @ts-ignore */
|
|
1511
|
+
...(ngDevMode ? [{ debugName: "cellTemplates" }] : /* istanbul ignore next */ []));
|
|
1512
|
+
sort = signal({ field: '', direction: null }, /* @ts-ignore */
|
|
1513
|
+
...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
|
|
1514
|
+
selectedKeys = signal(new Set(), /* @ts-ignore */
|
|
1515
|
+
...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
|
|
1516
|
+
// Filas de skeleton para el estado de carga.
|
|
1517
|
+
skeletonRows = [0, 1, 2, 3, 4];
|
|
1518
|
+
templateFor(field) {
|
|
1519
|
+
if (!field)
|
|
1520
|
+
return null;
|
|
1521
|
+
return this.cellTemplates().find((t) => t.field() === field)?.template ?? null;
|
|
1522
|
+
}
|
|
1523
|
+
value(row, field) {
|
|
1524
|
+
return getByPath(row, field);
|
|
1525
|
+
}
|
|
1526
|
+
rows = computed(() => {
|
|
1527
|
+
const list = [...this.data()];
|
|
1528
|
+
const { field, direction } = this.sort();
|
|
1529
|
+
if (!field || !direction)
|
|
1530
|
+
return list;
|
|
1531
|
+
return list.sort((a, b) => {
|
|
1532
|
+
const av = getByPath(a, field);
|
|
1533
|
+
const bv = getByPath(b, field);
|
|
1534
|
+
if (av == null)
|
|
1535
|
+
return 1;
|
|
1536
|
+
if (bv == null)
|
|
1537
|
+
return -1;
|
|
1538
|
+
const cmp = av < bv ? -1 : av > bv ? 1 : 0;
|
|
1539
|
+
return direction === 'asc' ? cmp : -cmp;
|
|
1540
|
+
});
|
|
1541
|
+
}, /* @ts-ignore */
|
|
1542
|
+
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
1543
|
+
align(a) {
|
|
1544
|
+
return a === 'right' ? 'text-right' : a === 'center' ? 'text-center' : 'text-left';
|
|
1545
|
+
}
|
|
1546
|
+
// --- Sorting ---
|
|
1547
|
+
toggleSort(col) {
|
|
1548
|
+
if (!col.sortable || !col.field)
|
|
1549
|
+
return;
|
|
1550
|
+
const cur = this.sort();
|
|
1551
|
+
let direction;
|
|
1552
|
+
if (cur.field !== col.field)
|
|
1553
|
+
direction = 'asc';
|
|
1554
|
+
else
|
|
1555
|
+
direction = cur.direction === 'asc' ? 'desc' : cur.direction === 'desc' ? null : 'asc';
|
|
1556
|
+
const next = { field: direction ? col.field : '', direction };
|
|
1557
|
+
this.sort.set(next);
|
|
1558
|
+
this.sortChange.emit(next);
|
|
1559
|
+
}
|
|
1560
|
+
sortDir(col) {
|
|
1561
|
+
return this.sort().field === col.field ? this.sort().direction : null;
|
|
1562
|
+
}
|
|
1563
|
+
// --- Selección ---
|
|
1564
|
+
keyOf(row) {
|
|
1565
|
+
const k = this.rowKey();
|
|
1566
|
+
return k ? getByPath(row, k) : row;
|
|
1567
|
+
}
|
|
1568
|
+
isSelected(row) {
|
|
1569
|
+
return this.selectedKeys().has(this.keyOf(row));
|
|
1570
|
+
}
|
|
1571
|
+
toggleRow(row) {
|
|
1572
|
+
const key = this.keyOf(row);
|
|
1573
|
+
this.selectedKeys.update((set) => {
|
|
1574
|
+
const next = new Set(set);
|
|
1575
|
+
if (next.has(key))
|
|
1576
|
+
next.delete(key);
|
|
1577
|
+
else
|
|
1578
|
+
next.add(key);
|
|
1579
|
+
return next;
|
|
1580
|
+
});
|
|
1581
|
+
this.emitSelection();
|
|
1582
|
+
}
|
|
1583
|
+
allSelected = computed(() => {
|
|
1584
|
+
const rows = this.rows();
|
|
1585
|
+
return rows.length > 0 && rows.every((r) => this.selectedKeys().has(this.keyOf(r)));
|
|
1586
|
+
}, /* @ts-ignore */
|
|
1587
|
+
...(ngDevMode ? [{ debugName: "allSelected" }] : /* istanbul ignore next */ []));
|
|
1588
|
+
someSelected = computed(() => {
|
|
1589
|
+
const rows = this.rows();
|
|
1590
|
+
const sel = rows.filter((r) => this.selectedKeys().has(this.keyOf(r))).length;
|
|
1591
|
+
return sel > 0 && sel < rows.length;
|
|
1592
|
+
}, /* @ts-ignore */
|
|
1593
|
+
...(ngDevMode ? [{ debugName: "someSelected" }] : /* istanbul ignore next */ []));
|
|
1594
|
+
toggleAll() {
|
|
1595
|
+
const rows = this.rows();
|
|
1596
|
+
this.selectedKeys.update((set) => {
|
|
1597
|
+
const next = new Set(set);
|
|
1598
|
+
if (rows.every((r) => next.has(this.keyOf(r)))) {
|
|
1599
|
+
rows.forEach((r) => next.delete(this.keyOf(r)));
|
|
1600
|
+
}
|
|
1601
|
+
else {
|
|
1602
|
+
rows.forEach((r) => next.add(this.keyOf(r)));
|
|
1603
|
+
}
|
|
1604
|
+
return next;
|
|
1605
|
+
});
|
|
1606
|
+
this.emitSelection();
|
|
1607
|
+
}
|
|
1608
|
+
emitSelection() {
|
|
1609
|
+
const sel = this.data().filter((r) => this.selectedKeys().has(this.keyOf(r)));
|
|
1610
|
+
this.selectionChange.emit(sel);
|
|
1611
|
+
}
|
|
1612
|
+
trackRow = (index, row) => (this.rowKey() ? this.keyOf(row) : index);
|
|
1613
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1614
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TableComponent, isStandalone: true, selector: "ui-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", selectionChange: "selectionChange" }, queries: [{ propertyName: "cellTemplates", predicate: TableCellDirective, isSignal: true }], ngImport: i0, template: "<div class=\"w-full overflow-x-auto rounded-[var(--radius)] border border-[var(--color-border)]\">\n <table class=\"w-full text-left text-sm\">\n <thead class=\"bg-[var(--color-muted)] text-[var(--color-muted-foreground)]\">\n <tr>\n @if (selectable()) {\n <th class=\"w-10 px-4 py-2.5\">\n <ui-checkbox\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (checkedChange)=\"toggleAll()\"\n />\n </th>\n }\n @for (col of columns(); track col.header) {\n <th\n class=\"px-4 py-2.5 font-medium whitespace-nowrap\"\n [class]=\"align(col.align)\"\n [style.width]=\"col.width\"\n >\n @if (col.sortable && col.field) {\n <button\n type=\"button\"\n (click)=\"toggleSort(col)\"\n class=\"inline-flex items-center gap-1 hover:text-[var(--color-foreground)] transition-colors cursor-pointer\"\n >\n {{ col.header }}\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n class=\"transition-opacity\"\n [class.opacity-30]=\"sortDir(col) === null\"\n >\n @if (sortDir(col) === 'desc') {\n <path d=\"m6 9 6 6 6-6\" />\n } @else {\n <path d=\"m18 15-6-6-6 6\" />\n }\n </svg>\n </button>\n } @else {\n {{ col.header }}\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody class=\"text-[var(--color-foreground)]\">\n @if (loading()) {\n @for (s of skeletonRows; track s) {\n <tr class=\"border-t border-[var(--color-border)]\">\n @if (selectable()) {\n <td class=\"px-4 py-3\"><ui-skeleton width=\"1rem\" height=\"1rem\" /></td>\n }\n @for (col of columns(); track col.header) {\n <td class=\"px-4 py-3\"><ui-skeleton width=\"70%\" /></td>\n }\n </tr>\n }\n } @else if (rows().length === 0) {\n <tr>\n <td\n [attr.colspan]=\"columns().length + (selectable() ? 1 : 0)\"\n class=\"px-4 py-10 text-center text-[var(--color-muted-foreground)]\"\n >\n {{ emptyMessage() }}\n </td>\n </tr>\n } @else {\n @for (row of rows(); track trackRow($index, row)) {\n <tr class=\"border-t border-[var(--color-border)] hover:bg-[var(--color-muted)]/40 transition-colors\">\n @if (selectable()) {\n <td class=\"px-4 py-3\">\n <ui-checkbox [checked]=\"isSelected(row)\" (checkedChange)=\"toggleRow(row)\" />\n </td>\n }\n @for (col of columns(); track col.header) {\n <td class=\"px-4 py-3 align-middle\" [class]=\"align(col.align)\">\n @if (templateFor(col.field); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value(row, col.field), row, index: $index }\"\n />\n } @else {\n {{ value(row, col.field) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: CheckboxComponent, selector: "ui-checkbox", inputs: ["checked", "label", "description", "size", "id", "name", "disabled", "required", "indeterminate", "invalid", "touched", "errors"], outputs: ["checkedChange"] }, { kind: "component", type: SkeletonComponent, selector: "ui-skeleton", inputs: ["width", "height", "circle"] }] });
|
|
1615
|
+
}
|
|
1616
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableComponent, decorators: [{
|
|
1617
|
+
type: Component,
|
|
1618
|
+
args: [{ selector: 'ui-table', imports: [NgTemplateOutlet, CheckboxComponent, SkeletonComponent], template: "<div class=\"w-full overflow-x-auto rounded-[var(--radius)] border border-[var(--color-border)]\">\n <table class=\"w-full text-left text-sm\">\n <thead class=\"bg-[var(--color-muted)] text-[var(--color-muted-foreground)]\">\n <tr>\n @if (selectable()) {\n <th class=\"w-10 px-4 py-2.5\">\n <ui-checkbox\n [checked]=\"allSelected()\"\n [indeterminate]=\"someSelected()\"\n (checkedChange)=\"toggleAll()\"\n />\n </th>\n }\n @for (col of columns(); track col.header) {\n <th\n class=\"px-4 py-2.5 font-medium whitespace-nowrap\"\n [class]=\"align(col.align)\"\n [style.width]=\"col.width\"\n >\n @if (col.sortable && col.field) {\n <button\n type=\"button\"\n (click)=\"toggleSort(col)\"\n class=\"inline-flex items-center gap-1 hover:text-[var(--color-foreground)] transition-colors cursor-pointer\"\n >\n {{ col.header }}\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n class=\"transition-opacity\"\n [class.opacity-30]=\"sortDir(col) === null\"\n >\n @if (sortDir(col) === 'desc') {\n <path d=\"m6 9 6 6 6-6\" />\n } @else {\n <path d=\"m18 15-6-6-6 6\" />\n }\n </svg>\n </button>\n } @else {\n {{ col.header }}\n }\n </th>\n }\n </tr>\n </thead>\n\n <tbody class=\"text-[var(--color-foreground)]\">\n @if (loading()) {\n @for (s of skeletonRows; track s) {\n <tr class=\"border-t border-[var(--color-border)]\">\n @if (selectable()) {\n <td class=\"px-4 py-3\"><ui-skeleton width=\"1rem\" height=\"1rem\" /></td>\n }\n @for (col of columns(); track col.header) {\n <td class=\"px-4 py-3\"><ui-skeleton width=\"70%\" /></td>\n }\n </tr>\n }\n } @else if (rows().length === 0) {\n <tr>\n <td\n [attr.colspan]=\"columns().length + (selectable() ? 1 : 0)\"\n class=\"px-4 py-10 text-center text-[var(--color-muted-foreground)]\"\n >\n {{ emptyMessage() }}\n </td>\n </tr>\n } @else {\n @for (row of rows(); track trackRow($index, row)) {\n <tr class=\"border-t border-[var(--color-border)] hover:bg-[var(--color-muted)]/40 transition-colors\">\n @if (selectable()) {\n <td class=\"px-4 py-3\">\n <ui-checkbox [checked]=\"isSelected(row)\" (checkedChange)=\"toggleRow(row)\" />\n </td>\n }\n @for (col of columns(); track col.header) {\n <td class=\"px-4 py-3 align-middle\" [class]=\"align(col.align)\">\n @if (templateFor(col.field); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value(row, col.field), row, index: $index }\"\n />\n } @else {\n {{ value(row, col.field) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n" }]
|
|
1619
|
+
}], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], cellTemplates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TableCellDirective), { isSignal: true }] }] } });
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Pestañas compositional. ui-tabs guarda la pestaña activa y arma la barra a
|
|
1623
|
+
* partir de los ui-tab hijos; cada ui-tab proyecta su panel (visible si activo).
|
|
1624
|
+
*/
|
|
1625
|
+
class TabsComponent {
|
|
1626
|
+
value = model('', /* @ts-ignore */
|
|
1627
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1628
|
+
variant = input('underline', /* @ts-ignore */
|
|
1629
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
1630
|
+
size = input('md', /* @ts-ignore */
|
|
1631
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1632
|
+
tabs = contentChildren(TabComponent, /* @ts-ignore */
|
|
1633
|
+
...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
1634
|
+
constructor() {
|
|
1635
|
+
// Selecciona la primera pestaña si no hay valor.
|
|
1636
|
+
effect(() => {
|
|
1637
|
+
const list = this.tabs();
|
|
1638
|
+
if (!this.value() && list.length)
|
|
1639
|
+
this.value.set(list[0].value());
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
listClasses = computed(() => {
|
|
1643
|
+
if (this.variant() === 'pills') {
|
|
1644
|
+
return 'inline-flex items-center gap-1 rounded-[var(--radius)] bg-[var(--color-muted)] p-1';
|
|
1645
|
+
}
|
|
1646
|
+
return 'flex items-center gap-1 border-b border-[var(--color-border)]';
|
|
1647
|
+
}, /* @ts-ignore */
|
|
1648
|
+
...(ngDevMode ? [{ debugName: "listClasses" }] : /* istanbul ignore next */ []));
|
|
1649
|
+
select(v) {
|
|
1650
|
+
this.value.set(v);
|
|
1651
|
+
}
|
|
1652
|
+
tabClasses(tab) {
|
|
1653
|
+
const active = this.value() === tab.value();
|
|
1654
|
+
const sizePad = this.size() === 'sm' ? 'h-8 px-2.5 text-xs gap-1.5' : 'h-9 px-3 text-sm gap-2';
|
|
1655
|
+
const base = 'inline-flex items-center font-medium transition-colors outline-none cursor-pointer disabled:opacity-50 disabled:pointer-events-none focus-visible:ring-2 focus-visible:ring-[var(--color-ring)] focus-visible:ring-offset-1 focus-visible:ring-offset-[var(--color-background)]';
|
|
1656
|
+
if (this.variant() === 'pills') {
|
|
1657
|
+
const state = active
|
|
1658
|
+
? 'bg-[var(--color-background)] text-[var(--color-foreground)] shadow-sm'
|
|
1659
|
+
: 'text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)]';
|
|
1660
|
+
return [base, sizePad, 'rounded-[calc(var(--radius)-0.25rem)]', state].join(' ');
|
|
1661
|
+
}
|
|
1662
|
+
// underline
|
|
1663
|
+
const state = active
|
|
1664
|
+
? 'text-[var(--color-foreground)] border-[var(--color-primary)]'
|
|
1665
|
+
: 'text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] border-transparent';
|
|
1666
|
+
return [base, sizePad, '-mb-px border-b-2', state].join(' ');
|
|
1667
|
+
}
|
|
1668
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1669
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TabsComponent, isStandalone: true, selector: "ui-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, queries: [{ propertyName: "tabs", predicate: TabComponent, isSignal: true }], ngImport: i0, template: "<div role=\"tablist\" [class]=\"listClasses()\">\n @for (tab of tabs(); track tab.value()) {\n <button\n type=\"button\"\n role=\"tab\"\n [attr.aria-selected]=\"value() === tab.value()\"\n [disabled]=\"tab.disabled()\"\n (click)=\"select(tab.value())\"\n [class]=\"tabClasses(tab)\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.iconTpl() ?? null\" />\n {{ tab.label() }}\n </button>\n }\n</div>\n\n<div class=\"mt-4\">\n <ng-content />\n</div>\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
1670
|
+
}
|
|
1671
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TabsComponent, decorators: [{
|
|
1672
|
+
type: Component,
|
|
1673
|
+
args: [{ selector: 'ui-tabs', imports: [NgTemplateOutlet], host: { class: 'block' }, template: "<div role=\"tablist\" [class]=\"listClasses()\">\n @for (tab of tabs(); track tab.value()) {\n <button\n type=\"button\"\n role=\"tab\"\n [attr.aria-selected]=\"value() === tab.value()\"\n [disabled]=\"tab.disabled()\"\n (click)=\"select(tab.value())\"\n [class]=\"tabClasses(tab)\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.iconTpl() ?? null\" />\n {{ tab.label() }}\n </button>\n }\n</div>\n\n<div class=\"mt-4\">\n <ng-content />\n</div>\n" }]
|
|
1674
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabComponent), { isSignal: true }] }] } });
|
|
1675
|
+
/** Una pestaña: label/ícono para la barra + su panel proyectado. */
|
|
1676
|
+
class TabComponent {
|
|
1677
|
+
value = input.required(/* @ts-ignore */
|
|
1678
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1679
|
+
label = input('', /* @ts-ignore */
|
|
1680
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1681
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1682
|
+
iconTpl = viewChild('icon', /* @ts-ignore */
|
|
1683
|
+
...(ngDevMode ? [{ debugName: "iconTpl" }] : /* istanbul ignore next */ []));
|
|
1684
|
+
tabs = inject(TabsComponent);
|
|
1685
|
+
isActive = computed(() => this.tabs.value() === this.value(), /* @ts-ignore */
|
|
1686
|
+
...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
|
|
1687
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1688
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TabComponent, isStandalone: true, selector: "ui-tab", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tabpanel" } }, viewQueries: [{ propertyName: "iconTpl", first: true, predicate: ["icon"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
1689
|
+
<ng-template #icon><ng-content select="[slot=icon]" /></ng-template>
|
|
1690
|
+
@if (isActive()) {
|
|
1691
|
+
<ng-content />
|
|
1692
|
+
}
|
|
1693
|
+
`, isInline: true });
|
|
1694
|
+
}
|
|
1695
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TabComponent, decorators: [{
|
|
1696
|
+
type: Component,
|
|
1697
|
+
args: [{
|
|
1698
|
+
selector: 'ui-tab',
|
|
1699
|
+
template: `
|
|
1700
|
+
<ng-template #icon><ng-content select="[slot=icon]" /></ng-template>
|
|
1701
|
+
@if (isActive()) {
|
|
1702
|
+
<ng-content />
|
|
1703
|
+
}
|
|
1704
|
+
`,
|
|
1705
|
+
host: { role: 'tabpanel' },
|
|
1706
|
+
}]
|
|
1707
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], iconTpl: [{ type: i0.ViewChild, args: ['icon', { isSignal: true }] }] } });
|
|
1708
|
+
|
|
1709
|
+
let nextId$1 = 0;
|
|
1710
|
+
/**
|
|
1711
|
+
* Campo de texto multilínea. Funciona con Signal Forms (`[formField]`),
|
|
1712
|
+
* ControlValueAccessor (ngModel/Reactive) y two-way `[(value)]`.
|
|
1713
|
+
*/
|
|
1714
|
+
class TextareaComponent {
|
|
1715
|
+
value = model('', /* @ts-ignore */
|
|
1716
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1717
|
+
label = input('', /* @ts-ignore */
|
|
1718
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1719
|
+
hint = input('', /* @ts-ignore */
|
|
1720
|
+
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
1721
|
+
error = input('', /* @ts-ignore */
|
|
1722
|
+
...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
1723
|
+
placeholder = input('', /* @ts-ignore */
|
|
1724
|
+
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
1725
|
+
name = input('', /* @ts-ignore */
|
|
1726
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
1727
|
+
id = input(`ui-textarea-${nextId$1++}`, /* @ts-ignore */
|
|
1728
|
+
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
1729
|
+
rows = input(4, { ...(ngDevMode ? { debugName: "rows" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
1730
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1731
|
+
readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1732
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1733
|
+
/** Crece en alto según el contenido. */
|
|
1734
|
+
autoGrow = input(false, { ...(ngDevMode ? { debugName: "autoGrow" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1735
|
+
invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1736
|
+
touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1737
|
+
errors = input([], /* @ts-ignore */
|
|
1738
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
1739
|
+
host = inject((ElementRef));
|
|
1740
|
+
cvaDisabled = signal(false, /* @ts-ignore */
|
|
1741
|
+
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
1742
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
1743
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
1744
|
+
errorMessage = computed(() => {
|
|
1745
|
+
if (this.error())
|
|
1746
|
+
return this.error();
|
|
1747
|
+
if (this.touched()) {
|
|
1748
|
+
const first = this.errors()[0];
|
|
1749
|
+
if (first)
|
|
1750
|
+
return first.message ?? first.kind ?? 'Campo inválido';
|
|
1751
|
+
}
|
|
1752
|
+
return '';
|
|
1753
|
+
}, /* @ts-ignore */
|
|
1754
|
+
...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
1755
|
+
hasError = computed(() => !!this.error() || !!this.errorMessage() || (this.invalid() && this.touched()), /* @ts-ignore */
|
|
1756
|
+
...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
1757
|
+
describedById = computed(() => this.hasError() || this.hint() ? `${this.id()}-desc` : null, /* @ts-ignore */
|
|
1758
|
+
...(ngDevMode ? [{ debugName: "describedById" }] : /* istanbul ignore next */ []));
|
|
1759
|
+
fieldClasses = computed(() => {
|
|
1760
|
+
const base = 'w-full rounded-[10px] border bg-[var(--color-background)] px-3 py-2 text-sm text-[var(--color-foreground)] placeholder:text-[var(--color-muted-foreground)] transition-all outline-none resize-y disabled:cursor-not-allowed';
|
|
1761
|
+
const state = this.hasError()
|
|
1762
|
+
? 'border-[var(--color-destructive)] focus:ring-[3px] focus:ring-[var(--color-destructive)]/25'
|
|
1763
|
+
: 'border-[var(--color-input)] focus:border-[var(--color-ring)] focus:ring-[3px] focus:ring-[var(--color-ring)]/40';
|
|
1764
|
+
const disabled = this.isDisabled() ? 'opacity-50 bg-[var(--color-muted)]/40' : '';
|
|
1765
|
+
const grow = this.autoGrow() ? 'resize-none overflow-hidden' : '';
|
|
1766
|
+
return [base, state, disabled, grow].join(' ');
|
|
1767
|
+
}, /* @ts-ignore */
|
|
1768
|
+
...(ngDevMode ? [{ debugName: "fieldClasses" }] : /* istanbul ignore next */ []));
|
|
1769
|
+
// --- ControlValueAccessor ---
|
|
1770
|
+
onChange = () => { };
|
|
1771
|
+
onTouched = () => { };
|
|
1772
|
+
writeValue(value) {
|
|
1773
|
+
this.value.set(value ?? '');
|
|
1774
|
+
}
|
|
1775
|
+
registerOnChange(fn) {
|
|
1776
|
+
this.onChange = fn;
|
|
1777
|
+
}
|
|
1778
|
+
registerOnTouched(fn) {
|
|
1779
|
+
this.onTouched = fn;
|
|
1780
|
+
}
|
|
1781
|
+
setDisabledState(isDisabled) {
|
|
1782
|
+
this.cvaDisabled.set(isDisabled);
|
|
1783
|
+
}
|
|
1784
|
+
onInput(event) {
|
|
1785
|
+
const el = event.target;
|
|
1786
|
+
this.value.set(el.value);
|
|
1787
|
+
this.onChange(el.value);
|
|
1788
|
+
if (this.autoGrow())
|
|
1789
|
+
this.grow(el);
|
|
1790
|
+
}
|
|
1791
|
+
onBlur() {
|
|
1792
|
+
this.onTouched();
|
|
1793
|
+
}
|
|
1794
|
+
grow(el) {
|
|
1795
|
+
el.style.height = 'auto';
|
|
1796
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
1797
|
+
}
|
|
1798
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1799
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TextareaComponent, isStandalone: true, selector: "ui-textarea", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, autoGrow: { classPropertyName: "autoGrow", publicName: "autoGrow", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { classAttribute: "block" }, providers: [
|
|
1800
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextareaComponent), multi: true },
|
|
1801
|
+
], ngImport: i0, template: "@if (label()) {\n <label [for]=\"id()\" class=\"block mb-1.5 text-sm font-medium text-[var(--color-foreground)]\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </label>\n}\n\n<textarea\n [id]=\"id()\"\n [name]=\"name()\"\n [rows]=\"rows()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"describedById()\"\n [class]=\"fieldClasses()\"\n></textarea>\n\n@if (hasError()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">{{ errorMessage() }}</p>\n} @else if (hint()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-muted-foreground)]\">{{ hint() }}</p>\n}\n" });
|
|
1802
|
+
}
|
|
1803
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TextareaComponent, decorators: [{
|
|
1804
|
+
type: Component,
|
|
1805
|
+
args: [{ selector: 'ui-textarea', host: { class: 'block' }, providers: [
|
|
1806
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextareaComponent), multi: true },
|
|
1807
|
+
], template: "@if (label()) {\n <label [for]=\"id()\" class=\"block mb-1.5 text-sm font-medium text-[var(--color-foreground)]\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </label>\n}\n\n<textarea\n [id]=\"id()\"\n [name]=\"name()\"\n [rows]=\"rows()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n [attr.aria-invalid]=\"hasError() || null\"\n [attr.aria-describedby]=\"describedById()\"\n [class]=\"fieldClasses()\"\n></textarea>\n\n@if (hasError()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-destructive)]\">{{ errorMessage() }}</p>\n} @else if (hint()) {\n <p [id]=\"describedById()\" class=\"mt-1.5 text-xs text-[var(--color-muted-foreground)]\">{{ hint() }}</p>\n}\n" }]
|
|
1808
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], autoGrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoGrow", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }] } });
|
|
1809
|
+
|
|
1810
|
+
/** Tarjeta individual de toast. */
|
|
1811
|
+
class ToastComponent {
|
|
1812
|
+
toast = input.required(/* @ts-ignore */
|
|
1813
|
+
...(ngDevMode ? [{ debugName: "toast" }] : /* istanbul ignore next */ []));
|
|
1814
|
+
close = output();
|
|
1815
|
+
/** Controla la transición de entrada (false → true tras montar). */
|
|
1816
|
+
shown = signal(false, /* @ts-ignore */
|
|
1817
|
+
...(ngDevMode ? [{ debugName: "shown" }] : /* istanbul ignore next */ []));
|
|
1818
|
+
constructor() {
|
|
1819
|
+
afterNextRender(() => this.shown.set(true));
|
|
1820
|
+
}
|
|
1821
|
+
iconColor = computed(() => {
|
|
1822
|
+
const map = {
|
|
1823
|
+
info: 'text-[var(--color-primary)]',
|
|
1824
|
+
success: 'text-emerald-600',
|
|
1825
|
+
warning: 'text-amber-600',
|
|
1826
|
+
error: 'text-[var(--color-destructive)]',
|
|
1827
|
+
};
|
|
1828
|
+
return map[this.toast().type];
|
|
1829
|
+
}, /* @ts-ignore */
|
|
1830
|
+
...(ngDevMode ? [{ debugName: "iconColor" }] : /* istanbul ignore next */ []));
|
|
1831
|
+
runAction() {
|
|
1832
|
+
this.toast().action?.handler();
|
|
1833
|
+
this.close.emit();
|
|
1834
|
+
}
|
|
1835
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToastComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1836
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ToastComponent, isStandalone: true, selector: "ui-toast", inputs: { toast: { classPropertyName: "toast", publicName: "toast", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { close: "close" }, ngImport: i0, template: "<div\n class=\"flex items-start gap-3 w-80 max-w-[calc(100vw-2rem)] rounded-[var(--radius)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3 text-sm shadow-lg transition-all duration-300\"\n [class.opacity-0]=\"!shown()\"\n [class.translate-y-2]=\"!shown()\"\n [class.opacity-100]=\"shown()\"\n>\n <span class=\"shrink-0 mt-0.5\" [class]=\"iconColor()\">\n @switch (toast().type) {\n @case ('success') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\" /><polyline points=\"22 4 12 14.01 9 11.01\" /></svg>\n }\n @case ('warning') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" /></svg>\n }\n @case ('error') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" /></svg>\n }\n @default {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" /></svg>\n }\n }\n </span>\n\n <div class=\"flex-1 min-w-0\">\n @if (toast().title) {\n <p class=\"font-semibold leading-tight text-[var(--color-foreground)]\">{{ toast().title }}</p>\n }\n <p class=\"text-[var(--color-muted-foreground)]\" [class.mt-0.5]=\"toast().title\">\n {{ toast().message }}\n </p>\n @if (toast().action; as action) {\n <button\n type=\"button\"\n (click)=\"runAction()\"\n class=\"mt-2 text-xs font-semibold text-[var(--color-primary)] hover:underline underline-offset-2 cursor-pointer\"\n >\n {{ action.label }}\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n (click)=\"close.emit()\"\n aria-label=\"Cerrar\"\n class=\"shrink-0 -mr-1 -mt-0.5 flex items-center justify-center size-6 rounded-md text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] hover:bg-[var(--color-accent)] transition-colors cursor-pointer\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n </button>\n</div>\n" });
|
|
1837
|
+
}
|
|
1838
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToastComponent, decorators: [{
|
|
1839
|
+
type: Component,
|
|
1840
|
+
args: [{ selector: 'ui-toast', template: "<div\n class=\"flex items-start gap-3 w-80 max-w-[calc(100vw-2rem)] rounded-[var(--radius)] border border-[var(--color-border)] bg-[var(--color-background)] px-4 py-3 text-sm shadow-lg transition-all duration-300\"\n [class.opacity-0]=\"!shown()\"\n [class.translate-y-2]=\"!shown()\"\n [class.opacity-100]=\"shown()\"\n>\n <span class=\"shrink-0 mt-0.5\" [class]=\"iconColor()\">\n @switch (toast().type) {\n @case ('success') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\" /><polyline points=\"22 4 12 14.01 9 11.01\" /></svg>\n }\n @case ('warning') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" /></svg>\n }\n @case ('error') {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" /></svg>\n }\n @default {\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" /></svg>\n }\n }\n </span>\n\n <div class=\"flex-1 min-w-0\">\n @if (toast().title) {\n <p class=\"font-semibold leading-tight text-[var(--color-foreground)]\">{{ toast().title }}</p>\n }\n <p class=\"text-[var(--color-muted-foreground)]\" [class.mt-0.5]=\"toast().title\">\n {{ toast().message }}\n </p>\n @if (toast().action; as action) {\n <button\n type=\"button\"\n (click)=\"runAction()\"\n class=\"mt-2 text-xs font-semibold text-[var(--color-primary)] hover:underline underline-offset-2 cursor-pointer\"\n >\n {{ action.label }}\n </button>\n }\n </div>\n\n <button\n type=\"button\"\n (click)=\"close.emit()\"\n aria-label=\"Cerrar\"\n class=\"shrink-0 -mr-1 -mt-0.5 flex items-center justify-center size-6 rounded-md text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] hover:bg-[var(--color-accent)] transition-colors cursor-pointer\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" /></svg>\n </button>\n</div>\n" }]
|
|
1841
|
+
}], ctorParameters: () => [], propDecorators: { toast: [{ type: i0.Input, args: [{ isSignal: true, alias: "toast", required: true }] }], close: [{ type: i0.Output, args: ["close"] }] } });
|
|
1842
|
+
|
|
1843
|
+
/** Contenedor flotante de toasts. Se auto-monta vía ToastService (interno). */
|
|
1844
|
+
class ToasterComponent {
|
|
1845
|
+
svc = inject(ToastService);
|
|
1846
|
+
containerClasses = computed(() => {
|
|
1847
|
+
const base = 'fixed z-[100] flex flex-col gap-2 p-4 pointer-events-none [&>*]:pointer-events-auto';
|
|
1848
|
+
const posMap = {
|
|
1849
|
+
'top-left': 'top-0 left-0 items-start',
|
|
1850
|
+
'top-center': 'top-0 left-1/2 -translate-x-1/2 items-center',
|
|
1851
|
+
'top-right': 'top-0 right-0 items-end',
|
|
1852
|
+
'bottom-left': 'bottom-0 left-0 items-start flex-col-reverse',
|
|
1853
|
+
'bottom-center': 'bottom-0 left-1/2 -translate-x-1/2 items-center flex-col-reverse',
|
|
1854
|
+
'bottom-right': 'bottom-0 right-0 items-end flex-col-reverse',
|
|
1855
|
+
};
|
|
1856
|
+
return [base, posMap[this.svc.position()]].join(' ');
|
|
1857
|
+
}, /* @ts-ignore */
|
|
1858
|
+
...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
|
|
1859
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToasterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1860
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ToasterComponent, isStandalone: true, selector: "ui-toaster", ngImport: i0, template: `
|
|
1861
|
+
<div [class]="containerClasses()">
|
|
1862
|
+
@for (t of svc.toasts(); track t.id) {
|
|
1863
|
+
<ui-toast [toast]="t" (close)="svc.dismiss(t.id)" />
|
|
1864
|
+
}
|
|
1865
|
+
</div>
|
|
1866
|
+
`, isInline: true, dependencies: [{ kind: "component", type: ToastComponent, selector: "ui-toast", inputs: ["toast"], outputs: ["close"] }] });
|
|
1867
|
+
}
|
|
1868
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToasterComponent, decorators: [{
|
|
1869
|
+
type: Component,
|
|
1870
|
+
args: [{
|
|
1871
|
+
selector: 'ui-toaster',
|
|
1872
|
+
imports: [ToastComponent],
|
|
1873
|
+
template: `
|
|
1874
|
+
<div [class]="containerClasses()">
|
|
1875
|
+
@for (t of svc.toasts(); track t.id) {
|
|
1876
|
+
<ui-toast [toast]="t" (close)="svc.dismiss(t.id)" />
|
|
1877
|
+
}
|
|
1878
|
+
</div>
|
|
1879
|
+
`,
|
|
1880
|
+
}]
|
|
1881
|
+
}] });
|
|
1882
|
+
|
|
1883
|
+
const MAX_TOASTS = 5;
|
|
1884
|
+
/**
|
|
1885
|
+
* API de notificaciones. Auto-monta el contenedor en el <body> al primer uso
|
|
1886
|
+
* (cero setup para el consumidor).
|
|
1887
|
+
*
|
|
1888
|
+
* private toast = inject(ToastService);
|
|
1889
|
+
* toast.success('Guardado');
|
|
1890
|
+
* toast.error('Falló', { title: 'Error' });
|
|
1891
|
+
*/
|
|
1892
|
+
class ToastService {
|
|
1893
|
+
appRef = inject(ApplicationRef);
|
|
1894
|
+
envInjector = inject(EnvironmentInjector);
|
|
1895
|
+
toasts = signal([], /* @ts-ignore */
|
|
1896
|
+
...(ngDevMode ? [{ debugName: "toasts" }] : /* istanbul ignore next */ []));
|
|
1897
|
+
position = signal('bottom-right', /* @ts-ignore */
|
|
1898
|
+
...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
1899
|
+
mounted = false;
|
|
1900
|
+
seq = 0;
|
|
1901
|
+
show(opts) {
|
|
1902
|
+
this.ensureMounted();
|
|
1903
|
+
const id = ++this.seq;
|
|
1904
|
+
const toast = {
|
|
1905
|
+
id,
|
|
1906
|
+
type: opts.type ?? 'info',
|
|
1907
|
+
title: opts.title,
|
|
1908
|
+
message: opts.message,
|
|
1909
|
+
action: opts.action,
|
|
1910
|
+
};
|
|
1911
|
+
this.toasts.update((list) => [...list, toast].slice(-MAX_TOASTS));
|
|
1912
|
+
const duration = opts.duration ?? 5000;
|
|
1913
|
+
if (duration > 0) {
|
|
1914
|
+
setTimeout(() => this.dismiss(id), duration);
|
|
1915
|
+
}
|
|
1916
|
+
return id;
|
|
1917
|
+
}
|
|
1918
|
+
success(message, opts) {
|
|
1919
|
+
return this.show({ ...opts, type: 'success', message });
|
|
1920
|
+
}
|
|
1921
|
+
error(message, opts) {
|
|
1922
|
+
return this.show({ ...opts, type: 'error', message });
|
|
1923
|
+
}
|
|
1924
|
+
warning(message, opts) {
|
|
1925
|
+
return this.show({ ...opts, type: 'warning', message });
|
|
1926
|
+
}
|
|
1927
|
+
info(message, opts) {
|
|
1928
|
+
return this.show({ ...opts, type: 'info', message });
|
|
1929
|
+
}
|
|
1930
|
+
dismiss(id) {
|
|
1931
|
+
this.toasts.update((list) => list.filter((t) => t.id !== id));
|
|
1932
|
+
}
|
|
1933
|
+
setPosition(position) {
|
|
1934
|
+
this.position.set(position);
|
|
1935
|
+
}
|
|
1936
|
+
ensureMounted() {
|
|
1937
|
+
if (this.mounted)
|
|
1938
|
+
return;
|
|
1939
|
+
const ref = createComponent(ToasterComponent, { environmentInjector: this.envInjector });
|
|
1940
|
+
this.appRef.attachView(ref.hostView);
|
|
1941
|
+
const node = ref.hostView.rootNodes[0];
|
|
1942
|
+
document.body.appendChild(node);
|
|
1943
|
+
this.mounted = true;
|
|
1944
|
+
}
|
|
1945
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToastService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1946
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToastService, providedIn: 'root' });
|
|
1947
|
+
}
|
|
1948
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ToastService, decorators: [{
|
|
1949
|
+
type: Injectable,
|
|
1950
|
+
args: [{ providedIn: 'root' }]
|
|
1951
|
+
}] });
|
|
1952
|
+
|
|
1953
|
+
const GAP = 8; // separación entre trigger y tooltip (px)
|
|
1954
|
+
const MARGIN = 8; // margen mínimo al borde del viewport (px)
|
|
1955
|
+
/**
|
|
1956
|
+
* Tooltip accesible sin dependencias: se añade a cualquier elemento.
|
|
1957
|
+
* Posiciona un elemento flotante (position: fixed) con auto-flip y flecha.
|
|
1958
|
+
*
|
|
1959
|
+
* <ui-button uiTooltip="Guardar" tooltipPlacement="top">Guardar</ui-button>
|
|
1960
|
+
*/
|
|
1961
|
+
class TooltipDirective {
|
|
1962
|
+
host = inject((ElementRef));
|
|
1963
|
+
renderer = inject(Renderer2);
|
|
1964
|
+
text = input('', { ...(ngDevMode ? { debugName: "text" } : /* istanbul ignore next */ {}), alias: 'uiTooltip' });
|
|
1965
|
+
placement = input('top', { ...(ngDevMode ? { debugName: "placement" } : /* istanbul ignore next */ {}), alias: 'tooltipPlacement' });
|
|
1966
|
+
delay = input(300, { ...(ngDevMode ? { debugName: "delay" } : /* istanbul ignore next */ {}), alias: 'tooltipDelay', transform: numberAttribute });
|
|
1967
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'tooltipDisabled', transform: booleanAttribute });
|
|
1968
|
+
tooltipEl = null;
|
|
1969
|
+
arrowEl = null;
|
|
1970
|
+
openTimer = null;
|
|
1971
|
+
tipId = `ui-tooltip-${Math.floor(performance.now() * 1000) % 1_000_000}`;
|
|
1972
|
+
onEnter() {
|
|
1973
|
+
this.scheduleShow();
|
|
1974
|
+
}
|
|
1975
|
+
onLeave() {
|
|
1976
|
+
this.hide();
|
|
1977
|
+
}
|
|
1978
|
+
onEscape() {
|
|
1979
|
+
this.hide();
|
|
1980
|
+
}
|
|
1981
|
+
scheduleShow() {
|
|
1982
|
+
if (this.disabled() || !this.text() || this.tooltipEl)
|
|
1983
|
+
return;
|
|
1984
|
+
this.clearTimer();
|
|
1985
|
+
this.openTimer = setTimeout(() => this.show(), this.delay());
|
|
1986
|
+
}
|
|
1987
|
+
show() {
|
|
1988
|
+
if (this.tooltipEl)
|
|
1989
|
+
return;
|
|
1990
|
+
const tip = this.renderer.createElement('div');
|
|
1991
|
+
tip.id = this.tipId;
|
|
1992
|
+
tip.setAttribute('role', 'tooltip');
|
|
1993
|
+
tip.className =
|
|
1994
|
+
'fixed z-50 px-2 py-1 rounded-md text-xs font-medium shadow-md max-w-xs whitespace-normal pointer-events-none opacity-0 transition-opacity duration-150 bg-[var(--color-foreground)] text-[var(--color-background)]';
|
|
1995
|
+
tip.textContent = this.text();
|
|
1996
|
+
const arrow = this.renderer.createElement('div');
|
|
1997
|
+
arrow.className = 'fixed z-50 size-2 rotate-45 pointer-events-none bg-[var(--color-foreground)]';
|
|
1998
|
+
this.renderer.appendChild(document.body, tip);
|
|
1999
|
+
this.renderer.appendChild(document.body, arrow);
|
|
2000
|
+
this.tooltipEl = tip;
|
|
2001
|
+
this.arrowEl = arrow;
|
|
2002
|
+
this.host.nativeElement.setAttribute('aria-describedby', this.tipId);
|
|
2003
|
+
this.position();
|
|
2004
|
+
// fade-in tras posicionar
|
|
2005
|
+
requestAnimationFrame(() => {
|
|
2006
|
+
if (this.tooltipEl)
|
|
2007
|
+
this.tooltipEl.style.opacity = '1';
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
position() {
|
|
2011
|
+
if (!this.tooltipEl || !this.arrowEl)
|
|
2012
|
+
return;
|
|
2013
|
+
const trigger = this.host.nativeElement.getBoundingClientRect();
|
|
2014
|
+
const tip = this.tooltipEl.getBoundingClientRect();
|
|
2015
|
+
const vw = window.innerWidth;
|
|
2016
|
+
const vh = window.innerHeight;
|
|
2017
|
+
let place = this.placement();
|
|
2018
|
+
// Auto-flip si no cabe en el lado elegido.
|
|
2019
|
+
const fits = (p) => {
|
|
2020
|
+
if (p === 'top')
|
|
2021
|
+
return trigger.top - GAP - tip.height >= MARGIN;
|
|
2022
|
+
if (p === 'bottom')
|
|
2023
|
+
return trigger.bottom + GAP + tip.height <= vh - MARGIN;
|
|
2024
|
+
if (p === 'left')
|
|
2025
|
+
return trigger.left - GAP - tip.width >= MARGIN;
|
|
2026
|
+
return trigger.right + GAP + tip.width <= vw - MARGIN; // right
|
|
2027
|
+
};
|
|
2028
|
+
const opposite = {
|
|
2029
|
+
top: 'bottom',
|
|
2030
|
+
bottom: 'top',
|
|
2031
|
+
left: 'right',
|
|
2032
|
+
right: 'left',
|
|
2033
|
+
};
|
|
2034
|
+
if (!fits(place) && fits(opposite[place]))
|
|
2035
|
+
place = opposite[place];
|
|
2036
|
+
let top = 0;
|
|
2037
|
+
let left = 0;
|
|
2038
|
+
let arrowTop = 0;
|
|
2039
|
+
let arrowLeft = 0;
|
|
2040
|
+
if (place === 'top' || place === 'bottom') {
|
|
2041
|
+
left = trigger.left + trigger.width / 2 - tip.width / 2;
|
|
2042
|
+
top = place === 'top' ? trigger.top - GAP - tip.height : trigger.bottom + GAP;
|
|
2043
|
+
arrowLeft = trigger.left + trigger.width / 2 - 4;
|
|
2044
|
+
arrowTop = place === 'top' ? trigger.top - GAP - 4 : trigger.bottom + GAP - 4;
|
|
2045
|
+
}
|
|
2046
|
+
else {
|
|
2047
|
+
top = trigger.top + trigger.height / 2 - tip.height / 2;
|
|
2048
|
+
left = place === 'left' ? trigger.left - GAP - tip.width : trigger.right + GAP;
|
|
2049
|
+
arrowTop = trigger.top + trigger.height / 2 - 4;
|
|
2050
|
+
arrowLeft = place === 'left' ? trigger.left - GAP - 4 : trigger.right + GAP - 4;
|
|
2051
|
+
}
|
|
2052
|
+
// Clamp horizontal/vertical al viewport.
|
|
2053
|
+
left = Math.max(MARGIN, Math.min(left, vw - tip.width - MARGIN));
|
|
2054
|
+
top = Math.max(MARGIN, Math.min(top, vh - tip.height - MARGIN));
|
|
2055
|
+
this.tooltipEl.style.top = `${Math.round(top)}px`;
|
|
2056
|
+
this.tooltipEl.style.left = `${Math.round(left)}px`;
|
|
2057
|
+
this.arrowEl.style.top = `${Math.round(arrowTop)}px`;
|
|
2058
|
+
this.arrowEl.style.left = `${Math.round(arrowLeft)}px`;
|
|
2059
|
+
}
|
|
2060
|
+
hide() {
|
|
2061
|
+
this.clearTimer();
|
|
2062
|
+
if (this.tooltipEl) {
|
|
2063
|
+
this.renderer.removeChild(document.body, this.tooltipEl);
|
|
2064
|
+
this.tooltipEl = null;
|
|
2065
|
+
}
|
|
2066
|
+
if (this.arrowEl) {
|
|
2067
|
+
this.renderer.removeChild(document.body, this.arrowEl);
|
|
2068
|
+
this.arrowEl = null;
|
|
2069
|
+
}
|
|
2070
|
+
this.host.nativeElement.removeAttribute('aria-describedby');
|
|
2071
|
+
}
|
|
2072
|
+
clearTimer() {
|
|
2073
|
+
if (this.openTimer) {
|
|
2074
|
+
clearTimeout(this.openTimer);
|
|
2075
|
+
this.openTimer = null;
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
ngOnDestroy() {
|
|
2079
|
+
this.hide();
|
|
2080
|
+
}
|
|
2081
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2082
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: TooltipDirective, isStandalone: true, selector: "[uiTooltip]", inputs: { text: { classPropertyName: "text", publicName: "uiTooltip", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "tooltipPlacement", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "tooltipDelay", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "tooltipDisabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onEnter()", "focusin": "onEnter()", "mouseleave": "onLeave()", "focusout": "onLeave()", "document:keydown.escape": "onEscape()" } }, ngImport: i0 });
|
|
2083
|
+
}
|
|
2084
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TooltipDirective, decorators: [{
|
|
2085
|
+
type: Directive,
|
|
2086
|
+
args: [{
|
|
2087
|
+
selector: '[uiTooltip]',
|
|
2088
|
+
}]
|
|
2089
|
+
}], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "uiTooltip", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPlacement", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDelay", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipDisabled", required: false }] }], onEnter: [{
|
|
2090
|
+
type: HostListener,
|
|
2091
|
+
args: ['mouseenter']
|
|
2092
|
+
}, {
|
|
2093
|
+
type: HostListener,
|
|
2094
|
+
args: ['focusin']
|
|
2095
|
+
}], onLeave: [{
|
|
2096
|
+
type: HostListener,
|
|
2097
|
+
args: ['mouseleave']
|
|
2098
|
+
}, {
|
|
2099
|
+
type: HostListener,
|
|
2100
|
+
args: ['focusout']
|
|
2101
|
+
}], onEscape: [{
|
|
2102
|
+
type: HostListener,
|
|
2103
|
+
args: ['document:keydown.escape']
|
|
2104
|
+
}] } });
|
|
2105
|
+
|
|
2106
|
+
let nextId = 0;
|
|
2107
|
+
/**
|
|
2108
|
+
* Interruptor on/off. Funciona con Signal Forms (`[formField]`, contrato
|
|
2109
|
+
* FormCheckboxControl), ControlValueAccessor (ngModel/Reactive) y two-way `[(checked)]`.
|
|
2110
|
+
*/
|
|
2111
|
+
class SwitchComponent {
|
|
2112
|
+
checked = model(false, /* @ts-ignore */
|
|
2113
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
2114
|
+
label = input('', /* @ts-ignore */
|
|
2115
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
2116
|
+
description = input('', /* @ts-ignore */
|
|
2117
|
+
...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
|
|
2118
|
+
size = input('md', /* @ts-ignore */
|
|
2119
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
2120
|
+
id = input(`ui-switch-${nextId++}`, /* @ts-ignore */
|
|
2121
|
+
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
2122
|
+
name = input('', /* @ts-ignore */
|
|
2123
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
2124
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2125
|
+
required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2126
|
+
cvaDisabled = signal(false, /* @ts-ignore */
|
|
2127
|
+
...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
2128
|
+
isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
|
|
2129
|
+
...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
2130
|
+
trackClasses = computed(() => {
|
|
2131
|
+
const sizeMap = {
|
|
2132
|
+
sm: 'h-4 w-7',
|
|
2133
|
+
md: 'h-5 w-9',
|
|
2134
|
+
};
|
|
2135
|
+
const base = 'relative shrink-0 inline-flex items-center rounded-full transition-colors outline-none peer-focus-visible:ring-[3px] peer-focus-visible:ring-[var(--color-ring)]/40';
|
|
2136
|
+
const state = this.checked()
|
|
2137
|
+
? 'bg-[var(--color-primary)]'
|
|
2138
|
+
: 'bg-[var(--color-input)]';
|
|
2139
|
+
const disabled = this.isDisabled() ? 'opacity-50' : '';
|
|
2140
|
+
return [base, sizeMap[this.size()], state, disabled].join(' ');
|
|
2141
|
+
}, /* @ts-ignore */
|
|
2142
|
+
...(ngDevMode ? [{ debugName: "trackClasses" }] : /* istanbul ignore next */ []));
|
|
2143
|
+
thumbClasses = computed(() => {
|
|
2144
|
+
// Tamaño del thumb y desplazamiento al activarse.
|
|
2145
|
+
const sm = this.size() === 'sm';
|
|
2146
|
+
const dims = sm ? 'size-3' : 'size-4';
|
|
2147
|
+
const off = 'translate-x-0.5';
|
|
2148
|
+
const on = sm ? 'translate-x-3.5' : 'translate-x-4';
|
|
2149
|
+
const base = 'inline-block rounded-full bg-white shadow-sm transition-transform';
|
|
2150
|
+
return [base, dims, this.checked() ? on : off].join(' ');
|
|
2151
|
+
}, /* @ts-ignore */
|
|
2152
|
+
...(ngDevMode ? [{ debugName: "thumbClasses" }] : /* istanbul ignore next */ []));
|
|
2153
|
+
// --- ControlValueAccessor ---
|
|
2154
|
+
onChange = () => { };
|
|
2155
|
+
onTouched = () => { };
|
|
2156
|
+
writeValue(value) {
|
|
2157
|
+
this.checked.set(!!value);
|
|
2158
|
+
}
|
|
2159
|
+
registerOnChange(fn) {
|
|
2160
|
+
this.onChange = fn;
|
|
2161
|
+
}
|
|
2162
|
+
registerOnTouched(fn) {
|
|
2163
|
+
this.onTouched = fn;
|
|
2164
|
+
}
|
|
2165
|
+
setDisabledState(isDisabled) {
|
|
2166
|
+
this.cvaDisabled.set(isDisabled);
|
|
2167
|
+
}
|
|
2168
|
+
toggle(event) {
|
|
2169
|
+
const next = event.target.checked;
|
|
2170
|
+
this.checked.set(next);
|
|
2171
|
+
this.onChange(next);
|
|
2172
|
+
this.onTouched();
|
|
2173
|
+
}
|
|
2174
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2175
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: SwitchComponent, isStandalone: true, selector: "ui-switch", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { classAttribute: "block" }, providers: [
|
|
2176
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SwitchComponent), multi: true },
|
|
2177
|
+
], ngImport: i0, template: "<label\n [for]=\"id()\"\n class=\"flex items-start gap-2.5\"\n [class.cursor-pointer]=\"!isDisabled()\"\n [class.cursor-not-allowed]=\"isDisabled()\"\n>\n <span class=\"relative inline-flex items-center\">\n <input\n type=\"checkbox\"\n role=\"switch\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n (change)=\"toggle($event)\"\n class=\"peer absolute inset-0 size-full opacity-0 m-0 cursor-pointer disabled:cursor-not-allowed\"\n />\n <span [class]=\"trackClasses()\">\n <span [class]=\"thumbClasses()\"></span>\n </span>\n </span>\n\n @if (label() || description()) {\n <span class=\"flex flex-col gap-0.5 -mt-px\">\n @if (label()) {\n <span class=\"text-sm font-medium text-[var(--color-foreground)] leading-tight\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </span>\n }\n @if (description()) {\n <span class=\"text-xs text-[var(--color-muted-foreground)]\">{{ description() }}</span>\n }\n </span>\n }\n</label>\n" });
|
|
2178
|
+
}
|
|
2179
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: SwitchComponent, decorators: [{
|
|
2180
|
+
type: Component,
|
|
2181
|
+
args: [{ selector: 'ui-switch', host: { class: 'block' }, providers: [
|
|
2182
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SwitchComponent), multi: true },
|
|
2183
|
+
], template: "<label\n [for]=\"id()\"\n class=\"flex items-start gap-2.5\"\n [class.cursor-pointer]=\"!isDisabled()\"\n [class.cursor-not-allowed]=\"isDisabled()\"\n>\n <span class=\"relative inline-flex items-center\">\n <input\n type=\"checkbox\"\n role=\"switch\"\n [id]=\"id()\"\n [name]=\"name()\"\n [checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n (change)=\"toggle($event)\"\n class=\"peer absolute inset-0 size-full opacity-0 m-0 cursor-pointer disabled:cursor-not-allowed\"\n />\n <span [class]=\"trackClasses()\">\n <span [class]=\"thumbClasses()\"></span>\n </span>\n </span>\n\n @if (label() || description()) {\n <span class=\"flex flex-col gap-0.5 -mt-px\">\n @if (label()) {\n <span class=\"text-sm font-medium text-[var(--color-foreground)] leading-tight\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-[var(--color-destructive)]\">*</span>\n }\n </span>\n }\n @if (description()) {\n <span class=\"text-xs text-[var(--color-muted-foreground)]\">{{ description() }}</span>\n }\n </span>\n }\n</label>\n" }]
|
|
2184
|
+
}], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
|
|
2185
|
+
|
|
2186
|
+
function provideHighstack(config) {
|
|
2187
|
+
return [
|
|
2188
|
+
{
|
|
2189
|
+
provide: APP_INITIALIZER,
|
|
2190
|
+
useFactory: () => {
|
|
2191
|
+
const document = inject(DOCUMENT);
|
|
2192
|
+
return () => {
|
|
2193
|
+
const body = document.body;
|
|
2194
|
+
// Remove pre-existing theme classes
|
|
2195
|
+
body.classList.forEach((className) => {
|
|
2196
|
+
if (className.startsWith('theme-')) {
|
|
2197
|
+
body.classList.remove(className);
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
// Apply selected theme if not default
|
|
2201
|
+
if (config.theme && config.theme !== 'default') {
|
|
2202
|
+
body.classList.add(`theme-${config.theme}`);
|
|
2203
|
+
}
|
|
2204
|
+
// Apply dark mode
|
|
2205
|
+
body.classList.toggle('dark', !!config.dark);
|
|
2206
|
+
};
|
|
2207
|
+
},
|
|
2208
|
+
multi: true,
|
|
2209
|
+
},
|
|
2210
|
+
];
|
|
2211
|
+
}
|
|
51
2212
|
|
|
52
2213
|
/*
|
|
53
2214
|
* Public API Surface of @highstacklabs2026/ui
|
|
@@ -57,5 +2218,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
57
2218
|
* Generated bundle index. Do not edit.
|
|
58
2219
|
*/
|
|
59
2220
|
|
|
60
|
-
export { ButtonComponent };
|
|
2221
|
+
export { AccordionComponent, AccordionItemComponent, AlertComponent, AvatarComponent, AvatarGroupComponent, BadgeComponent, BreadcrumbComponent, BreadcrumbItemComponent, ButtonComponent, CardComponent, CardContentComponent, CardDescriptionComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CheckboxComponent, DropdownComponent, DropdownItemComponent, DropdownLabelComponent, DropdownSeparatorComponent, DropdownTriggerDirective, InputComponent, OptionComponent, PaginationComponent, ProgressComponent, RadioComponent, RadioGroupComponent, SelectComponent, SkeletonComponent, SpinnerComponent, SwitchComponent, TabComponent, TableCellDirective, TableComponent, TabsComponent, TextareaComponent, ToastService, TooltipDirective, getByPath, provideHighstack };
|
|
61
2222
|
//# sourceMappingURL=highstacklabs2026-ui.mjs.map
|