@kaufmann-lab/ui 0.1.2 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,321 +1,710 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ElementRef, HostListener, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
2
+ import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, HostListener, TemplateRef, contentChild, ElementRef, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
3
3
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
5
+ import { Overlay } from '@angular/cdk/overlay';
6
+ import { TemplatePortal } from '@angular/cdk/portal';
5
7
 
6
- /**
7
- * Sprite SVG con los 59 íconos de trazo del design system (página 5:
8
- * "Set de 59 iconos de trazo (1.9px, grilla 24×24)"). Cada ícono es un
9
- * `<symbol>` con su nombre real de la spec como id (`kf-icon-check`,
10
- * `kf-icon-alert-triangle`, etc.) — `Icon` lo referencia con `<use>`.
11
- *
12
- * Los paths vienen de Lucide (https://lucide.dev, licencia ISC), la
13
- * librería de iconos de trazo cuyo set de nombres coincide exactamente con
14
- * el que pide el design system (chevron-right, alert-triangle, bar-chart…).
15
- * Se embeben acá en vez de agregar `lucide` como dependencia del paquete
16
- * para no sumar peso/versión externa a algo que consumen todos los
17
- * frontends del ecosistema — son 59 <path>/<circle>/<rect> estáticos, no
18
- * lógica que necesite actualizarse seguido.
19
- *
20
- * Mapeo de 4 nombres de la spec que no existen tal cual en Lucide (renombres
21
- * entre versiones de esa librería, o conceptos vecinos):
22
- * tool -> wrench
23
- * sort -> arrow-up-down (control de orden bidireccional simple)
24
- * cart -> shopping-cart
25
- * chevron-left-bar -> panel-left (icono de "colapsar panel lateral")
26
- *
27
- * Se inyecta UNA sola vez en el DOM (ver `Icon`), sin importar cuántos
28
- * `<kf-icon>` distintos haya en la página — todos referencian el mismo
29
- * sprite vía `<use href="#kf-icon-...">`.
30
- */
31
- const KF_ICON_SPRITE = `<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
32
- <symbol id="kf-icon-activity" viewBox="0 0 24 24">
8
+ /** Símbolo de trazo "activity" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
9
+ const activity = {
10
+ viewBox: '0 0 24 24',
11
+ markup: `
33
12
  <path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" />
34
- </symbol>
35
- <symbol id="kf-icon-alert-circle" viewBox="0 0 24 24">
13
+ `,
14
+ };
15
+
16
+ /** Símbolo de trazo "alert-circle" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
17
+ const alertCircle = {
18
+ viewBox: '0 0 24 24',
19
+ markup: `
36
20
  <circle cx="12" cy="12" r="10" />
37
- <line x1="12" x2="12" y1="8" y2="12" />
38
- <line x1="12" x2="12.01" y1="16" y2="16" />
39
- </symbol>
40
- <symbol id="kf-icon-alert-octagon" viewBox="0 0 24 24">
21
+ <line x1="12" x2="12" y1="8" y2="12" />
22
+ <line x1="12" x2="12.01" y1="16" y2="16" />
23
+ `,
24
+ };
25
+
26
+ /** Símbolo de trazo "alert-octagon" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
27
+ const alertOctagon = {
28
+ viewBox: '0 0 24 24',
29
+ markup: `
41
30
  <path d="M12 16h.01" />
42
- <path d="M12 8v4" />
43
- <path d="M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z" />
44
- </symbol>
45
- <symbol id="kf-icon-alert-triangle" viewBox="0 0 24 24">
31
+ <path d="M12 8v4" />
32
+ <path d="M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z" />
33
+ `,
34
+ };
35
+
36
+ /** Símbolo de trazo "alert-triangle" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
37
+ const alertTriangle = {
38
+ viewBox: '0 0 24 24',
39
+ markup: `
46
40
  <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-3" />
47
- <path d="M12 9v4" />
48
- <path d="M12 17h.01" />
49
- </symbol>
50
- <symbol id="kf-icon-award" viewBox="0 0 24 24">
41
+ <path d="M12 9v4" />
42
+ <path d="M12 17h.01" />
43
+ `,
44
+ };
45
+
46
+ /** Símbolo de trazo "award" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
47
+ const award = {
48
+ viewBox: '0 0 24 24',
49
+ markup: `
51
50
  <path d="m15.477 12.89 1.515 8.526a.5.5 0 0 1-.81.47l-3.58-2.687a1 1 0 0 0-1.197 0l-3.586 2.686a.5.5 0 0 1-.81-.469l1.514-8.526" />
52
- <circle cx="12" cy="8" r="6" />
53
- </symbol>
54
- <symbol id="kf-icon-bar-chart" viewBox="0 0 24 24">
51
+ <circle cx="12" cy="8" r="6" />
52
+ `,
53
+ };
54
+
55
+ /** Símbolo de trazo "bar-chart" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
56
+ const barChart = {
57
+ viewBox: '0 0 24 24',
58
+ markup: `
55
59
  <path d="M5 21v-6" />
56
- <path d="M12 21V9" />
57
- <path d="M19 21V3" />
58
- </symbol>
59
- <symbol id="kf-icon-bell" viewBox="0 0 24 24">
60
+ <path d="M12 21V9" />
61
+ <path d="M19 21V3" />
62
+ `,
63
+ };
64
+
65
+ /** Símbolo de trazo "bell" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
66
+ const bell = {
67
+ viewBox: '0 0 24 24',
68
+ markup: `
60
69
  <path d="M10.268 21a2 2 0 0 0 3.464 0" />
61
- <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" />
62
- </symbol>
63
- <symbol id="kf-icon-bus" viewBox="0 0 24 24">
70
+ <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" />
71
+ `,
72
+ };
73
+
74
+ /** Símbolo de trazo "bus" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
75
+ const bus = {
76
+ viewBox: '0 0 24 24',
77
+ markup: `
64
78
  <path d="M8 6v6" />
65
- <path d="M15 6v6" />
66
- <path d="M2 12h19.6" />
67
- <path d="M18 18h3s.5-1.7.8-2.8c.1-.4.2-.8.2-1.2 0-.4-.1-.8-.2-1.2l-1.4-5C20.1 6.8 19.1 6 18 6H4a2 2 0 0 0-2 2v10h3" />
68
- <circle cx="7" cy="18" r="2" />
69
- <path d="M9 18h5" />
70
- <circle cx="16" cy="18" r="2" />
71
- </symbol>
72
- <symbol id="kf-icon-calculator" viewBox="0 0 24 24">
79
+ <path d="M15 6v6" />
80
+ <path d="M2 12h19.6" />
81
+ <path d="M18 18h3s.5-1.7.8-2.8c.1-.4.2-.8.2-1.2 0-.4-.1-.8-.2-1.2l-1.4-5C20.1 6.8 19.1 6 18 6H4a2 2 0 0 0-2 2v10h3" />
82
+ <circle cx="7" cy="18" r="2" />
83
+ <path d="M9 18h5" />
84
+ <circle cx="16" cy="18" r="2" />
85
+ `,
86
+ };
87
+
88
+ /** Símbolo de trazo "calculator" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
89
+ const calculator = {
90
+ viewBox: '0 0 24 24',
91
+ markup: `
73
92
  <rect width="16" height="20" x="4" y="2" rx="2" />
74
- <line x1="8" x2="16" y1="6" y2="6" />
75
- <line x1="16" x2="16" y1="14" y2="18" />
76
- <path d="M16 10h.01" />
77
- <path d="M12 10h.01" />
78
- <path d="M8 10h.01" />
79
- <path d="M12 14h.01" />
80
- <path d="M8 14h.01" />
81
- <path d="M12 18h.01" />
82
- <path d="M8 18h.01" />
83
- </symbol>
84
- <symbol id="kf-icon-calendar" viewBox="0 0 24 24">
93
+ <line x1="8" x2="16" y1="6" y2="6" />
94
+ <line x1="16" x2="16" y1="14" y2="18" />
95
+ <path d="M16 10h.01" />
96
+ <path d="M12 10h.01" />
97
+ <path d="M8 10h.01" />
98
+ <path d="M12 14h.01" />
99
+ <path d="M8 14h.01" />
100
+ <path d="M12 18h.01" />
101
+ <path d="M8 18h.01" />
102
+ `,
103
+ };
104
+
105
+ /** Símbolo de trazo "calendar" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
106
+ const calendar = {
107
+ viewBox: '0 0 24 24',
108
+ markup: `
85
109
  <path d="M8 2v3" />
86
- <path d="M16 2v3" />
87
- <rect x="3" y="3" width="18" height="18" rx="2" />
88
- <path d="M3 9h18" />
89
- </symbol>
90
- <symbol id="kf-icon-car" viewBox="0 0 24 24">
110
+ <path d="M16 2v3" />
111
+ <rect x="3" y="3" width="18" height="18" rx="2" />
112
+ <path d="M3 9h18" />
113
+ `,
114
+ };
115
+
116
+ /** Símbolo de trazo "car" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
117
+ const car = {
118
+ viewBox: '0 0 24 24',
119
+ markup: `
91
120
  <path d="M19 17h2c.6 0 1-.4 1-1v-3c0-.9-.7-1.7-1.5-1.9C18.7 10.6 16 10 16 10s-1.3-1.4-2.2-2.3c-.5-.4-1.1-.7-1.8-.7H5c-.6 0-1.1.4-1.4.9l-1.4 2.9A3.7 3.7 0 0 0 2 12v4c0 .6.4 1 1 1h2" />
92
- <circle cx="7" cy="17" r="2" />
93
- <path d="M9 17h6" />
94
- <circle cx="17" cy="17" r="2" />
95
- </symbol>
96
- <symbol id="kf-icon-cart" viewBox="0 0 24 24">
121
+ <circle cx="7" cy="17" r="2" />
122
+ <path d="M9 17h6" />
123
+ <circle cx="17" cy="17" r="2" />
124
+ `,
125
+ };
126
+
127
+ /** Símbolo de trazo "cart" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
128
+ const cart = {
129
+ viewBox: '0 0 24 24',
130
+ markup: `
97
131
  <circle cx="8" cy="21" r="1" />
98
- <circle cx="19" cy="21" r="1" />
99
- <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
100
- </symbol>
101
- <symbol id="kf-icon-check" viewBox="0 0 24 24">
132
+ <circle cx="19" cy="21" r="1" />
133
+ <path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" />
134
+ `,
135
+ };
136
+
137
+ /** Símbolo de trazo "check" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
138
+ const check = {
139
+ viewBox: '0 0 24 24',
140
+ markup: `
102
141
  <path d="M20 6 9 17l-5-5" />
103
- </symbol>
104
- <symbol id="kf-icon-check-circle" viewBox="0 0 24 24">
142
+ `,
143
+ };
144
+
145
+ /** Símbolo de trazo "check-circle" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
146
+ const checkCircle = {
147
+ viewBox: '0 0 24 24',
148
+ markup: `
105
149
  <path d="M21.801 10A10 10 0 1 1 17 3.335" />
106
- <path d="m9 11 3 3L22 4" />
107
- </symbol>
108
- <symbol id="kf-icon-check-square" viewBox="0 0 24 24">
150
+ <path d="m9 11 3 3L22 4" />
151
+ `,
152
+ };
153
+
154
+ /** Símbolo de trazo "check-square" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
155
+ const checkSquare = {
156
+ viewBox: '0 0 24 24',
157
+ markup: `
109
158
  <rect width="18" height="18" x="3" y="3" rx="2" />
110
- <path d="m9 12 2 2 4-4" />
111
- </symbol>
112
- <symbol id="kf-icon-chevron-down" viewBox="0 0 24 24">
159
+ <path d="m9 12 2 2 4-4" />
160
+ `,
161
+ };
162
+
163
+ /** Símbolo de trazo "chevron-down" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
164
+ const chevronDown = {
165
+ viewBox: '0 0 24 24',
166
+ markup: `
113
167
  <path d="m6 9 6 6 6-6" />
114
- </symbol>
115
- <symbol id="kf-icon-chevron-left" viewBox="0 0 24 24">
168
+ `,
169
+ };
170
+
171
+ /** Símbolo de trazo "chevron-left" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
172
+ const chevronLeft = {
173
+ viewBox: '0 0 24 24',
174
+ markup: `
116
175
  <path d="m15 18-6-6 6-6" />
117
- </symbol>
118
- <symbol id="kf-icon-chevron-left-bar" viewBox="0 0 24 24">
176
+ `,
177
+ };
178
+
179
+ /** Símbolo de trazo "chevron-left-bar" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
180
+ const chevronLeftBar = {
181
+ viewBox: '0 0 24 24',
182
+ markup: `
119
183
  <rect width="18" height="18" x="3" y="3" rx="2" />
120
- <path d="M9 3v18" />
121
- </symbol>
122
- <symbol id="kf-icon-chevron-right" viewBox="0 0 24 24">
184
+ <path d="M9 3v18" />
185
+ `,
186
+ };
187
+
188
+ /** Símbolo de trazo "chevron-right" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
189
+ const chevronRight = {
190
+ viewBox: '0 0 24 24',
191
+ markup: `
123
192
  <path d="m9 18 6-6-6-6" />
124
- </symbol>
125
- <symbol id="kf-icon-clipboard" viewBox="0 0 24 24">
193
+ `,
194
+ };
195
+
196
+ /** Símbolo de trazo "clipboard" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
197
+ const clipboard = {
198
+ viewBox: '0 0 24 24',
199
+ markup: `
126
200
  <rect width="8" height="4" x="8" y="2" rx="1" ry="1" />
127
- <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
128
- </symbol>
129
- <symbol id="kf-icon-clock" viewBox="0 0 24 24">
201
+ <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />
202
+ `,
203
+ };
204
+
205
+ /** Símbolo de trazo "clock" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
206
+ const clock = {
207
+ viewBox: '0 0 24 24',
208
+ markup: `
130
209
  <circle cx="12" cy="12" r="10" />
131
- <path d="M12 6v6l4 2" />
132
- </symbol>
133
- <symbol id="kf-icon-credit-card" viewBox="0 0 24 24">
210
+ <path d="M12 6v6l4 2" />
211
+ `,
212
+ };
213
+
214
+ /** Símbolo de trazo "credit-card" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
215
+ const creditCard = {
216
+ viewBox: '0 0 24 24',
217
+ markup: `
134
218
  <rect width="20" height="14" x="2" y="5" rx="2" />
135
- <line x1="2" x2="22" y1="10" y2="10" />
136
- </symbol>
137
- <symbol id="kf-icon-download" viewBox="0 0 24 24">
219
+ <line x1="2" x2="22" y1="10" y2="10" />
220
+ `,
221
+ };
222
+
223
+ /** Símbolo de trazo "download" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
224
+ const download = {
225
+ viewBox: '0 0 24 24',
226
+ markup: `
138
227
  <path d="M12 15V3" />
139
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
140
- <path d="m7 10 5 5 5-5" />
141
- </symbol>
142
- <symbol id="kf-icon-edit" viewBox="0 0 24 24">
228
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
229
+ <path d="m7 10 5 5 5-5" />
230
+ `,
231
+ };
232
+
233
+ /** Símbolo de trazo "edit" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
234
+ const edit = {
235
+ viewBox: '0 0 24 24',
236
+ markup: `
143
237
  <path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
144
- <path d="m15 5 4 4" />
145
- </symbol>
146
- <symbol id="kf-icon-external-link" viewBox="0 0 24 24">
238
+ <path d="m15 5 4 4" />
239
+ `,
240
+ };
241
+
242
+ /** Símbolo de trazo "external-link" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
243
+ const externalLink = {
244
+ viewBox: '0 0 24 24',
245
+ markup: `
147
246
  <path d="M15 3h6v6" />
148
- <path d="M10 14 21 3" />
149
- <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
150
- </symbol>
151
- <symbol id="kf-icon-eye" viewBox="0 0 24 24">
247
+ <path d="M10 14 21 3" />
248
+ <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
249
+ `,
250
+ };
251
+
252
+ /** Símbolo de trazo "eye" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
253
+ const eye = {
254
+ viewBox: '0 0 24 24',
255
+ markup: `
152
256
  <path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" />
153
- <circle cx="12" cy="12" r="3" />
154
- </symbol>
155
- <symbol id="kf-icon-eye-off" viewBox="0 0 24 24">
257
+ <circle cx="12" cy="12" r="3" />
258
+ `,
259
+ };
260
+
261
+ /** Símbolo de trazo "eye-off" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
262
+ const eyeOff = {
263
+ viewBox: '0 0 24 24',
264
+ markup: `
156
265
  <path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" />
157
266
  <path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" />
158
267
  <path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" />
159
268
  <path d="m2 2 20 20" />
160
- </symbol>
161
- <symbol id="kf-icon-file" viewBox="0 0 24 24">
269
+ `,
270
+ };
271
+
272
+ /** Símbolo de trazo "file" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
273
+ const file = {
274
+ viewBox: '0 0 24 24',
275
+ markup: `
162
276
  <path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
163
- <path d="M14 2v5a1 1 0 0 0 1 1h5" />
164
- </symbol>
165
- <symbol id="kf-icon-filter" viewBox="0 0 24 24">
277
+ <path d="M14 2v5a1 1 0 0 0 1 1h5" />
278
+ `,
279
+ };
280
+
281
+ /** Símbolo de trazo "filter" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
282
+ const filter = {
283
+ viewBox: '0 0 24 24',
284
+ markup: `
166
285
  <path d="M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z" />
167
- </symbol>
168
- <symbol id="kf-icon-gauge" viewBox="0 0 24 24">
286
+ `,
287
+ };
288
+
289
+ /** Símbolo de trazo "gauge" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
290
+ const gauge = {
291
+ viewBox: '0 0 24 24',
292
+ markup: `
169
293
  <path d="m12 14 4-4" />
170
- <path d="M3.34 19a10 10 0 1 1 17.32 0" />
171
- </symbol>
172
- <symbol id="kf-icon-gift" viewBox="0 0 24 24">
294
+ <path d="M3.34 19a10 10 0 1 1 17.32 0" />
295
+ `,
296
+ };
297
+
298
+ /** Símbolo de trazo "gift" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
299
+ const gift = {
300
+ viewBox: '0 0 24 24',
301
+ markup: `
173
302
  <path d="M12 7v14" />
174
- <path d="M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8" />
175
- <path d="M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5" />
176
- <rect x="3" y="7" width="18" height="4" rx="1" />
177
- </symbol>
178
- <symbol id="kf-icon-grid" viewBox="0 0 24 24">
303
+ <path d="M20 11v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8" />
304
+ <path d="M7.5 7a1 1 0 0 1 0-5A4.8 8 0 0 1 12 7a4.8 8 0 0 1 4.5-5 1 1 0 0 1 0 5" />
305
+ <rect x="3" y="7" width="18" height="4" rx="1" />
306
+ `,
307
+ };
308
+
309
+ /** Símbolo de trazo "grid" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
310
+ const grid = {
311
+ viewBox: '0 0 24 24',
312
+ markup: `
179
313
  <rect width="18" height="18" x="3" y="3" rx="2" />
180
- <path d="M3 9h18" />
181
- <path d="M3 15h18" />
182
- <path d="M9 3v18" />
183
- <path d="M15 3v18" />
184
- </symbol>
185
- <symbol id="kf-icon-history" viewBox="0 0 24 24">
314
+ <path d="M3 9h18" />
315
+ <path d="M3 15h18" />
316
+ <path d="M9 3v18" />
317
+ <path d="M15 3v18" />
318
+ `,
319
+ };
320
+
321
+ /** Símbolo de trazo "history" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
322
+ const history = {
323
+ viewBox: '0 0 24 24',
324
+ markup: `
186
325
  <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
187
- <path d="M3 3v5h5" />
188
- <path d="M12 7v5l4 2" />
189
- </symbol>
190
- <symbol id="kf-icon-inbox" viewBox="0 0 24 24">
326
+ <path d="M3 3v5h5" />
327
+ <path d="M12 7v5l4 2" />
328
+ `,
329
+ };
330
+
331
+ /** Símbolo de trazo "inbox" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
332
+ const inbox = {
333
+ viewBox: '0 0 24 24',
334
+ markup: `
191
335
  <polyline points="22 12 16 12 14 15 10 15 8 12 2 12" />
192
- <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
193
- </symbol>
194
- <symbol id="kf-icon-layers" viewBox="0 0 24 24">
336
+ <path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" />
337
+ `,
338
+ };
339
+
340
+ /** Símbolo de trazo "layers" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
341
+ const layers = {
342
+ viewBox: '0 0 24 24',
343
+ markup: `
195
344
  <path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z" />
196
- <path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12" />
197
- <path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17" />
198
- </symbol>
199
- <symbol id="kf-icon-list" viewBox="0 0 24 24">
345
+ <path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12" />
346
+ <path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17" />
347
+ `,
348
+ };
349
+
350
+ /** Símbolo de trazo "list" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
351
+ const list = {
352
+ viewBox: '0 0 24 24',
353
+ markup: `
200
354
  <path d="M3 5h.01" />
201
- <path d="M3 12h.01" />
202
- <path d="M3 19h.01" />
203
- <path d="M8 5h13" />
204
- <path d="M8 12h13" />
205
- <path d="M8 19h13" />
206
- </symbol>
207
- <symbol id="kf-icon-map" viewBox="0 0 24 24">
355
+ <path d="M3 12h.01" />
356
+ <path d="M3 19h.01" />
357
+ <path d="M8 5h13" />
358
+ <path d="M8 12h13" />
359
+ <path d="M8 19h13" />
360
+ `,
361
+ };
362
+
363
+ /** Símbolo de trazo "map" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
364
+ const map = {
365
+ viewBox: '0 0 24 24',
366
+ markup: `
208
367
  <path d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z" />
209
- <path d="M15 5.764v15" />
210
- <path d="M9 3.236v15" />
211
- </symbol>
212
- <symbol id="kf-icon-map-pin" viewBox="0 0 24 24">
368
+ <path d="M15 5.764v15" />
369
+ <path d="M9 3.236v15" />
370
+ `,
371
+ };
372
+
373
+ /** Símbolo de trazo "map-pin" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
374
+ const mapPin = {
375
+ viewBox: '0 0 24 24',
376
+ markup: `
213
377
  <path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" />
214
- <circle cx="12" cy="10" r="3" />
215
- </symbol>
216
- <symbol id="kf-icon-menu" viewBox="0 0 24 24">
378
+ <circle cx="12" cy="10" r="3" />
379
+ `,
380
+ };
381
+
382
+ /** Símbolo de trazo "menu" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
383
+ const menu = {
384
+ viewBox: '0 0 24 24',
385
+ markup: `
217
386
  <path d="M4 5h16" />
218
- <path d="M4 12h16" />
219
- <path d="M4 19h16" />
220
- </symbol>
221
- <symbol id="kf-icon-mic" viewBox="0 0 24 24">
387
+ <path d="M4 12h16" />
388
+ <path d="M4 19h16" />
389
+ `,
390
+ };
391
+
392
+ /** Símbolo de trazo "mic" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
393
+ const mic = {
394
+ viewBox: '0 0 24 24',
395
+ markup: `
222
396
  <path d="M12 19v3" />
223
- <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
224
- <rect x="9" y="2" width="6" height="13" rx="3" />
225
- </symbol>
226
- <symbol id="kf-icon-mic-off" viewBox="0 0 24 24">
397
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
398
+ <rect x="9" y="2" width="6" height="13" rx="3" />
399
+ `,
400
+ };
401
+
402
+ /** Símbolo de trazo "mic-off" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
403
+ const micOff = {
404
+ viewBox: '0 0 24 24',
405
+ markup: `
227
406
  <path d="M12 19v3" />
228
- <path d="M15 9.34V5a3 3 0 0 0-5.68-1.33" />
229
- <path d="M16.95 16.95A7 7 0 0 1 5 12v-2" />
230
- <path d="M18.89 13.23A7 7 0 0 0 19 12v-2" />
231
- <path d="m2 2 20 20" />
232
- <path d="M9 9v3a3 3 0 0 0 5.12 2.12" />
233
- </symbol>
234
- <symbol id="kf-icon-more-vertical" viewBox="0 0 24 24">
407
+ <path d="M15 9.34V5a3 3 0 0 0-5.68-1.33" />
408
+ <path d="M16.95 16.95A7 7 0 0 1 5 12v-2" />
409
+ <path d="M18.89 13.23A7 7 0 0 0 19 12v-2" />
410
+ <path d="m2 2 20 20" />
411
+ <path d="M9 9v3a3 3 0 0 0 5.12 2.12" />
412
+ `,
413
+ };
414
+
415
+ /** Símbolo de trazo "more-vertical" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
416
+ const moreVertical = {
417
+ viewBox: '0 0 24 24',
418
+ markup: `
235
419
  <circle cx="12" cy="12" r="1" />
236
- <circle cx="12" cy="5" r="1" />
237
- <circle cx="12" cy="19" r="1" />
238
- </symbol>
239
- <symbol id="kf-icon-pause" viewBox="0 0 24 24">
420
+ <circle cx="12" cy="5" r="1" />
421
+ <circle cx="12" cy="19" r="1" />
422
+ `,
423
+ };
424
+
425
+ /** Símbolo de trazo "pause" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
426
+ const pause = {
427
+ viewBox: '0 0 24 24',
428
+ markup: `
240
429
  <rect x="14" y="3" width="5" height="18" rx="1" />
241
- <rect x="5" y="3" width="5" height="18" rx="1" />
242
- </symbol>
243
- <symbol id="kf-icon-phone" viewBox="0 0 24 24">
430
+ <rect x="5" y="3" width="5" height="18" rx="1" />
431
+ `,
432
+ };
433
+
434
+ /** Símbolo de trazo "phone" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
435
+ const phone = {
436
+ viewBox: '0 0 24 24',
437
+ markup: `
244
438
  <path d="M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384" />
245
- </symbol>
246
- <symbol id="kf-icon-play" viewBox="0 0 24 24">
439
+ `,
440
+ };
441
+
442
+ /** Símbolo de trazo "play" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
443
+ const play = {
444
+ viewBox: '0 0 24 24',
445
+ markup: `
247
446
  <path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" />
248
- </symbol>
249
- <symbol id="kf-icon-plus" viewBox="0 0 24 24">
447
+ `,
448
+ };
449
+
450
+ /** Símbolo de trazo "plus" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
451
+ const plus = {
452
+ viewBox: '0 0 24 24',
453
+ markup: `
250
454
  <path d="M5 12h14" />
251
- <path d="M12 5v14" />
252
- </symbol>
253
- <symbol id="kf-icon-search" viewBox="0 0 24 24">
455
+ <path d="M12 5v14" />
456
+ `,
457
+ };
458
+
459
+ /** Símbolo de trazo "search" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
460
+ const search = {
461
+ viewBox: '0 0 24 24',
462
+ markup: `
254
463
  <path d="m21 21-4.34-4.34" />
255
- <circle cx="11" cy="11" r="8" />
256
- </symbol>
257
- <symbol id="kf-icon-settings" viewBox="0 0 24 24">
464
+ <circle cx="11" cy="11" r="8" />
465
+ `,
466
+ };
467
+
468
+ /** Símbolo de trazo "settings" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
469
+ const settings = {
470
+ viewBox: '0 0 24 24',
471
+ markup: `
258
472
  <path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" />
259
- <circle cx="12" cy="12" r="3" />
260
- </symbol>
261
- <symbol id="kf-icon-shield-check" viewBox="0 0 24 24">
473
+ <circle cx="12" cy="12" r="3" />
474
+ `,
475
+ };
476
+
477
+ /** Símbolo de trazo "shield-check" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
478
+ const shieldCheck = {
479
+ viewBox: '0 0 24 24',
480
+ markup: `
262
481
  <path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" />
263
- <path d="m9 12 2 2 4-4" />
264
- </symbol>
265
- <symbol id="kf-icon-sort" viewBox="0 0 24 24">
482
+ <path d="m9 12 2 2 4-4" />
483
+ `,
484
+ };
485
+
486
+ /** Símbolo de trazo "sort" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
487
+ const sort = {
488
+ viewBox: '0 0 24 24',
489
+ markup: `
266
490
  <path d="m21 16-4 4-4-4" />
267
- <path d="M17 20V4" />
268
- <path d="m3 8 4-4 4 4" />
269
- <path d="M7 4v16" />
270
- </symbol>
271
- <symbol id="kf-icon-star" viewBox="0 0 24 24">
491
+ <path d="M17 20V4" />
492
+ <path d="m3 8 4-4 4 4" />
493
+ <path d="M7 4v16" />
494
+ `,
495
+ };
496
+
497
+ /** Símbolo de trazo "star" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
498
+ const star = {
499
+ viewBox: '0 0 24 24',
500
+ markup: `
272
501
  <path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z" />
273
- </symbol>
274
- <symbol id="kf-icon-ticket" viewBox="0 0 24 24">
502
+ `,
503
+ };
504
+
505
+ /** Símbolo de trazo "ticket" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
506
+ const ticket = {
507
+ viewBox: '0 0 24 24',
508
+ markup: `
275
509
  <path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" />
276
- <path d="M13 5v2" />
277
- <path d="M13 17v2" />
278
- <path d="M13 11v2" />
279
- </symbol>
280
- <symbol id="kf-icon-tool" viewBox="0 0 24 24">
510
+ <path d="M13 5v2" />
511
+ <path d="M13 17v2" />
512
+ <path d="M13 11v2" />
513
+ `,
514
+ };
515
+
516
+ /** Símbolo de trazo "tool" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
517
+ const tool = {
518
+ viewBox: '0 0 24 24',
519
+ markup: `
281
520
  <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" />
282
- </symbol>
283
- <symbol id="kf-icon-trash" viewBox="0 0 24 24">
521
+ `,
522
+ };
523
+
524
+ /** Símbolo de trazo "trash" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
525
+ const trash = {
526
+ viewBox: '0 0 24 24',
527
+ markup: `
284
528
  <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
285
- <path d="M3 6h18" />
286
- <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
287
- </symbol>
288
- <symbol id="kf-icon-truck" viewBox="0 0 24 24">
529
+ <path d="M3 6h18" />
530
+ <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
531
+ `,
532
+ };
533
+
534
+ /** Símbolo de trazo "truck" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
535
+ const truck = {
536
+ viewBox: '0 0 24 24',
537
+ markup: `
289
538
  <path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" />
290
- <path d="M15 18H9" />
291
- <path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14" />
292
- <circle cx="17" cy="18" r="2" />
293
- <circle cx="7" cy="18" r="2" />
294
- </symbol>
295
- <symbol id="kf-icon-upload-cloud" viewBox="0 0 24 24">
539
+ <path d="M15 18H9" />
540
+ <path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14" />
541
+ <circle cx="17" cy="18" r="2" />
542
+ <circle cx="7" cy="18" r="2" />
543
+ `,
544
+ };
545
+
546
+ /** Símbolo de trazo "upload-cloud" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
547
+ const uploadCloud = {
548
+ viewBox: '0 0 24 24',
549
+ markup: `
296
550
  <path d="M12 13v8" />
297
- <path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" />
298
- <path d="m8 17 4-4 4 4" />
299
- </symbol>
300
- <symbol id="kf-icon-user" viewBox="0 0 24 24">
551
+ <path d="M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" />
552
+ <path d="m8 17 4-4 4 4" />
553
+ `,
554
+ };
555
+
556
+ /** Símbolo de trazo "user" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
557
+ const user = {
558
+ viewBox: '0 0 24 24',
559
+ markup: `
301
560
  <path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
302
- <circle cx="12" cy="7" r="4" />
303
- </symbol>
304
- <symbol id="kf-icon-user-search" viewBox="0 0 24 24">
561
+ <circle cx="12" cy="7" r="4" />
562
+ `,
563
+ };
564
+
565
+ /** Símbolo de trazo "user-search" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
566
+ const userSearch = {
567
+ viewBox: '0 0 24 24',
568
+ markup: `
305
569
  <circle cx="10" cy="7" r="4" />
306
- <path d="M10.3 15H7a4 4 0 0 0-4 4v2" />
307
- <circle cx="17" cy="17" r="3" />
308
- <path d="m21 21-1.9-1.9" />
309
- </symbol>
310
- <symbol id="kf-icon-video" viewBox="0 0 24 24">
570
+ <path d="M10.3 15H7a4 4 0 0 0-4 4v2" />
571
+ <circle cx="17" cy="17" r="3" />
572
+ <path d="m21 21-1.9-1.9" />
573
+ `,
574
+ };
575
+
576
+ /** Símbolo de trazo "video" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
577
+ const video = {
578
+ viewBox: '0 0 24 24',
579
+ markup: `
311
580
  <path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" />
312
- <rect x="2" y="6" width="14" height="12" rx="2" />
313
- </symbol>
314
- <symbol id="kf-icon-x" viewBox="0 0 24 24">
581
+ <rect x="2" y="6" width="14" height="12" rx="2" />
582
+ `,
583
+ };
584
+
585
+ /** Símbolo de trazo "x" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
586
+ const x = {
587
+ viewBox: '0 0 24 24',
588
+ markup: `
315
589
  <path d="M18 6 6 18" />
316
- <path d="m6 6 12 12" />
317
- </symbol>
590
+ <path d="m6 6 12 12" />
591
+ `,
592
+ };
593
+
594
+ /**
595
+ * Registro tipado de todos los símbolos de ícono. `Record<IconName,
596
+ * IconDefinition>` obliga al compilador a exigir una entrada por cada
597
+ * nombre de `IconName` — agregar un nombre acá sin su archivo en
598
+ * `icons/`, o viceversa, es un error de TypeScript, no un ícono roto
599
+ * en runtime (que era el riesgo real del sprite como string único).
600
+ *
601
+ * Agregar un ícono nuevo es: crear `icons/nombre-del-icono.ts` (copiar
602
+ * el patrón de cualquier archivo vecino), agregarlo a `IconName` en
603
+ * `icon-name.ts`, e importarlo/registrarlo acá.
604
+ */
605
+ const ICON_DEFINITIONS = {
606
+ activity: activity,
607
+ 'alert-circle': alertCircle,
608
+ 'alert-octagon': alertOctagon,
609
+ 'alert-triangle': alertTriangle,
610
+ award: award,
611
+ 'bar-chart': barChart,
612
+ bell: bell,
613
+ bus: bus,
614
+ calculator: calculator,
615
+ calendar: calendar,
616
+ car: car,
617
+ cart: cart,
618
+ check: check,
619
+ 'check-circle': checkCircle,
620
+ 'check-square': checkSquare,
621
+ 'chevron-down': chevronDown,
622
+ 'chevron-left': chevronLeft,
623
+ 'chevron-left-bar': chevronLeftBar,
624
+ 'chevron-right': chevronRight,
625
+ clipboard: clipboard,
626
+ clock: clock,
627
+ 'credit-card': creditCard,
628
+ download: download,
629
+ edit: edit,
630
+ 'external-link': externalLink,
631
+ eye: eye,
632
+ 'eye-off': eyeOff,
633
+ file: file,
634
+ filter: filter,
635
+ gauge: gauge,
636
+ gift: gift,
637
+ grid: grid,
638
+ history: history,
639
+ inbox: inbox,
640
+ layers: layers,
641
+ list: list,
642
+ map: map,
643
+ 'map-pin': mapPin,
644
+ menu: menu,
645
+ mic: mic,
646
+ 'mic-off': micOff,
647
+ 'more-vertical': moreVertical,
648
+ pause: pause,
649
+ phone: phone,
650
+ play: play,
651
+ plus: plus,
652
+ search: search,
653
+ settings: settings,
654
+ 'shield-check': shieldCheck,
655
+ sort: sort,
656
+ star: star,
657
+ ticket: ticket,
658
+ tool: tool,
659
+ trash: trash,
660
+ truck: truck,
661
+ 'upload-cloud': uploadCloud,
662
+ user: user,
663
+ 'user-search': userSearch,
664
+ video: video,
665
+ x: x,
666
+ };
667
+
668
+ /**
669
+ * Sprite SVG con los 60 íconos de trazo del design system (página 5:
670
+ * "Set de 59 iconos de trazo (1.9px, grilla 24×24)", más `eye-off` como
671
+ * extensión propia). Cada ícono es un `<symbol>` con su nombre real de la
672
+ * spec como id (`kf-icon-check`, `kf-icon-alert-triangle`, etc.) — `Icon`
673
+ * lo referencia con `<use>`.
674
+ *
675
+ * Los paths vienen de Lucide (https://lucide.dev, licencia ISC), la
676
+ * librería de iconos de trazo cuyo set de nombres coincide exactamente con
677
+ * el que pide el design system (chevron-right, alert-triangle, bar-chart…).
678
+ * Se embeben acá en vez de agregar `lucide` como dependencia del paquete
679
+ * para no sumar peso/versión externa a algo que consumen todos los
680
+ * frontends del ecosistema — son símbolos estáticos, no lógica que
681
+ * necesite actualizarse seguido.
682
+ *
683
+ * Mapeo de 4 nombres de la spec que no existen tal cual en Lucide (renombres
684
+ * entre versiones de esa librería, o conceptos vecinos):
685
+ * tool -> wrench
686
+ * sort -> arrow-up-down (control de orden bidireccional simple)
687
+ * cart -> shopping-cart
688
+ * chevron-left-bar -> panel-left (icono de "colapsar panel lateral")
689
+ *
690
+ * El sprite ya no es un string único de mano: se construye acá a partir de
691
+ * `ICON_DEFINITIONS` (ver `icons/index.ts`), el registro tipado donde cada
692
+ * ícono vive en su propio archivo bajo `icons/`. Esto es responsabilidad de
693
+ * ensamblado pura — los datos de cada glyph viven en otro lado.
694
+ *
695
+ * Se inyecta UNA sola vez en el DOM (ver `Icon`), sin importar cuántos
696
+ * `<kf-icon>` distintos haya en la página — todos referencian el mismo
697
+ * sprite vía `<use href="#kf-icon-...">`.
698
+ */
699
+ const KF_ICON_SPRITE = buildIconSprite();
700
+ function buildIconSprite() {
701
+ const symbols = Object.entries(ICON_DEFINITIONS)
702
+ .map(([name, { viewBox, markup }]) => `<symbol id="kf-icon-${name}" viewBox="${viewBox}">${markup}</symbol>`)
703
+ .join('\n ');
704
+ return `<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
705
+ ${symbols}
318
706
  </svg>`;
707
+ }
319
708
 
320
709
  /** Marca el sprite ya inyectado, para no depender de una bandera de módulo
321
710
  * — una bandera compartida entre instancias de test (o entre múltiples
@@ -324,7 +713,7 @@ const KF_ICON_SPRITE = `<svg xmlns="http://www.w3.org/2000/svg" style="display:
324
713
  * nunca se inyecta. Comprobar el propio DOM es la fuente de verdad real. */
325
714
  const SPRITE_ROOT_ID = 'kf-icon-sprite-root';
326
715
  /**
327
- * Ícono de trazo del design system — 59 glyphs (página 5), grilla 24×24,
716
+ * Ícono de trazo del design system — 60 glyphs (59 de la página 5 + `eye-off`), grilla 24×24,
328
717
  * grosor de trazo 1.9px. Referencia un sprite SVG embebido (ver
329
718
  * `icon-sprite.ts`) vía `<use>`, así que no hay peso de red adicional por
330
719
  * cada instancia: el sprite se inyecta al DOM una única vez, la primera
@@ -462,6 +851,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
462
851
  }, styles: [":host{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);background:var(--kf-color-warning-soft);border:1px solid var(--kf-color-warning);color:var(--kf-color-warning-strong);font-size:.75rem;font-weight:700;letter-spacing:.02em;text-transform:uppercase;line-height:1}:host(.kf-admin-badge--inline){background:transparent;border-color:#e2b93b80;color:var(--kf-color-warning)}\n"] }]
463
852
  }], propDecorators: { inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }] } });
464
853
 
854
+ /**
855
+ * Indicador de carga indeterminado: inline, dentro de un botón, o como único
856
+ * contenido de una sección mientras llega la data.
857
+ *
858
+ * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
859
+ * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
860
+ * conocida — guardar, enviar, reintentar.
861
+ *
862
+ * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
863
+ * cuando aparece. Sin `label` cae en un texto accesible por defecto.
864
+ *
865
+ * <kf-spinner />
866
+ * <kf-spinner size="sm" label="Cargando…" />
867
+ * <kf-spinner onDark />
868
+ */
869
+ class Spinner {
870
+ size = input('md', /* @ts-ignore */
871
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
872
+ /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
873
+ label = input('', /* @ts-ignore */
874
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
875
+ /** Invierte los colores para fondos oscuros. */
876
+ onDark = input(false, { ...(ngDevMode ? { debugName: "onDark" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
877
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
878
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Spinner, isStandalone: true, selector: "kf-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, onDark: { classPropertyName: "onDark", publicName: "onDark", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
879
+ <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
880
+ <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
881
+ @if (label()) {
882
+ <span class="kf-spinner__label">{{ label() }}</span>
883
+ } @else {
884
+ <span class="kf-spinner__sr">Cargando</span>
885
+ }
886
+ </span>
887
+ `, isInline: true, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] });
888
+ }
889
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, decorators: [{
890
+ type: Component,
891
+ args: [{ selector: 'kf-spinner', template: `
892
+ <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
893
+ <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
894
+ @if (label()) {
895
+ <span class="kf-spinner__label">{{ label() }}</span>
896
+ } @else {
897
+ <span class="kf-spinner__sr">Cargando</span>
898
+ }
899
+ </span>
900
+ `, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] }]
901
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onDark: [{ type: i0.Input, args: [{ isSignal: true, alias: "onDark", required: false }] }] } });
902
+
465
903
  /**
466
904
  * Wraps a native `<button>`, so keyboard handling, focus and form submission
467
905
  * keep working. The cost of the wrapper is forwarding `disabled` and `type` by
@@ -477,10 +915,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
477
915
  *
478
916
  * `(click)` is not forwarded — it bubbles from the inner button to the host.
479
917
  *
918
+ * `loading` replaces the projected content with `kf-spinner` and forces
919
+ * `disabled` — for the common "Ingresando…"/"Enviando…" submit pattern,
920
+ * which before this prop existed as plain text swaps with no visual
921
+ * indicator (see `web-portal-latam-frontend`'s login submit buttons).
922
+ *
480
923
  * <kf-button (click)="save()">Save</kf-button>
481
924
  * <kf-button variant="ghost" disabled>Cancel</kf-button>
482
925
  * <kf-button variant="danger" (click)="remove()">Delete</kf-button>
483
926
  * <kf-button size="sm" icon="calendar" block>Reservar hora</kf-button>
927
+ * <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
484
928
  */
485
929
  class Button {
486
930
  variant = input('primary', /* @ts-ignore */
@@ -496,11 +940,19 @@ class Button {
496
940
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
497
941
  /** Stretches the button to the full width of its container. */
498
942
  block = input(false, { ...(ngDevMode ? { debugName: "block" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
943
+ /** Replaces the projected content with `kf-spinner` and forces `disabled`. */
944
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
945
+ /** `ghost`/`outline` have no solid background — the default spinner ring already reads fine there. */
946
+ hasSolidFill = computed(() => {
947
+ const variant = this.variant();
948
+ return variant !== 'ghost' && variant !== 'outline';
949
+ }, /* @ts-ignore */
950
+ ...(ngDevMode ? [{ debugName: "hasSolidFill" }] : /* istanbul ignore next */ []));
499
951
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
500
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-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 }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: `
952
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-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 }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: `
501
953
  <button
502
954
  [type]="type()"
503
- [disabled]="disabled()"
955
+ [disabled]="disabled() || loading()"
504
956
  [class.kf-button--secondary]="variant() === 'secondary'"
505
957
  [class.kf-button--ghost]="variant() === 'ghost'"
506
958
  [class.kf-button--outline]="variant() === 'outline'"
@@ -508,19 +960,27 @@ class Button {
508
960
  [class.kf-button--sm]="size() === 'sm'"
509
961
  [class.kf-button--lg]="size() === 'lg'"
510
962
  >
511
- @if (icon(); as iconName) {
512
- <kf-icon class="kf-button__icon" [name]="iconName" [size]="size() === 'lg' ? 'md' : 'sm'" />
963
+ @if (loading()) {
964
+ <kf-spinner size="sm" [onDark]="hasSolidFill()" />
965
+ } @else {
966
+ @if (icon(); as iconName) {
967
+ <kf-icon
968
+ class="kf-button__icon"
969
+ [name]="iconName"
970
+ [size]="size() === 'lg' ? 'md' : 'sm'"
971
+ />
972
+ }
973
+ <ng-content />
513
974
  }
514
- <ng-content />
515
975
  </button>
516
- `, isInline: true, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
976
+ `, isInline: true, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
517
977
  }
518
978
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, decorators: [{
519
979
  type: Component,
520
- args: [{ selector: 'kf-button', imports: [Icon], template: `
980
+ args: [{ selector: 'kf-button', imports: [Icon, Spinner], template: `
521
981
  <button
522
982
  [type]="type()"
523
- [disabled]="disabled()"
983
+ [disabled]="disabled() || loading()"
524
984
  [class.kf-button--secondary]="variant() === 'secondary'"
525
985
  [class.kf-button--ghost]="variant() === 'ghost'"
526
986
  [class.kf-button--outline]="variant() === 'outline'"
@@ -528,15 +988,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
528
988
  [class.kf-button--sm]="size() === 'sm'"
529
989
  [class.kf-button--lg]="size() === 'lg'"
530
990
  >
531
- @if (icon(); as iconName) {
532
- <kf-icon class="kf-button__icon" [name]="iconName" [size]="size() === 'lg' ? 'md' : 'sm'" />
991
+ @if (loading()) {
992
+ <kf-spinner size="sm" [onDark]="hasSolidFill()" />
993
+ } @else {
994
+ @if (icon(); as iconName) {
995
+ <kf-icon
996
+ class="kf-button__icon"
997
+ [name]="iconName"
998
+ [size]="size() === 'lg' ? 'md' : 'sm'"
999
+ />
1000
+ }
1001
+ <ng-content />
533
1002
  }
534
- <ng-content />
535
1003
  </button>
536
1004
  `, host: {
537
1005
  '[attr.block]': "block() ? '' : null",
538
1006
  }, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"] }]
539
- }], 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 }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }] } });
1007
+ }], 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 }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
540
1008
 
541
1009
  /**
542
1010
  * Banner contextual que vive dentro del flujo: validaciones, avisos de
@@ -955,11 +1423,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
955
1423
  */
956
1424
  class CarouselSlide {
957
1425
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CarouselSlide, deps: [], target: i0.ɵɵFactoryTarget.Component });
958
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: CarouselSlide, isStandalone: true, selector: "kf-carousel-slide", ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:block;flex:0 0 100%;width:100%}\n"] });
1426
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: CarouselSlide, isStandalone: true, selector: "kf-carousel-slide", ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:block;flex:0 0 100%;width:100%;height:100%}\n"] });
959
1427
  }
960
1428
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CarouselSlide, decorators: [{
961
1429
  type: Component,
962
- args: [{ selector: 'kf-carousel-slide', template: `<ng-content />`, styles: [":host{display:block;flex:0 0 100%;width:100%}\n"] }]
1430
+ args: [{ selector: 'kf-carousel-slide', template: `<ng-content />`, styles: [":host{display:block;flex:0 0 100%;width:100%;height:100%}\n"] }]
963
1431
  }] });
964
1432
  /**
965
1433
  * Carrusel de slides con flechas, dots y autoplay opcional.
@@ -1056,43 +1524,44 @@ class Carousel {
1056
1524
  </div>
1057
1525
 
1058
1526
  @if (slides().length > 1) {
1059
- <button
1060
- type="button"
1061
- class="kf-carousel__arrow kf-carousel__arrow--prev"
1062
- aria-label="Slide anterior"
1063
- (click)="previous()"
1064
- >
1065
- ‹
1066
- </button>
1067
- <button
1068
- type="button"
1069
- class="kf-carousel__arrow kf-carousel__arrow--next"
1070
- aria-label="Slide siguiente"
1071
- (click)="next()"
1072
- >
1073
- ›
1074
- </button>
1075
-
1076
- <div class="kf-carousel__dots" role="tablist">
1077
- @for (slide of slides(); track slide; let i = $index) {
1078
- <button
1079
- type="button"
1080
- role="tab"
1081
- class="kf-carousel__dot"
1082
- [class.kf-carousel__dot--active]="i === activeIndex()"
1083
- [attr.aria-selected]="i === activeIndex()"
1084
- [attr.aria-label]="'Ir al slide ' + (i + 1)"
1085
- (click)="goTo(i)"
1086
- ></button>
1087
- }
1527
+ <div class="kf-carousel__controls">
1528
+ <div class="kf-carousel__dots" role="tablist">
1529
+ @for (slide of slides(); track slide; let i = $index) {
1530
+ <button
1531
+ type="button"
1532
+ role="tab"
1533
+ class="kf-carousel__dot"
1534
+ [class.kf-carousel__dot--active]="i === activeIndex()"
1535
+ [attr.aria-selected]="i === activeIndex()"
1536
+ [attr.aria-label]="'Ir al slide ' + (i + 1)"
1537
+ (click)="goTo(i)"
1538
+ ></button>
1539
+ }
1540
+ </div>
1541
+ <button
1542
+ type="button"
1543
+ class="kf-carousel__arrow"
1544
+ aria-label="Slide anterior"
1545
+ (click)="previous()"
1546
+ >
1547
+ <kf-icon name="chevron-left" size="sm" />
1548
+ </button>
1549
+ <button
1550
+ type="button"
1551
+ class="kf-carousel__arrow"
1552
+ aria-label="Slide siguiente"
1553
+ (click)="next()"
1554
+ >
1555
+ <kf-icon name="chevron-right" size="sm" />
1556
+ </button>
1088
1557
  </div>
1089
1558
  }
1090
1559
  </div>
1091
- `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-carousel{position:relative}.kf-carousel__viewport{overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;transition:transform .32s ease}.kf-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-pill);background:var(--kf-color-white);color:var(--kf-color-ink);font-size:1.25rem;line-height:1;cursor:pointer;box-shadow:var(--kf-shadow-sm);transition:background .14s,border-color .14s}.kf-carousel__arrow:hover{background:var(--kf-color-surface);border-color:var(--kf-color-border-strong)}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-carousel__arrow--prev{left:var(--kf-space-sm)}.kf-carousel__arrow--next{right:var(--kf-space-sm)}.kf-carousel__dots{display:flex;justify-content:center;gap:var(--kf-space-xs);margin-top:var(--kf-space-sm)}.kf-carousel__dot{width:.5rem;height:.5rem;padding:0;border:none;border-radius:var(--kf-radius-pill);background:var(--kf-color-border-strong);cursor:pointer;transition:background .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-carousel__dot--active{background:var(--kf-color-primary)}\n"] });
1560
+ `, isInline: true, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
1092
1561
  }
1093
1562
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Carousel, decorators: [{
1094
1563
  type: Component,
1095
- args: [{ selector: 'kf-carousel', template: `
1564
+ args: [{ selector: 'kf-carousel', imports: [Icon], template: `
1096
1565
  <div
1097
1566
  class="kf-carousel"
1098
1567
  (mouseenter)="pauseAutoplay()"
@@ -1110,39 +1579,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1110
1579
  </div>
1111
1580
 
1112
1581
  @if (slides().length > 1) {
1113
- <button
1114
- type="button"
1115
- class="kf-carousel__arrow kf-carousel__arrow--prev"
1116
- aria-label="Slide anterior"
1117
- (click)="previous()"
1118
- >
1119
- ‹
1120
- </button>
1121
- <button
1122
- type="button"
1123
- class="kf-carousel__arrow kf-carousel__arrow--next"
1124
- aria-label="Slide siguiente"
1125
- (click)="next()"
1126
- >
1127
- ›
1128
- </button>
1129
-
1130
- <div class="kf-carousel__dots" role="tablist">
1131
- @for (slide of slides(); track slide; let i = $index) {
1132
- <button
1133
- type="button"
1134
- role="tab"
1135
- class="kf-carousel__dot"
1136
- [class.kf-carousel__dot--active]="i === activeIndex()"
1137
- [attr.aria-selected]="i === activeIndex()"
1138
- [attr.aria-label]="'Ir al slide ' + (i + 1)"
1139
- (click)="goTo(i)"
1140
- ></button>
1141
- }
1582
+ <div class="kf-carousel__controls">
1583
+ <div class="kf-carousel__dots" role="tablist">
1584
+ @for (slide of slides(); track slide; let i = $index) {
1585
+ <button
1586
+ type="button"
1587
+ role="tab"
1588
+ class="kf-carousel__dot"
1589
+ [class.kf-carousel__dot--active]="i === activeIndex()"
1590
+ [attr.aria-selected]="i === activeIndex()"
1591
+ [attr.aria-label]="'Ir al slide ' + (i + 1)"
1592
+ (click)="goTo(i)"
1593
+ ></button>
1594
+ }
1595
+ </div>
1596
+ <button
1597
+ type="button"
1598
+ class="kf-carousel__arrow"
1599
+ aria-label="Slide anterior"
1600
+ (click)="previous()"
1601
+ >
1602
+ <kf-icon name="chevron-left" size="sm" />
1603
+ </button>
1604
+ <button
1605
+ type="button"
1606
+ class="kf-carousel__arrow"
1607
+ aria-label="Slide siguiente"
1608
+ (click)="next()"
1609
+ >
1610
+ <kf-icon name="chevron-right" size="sm" />
1611
+ </button>
1142
1612
  </div>
1143
1613
  }
1144
1614
  </div>
1145
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-carousel{position:relative}.kf-carousel__viewport{overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;transition:transform .32s ease}.kf-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-pill);background:var(--kf-color-white);color:var(--kf-color-ink);font-size:1.25rem;line-height:1;cursor:pointer;box-shadow:var(--kf-shadow-sm);transition:background .14s,border-color .14s}.kf-carousel__arrow:hover{background:var(--kf-color-surface);border-color:var(--kf-color-border-strong)}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-carousel__arrow--prev{left:var(--kf-space-sm)}.kf-carousel__arrow--next{right:var(--kf-space-sm)}.kf-carousel__dots{display:flex;justify-content:center;gap:var(--kf-space-xs);margin-top:var(--kf-space-sm)}.kf-carousel__dot{width:.5rem;height:.5rem;padding:0;border:none;border-radius:var(--kf-radius-pill);background:var(--kf-color-border-strong);cursor:pointer;transition:background .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-carousel__dot--active{background:var(--kf-color-primary)}\n"] }]
1615
+ `, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] }]
1146
1616
  }], propDecorators: { autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], indexChange: [{ type: i0.Output, args: ["indexChange"] }], slides: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CarouselSlide), { isSignal: true }] }] } });
1147
1617
 
1148
1618
  /**
@@ -1310,6 +1780,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1310
1780
  }, styles: [":host{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);border:1px solid transparent;font:inherit;font-size:.875em;line-height:1;color:var(--kf-color-text);background:var(--kf-color-surface);border-color:var(--kf-color-border);white-space:nowrap}:host(.kf-chip--ok){background:var(--kf-color-success-soft);border-color:var(--kf-color-success-soft);color:var(--kf-color-text)}:host(.kf-chip--warn){background:var(--kf-color-warning-soft);border-color:var(--kf-color-warning-soft);color:var(--kf-color-text)}:host(.kf-chip--error){background:var(--kf-color-error-soft);border-color:var(--kf-color-error-soft);color:var(--kf-color-text)}:host(.kf-chip--neutral){background:var(--kf-color-surface);border-color:var(--kf-color-border);color:var(--kf-color-text)}.kf-chip__dot{inline-size:.5em;block-size:.5em;border-radius:var(--kf-radius-pill);flex:none;background:var(--kf-color-text-muted)}.kf-chip__label{display:inline-block}.kf-chip__dot--ok{background:var(--kf-color-success)}.kf-chip__dot--warn{background:var(--kf-color-warning)}.kf-chip__dot--error{background:var(--kf-color-error)}.kf-chip__dot--neutral{background:var(--kf-color-text-muted)}\n"] }]
1311
1781
  }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
1312
1782
 
1783
+ /**
1784
+ * Separador visual simple entre bloques de contenido. `orientation`
1785
+ * controla si es una línea horizontal (para separar secciones apiladas) o
1786
+ * vertical (para separar elementos en una fila, ej. acciones en una toolbar,
1787
+ * o las columnas de metadata en `kf-company-selector`).
1788
+ *
1789
+ * En `vertical`, el divisor necesita una altura del contexto donde vive —
1790
+ * `:host { align-self: stretch }` lo hace ocupar la altura del contenedor
1791
+ * flex/grid padre sin que el consumidor tenga que fijarla a mano. `flex:
1792
+ * none` evita que el propio divisor se achique a 0 dentro de ese contenedor
1793
+ * flex cuando hay contenido hermano compitiendo por espacio.
1794
+ *
1795
+ * <kf-divider />
1796
+ * <kf-divider orientation="vertical" />
1797
+ */
1798
+ class Divider {
1799
+ orientation = input('horizontal', /* @ts-ignore */
1800
+ ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
1801
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, deps: [], target: i0.ɵɵFactoryTarget.Component });
1802
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Divider, isStandalone: true, selector: "kf-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.orientation": "orientation()", "attr.aria-orientation": "orientation()" } }, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;flex:none;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){width:100%;height:1px}:host([orientation=\"vertical\"]){align-self:stretch;width:1px;height:auto}\n"] });
1803
+ }
1804
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, decorators: [{
1805
+ type: Component,
1806
+ args: [{ selector: 'kf-divider', template: '', host: {
1807
+ role: 'separator',
1808
+ '[attr.orientation]': 'orientation()',
1809
+ '[attr.aria-orientation]': 'orientation()',
1810
+ }, styles: [":host{display:block;flex:none;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){width:100%;height:1px}:host([orientation=\"vertical\"]){align-self:stretch;width:1px;height:auto}\n"] }]
1811
+ }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }] } });
1812
+
1313
1813
  /**
1314
1814
  * Label superior compartido por todos los campos de formulario del design
1315
1815
  * system (páginas 11-12: Text Field, Select, Multiselect) — uppercase,
@@ -1358,23 +1858,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1358
1858
  `, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-field-label__required{color:var(--kf-color-error);margin-inline-start:2px}\n"] }]
1359
1859
  }], propDecorators: { for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
1360
1860
 
1361
- let nextId$2 = 0;
1861
+ let nextId$1 = 0;
1362
1862
  /**
1363
1863
  * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
1364
1864
  * nativo del navegador. Un `<select>` real siempre delega el popup de
1365
1865
  * opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
1366
- * ese popup (ver el mismo problema ya resuelto en `SelectMenu`, del que
1367
- * este componente reutiliza el patrón completo: trigger + panel flotante
1368
- * propio, cerrado con click-afuera y Escape).
1866
+ * ese popup.
1867
+ *
1868
+ * Hasta la `0.1.5` existía además `kf-select-menu`, un segundo componente
1869
+ * prácticamente idéntico (mismo trigger, mismo panel, mismo CSS) que solo
1870
+ * se diferenciaba en aceptar `options` únicamente como `{ value, label }[]`
1871
+ * (sin el atajo `string[]` que `Select` ya tenía). Se fusionaron en este
1872
+ * componente en la `0.2.0` — mantener dos componentes con la misma
1873
+ * interacción y el mismo aspecto visual, cuya única diferencia real era la
1874
+ * forma de un input, no se sostenía como dos piezas separadas del catálogo.
1369
1875
  *
1370
- * Pensado para sets simples y cortos. Para listas más ricas o con más
1371
- * opciones, es el mismo componente que `SelectMenu` — la diferencia es
1372
- * solo de estilo (label uppercase, fondo gris que pasa a blanco al foco,
1373
- * a tono con `Input`) y que acepta `options` como `string[]` además de
1374
- * `{ value, label }[]`.
1876
+ * El panel flotante se conecta al trigger vía `@angular/cdk/overlay` — el
1877
+ * mismo mecanismo que `Dropdown` (ver `../dropdown/Dropdown.ts`).
1375
1878
  *
1376
1879
  * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
1377
1880
  * <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
1881
+ *
1882
+ * Pendiente: el prop `icon` de otros componentes del design system requiere
1883
+ * un componente Icon / sprite SVG que esta librería todavía no tiene — este
1884
+ * componente no usa iconos, así que no aplica acá.
1378
1885
  */
1379
1886
  class Select {
1380
1887
  label = input(/* @ts-ignore */
@@ -1387,7 +1894,7 @@ class Select {
1387
1894
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1388
1895
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1389
1896
  valueChange = output();
1390
- instanceId = `kf-select-${nextId$2++}`;
1897
+ instanceId = `kf-select-${nextId$1++}`;
1391
1898
  triggerId = `${this.instanceId}-trigger`;
1392
1899
  listboxId = `${this.instanceId}-listbox`;
1393
1900
  isOpen = signal(false, /* @ts-ignore */
@@ -1397,7 +1904,34 @@ class Select {
1397
1904
  ...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
1398
1905
  selectedOption = computed(() => this.normalizedOptions().find((option) => option.value === this.value()), /* @ts-ignore */
1399
1906
  ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
1400
- host = inject((ElementRef));
1907
+ triggerRef = viewChild.required('trigger', /* @ts-ignore */
1908
+ ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
1909
+ panelTemplate = viewChild.required('panelTemplate', /* @ts-ignore */
1910
+ ...(ngDevMode ? [{ debugName: "panelTemplate" }] : /* istanbul ignore next */ []));
1911
+ overlayService = inject(Overlay);
1912
+ viewContainerRef = inject(ViewContainerRef);
1913
+ destroyRef = inject(DestroyRef);
1914
+ overlayRef = null;
1915
+ constructor() {
1916
+ effect(() => {
1917
+ if (this.isOpen()) {
1918
+ this.attach();
1919
+ }
1920
+ else {
1921
+ this.detach();
1922
+ }
1923
+ });
1924
+ this.destroyRef.onDestroy(() => this.overlayRef?.dispose());
1925
+ }
1926
+ /**
1927
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo ve
1928
+ * teclas presionadas con el foco DENTRO del pane del overlay. Si el foco
1929
+ * sigue en el trigger (o en cualquier otro lado de la página) mientras el
1930
+ * panel está abierto, `keydownEvents()` nunca dispara.
1931
+ */
1932
+ onDocumentEscape() {
1933
+ this.isOpen.set(false);
1934
+ }
1401
1935
  toggle() {
1402
1936
  if (this.disabled()) {
1403
1937
  return;
@@ -1410,16 +1944,53 @@ class Select {
1410
1944
  this.valueChange.emit(option.value);
1411
1945
  }
1412
1946
  }
1413
- onDocumentClick(event) {
1414
- if (this.isOpen() && !this.host.nativeElement.contains(event.target)) {
1415
- this.isOpen.set(false);
1947
+ attach() {
1948
+ if (this.overlayRef?.hasAttached()) {
1949
+ return;
1416
1950
  }
1951
+ const overlayRef = this.getOrCreateOverlayRef();
1952
+ // Se remide el ancho del trigger en cada apertura, no solo la primera —
1953
+ // ver la misma nota en SelectMenu: cachear el ancho en la creación del
1954
+ // overlay lo deja desincronizado si el trigger cambia de tamaño entre
1955
+ // una apertura y la siguiente.
1956
+ overlayRef.updateSize({ width: this.triggerRef().nativeElement.getBoundingClientRect().width });
1957
+ const portal = new TemplatePortal(this.panelTemplate(), this.viewContainerRef);
1958
+ overlayRef.attach(portal);
1959
+ overlayRef.outsidePointerEvents().subscribe(() => this.isOpen.set(false));
1960
+ overlayRef.keydownEvents().subscribe((event) => {
1961
+ if (event.key === 'Escape') {
1962
+ this.isOpen.set(false);
1963
+ }
1964
+ });
1417
1965
  }
1418
- onEscape() {
1419
- this.isOpen.set(false);
1966
+ detach() {
1967
+ this.overlayRef?.detach();
1968
+ }
1969
+ getOrCreateOverlayRef() {
1970
+ if (this.overlayRef) {
1971
+ return this.overlayRef;
1972
+ }
1973
+ const positionStrategy = this.overlayService
1974
+ .position()
1975
+ .flexibleConnectedTo(this.triggerRef())
1976
+ .withPositions([
1977
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
1978
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
1979
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
1980
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
1981
+ ])
1982
+ .withViewportMargin(8)
1983
+ .withPush(true);
1984
+ this.overlayRef = this.overlayService.create({
1985
+ positionStrategy,
1986
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
1987
+ width: this.triggerRef().nativeElement.getBoundingClientRect().width,
1988
+ maxWidth: '90vw',
1989
+ });
1990
+ return this.overlayRef;
1420
1991
  }
1421
1992
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, deps: [], target: i0.ɵɵFactoryTarget.Component });
1422
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: `
1993
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelTemplate", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: `
1423
1994
  @if (label()) {
1424
1995
  <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
1425
1996
  }
@@ -1444,7 +2015,7 @@ class Select {
1444
2015
  aria-hidden="true"
1445
2016
  ></span>
1446
2017
  </button>
1447
- @if (isOpen()) {
2018
+ <ng-template #panelTemplate>
1448
2019
  <ul
1449
2020
  class="kf-select__panel"
1450
2021
  [id]="listboxId"
@@ -1468,8 +2039,8 @@ class Select {
1468
2039
  <li class="kf-select__empty">Sin opciones disponibles</li>
1469
2040
  }
1470
2041
  </ul>
1471
- }
1472
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
2042
+ </ng-template>
2043
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:240px;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
1473
2044
  }
1474
2045
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, decorators: [{
1475
2046
  type: Component,
@@ -1498,7 +2069,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1498
2069
  aria-hidden="true"
1499
2070
  ></span>
1500
2071
  </button>
1501
- @if (isOpen()) {
2072
+ <ng-template #panelTemplate>
1502
2073
  <ul
1503
2074
  class="kf-select__panel"
1504
2075
  [id]="listboxId"
@@ -1522,12 +2093,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1522
2093
  <li class="kf-select__empty">Sin opciones disponibles</li>
1523
2094
  }
1524
2095
  </ul>
1525
- }
1526
- `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
1527
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], onDocumentClick: [{
1528
- type: HostListener,
1529
- args: ['document:click', ['$event']]
1530
- }], onEscape: [{
2096
+ </ng-template>
2097
+ `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:240px;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
2098
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelTemplate: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], onDocumentEscape: [{
1531
2099
  type: HostListener,
1532
2100
  args: ['document:keydown.escape']
1533
2101
  }] } });
@@ -1571,19 +2139,22 @@ class CompanySelector {
1571
2139
  />
1572
2140
  @if (meta().length) {
1573
2141
  <div class="kf-company-selector__meta">
1574
- @for (item of meta(); track item.label) {
2142
+ @for (item of meta(); track item.label; let last = $last) {
1575
2143
  <div class="kf-company-selector__meta-item">
1576
2144
  <span class="kf-company-selector__meta-label">{{ item.label }}</span>
1577
2145
  <span class="kf-company-selector__meta-value">{{ item.value }}</span>
1578
2146
  </div>
2147
+ @if (!last) {
2148
+ <kf-divider orientation="vertical" />
2149
+ }
1579
2150
  }
1580
2151
  </div>
1581
2152
  }
1582
- `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-lg);flex-wrap:wrap;padding:var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white);font-family:var(--kf-font-body)}.kf-company-selector__select{flex:1 1 260px;min-width:0}.kf-company-selector__meta{display:flex;gap:var(--kf-space-lg);flex-wrap:wrap}.kf-company-selector__meta-item{display:flex;flex-direction:column;gap:2px}.kf-company-selector__meta-label{font-family:var(--kf-font-title);font-size:.72rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-company-selector__meta-value{font-family:var(--kf-font-mono);font-size:.875rem;color:var(--kf-color-ink)}\n"], dependencies: [{ kind: "component", type: Select, selector: "kf-select", inputs: ["label", "options", "value", "placeholder", "disabled"], outputs: ["valueChange"] }] });
2153
+ `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-lg);flex-wrap:wrap;padding:var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white);font-family:var(--kf-font-body)}.kf-company-selector__select{flex:1 1 260px;min-width:0}.kf-company-selector__meta{display:flex;gap:var(--kf-space-lg);flex-wrap:wrap}.kf-company-selector__meta-item{display:flex;flex-direction:column;gap:2px}.kf-company-selector__meta-label{font-family:var(--kf-font-title);font-size:.72rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-company-selector__meta-value{font-family:var(--kf-font-mono);font-size:.875rem;color:var(--kf-color-ink)}\n"], dependencies: [{ kind: "component", type: Divider, selector: "kf-divider", inputs: ["orientation"] }, { kind: "component", type: Select, selector: "kf-select", inputs: ["label", "options", "value", "placeholder", "disabled"], outputs: ["valueChange"] }] });
1583
2154
  }
1584
2155
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CompanySelector, decorators: [{
1585
2156
  type: Component,
1586
- args: [{ selector: 'kf-company-selector', imports: [Select], template: `
2157
+ args: [{ selector: 'kf-company-selector', imports: [Divider, Select], template: `
1587
2158
  <kf-select
1588
2159
  class="kf-company-selector__select"
1589
2160
  [label]="label()"
@@ -1593,11 +2164,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1593
2164
  />
1594
2165
  @if (meta().length) {
1595
2166
  <div class="kf-company-selector__meta">
1596
- @for (item of meta(); track item.label) {
2167
+ @for (item of meta(); track item.label; let last = $last) {
1597
2168
  <div class="kf-company-selector__meta-item">
1598
2169
  <span class="kf-company-selector__meta-label">{{ item.label }}</span>
1599
2170
  <span class="kf-company-selector__meta-value">{{ item.value }}</span>
1600
2171
  </div>
2172
+ @if (!last) {
2173
+ <kf-divider orientation="vertical" />
2174
+ }
1601
2175
  }
1602
2176
  </div>
1603
2177
  }
@@ -1760,55 +2334,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1760
2334
  `, styles: [":host{display:block}.kf-skeleton{display:block;background:linear-gradient(90deg,var(--kf-color-border) 25%,var(--kf-color-surface) 37%,var(--kf-color-border) 63%);background-size:400% 100%;animation:kf-skeleton-shimmer 1.4s ease infinite}.kf-skeleton--text{height:14px;border-radius:var(--kf-radius-xs)}.kf-skeleton--rect{height:80px;border-radius:var(--kf-radius-sm)}.kf-skeleton--circle{aspect-ratio:1 / 1;border-radius:var(--kf-radius-pill)}@keyframes kf-skeleton-shimmer{0%{background-position:100% 0}to{background-position:0 0}}@media(prefers-reduced-motion:reduce){.kf-skeleton{animation:none;background:var(--kf-color-border)}}\n"] }]
1761
2335
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
1762
2336
 
1763
- /**
1764
- * Indicador de carga indeterminado: inline, dentro de un botón, o como único
1765
- * contenido de una sección mientras llega la data.
1766
- *
1767
- * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
1768
- * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
1769
- * conocida — guardar, enviar, reintentar.
1770
- *
1771
- * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
1772
- * cuando aparece. Sin `label` cae en un texto accesible por defecto.
1773
- *
1774
- * <kf-spinner />
1775
- * <kf-spinner size="sm" label="Cargando…" />
1776
- * <kf-spinner onDark />
1777
- */
1778
- class Spinner {
1779
- size = input('md', /* @ts-ignore */
1780
- ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1781
- /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
1782
- label = input('', /* @ts-ignore */
1783
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1784
- /** Invierte los colores para fondos oscuros. */
1785
- onDark = input(false, { ...(ngDevMode ? { debugName: "onDark" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1786
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
1787
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Spinner, isStandalone: true, selector: "kf-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, onDark: { classPropertyName: "onDark", publicName: "onDark", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1788
- <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
1789
- <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
1790
- @if (label()) {
1791
- <span class="kf-spinner__label">{{ label() }}</span>
1792
- } @else {
1793
- <span class="kf-spinner__sr">Cargando</span>
1794
- }
1795
- </span>
1796
- `, isInline: true, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] });
1797
- }
1798
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, decorators: [{
1799
- type: Component,
1800
- args: [{ selector: 'kf-spinner', template: `
1801
- <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
1802
- <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
1803
- @if (label()) {
1804
- <span class="kf-spinner__label">{{ label() }}</span>
1805
- } @else {
1806
- <span class="kf-spinner__sr">Cargando</span>
1807
- }
1808
- </span>
1809
- `, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] }]
1810
- }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onDark: [{ type: i0.Input, args: [{ isSignal: true, alias: "onDark", required: false }] }] } });
1811
-
1812
2337
  /**
1813
2338
  * Wrapper de los cuatro estados que toda sección tiene: cargando, con datos,
1814
2339
  * vacía y con error. Un componente en vez de tres `@if` sueltos, porque es
@@ -2358,16 +2883,239 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2358
2883
  </div>
2359
2884
  }
2360
2885
  </div>
2361
- }
2362
-
2363
- @if (rows().length > 0) {
2364
- <div class="kf-data-table__footer">
2365
- <ng-content select="[footer]" />
2886
+ }
2887
+
2888
+ @if (rows().length > 0) {
2889
+ <div class="kf-data-table__footer">
2890
+ <ng-content select="[footer]" />
2891
+ </div>
2892
+ }
2893
+ </div>
2894
+ `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__view-toggle{display:inline-flex;gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm)}.kf-data-table__view-option{font:inherit;font-size:.875em;padding:var(--kf-space-xs) var(--kf-space-sm);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer;transition:background .14s,color .14s}.kf-data-table__view-option:hover:not(.kf-data-table__view-option--active){color:var(--kf-color-text)}.kf-data-table__view-option--active{background:var(--kf-color-white);color:var(--kf-color-ink);box-shadow:var(--kf-shadow-xs)}.kf-data-table__view-option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__scroll{overflow-x:auto}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table__table th{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted);background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{cursor:pointer;transition:background .14s}.kf-data-table__row:hover{background:var(--kf-color-primary-soft)}.kf-data-table__cell--right{text-align:right}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{display:flex;flex-direction:column;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);overflow:hidden;transition:border-color .14s,box-shadow .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0}.kf-data-table__card-title{font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-statuses,.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__thumb{width:32px;height:32px}.kf-data-table__card-label{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted)}.kf-data-table__card-value{color:var(--kf-color-text);text-align:right}\n"] }]
2895
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], cardable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardable", required: false }] }], defaultView: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultView", required: false }] }], render: [{ type: i0.Input, args: [{ isSignal: true, alias: "render", required: false }] }], cellTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplates", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }] } });
2896
+
2897
+ /**
2898
+ * Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
2899
+ * por dentro (no un `<a>`): las acciones de un menú de este tipo disparan
2900
+ * comportamiento en la pantalla actual (cerrar sesión, ejecutar un
2901
+ * comando), no navegan a otra URL.
2902
+ *
2903
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
2904
+ */
2905
+ class DropdownItem {
2906
+ tone = input('neutral', /* @ts-ignore */
2907
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
2908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
2909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: DropdownItem, isStandalone: true, selector: "kf-dropdown-item", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2910
+ <button
2911
+ type="button"
2912
+ class="kf-dropdown-item"
2913
+ [class.kf-dropdown-item--error]="tone() === 'error'"
2914
+ role="menuitem"
2915
+ >
2916
+ <ng-content />
2917
+ </button>
2918
+ `, isInline: true, styles: [":host{display:block}.kf-dropdown-item{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;font:inherit;text-align:left;color:var(--kf-color-text);cursor:pointer}.kf-dropdown-item:hover{background:var(--kf-color-surface)}.kf-dropdown-item:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}\n"] });
2919
+ }
2920
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, decorators: [{
2921
+ type: Component,
2922
+ args: [{ selector: 'kf-dropdown-item', template: `
2923
+ <button
2924
+ type="button"
2925
+ class="kf-dropdown-item"
2926
+ [class.kf-dropdown-item--error]="tone() === 'error'"
2927
+ role="menuitem"
2928
+ >
2929
+ <ng-content />
2930
+ </button>
2931
+ `, styles: [":host{display:block}.kf-dropdown-item{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;font:inherit;text-align:left;color:var(--kf-color-text);cursor:pointer}.kf-dropdown-item:hover{background:var(--kf-color-surface)}.kf-dropdown-item:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}\n"] }]
2932
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
2933
+ /**
2934
+ * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
2935
+ * (`<ng-template>` por dentro, capturada vía `TemplateRef`) — `Dropdown` la
2936
+ * conecta al overlay real cuando `open` pasa a `true`, no la renderiza en su
2937
+ * propio lugar del DOM. Esto es lo que permite que el panel aparezca pegado
2938
+ * al host de `Dropdown` sin importar dónde esté ese host en la página.
2939
+ *
2940
+ * <kf-dropdown [(open)]="menuOpen">
2941
+ * <kf-dropdown-panel>
2942
+ * <kf-dropdown-item (click)="onLogout()">Cerrar sesión</kf-dropdown-item>
2943
+ * </kf-dropdown-panel>
2944
+ * </kf-dropdown>
2945
+ */
2946
+ class DropdownPanel {
2947
+ templateRef = viewChild.required(TemplateRef, /* @ts-ignore */
2948
+ ...(ngDevMode ? [{ debugName: "templateRef" }] : /* istanbul ignore next */ []));
2949
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
2950
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: DropdownPanel, isStandalone: true, selector: "kf-dropdown-panel", viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: `
2951
+ <ng-template>
2952
+ <div class="kf-dropdown-panel">
2953
+ <div class="kf-dropdown-panel__inner" role="menu">
2954
+ <ng-content />
2955
+ </div>
2956
+ </div>
2957
+ </ng-template>
2958
+ `, isInline: true, styles: [".kf-dropdown-panel{min-width:220px;max-width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg);font-family:var(--kf-font-body);animation:kf-dropdown-panel-enter .14s ease-out}.kf-dropdown-panel__inner{padding:var(--kf-space-xs) 0;overflow:hidden;border-radius:inherit}@media(prefers-reduced-motion:reduce){.kf-dropdown-panel{animation:none}}@keyframes kf-dropdown-panel-enter{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] });
2959
+ }
2960
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, decorators: [{
2961
+ type: Component,
2962
+ args: [{ selector: 'kf-dropdown-panel', template: `
2963
+ <ng-template>
2964
+ <div class="kf-dropdown-panel">
2965
+ <div class="kf-dropdown-panel__inner" role="menu">
2966
+ <ng-content />
2366
2967
  </div>
2367
- }
2368
- </div>
2369
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__view-toggle{display:inline-flex;gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm)}.kf-data-table__view-option{font:inherit;font-size:.875em;padding:var(--kf-space-xs) var(--kf-space-sm);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer;transition:background .14s,color .14s}.kf-data-table__view-option:hover:not(.kf-data-table__view-option--active){color:var(--kf-color-text)}.kf-data-table__view-option--active{background:var(--kf-color-white);color:var(--kf-color-ink);box-shadow:var(--kf-shadow-xs)}.kf-data-table__view-option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__scroll{overflow-x:auto}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table__table th{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted);background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{cursor:pointer;transition:background .14s}.kf-data-table__row:hover{background:var(--kf-color-primary-soft)}.kf-data-table__cell--right{text-align:right}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{display:flex;flex-direction:column;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);overflow:hidden;transition:border-color .14s,box-shadow .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0}.kf-data-table__card-title{font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-statuses,.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__thumb{width:32px;height:32px}.kf-data-table__card-label{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted)}.kf-data-table__card-value{color:var(--kf-color-text);text-align:right}\n"] }]
2370
- }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], cardable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardable", required: false }] }], defaultView: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultView", required: false }] }], render: [{ type: i0.Input, args: [{ isSignal: true, alias: "render", required: false }] }], cellTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplates", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }] } });
2968
+ </div>
2969
+ </ng-template>
2970
+ `, styles: [".kf-dropdown-panel{min-width:220px;max-width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg);font-family:var(--kf-font-body);animation:kf-dropdown-panel-enter .14s ease-out}.kf-dropdown-panel__inner{padding:var(--kf-space-xs) 0;overflow:hidden;border-radius:inherit}@media(prefers-reduced-motion:reduce){.kf-dropdown-panel{animation:none}}@keyframes kf-dropdown-panel-enter{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] }]
2971
+ }], propDecorators: { templateRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
2972
+ /**
2973
+ * Menú de acciones flotante, conectado a un elemento disparador vía
2974
+ * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
2975
+ * `mat-menu`/`mat-select`. Resuelve el patrón que antes se reimplementaba a
2976
+ * mano en cada consumidor (ver el session-menu de `web-portal-latam-frontend`,
2977
+ * ~80 líneas de HTML/TS/SCSS para toggle + click-afuera + Escape + animación
2978
+ * de entrada, acopladas a una clase interna no pública de `PortalHeader`).
2979
+ *
2980
+ * Se apoya en el CDK en vez de un `position: absolute` casero (como sí hace
2981
+ * `SelectMenu`, que puede permitírselo porque su trigger vive siempre dentro
2982
+ * del propio componente) porque acá el disparador real puede vivir en OTRO
2983
+ * componente por completo — ej. el avatar dentro de `kf-portal-header` — y
2984
+ * el panel tiene que aparecer pegado a ESE elemento sin importar dónde el
2985
+ * consumidor ubique al `Dropdown` en su HTML. El Overlay CDK calcula esa
2986
+ * posición (con fallback si no entra en pantalla) en vez de que cada
2987
+ * consumidor lo recalcule a mano.
2988
+ *
2989
+ * `open` es two-way, igual que `Alert`/`Modal`: el consumidor decide qué
2990
+ * dispara la apertura (puede ser un `(click)` en un botón propio, o un
2991
+ * evento de otro componente como `PortalHeader.sessionClick`) — `Dropdown`
2992
+ * en sí no renderiza ningún trigger, su propio host es el punto de anclaje
2993
+ * visual del panel.
2994
+ *
2995
+ * <kf-dropdown [(open)]="menuOpen">
2996
+ * <kf-dropdown-panel>
2997
+ * <div class="mi-header">Sesión iniciada como {{ email() }}</div>
2998
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
2999
+ * </kf-dropdown-panel>
3000
+ * </kf-dropdown>
3001
+ */
3002
+ class Dropdown {
3003
+ /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
3004
+ open = model(false, /* @ts-ignore */
3005
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3006
+ /**
3007
+ * Elemento al que se conecta el overlay, si es distinto del propio host
3008
+ * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es visualmente
3009
+ * el disparador (caso normal: el consumidor arma su propio botón como
3010
+ * contenido, y `Dropdown` envuelve ese contenido). Cuando el disparador
3011
+ * real vive dentro de OTRO componente que no se puede envolver sin
3012
+ * ensanchar el área de click a algo más grande que el botón (ej. el
3013
+ * avatar de sesión dentro de `PortalHeader`, que convive con logo y
3014
+ * navegación en el mismo host), `anchor` permite apuntar el overlay a la
3015
+ * referencia real de ese componente en vez de al host vacío de
3016
+ * `Dropdown`.
3017
+ *
3018
+ * <kf-dropdown [(open)]="menuOpen" [anchor]="headerRef()">
3019
+ */
3020
+ anchor = input(/* @ts-ignore */
3021
+ ...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
3022
+ panel = contentChild(DropdownPanel, /* @ts-ignore */
3023
+ ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
3024
+ overlayService = inject(Overlay);
3025
+ host = inject((ElementRef));
3026
+ viewContainerRef = inject(ViewContainerRef);
3027
+ destroyRef = inject(DestroyRef);
3028
+ overlayRef = null;
3029
+ constructor() {
3030
+ effect(() => {
3031
+ if (this.open()) {
3032
+ this.attach();
3033
+ }
3034
+ else {
3035
+ this.detach();
3036
+ }
3037
+ });
3038
+ this.destroyRef.onDestroy(() => this.overlayRef?.dispose());
3039
+ }
3040
+ /**
3041
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
3042
+ * ve teclas presionadas con el foco DENTRO del pane del overlay. Si el
3043
+ * usuario abre el menú y presiona Escape con el foco en otro lado de la
3044
+ * página (caso común — el foco puede seguir en el botón disparador, que
3045
+ * vive fuera del overlay), `keydownEvents()` nunca dispara. Este listener
3046
+ * a nivel de documento cubre ese caso; el de `keydownEvents()` sigue
3047
+ * activo para cuando el foco sí está dentro del panel.
3048
+ */
3049
+ onDocumentEscape() {
3050
+ this.open.set(false);
3051
+ }
3052
+ attach() {
3053
+ const panel = this.panel();
3054
+ if (!panel || this.overlayRef?.hasAttached()) {
3055
+ return;
3056
+ }
3057
+ const overlayRef = this.getOrCreateOverlayRef();
3058
+ const portal = new TemplatePortal(panel.templateRef(), this.viewContainerRef);
3059
+ overlayRef.attach(portal);
3060
+ overlayRef.outsidePointerEvents().subscribe(() => this.open.set(false));
3061
+ overlayRef.keydownEvents().subscribe((event) => {
3062
+ if (event.key === 'Escape') {
3063
+ this.open.set(false);
3064
+ }
3065
+ });
3066
+ }
3067
+ detach() {
3068
+ this.overlayRef?.detach();
3069
+ }
3070
+ getOrCreateOverlayRef() {
3071
+ if (this.overlayRef) {
3072
+ return this.overlayRef;
3073
+ }
3074
+ const positionStrategy = this.overlayService
3075
+ .position()
3076
+ .flexibleConnectedTo(this.anchor() ?? this.host)
3077
+ // 4 posiciones, no 2: además de abajo/arriba (alineado al borde
3078
+ // derecho del anchor, el caso común), agrega el mismo par alineado al
3079
+ // borde IZQUIERDO. Con solo el par "end" — como estaba antes — un
3080
+ // trigger cerca del borde derecho de la pantalla no tenía ningún
3081
+ // fallback horizontal: el panel se salía del viewport en vez de
3082
+ // reacomodarse hacia la izquierda. El CDK prueba cada posición en
3083
+ // orden y usa la primera que entra completa en el viewport.
3084
+ .withPositions([
3085
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
3086
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
3087
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
3088
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3089
+ ])
3090
+ // Margen de seguridad contra los bordes del viewport — sin esto, el
3091
+ // CDK puede considerar una posición "válida" aunque quede pegada al
3092
+ // borde con 0px de aire.
3093
+ .withViewportMargin(8)
3094
+ // Si ninguna de las 4 posiciones entra completa (viewport muy
3095
+ // angosto), la última se ajusta con push en vez de desbordarse fuera
3096
+ // de pantalla sin control.
3097
+ .withPush(true);
3098
+ this.overlayRef = this.overlayService.create({
3099
+ positionStrategy,
3100
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
3101
+ // Sin esto, el panel puede quedar más angosto que su propio
3102
+ // `min-width` en pantallas chicas si el CDK decide recortar — fija un
3103
+ // techo razonable en vez de dejarlo crecer sin límite ni encogerse
3104
+ // por debajo de lo legible.
3105
+ maxWidth: '90vw',
3106
+ });
3107
+ return this.overlayRef;
3108
+ }
3109
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
3110
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Dropdown, isStandalone: true, selector: "kf-dropdown", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" } }, queries: [{ propertyName: "panel", first: true, predicate: DropdownPanel, descendants: true, isSignal: true }], ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:inline-block}\n"] });
3111
+ }
3112
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, decorators: [{
3113
+ type: Component,
3114
+ args: [{ selector: 'kf-dropdown', template: `<ng-content />`, styles: [":host{display:inline-block}\n"] }]
3115
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: false }] }], panel: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownPanel), { isSignal: true }] }], onDocumentEscape: [{
3116
+ type: HostListener,
3117
+ args: ['document:keydown.escape']
3118
+ }] } });
2371
3119
 
2372
3120
  /**
2373
3121
  * Bloque promocional sobre fondo oscuro de marca (página 37) — campañas,
@@ -2950,6 +3698,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2950
3698
  `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"] }]
2951
3699
  }], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }] } });
2952
3700
 
3701
+ /**
3702
+ * Acción de bajo énfasis con apariencia de texto — "¿Olvidaste tu
3703
+ * contraseña?", "Volver a iniciar sesión". Antes de este componente,
3704
+ * `web-portal-latam-frontend` reimplementaba esto dos veces (`.login__forgot`
3705
+ * como `<a href="#">` con `preventDefault()`, `.login__back` como
3706
+ * `<button>`), cada una con su propio `:focus-visible` copiado literal.
3707
+ *
3708
+ * Es siempre un `<button type="button">` por dentro, nunca un `<a>`: ninguno
3709
+ * de los dos casos reales navega a otra URL, ambos disparan una acción en la
3710
+ * misma pantalla (cambiar de vista, someter un flujo). Si en el futuro un
3711
+ * consumidor necesita navegación real con href, esa es una discusión de API
3712
+ * distinta (`kf-link` con `[routerLink]`/`href`), no se fuerza acá sin caso
3713
+ * de uso confirmado.
3714
+ *
3715
+ * `variant="default"` (color primario, subrayado en hover) es para la
3716
+ * acción de mayor jerarquía relativa de la pantalla (ej. "¿Olvidaste tu
3717
+ * contraseña?"); `variant="muted"` (gris que pasa a primario en hover) es
3718
+ * para acciones de retroceso/salida (ej. "← Volver a iniciar sesión").
3719
+ *
3720
+ * <kf-link (click)="onForgotPassword()">¿Olvidaste tu contraseña?</kf-link>
3721
+ * <kf-link variant="muted" (click)="onBack()">← Volver a iniciar sesión</kf-link>
3722
+ */
3723
+ class Link {
3724
+ variant = input('default', /* @ts-ignore */
3725
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3726
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3727
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Link, deps: [], target: i0.ɵɵFactoryTarget.Component });
3728
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Link, isStandalone: true, selector: "kf-link", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3729
+ <button
3730
+ type="button"
3731
+ class="kf-link"
3732
+ [class.kf-link--muted]="variant() === 'muted'"
3733
+ [disabled]="disabled()"
3734
+ >
3735
+ <ng-content />
3736
+ </button>
3737
+ `, isInline: true, styles: [":host{display:inline-block}.kf-link{padding:0;border:none;background:transparent;font:inherit;font-weight:500;color:var(--kf-color-primary);text-decoration:none;cursor:pointer}.kf-link:hover:not(:disabled){text-decoration:underline}.kf-link:focus-visible{outline:2px solid var(--kf-color-primary);outline-offset:2px;border-radius:var(--kf-radius-xs)}.kf-link:disabled{opacity:.5;cursor:not-allowed}.kf-link--muted{color:var(--kf-color-text-muted)}.kf-link--muted:hover:not(:disabled){color:var(--kf-color-primary);text-decoration:none}\n"] });
3738
+ }
3739
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Link, decorators: [{
3740
+ type: Component,
3741
+ args: [{ selector: 'kf-link', template: `
3742
+ <button
3743
+ type="button"
3744
+ class="kf-link"
3745
+ [class.kf-link--muted]="variant() === 'muted'"
3746
+ [disabled]="disabled()"
3747
+ >
3748
+ <ng-content />
3749
+ </button>
3750
+ `, styles: [":host{display:inline-block}.kf-link{padding:0;border:none;background:transparent;font:inherit;font-weight:500;color:var(--kf-color-primary);text-decoration:none;cursor:pointer}.kf-link:hover:not(:disabled){text-decoration:underline}.kf-link:focus-visible{outline:2px solid var(--kf-color-primary);outline-offset:2px;border-radius:var(--kf-radius-xs)}.kf-link:disabled{opacity:.5;cursor:not-allowed}.kf-link--muted{color:var(--kf-color-text-muted)}.kf-link--muted:hover:not(:disabled){color:var(--kf-color-primary);text-decoration:none}\n"] }]
3751
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
3752
+
2953
3753
  /**
2954
3754
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
2955
3755
  * superior derecha, badge de esquina opcional (pegado al valor) y una nota
@@ -3201,7 +4001,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3201
4001
  args: ['click', ['$event']]
3202
4002
  }] } });
3203
4003
 
3204
- let nextId$1 = 0;
4004
+ let nextId = 0;
3205
4005
  /**
3206
4006
  * Dropdown de selección múltiple con checkboxes internos, siguiendo el mismo
3207
4007
  * patrón de panel flotante que `SelectMenu` (panel con `position: absolute`
@@ -3238,7 +4038,7 @@ class MultiSelect {
3238
4038
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3239
4039
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3240
4040
  valuesChange = output();
3241
- instanceId = `kf-multi-select-${nextId$1++}`;
4041
+ instanceId = `kf-multi-select-${nextId++}`;
3242
4042
  triggerId = `${this.instanceId}-trigger`;
3243
4043
  listboxId = `${this.instanceId}-listbox`;
3244
4044
  isOpen = signal(false, /* @ts-ignore */
@@ -3374,7 +4174,7 @@ class MultiSelect {
3374
4174
  }
3375
4175
  </ul>
3376
4176
  }
3377
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4177
+ `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;width:240px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
3378
4178
  }
3379
4179
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MultiSelect, decorators: [{
3380
4180
  type: Component,
@@ -3456,7 +4256,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3456
4256
  }
3457
4257
  </ul>
3458
4258
  }
3459
- `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
4259
+ `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;width:240px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
3460
4260
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valuesChange: [{ type: i0.Output, args: ["valuesChange"] }], onDocumentClick: [{
3461
4261
  type: HostListener,
3462
4262
  args: ['document:click', ['$event']]
@@ -3715,15 +4515,16 @@ class Panel {
3715
4515
  </a>
3716
4516
  }
3717
4517
  </div>
4518
+ <kf-divider />
3718
4519
  <div class="kf-panel__body">
3719
4520
  <ng-content />
3720
4521
  </div>
3721
4522
  </kf-card>
3722
- `, isInline: true, styles: [":host{display:block}.kf-panel__head{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);margin-bottom:var(--kf-space-sm)}.kf-panel__title{font-family:var(--kf-font-title);font-size:1.05rem;font-weight:700;color:var(--kf-color-ink)}.kf-panel__action{display:inline-flex;align-items:center;gap:2px;flex:none;font-size:.8125rem;font-weight:600;color:var(--kf-color-primary);text-decoration:none}.kf-panel__action:hover{text-decoration:underline}.kf-panel__body{display:flex;flex-direction:column;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Card, selector: "kf-card", inputs: ["flush"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4523
+ `, isInline: true, styles: [":host{display:block}.kf-panel__head{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);margin-bottom:var(--kf-space-sm)}.kf-panel__title{font-family:var(--kf-font-title);font-size:1.05rem;font-weight:700;color:var(--kf-color-ink)}.kf-panel__action{display:inline-flex;align-items:center;gap:2px;flex:none;font-size:.8125rem;font-weight:600;color:var(--kf-color-primary);text-decoration:none}.kf-panel__action:hover{text-decoration:underline}.kf-panel__body{display:flex;flex-direction:column;gap:var(--kf-space-sm);margin-top:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Card, selector: "kf-card", inputs: ["flush"] }, { kind: "component", type: Divider, selector: "kf-divider", inputs: ["orientation"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
3723
4524
  }
3724
4525
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Panel, decorators: [{
3725
4526
  type: Component,
3726
- args: [{ selector: 'kf-panel', imports: [Card, Icon], template: `
4527
+ args: [{ selector: 'kf-panel', imports: [Card, Divider, Icon], template: `
3727
4528
  <kf-card>
3728
4529
  <div class="kf-panel__head">
3729
4530
  <span class="kf-panel__title">{{ title() }}</span>
@@ -3734,11 +4535,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3734
4535
  </a>
3735
4536
  }
3736
4537
  </div>
4538
+ <kf-divider />
3737
4539
  <div class="kf-panel__body">
3738
4540
  <ng-content />
3739
4541
  </div>
3740
4542
  </kf-card>
3741
- `, styles: [":host{display:block}.kf-panel__head{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);margin-bottom:var(--kf-space-sm)}.kf-panel__title{font-family:var(--kf-font-title);font-size:1.05rem;font-weight:700;color:var(--kf-color-ink)}.kf-panel__action{display:inline-flex;align-items:center;gap:2px;flex:none;font-size:.8125rem;font-weight:600;color:var(--kf-color-primary);text-decoration:none}.kf-panel__action:hover{text-decoration:underline}.kf-panel__body{display:flex;flex-direction:column;gap:var(--kf-space-sm)}\n"] }]
4543
+ `, styles: [":host{display:block}.kf-panel__head{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);margin-bottom:var(--kf-space-sm)}.kf-panel__title{font-family:var(--kf-font-title);font-size:1.05rem;font-weight:700;color:var(--kf-color-ink)}.kf-panel__action{display:inline-flex;align-items:center;gap:2px;flex:none;font-size:.8125rem;font-weight:600;color:var(--kf-color-primary);text-decoration:none}.kf-panel__action:hover{text-decoration:underline}.kf-panel__body{display:flex;flex-direction:column;gap:var(--kf-space-sm);margin-top:var(--kf-space-sm)}\n"] }]
3742
4544
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], actionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionLabel", required: false }] }], actionHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionHref", required: false }] }] } });
3743
4545
 
3744
4546
  /**
@@ -3926,10 +4728,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3926
4728
  * `<ng-content select="[actions]">` para no acoplar el componente a un set
3927
4729
  * fijo de botones.
3928
4730
  *
3929
- * `size="sm"` reduce tipografía y espaciado para usarse dentro de cards ya
3930
- * compactas. `solid` rellena el chip de ícono con el color primario sólido
3931
- * en vez del tratamiento "soft" (fondo tenue) — pensado para superficies que
3932
- * ya tienen un tinte de color propio, donde el chip "soft" perdería contraste.
4731
+ * `size` sigue la misma escala `'sm'|'md'|'lg'` que `Button`/`Icon`/`Spinner`
4732
+ * (`md` es el default): `sm` reduce tipografía y espaciado para usarse
4733
+ * dentro de cards ya compactas, `lg` los agranda para encabezar un bloque
4734
+ * más prominente. Antes de la `0.2.0`, `size` solo aceptaba `'sm'` —
4735
+ * placeholder incompleto de esta misma escala; los valores de `lg` son una
4736
+ * extrapolación proporcional de la escala existente (sin un caso de uso
4737
+ * real todavía que los valide, a diferencia de `sm`, que sí viene de un
4738
+ * patrón real de uso dentro de cards). `solid` rellena el chip de ícono con
4739
+ * el color primario sólido en vez del tratamiento "soft" (fondo tenue) —
4740
+ * pensado para superficies que ya tienen un tinte de color propio, donde el
4741
+ * chip "soft" perdería contraste.
3933
4742
  *
3934
4743
  * <kf-section-header icon="settings" title="Preferencias"
3935
4744
  * subtitle="Configuración general de la cuenta">
@@ -3937,6 +4746,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3937
4746
  * </kf-section-header>
3938
4747
  *
3939
4748
  * <kf-section-header title="Detalle" size="sm" [solid]="true" />
4749
+ * <kf-section-header title="Resumen de flota" size="lg" />
3940
4750
  */
3941
4751
  class SectionHeader {
3942
4752
  icon = input(/* @ts-ignore */
@@ -3945,14 +4755,24 @@ class SectionHeader {
3945
4755
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
3946
4756
  subtitle = input(/* @ts-ignore */
3947
4757
  ...(ngDevMode ? [undefined, { debugName: "subtitle" }] : /* istanbul ignore next */ []));
3948
- size = input(/* @ts-ignore */
3949
- ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
4758
+ size = input('md', /* @ts-ignore */
4759
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3950
4760
  solid = input(false, { ...(ngDevMode ? { debugName: "solid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4761
+ iconSize = computed(() => {
4762
+ if (this.size() === 'sm') {
4763
+ return 'sm';
4764
+ }
4765
+ if (this.size() === 'lg') {
4766
+ return 'lg';
4767
+ }
4768
+ return 'md';
4769
+ }, /* @ts-ignore */
4770
+ ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
3951
4771
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
3952
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionHeader, isStandalone: true, selector: "kf-section-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, solid: { classPropertyName: "solid", publicName: "solid", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-section-header--sm": "size() === 'sm'" } }, ngImport: i0, template: `
4772
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionHeader, isStandalone: true, selector: "kf-section-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, solid: { classPropertyName: "solid", publicName: "solid", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-section-header--sm": "size() === 'sm'", "class.kf-section-header--lg": "size() === 'lg'" } }, ngImport: i0, template: `
3953
4773
  @if (icon(); as iconName) {
3954
4774
  <span class="kf-section-header__icon" [class.kf-section-header__icon--solid]="solid()">
3955
- <kf-icon [name]="iconName" [size]="size() === 'sm' ? 'sm' : 'md'" />
4775
+ <kf-icon [name]="iconName" [size]="iconSize()" />
3956
4776
  </span>
3957
4777
  }
3958
4778
 
@@ -3966,14 +4786,14 @@ class SectionHeader {
3966
4786
  <span class="kf-section-header__actions">
3967
4787
  <ng-content select="[actions]" />
3968
4788
  </span>
3969
- `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4789
+ `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}:host(.kf-section-header--lg){gap:var(--kf-space-md)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}:host(.kf-section-header--lg) .kf-section-header__icon{inline-size:3.25rem;block-size:3.25rem;border-radius:var(--kf-radius-md)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}:host(.kf-section-header--lg) .kf-section-header__title{font-size:1.25rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}:host(.kf-section-header--lg) .kf-section-header__subtitle{font-size:.9375rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
3970
4790
  }
3971
4791
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionHeader, decorators: [{
3972
4792
  type: Component,
3973
4793
  args: [{ selector: 'kf-section-header', imports: [Icon], template: `
3974
4794
  @if (icon(); as iconName) {
3975
4795
  <span class="kf-section-header__icon" [class.kf-section-header__icon--solid]="solid()">
3976
- <kf-icon [name]="iconName" [size]="size() === 'sm' ? 'sm' : 'md'" />
4796
+ <kf-icon [name]="iconName" [size]="iconSize()" />
3977
4797
  </span>
3978
4798
  }
3979
4799
 
@@ -3989,7 +4809,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3989
4809
  </span>
3990
4810
  `, host: {
3991
4811
  '[class.kf-section-header--sm]': "size() === 'sm'",
3992
- }, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"] }]
4812
+ '[class.kf-section-header--lg]': "size() === 'lg'",
4813
+ }, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}:host(.kf-section-header--lg){gap:var(--kf-space-md)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}:host(.kf-section-header--lg) .kf-section-header__icon{inline-size:3.25rem;block-size:3.25rem;border-radius:var(--kf-radius-md)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}:host(.kf-section-header--lg) .kf-section-header__title{font-size:1.25rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}:host(.kf-section-header--lg) .kf-section-header__subtitle{font-size:.9375rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"] }]
3993
4814
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], solid: [{ type: i0.Input, args: [{ isSignal: true, alias: "solid", required: false }] }] } });
3994
4815
 
3995
4816
  /**
@@ -4063,184 +4884,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4063
4884
  `, styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-white);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-xs);transform:translate(calc(var(--kf-segmented-index, 0) * 100%));transition:transform .2s ease}.kf-segmented__option{position:relative;z-index:1;font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-sm);cursor:pointer;transition:color .14s}.kf-segmented__option:hover:not(.kf-segmented__option--active){color:var(--kf-color-text)}.kf-segmented__option--active{color:var(--kf-color-ink)}.kf-segmented__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] }]
4064
4885
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
4065
4886
 
4066
- let nextId = 0;
4067
- /**
4068
- * Dropdown de selección simple pero con panel propio (no usa `<select>` nativo),
4069
- * para cuando se necesita más control visual que un `<select>` estándar permite.
4070
- *
4071
- * El panel flotante se implementa con `position: absolute` sobre el host
4072
- * (sin Angular CDK Overlay, que no es dependencia del proyecto). Se abre y
4073
- * cierra por click en el disparador, y se cierra al hacer click afuera o al
4074
- * presionar Escape.
4075
- *
4076
- * <kf-select-menu
4077
- * label="País"
4078
- * [options]="countries"
4079
- * [value]="selectedCountry()"
4080
- * (valueChange)="selectedCountry.set($event)"
4081
- * />
4082
- *
4083
- * Pendiente: el prop `icon` de otros componentes del design system requiere
4084
- * un componente Icon / sprite SVG que esta librería todavía no tiene — este
4085
- * componente no usa iconos, así que no aplica acá.
4086
- */
4087
- class SelectMenu {
4088
- label = input(/* @ts-ignore */
4089
- ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
4090
- options = input([], /* @ts-ignore */
4091
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4092
- value = input(/* @ts-ignore */
4093
- ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
4094
- placeholder = input('Seleccionar', /* @ts-ignore */
4095
- ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4096
- disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4097
- valueChange = output();
4098
- instanceId = `kf-select-menu-${nextId++}`;
4099
- triggerId = `${this.instanceId}-trigger`;
4100
- listboxId = `${this.instanceId}-listbox`;
4101
- isOpen = signal(false, /* @ts-ignore */
4102
- ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
4103
- selectedOption = computed(() => this.options().find((option) => option.value === this.value()), /* @ts-ignore */
4104
- ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
4105
- host = inject((ElementRef));
4106
- toggle() {
4107
- if (this.disabled()) {
4108
- return;
4109
- }
4110
- this.isOpen.update((open) => !open);
4111
- }
4112
- select(option) {
4113
- this.isOpen.set(false);
4114
- if (option.value !== this.value()) {
4115
- this.valueChange.emit(option.value);
4116
- }
4117
- }
4118
- onDocumentClick(event) {
4119
- if (this.isOpen() && !this.host.nativeElement.contains(event.target)) {
4120
- this.isOpen.set(false);
4121
- }
4122
- }
4123
- onEscape() {
4124
- this.isOpen.set(false);
4125
- }
4126
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
4127
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SelectMenu, isStandalone: true, selector: "kf-select-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: `
4128
- @if (label()) {
4129
- <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
4130
- }
4131
- <button
4132
- #trigger
4133
- type="button"
4134
- [id]="triggerId"
4135
- class="kf-select-menu__trigger"
4136
- [disabled]="disabled()"
4137
- [attr.aria-haspopup]="'listbox'"
4138
- [attr.aria-expanded]="isOpen()"
4139
- [attr.aria-controls]="isOpen() ? listboxId : null"
4140
- (click)="toggle()"
4141
- >
4142
- <span
4143
- class="kf-select-menu__value"
4144
- [class.kf-select-menu__value--placeholder]="!selectedOption()"
4145
- >
4146
- {{ selectedOption()?.label ?? placeholder() }}
4147
- </span>
4148
- <span
4149
- class="kf-select-menu__caret"
4150
- [class.kf-select-menu__caret--open]="isOpen()"
4151
- aria-hidden="true"
4152
- ></span>
4153
- </button>
4154
- @if (isOpen()) {
4155
- <ul
4156
- class="kf-select-menu__panel"
4157
- [id]="listboxId"
4158
- role="listbox"
4159
- [attr.aria-labelledby]="triggerId"
4160
- >
4161
- @for (option of options(); track option.value) {
4162
- <li
4163
- class="kf-select-menu__option"
4164
- [class.kf-select-menu__option--selected]="option.value === value()"
4165
- role="option"
4166
- tabindex="0"
4167
- [attr.aria-selected]="option.value === value()"
4168
- (click)="select(option)"
4169
- (keydown.enter)="select(option)"
4170
- (keydown.space)="$event.preventDefault(); select(option)"
4171
- >
4172
- {{ option.label }}
4173
- </li>
4174
- } @empty {
4175
- <li class="kf-select-menu__empty">Sin opciones disponibles</li>
4176
- }
4177
- </ul>
4178
- }
4179
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4180
- }
4181
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, decorators: [{
4182
- type: Component,
4183
- args: [{ selector: 'kf-select-menu', imports: [FieldLabel], template: `
4184
- @if (label()) {
4185
- <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
4186
- }
4187
- <button
4188
- #trigger
4189
- type="button"
4190
- [id]="triggerId"
4191
- class="kf-select-menu__trigger"
4192
- [disabled]="disabled()"
4193
- [attr.aria-haspopup]="'listbox'"
4194
- [attr.aria-expanded]="isOpen()"
4195
- [attr.aria-controls]="isOpen() ? listboxId : null"
4196
- (click)="toggle()"
4197
- >
4198
- <span
4199
- class="kf-select-menu__value"
4200
- [class.kf-select-menu__value--placeholder]="!selectedOption()"
4201
- >
4202
- {{ selectedOption()?.label ?? placeholder() }}
4203
- </span>
4204
- <span
4205
- class="kf-select-menu__caret"
4206
- [class.kf-select-menu__caret--open]="isOpen()"
4207
- aria-hidden="true"
4208
- ></span>
4209
- </button>
4210
- @if (isOpen()) {
4211
- <ul
4212
- class="kf-select-menu__panel"
4213
- [id]="listboxId"
4214
- role="listbox"
4215
- [attr.aria-labelledby]="triggerId"
4216
- >
4217
- @for (option of options(); track option.value) {
4218
- <li
4219
- class="kf-select-menu__option"
4220
- [class.kf-select-menu__option--selected]="option.value === value()"
4221
- role="option"
4222
- tabindex="0"
4223
- [attr.aria-selected]="option.value === value()"
4224
- (click)="select(option)"
4225
- (keydown.enter)="select(option)"
4226
- (keydown.space)="$event.preventDefault(); select(option)"
4227
- >
4228
- {{ option.label }}
4229
- </li>
4230
- } @empty {
4231
- <li class="kf-select-menu__empty">Sin opciones disponibles</li>
4232
- }
4233
- </ul>
4234
- }
4235
- `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
4236
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], onDocumentClick: [{
4237
- type: HostListener,
4238
- args: ['document:click', ['$event']]
4239
- }], onEscape: [{
4240
- type: HostListener,
4241
- args: ['document:keydown.escape']
4242
- }] } });
4243
-
4244
4887
  /**
4245
4888
  * Indicador numérico compacto (página 34): label + valor grande, con un
4246
4889
  * ícono a la derecha del label — más chico y sin badge de esquina que
@@ -4509,6 +5152,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4509
5152
  `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-tabs{position:relative;display:flex;align-items:stretch;gap:var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-tabs__tab{display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:transparent;border:none;padding:var(--kf-space-sm) 0;cursor:pointer;transition:color .14s}.kf-tabs__tab:hover:not(.kf-tabs__tab--active){color:var(--kf-color-text)}.kf-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);border-radius:var(--kf-radius-xs)}.kf-tabs__tab--active{color:var(--kf-color-primary);font-size:1rem;font-weight:700}.kf-tabs__indicator{position:absolute;bottom:-1px;left:0;height:2px;background:var(--kf-color-primary);border-radius:2px 2px 0 0;pointer-events:none;transition:transform .28s cubic-bezier(.32,.72,0,1),width .28s cubic-bezier(.32,.72,0,1),opacity .14s}@media(prefers-reduced-motion:reduce){.kf-tabs__indicator{transition:opacity .14s}}\n"] }]
4510
5153
  }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
4511
5154
 
5155
+ /**
5156
+ * Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
5157
+ * texto para el DOM/accesibilidad) del estilo visual (`variant`, cómo se
5158
+ * ve). Antes de este componente, cada pantalla que consume la librería
5159
+ * definía su propio `h1`/`p` con `font-size` suelto para el mismo rol
5160
+ * (título de página, subtítulo, texto secundario) — auditoría sobre
5161
+ * `web-portal-latam-frontend` encontró 9 tamaños distintos entre login y
5162
+ * home sin ningún paso de escala en común. `variant` resuelve eso contra
5163
+ * la escala de `tokens/base.css` (`--kf-font-size-*`).
5164
+ *
5165
+ * `tone` sigue la misma convención cerrada que `Alert`/`Badge`/`Chip`: sin
5166
+ * escape hatch de color libre. Si un caso necesita un color que `tone` no
5167
+ * cubre, es una discusión de design system, no una prop nueva acá.
5168
+ *
5169
+ * <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
5170
+ * <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
5171
+ * <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
5172
+ */
5173
+ class Text {
5174
+ /** Etiqueta semántica real que se renderiza — nunca es solo un `div`. */
5175
+ as = input('p', /* @ts-ignore */
5176
+ ...(ngDevMode ? [{ debugName: "as" }] : /* istanbul ignore next */ []));
5177
+ variant = input('body', /* @ts-ignore */
5178
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
5179
+ tone = input('neutral', /* @ts-ignore */
5180
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
5181
+ /**
5182
+ * Copia de `as` para uso dentro del template: `as` es palabra reservada
5183
+ * en las expresiones de Angular (`@if (x; as y)`), así que `@switch (as())`
5184
+ * no parsea — NG5002. La prop pública sigue llamándose `as` (es solo un
5185
+ * atributo HTML ahí, `<kf-text as="h1">`, no una expresión).
5186
+ */
5187
+ tag = computed(() => this.as(), /* @ts-ignore */
5188
+ ...(ngDevMode ? [{ debugName: "tag" }] : /* istanbul ignore next */ []));
5189
+ hostClass() {
5190
+ const classes = [`kf-text--${this.variant()}`];
5191
+ if (this.tone() !== 'neutral') {
5192
+ classes.push(`kf-text--${this.tone()}`);
5193
+ }
5194
+ return classes.join(' ');
5195
+ }
5196
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Text, deps: [], target: i0.ɵɵFactoryTarget.Component });
5197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Text, isStandalone: true, selector: "kf-text", inputs: { as: { classPropertyName: "as", publicName: "as", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
5198
+ <!--
5199
+ Una sola proyección compartida (ng-template + ngTemplateOutlet) en vez
5200
+ de un <ng-content> por cada rama del @switch: Angular solo conecta la
5201
+ proyección real del consumidor a la PRIMERA instancia de <ng-content>
5202
+ que encuentra en el árbol de compilación — con un <ng-content> por
5203
+ rama, todas menos una quedan vacías en runtime (bug detectado por
5204
+ Text.spec.ts, no algo documentado a simple vista en los docs de Angular).
5205
+ -->
5206
+ <ng-template #projected><ng-content /></ng-template>
5207
+ @switch (tag()) {
5208
+ @case ('h1') {
5209
+ <h1 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h1>
5210
+ }
5211
+ @case ('h2') {
5212
+ <h2 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h2>
5213
+ }
5214
+ @case ('h3') {
5215
+ <h3 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h3>
5216
+ }
5217
+ @case ('span') {
5218
+ <span [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></span>
5219
+ }
5220
+ @default {
5221
+ <p [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></p>
5222
+ }
5223
+ }
5224
+ `, isInline: true, styles: [":host{display:contents}h1,h2,h3,p,span{margin:0;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-text--title-lg{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-lg);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-md{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-md);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-sm{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--body{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body)}.kf-text--body-muted{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--caption{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--ok{color:var(--kf-color-success-strong)}.kf-text--warn{color:var(--kf-color-warning-strong)}.kf-text--error{color:var(--kf-color-error-strong)}.kf-text--info{color:var(--kf-color-info-strong)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
5225
+ }
5226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Text, decorators: [{
5227
+ type: Component,
5228
+ args: [{ selector: 'kf-text', imports: [NgTemplateOutlet], template: `
5229
+ <!--
5230
+ Una sola proyección compartida (ng-template + ngTemplateOutlet) en vez
5231
+ de un <ng-content> por cada rama del @switch: Angular solo conecta la
5232
+ proyección real del consumidor a la PRIMERA instancia de <ng-content>
5233
+ que encuentra en el árbol de compilación — con un <ng-content> por
5234
+ rama, todas menos una quedan vacías en runtime (bug detectado por
5235
+ Text.spec.ts, no algo documentado a simple vista en los docs de Angular).
5236
+ -->
5237
+ <ng-template #projected><ng-content /></ng-template>
5238
+ @switch (tag()) {
5239
+ @case ('h1') {
5240
+ <h1 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h1>
5241
+ }
5242
+ @case ('h2') {
5243
+ <h2 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h2>
5244
+ }
5245
+ @case ('h3') {
5246
+ <h3 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h3>
5247
+ }
5248
+ @case ('span') {
5249
+ <span [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></span>
5250
+ }
5251
+ @default {
5252
+ <p [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></p>
5253
+ }
5254
+ }
5255
+ `, styles: [":host{display:contents}h1,h2,h3,p,span{margin:0;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-text--title-lg{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-lg);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-md{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-md);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-sm{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--body{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body)}.kf-text--body-muted{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--caption{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--ok{color:var(--kf-color-success-strong)}.kf-text--warn{color:var(--kf-color-warning-strong)}.kf-text--error{color:var(--kf-color-error-strong)}.kf-text--info{color:var(--kf-color-info-strong)}\n"] }]
5256
+ }], propDecorators: { as: [{ type: i0.Input, args: [{ isSignal: true, alias: "as", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
5257
+
4512
5258
  /** Fixed icon per tone (page 20 of the design system) — not configurable per call. */
4513
5259
  const TOAST_ICON = {
4514
5260
  success: 'check-circle',
@@ -4858,5 +5604,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4858
5604
  * Generated bundle index. Do not edit.
4859
5605
  */
4860
5606
 
4861
- export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, Icon, Input, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, SelectMenu, Skeleton, Spinner, StatTile, StatusPill, Tabs, ToastHost, ToastService, VehicleHeader, ViewToggle };
5607
+ export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, Dropdown, DropdownItem, DropdownPanel, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, Icon, Input, Link, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, Skeleton, Spinner, StatTile, StatusPill, Tabs, Text, ToastHost, ToastService, VehicleHeader, ViewToggle };
4862
5608
  //# sourceMappingURL=kaufmann-lab-ui.mjs.map