@eduardoalvarez/arrecife 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/README.md +121 -1
- package/dist/brand/index.cjs +29 -10
- package/dist/brand/index.d.cts +9 -1
- package/dist/brand/index.d.ts +9 -1
- package/dist/brand/index.js +4 -2
- package/dist/chart/index.cjs +287 -0
- package/dist/chart/index.d.cts +127 -0
- package/dist/chart/index.d.ts +127 -0
- package/dist/chart/index.js +103 -0
- package/dist/chunk-KPZNNMV5.js +83 -0
- package/dist/chunk-MLKGABMK.js +7 -0
- package/dist/chunk-NHS7ETKJ.js +27 -0
- package/dist/{chunk-I7GYZD2A.js → chunk-TSPJOM6K.js} +5 -7
- package/dist/chunk-UOWIDFCB.js +81 -0
- package/dist/{chunk-VSMW2CT4.js → chunk-VPT32GPG.js} +2 -1
- package/dist/{chunk-ND45OQOC.js → chunk-YZ2SDOVZ.js} +29 -35
- package/dist/chunk-ZEOQKRQ7.js +19 -0
- package/dist/form/index.cjs +301 -0
- package/dist/form/index.d.cts +86 -0
- package/dist/form/index.d.ts +86 -0
- package/dist/form/index.js +84 -0
- package/dist/index.cjs +1094 -293
- package/dist/index.d.cts +452 -37
- package/dist/index.d.ts +452 -37
- package/dist/index.js +963 -373
- package/dist/label-DuTvJGxD.d.cts +14 -0
- package/dist/label-DuTvJGxD.d.ts +14 -0
- package/dist/og/index.js +2 -1
- package/dist/shiki/index.js +2 -1
- package/dist/tema/index.cjs +94 -0
- package/dist/tema/index.d.cts +110 -0
- package/dist/tema/index.d.ts +110 -0
- package/dist/tema/index.js +2 -0
- package/dist/tokens/index.cjs +6 -0
- package/dist/tokens/index.d.cts +33 -1
- package/dist/tokens/index.d.ts +33 -1
- package/dist/tokens/index.js +3 -2
- package/dist/tokens/theme.css +43 -0
- package/llms.txt +351 -44
- package/package.json +31 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
export { BrandToken, ColorMode, ColorToken, ControlToken, FontToken, GradientToken, RadiusToken, SintaxisToken, SizeToken, SpacingToken, Tokens, TypeScaleToken, brand, colors, control, dark, fonts, gradient, light, limits, motion, naming, radius, shadow, sintaxis, size, spacing, tagline, tokens, typeScale } from './tokens/index.js';
|
|
1
|
+
export { BrandToken, ColorMode, ColorToken, ControlToken, FontToken, GradientToken, RadiusToken, SeriesToken, SintaxisToken, SizeToken, SpacingToken, Tokens, TypeScaleToken, brand, colors, control, dark, fonts, gradient, light, limits, motion, naming, radius, series, shadow, sintaxis, size, spacing, tagline, tokens, typeScale } from './tokens/index.js';
|
|
2
|
+
import { Tema } from './tema/index.js';
|
|
3
|
+
export { TEMA_ATRIBUTO, TEMA_CLAVE, TEMA_EVENTO, alternarTema, aplicarTema, escucharTema, scriptTema, temaActual, temaGuardado, temaPreferido } from './tema/index.js';
|
|
2
4
|
import * as react from 'react';
|
|
3
|
-
import { ComponentPropsWithoutRef, ReactNode, ComponentProps, SVGProps } from 'react';
|
|
5
|
+
import { ComponentPropsWithoutRef, ReactNode, ComponentProps, RefObject, SVGProps } from 'react';
|
|
6
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
4
7
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
5
8
|
import { VariantProps } from 'class-variance-authority';
|
|
9
|
+
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
6
10
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
7
11
|
import { DayPicker } from 'react-day-picker';
|
|
8
12
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
9
13
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
10
14
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
11
|
-
|
|
15
|
+
export { L as Label, a as LabelProps } from './label-DuTvJGxD.js';
|
|
12
16
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
13
17
|
import * as ProgressPrimitive from '@radix-ui/react-progress';
|
|
14
18
|
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
|
@@ -22,6 +26,54 @@ import { C as Cara, P as Pose } from './catalogo-Du5ID-Hi.js';
|
|
|
22
26
|
export { A as Aleta, F as Fondo, R as RUTA_ASSETS, a as aletas, c as caras, l as listaCaras, b as listaPoses, p as poses, u as usoDeCara } from './catalogo-Du5ID-Hi.js';
|
|
23
27
|
export { CaraDeMascota, CaraDeMascotaProps, Isotipo, IsotipoProps, Logo, LogoProps, Mascota, MascotaProps } from './brand/index.js';
|
|
24
28
|
import { ClassValue } from 'clsx';
|
|
29
|
+
import '@radix-ui/react-label';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* El plegable. Lo pedían dos proyectos: el FAQ del portafolio y el temario de
|
|
33
|
+
* cursos, que es literalmente una lista de secciones que se abren.
|
|
34
|
+
*
|
|
35
|
+
* La altura SÍ se anima, y es la cuarta excepción declarada del sistema.
|
|
36
|
+
*
|
|
37
|
+
* Merece explicarse, porque la regla general es la contraria y este componente
|
|
38
|
+
* nació sin animar citándola. La diferencia es que aquí no APARECE nada: se
|
|
39
|
+
* abre un hueco, y todo lo que hay debajo del acordeón se desplaza. Sin
|
|
40
|
+
* transición ese desplazamiento es un salto, y quien acaba de pulsar pierde el
|
|
41
|
+
* sitio en la página — que es justo el daño que la regla «nada de movimiento»
|
|
42
|
+
* existe para evitar. Es la misma categoría que el panel lateral, la segunda
|
|
43
|
+
* excepción, y no la de una animación de entrada.
|
|
44
|
+
*
|
|
45
|
+
* Va detrás de `motion-safe`, dura `--duration-standard` y usa
|
|
46
|
+
* `--ease-standard`, así que no estrena un tiempo ni una curva. Quien pidió
|
|
47
|
+
* menos movimiento sigue viendo el panel aparecer donde va a quedarse.
|
|
48
|
+
*
|
|
49
|
+
* Ver `docs/decisiones.md` § 20.
|
|
50
|
+
*
|
|
51
|
+
* El galón, en cambio, gira sin transición: `transition-standard` solo cubre
|
|
52
|
+
* color y borde, así que `rotate` salta aunque la clase esté puesta. Es el mismo
|
|
53
|
+
* trato que recibe el ancho de `Progress`.
|
|
54
|
+
*
|
|
55
|
+
* La división entre items es `hairline`, no `border`: es una separación de
|
|
56
|
+
* lectura, no el borde de un control.
|
|
57
|
+
*/
|
|
58
|
+
type AccordionProps = ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>;
|
|
59
|
+
declare function Accordion({ className, ...props }: AccordionProps): react.JSX.Element;
|
|
60
|
+
declare function AccordionItem({ className, ...props }: ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>): react.JSX.Element;
|
|
61
|
+
/**
|
|
62
|
+
* El disparador es el encabezado, así que va DENTRO de un `<h3>`: Radix envuelve
|
|
63
|
+
* el botón en `AccordionPrimitive.Header`, que renderiza el elemento que se le
|
|
64
|
+
* pida. Sin eso, un lector de pantalla ve una lista de botones sueltos y pierde
|
|
65
|
+
* la estructura de la página, que es justo lo que un FAQ necesita conservar.
|
|
66
|
+
*
|
|
67
|
+
* `headingLevel` existe porque el nivel correcto depende de dónde se monte: en
|
|
68
|
+
* una página de FAQ el bloque cuelga de un `<h2>` de sección, y en un temario
|
|
69
|
+
* puede colgar de un `<h3>`. Fijarlo aquí sería adivinar.
|
|
70
|
+
*/
|
|
71
|
+
type AccordionTriggerProps = ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> & {
|
|
72
|
+
/** Nivel del encabezado que envuelve al disparador. */
|
|
73
|
+
headingLevel?: 2 | 3 | 4;
|
|
74
|
+
};
|
|
75
|
+
declare function AccordionTrigger({ className, children, headingLevel, ...props }: AccordionTriggerProps): react.JSX.Element;
|
|
76
|
+
declare function AccordionContent({ className, children, ...props }: ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>): react.JSX.Element;
|
|
25
77
|
|
|
26
78
|
/**
|
|
27
79
|
* El aviso lleva el color en el fondo, no solo en el borde.
|
|
@@ -70,7 +122,7 @@ import { ClassValue } from 'clsx';
|
|
|
70
122
|
* `docs/decisiones.md`.
|
|
71
123
|
*/
|
|
72
124
|
declare const alert: (props?: ({
|
|
73
|
-
variant?: "
|
|
125
|
+
variant?: "error" | "accent" | "success" | "warning" | null | undefined;
|
|
74
126
|
enfasis?: "sutil" | "fuerte" | null | undefined;
|
|
75
127
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
76
128
|
type AlertProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & VariantProps<typeof alert> & {
|
|
@@ -83,6 +135,50 @@ type AlertProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & VariantProps<
|
|
|
83
135
|
};
|
|
84
136
|
declare function Alert({ className, variant, enfasis, title, icon, children, ...props }: AlertProps): react.JSX.Element;
|
|
85
137
|
|
|
138
|
+
/**
|
|
139
|
+
* La confirmación destructiva. NO es un `Dialog` con otro texto, y por eso está
|
|
140
|
+
* en su propio archivo y sobre su propia primitiva de Radix.
|
|
141
|
+
*
|
|
142
|
+
* Tres diferencias, y las tres importan en el momento en que alguien va a
|
|
143
|
+
* borrar un artículo:
|
|
144
|
+
*
|
|
145
|
+
* 1. El rol es `alertdialog`, no `dialog`. Un lector de pantalla lo anuncia
|
|
146
|
+
* con la descripción incluida, sin esperar a que se navegue hasta ella.
|
|
147
|
+
* 2. El foco inicial va al CANCELAR, no al primer elemento. Quien pulsa Enter
|
|
148
|
+
* por inercia no borra nada. Radix lo hace solo si el cancelar existe, y
|
|
149
|
+
* por eso `AlertDialogCancel` no es opcional en la práctica.
|
|
150
|
+
* 3. NO se cierra al pulsar fuera ni tiene aspa. Salir de una confirmación es
|
|
151
|
+
* una decisión, no un descuido: hay que decir que no.
|
|
152
|
+
*
|
|
153
|
+
* El botón de confirmar NO es rojo. El sistema no tiene variante de peligro
|
|
154
|
+
* —`Button` lo dice explícito— y el error vive en los avisos y en la validación
|
|
155
|
+
* de campo, no en un botón. Lo que comunica la gravedad es el texto: «Borrar el
|
|
156
|
+
* artículo», no «Aceptar».
|
|
157
|
+
*/
|
|
158
|
+
declare const AlertDialog: react.FC<AlertDialogPrimitive.AlertDialogProps>;
|
|
159
|
+
declare const AlertDialogTrigger: react.ForwardRefExoticComponent<AlertDialogPrimitive.AlertDialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
|
|
160
|
+
declare function AlertDialogOverlay({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>): react.JSX.Element;
|
|
161
|
+
/** Sin entrada animada, igual que `Dialog`: aparece donde va a quedarse. */
|
|
162
|
+
declare function AlertDialogContent({ className, children, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>): react.JSX.Element;
|
|
163
|
+
declare function AlertDialogHeader({ className, ...props }: ComponentPropsWithoutRef<'div'>): react.JSX.Element;
|
|
164
|
+
/**
|
|
165
|
+
* Cancelar a la IZQUIERDA de confirmar en escritorio y ABAJO en móvil, que es lo
|
|
166
|
+
* que da `flex-col-reverse`: el orden del DOM pone cancelar primero —es donde va
|
|
167
|
+
* el foco— y en columna el dedo lo encuentra donde toca sin cambiar la
|
|
168
|
+
* tabulación.
|
|
169
|
+
*/
|
|
170
|
+
declare function AlertDialogFooter({ className, ...props }: ComponentPropsWithoutRef<'div'>): react.JSX.Element;
|
|
171
|
+
declare function AlertDialogTitle({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>): react.JSX.Element;
|
|
172
|
+
/**
|
|
173
|
+
* Lo que se pierde, dicho entero. Es lo que el rol `alertdialog` hace que se
|
|
174
|
+
* anuncie de entrada, así que aquí no va «esta acción no se puede deshacer»
|
|
175
|
+
* suelto: va qué se borra y qué se lleva por delante.
|
|
176
|
+
*/
|
|
177
|
+
declare function AlertDialogDescription({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>): react.JSX.Element;
|
|
178
|
+
/** El que se lleva el foco al abrir. */
|
|
179
|
+
declare function AlertDialogCancel({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>): react.JSX.Element;
|
|
180
|
+
declare function AlertDialogAction({ className, ...props }: ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>): react.JSX.Element;
|
|
181
|
+
|
|
86
182
|
declare const avatar: (props?: ({
|
|
87
183
|
size?: "sm" | "md" | "lg" | "xl" | null | undefined;
|
|
88
184
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
@@ -96,6 +192,40 @@ declare function Avatar({ className, size, ...props }: AvatarProps): react.JSX.E
|
|
|
96
192
|
declare function AvatarImage({ className, ...props }: ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>): react.JSX.Element;
|
|
97
193
|
/** Iniciales mientras la imagen carga, o cuando no hay imagen. */
|
|
98
194
|
declare function AvatarFallback({ className, ...props }: ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>): react.JSX.Element;
|
|
195
|
+
type AvatarUploadProps = Omit<ComponentPropsWithoutRef<'div'>, 'onSelect' | 'children'> & VariantProps<typeof avatar> & {
|
|
196
|
+
/** La imagen actual, ya subida. La previsualización local la gana mientras dure. */
|
|
197
|
+
src?: string | undefined;
|
|
198
|
+
/** Iniciales mientras no hay imagen. */
|
|
199
|
+
fallback?: ReactNode;
|
|
200
|
+
/** Se dispara con el archivo elegido. La subida la hace el proyecto. */
|
|
201
|
+
onSelectFile?: ((archivo: File) => void) | undefined;
|
|
202
|
+
/** Qué acepta el diálogo del sistema. */
|
|
203
|
+
accept?: string;
|
|
204
|
+
/** Nombre accesible del control. Es lo único que lo nombra: no hay texto visible. */
|
|
205
|
+
label?: string;
|
|
206
|
+
disabled?: boolean | undefined;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* El avatar que se puede cambiar. `Avatar` muestra; este además deja elegir.
|
|
210
|
+
*
|
|
211
|
+
* Es presentacional, como `NewsletterForm`: emite `onSelectFile` con el `File` y
|
|
212
|
+
* ahí se acaba su trabajo. La subida no entra —cada proyecto tiene su
|
|
213
|
+
* almacenamiento y su endpoint— y un componente que hiciera el `POST` sería
|
|
214
|
+
* infraestructura, que es el tercer criterio de entrada y el que más se salta.
|
|
215
|
+
*
|
|
216
|
+
* La previsualización es LOCAL y no espera a que la subida termine. Es la
|
|
217
|
+
* diferencia entre un control que responde y uno que parece roto: entre elegir
|
|
218
|
+
* el archivo y que el servidor devuelva la URL pueden pasar segundos, y sin
|
|
219
|
+
* previa el avatar se queda con la foto vieja como si no hubiera pasado nada.
|
|
220
|
+
* El `objectURL` se revoca al cambiar y al desmontar; no revocarlo es una fuga
|
|
221
|
+
* de memoria que no da la cara hasta la décima foto.
|
|
222
|
+
*
|
|
223
|
+
* El control es un `<label>` con un `<input type="file">` oculto dentro, no un
|
|
224
|
+
* `<button>` que dispara un click sintético. El input real trae el diálogo del
|
|
225
|
+
* sistema, el arrastrar-y-soltar del navegador y el foco por teclado; el botón
|
|
226
|
+
* falso hay que reconstruirlo entero y siempre falta algo.
|
|
227
|
+
*/
|
|
228
|
+
declare function AvatarUpload({ src, fallback, onSelectFile, accept, label, size, disabled, className, ...props }: AvatarUploadProps): react.JSX.Element;
|
|
99
229
|
|
|
100
230
|
/**
|
|
101
231
|
* TRES familias de etiqueta, no una.
|
|
@@ -130,7 +260,7 @@ declare function AvatarFallback({ className, ...props }: ComponentPropsWithoutRe
|
|
|
130
260
|
* «Publicado» o «Borrador» con todas sus letras.
|
|
131
261
|
*/
|
|
132
262
|
declare const badge: (props?: ({
|
|
133
|
-
variant?: "
|
|
263
|
+
variant?: "error" | "accent" | "success" | "warning" | "neutral" | "warm" | null | undefined;
|
|
134
264
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
135
265
|
type BadgeProps = ComponentPropsWithoutRef<'span'> & VariantProps<typeof badge>;
|
|
136
266
|
declare function Badge({ className, variant, ...props }: BadgeProps): react.JSX.Element;
|
|
@@ -329,15 +459,6 @@ type InputProps = ComponentPropsWithoutRef<'input'> & {
|
|
|
329
459
|
};
|
|
330
460
|
declare function Input({ className, invalid, ...props }: InputProps): react.JSX.Element;
|
|
331
461
|
|
|
332
|
-
type LabelProps = ComponentPropsWithoutRef<typeof LabelPrimitive.Root>;
|
|
333
|
-
/**
|
|
334
|
-
* La escala `label`: 13px, que es el mínimo absoluto en pantalla del sistema.
|
|
335
|
-
*
|
|
336
|
-
* Se atenúa junto al control cuando este está deshabilitado, para que la
|
|
337
|
-
* pareja etiqueta-control se lea siempre como una sola unidad.
|
|
338
|
-
*/
|
|
339
|
-
declare function Label({ className, ...props }: LabelProps): react.JSX.Element;
|
|
340
|
-
|
|
341
462
|
declare function Pagination({ className, ...props }: ComponentPropsWithoutRef<'nav'>): react.JSX.Element;
|
|
342
463
|
declare function PaginationContent({ className, ...props }: ComponentPropsWithoutRef<'ul'>): react.JSX.Element;
|
|
343
464
|
declare function PaginationItem(props: ComponentPropsWithoutRef<'li'>): react.JSX.Element;
|
|
@@ -482,17 +603,73 @@ type TextareaProps = ComponentPropsWithoutRef<'textarea'> & {
|
|
|
482
603
|
};
|
|
483
604
|
declare function Textarea({ className, invalid, ...props }: TextareaProps): react.JSX.Element;
|
|
484
605
|
|
|
485
|
-
declare const ToastProvider: react.FC<ToastPrimitive.ToastProviderProps>;
|
|
486
606
|
declare const ToastAction: react.ForwardRefExoticComponent<ToastPrimitive.ToastActionProps & react.RefAttributes<HTMLButtonElement>>;
|
|
487
|
-
declare
|
|
488
|
-
|
|
489
|
-
variant?: "success" | "error" | "neutral" | null | undefined;
|
|
607
|
+
declare const toast$1: (props?: ({
|
|
608
|
+
variant?: "error" | "success" | "neutral" | null | undefined;
|
|
490
609
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
491
|
-
type ToastProps = ComponentPropsWithoutRef<typeof ToastPrimitive.Root> & VariantProps<typeof toast>;
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
610
|
+
type ToastProps = ComponentPropsWithoutRef<typeof ToastPrimitive.Root> & VariantProps<typeof toast$1>;
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* La cara imperativa del `Toast` de Radix: `toast('Guardado')` desde cualquier
|
|
614
|
+
* sitio, sin pasar el aviso por props hasta el componente que lo dispara.
|
|
615
|
+
*
|
|
616
|
+
* Existe porque dos proyectos traían `sonner` para esto. `Toast` cubre el mismo
|
|
617
|
+
* rol y tiene otra forma: Radix es declarativo con proveedor, y para mostrar un
|
|
618
|
+
* aviso desde el `catch` de un `fetch` hay que subir estado hasta donde vive el
|
|
619
|
+
* proveedor. Eso es exactamente lo que `sonner` evita, y es una necesidad real,
|
|
620
|
+
* no una preferencia de API.
|
|
621
|
+
*
|
|
622
|
+
* La alternativa era que los dos proyectos se adaptaran. Se descartó: el aviso
|
|
623
|
+
* lo dispara la capa de datos, que no tiene —ni debería tener— un componente
|
|
624
|
+
* cerca al que subirle un `useState`.
|
|
625
|
+
*
|
|
626
|
+
* Lo que NO se copió de `sonner` es el catálogo entero. No hay `toast.promise`,
|
|
627
|
+
* ni `toast.custom`, ni posiciones configurables, ni apilado con perspectiva:
|
|
628
|
+
* son cuatro variantes de lo mismo y cada una es superficie pública que hay que
|
|
629
|
+
* mantener. Están las tres formas que los proyectos usan de verdad —neutral,
|
|
630
|
+
* éxito, error—, `dismiss` y nada más.
|
|
631
|
+
*
|
|
632
|
+
* El estado vive en un módulo, no en un contexto, porque el punto es que se
|
|
633
|
+
* pueda llamar desde fuera del árbol. `Toaster` se suscribe con
|
|
634
|
+
* `useSyncExternalStore`, que es la forma que React 19 tiene de leer un estado
|
|
635
|
+
* externo sin efectos ni renders en cascada.
|
|
636
|
+
*/
|
|
637
|
+
type ToastVariant = NonNullable<ToastProps['variant']>;
|
|
638
|
+
type ToastOptions = {
|
|
639
|
+
/** La primera línea, en negrita. Sin ella el aviso es una sola frase. */
|
|
640
|
+
title?: ReactNode;
|
|
641
|
+
description?: ReactNode;
|
|
642
|
+
variant?: ToastVariant;
|
|
643
|
+
/** Milisegundos en pantalla. `Infinity` lo deja hasta que se cierre a mano. */
|
|
644
|
+
duration?: number;
|
|
645
|
+
/** Un `ToastAction`, si el aviso ofrece deshacer. */
|
|
646
|
+
action?: ReactNode;
|
|
647
|
+
};
|
|
648
|
+
type Lanzador = {
|
|
649
|
+
(mensaje: ReactNode, opciones?: ToastOptions): string;
|
|
650
|
+
success: (mensaje: ReactNode, opciones?: ToastOptions) => string;
|
|
651
|
+
error: (mensaje: ReactNode, opciones?: ToastOptions) => string;
|
|
652
|
+
dismiss: (id?: string) => void;
|
|
653
|
+
};
|
|
654
|
+
/**
|
|
655
|
+
* Lanza un aviso. Devuelve su id, que es lo que hay que guardar para cerrarlo a
|
|
656
|
+
* mano —el caso de «guardando…» que se reemplaza cuando termina la petición.
|
|
657
|
+
*/
|
|
658
|
+
declare const toast: Lanzador;
|
|
659
|
+
type ToasterProps = {
|
|
660
|
+
/** Cuánto dura un aviso que no dice lo contrario. */
|
|
661
|
+
duration?: number;
|
|
662
|
+
/**
|
|
663
|
+
* Nombre del landmark que Radix crea para la región de avisos. Se traduce
|
|
664
|
+
* porque lo lee un lector de pantalla, y el default de Radix está en inglés.
|
|
665
|
+
*/
|
|
666
|
+
label?: string;
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* Va UNA vez, lo más arriba posible del árbol. Dos `Toaster` montados pintan
|
|
670
|
+
* cada aviso dos veces: la lista es del módulo, no de la instancia.
|
|
671
|
+
*/
|
|
672
|
+
declare function Toaster({ duration, label }: ToasterProps): react.JSX.Element;
|
|
496
673
|
|
|
497
674
|
declare const TooltipProvider: react.FC<TooltipPrimitive.TooltipProviderProps>;
|
|
498
675
|
declare const Tooltip: react.FC<TooltipPrimitive.TooltipProps>;
|
|
@@ -500,8 +677,8 @@ declare const TooltipTrigger: react.ForwardRefExoticComponent<TooltipPrimitive.T
|
|
|
500
677
|
declare function TooltipContent({ className, sideOffset, ...props }: ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>): react.JSX.Element;
|
|
501
678
|
|
|
502
679
|
declare const texto: (props?: ({
|
|
503
|
-
variant?: "display" | "
|
|
504
|
-
tone?: "
|
|
680
|
+
variant?: "display" | "h2" | "h3" | "label" | "body" | "h1" | "meta" | "stat" | "lead" | "ui" | "tag" | "chip" | "eyebrow" | null | undefined;
|
|
681
|
+
tone?: "error" | "accent" | "success" | "warning" | "primary" | "secondary" | "warm" | "muted" | null | undefined;
|
|
505
682
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
506
683
|
/** Etiquetas admitidas. La lista es corta a propósito: no es un `div` con estilo. */
|
|
507
684
|
type Etiqueta = 'h1' | 'h2' | 'h3' | 'h4' | 'p' | 'span' | 'strong' | 'em' | 'figcaption' | 'caption' | 'legend' | 'dt' | 'dd' | 'li';
|
|
@@ -537,6 +714,16 @@ type ArticleCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
|
|
|
537
714
|
dateTime?: string | undefined;
|
|
538
715
|
readingMinutes?: number | undefined;
|
|
539
716
|
tags?: readonly string[] | undefined;
|
|
717
|
+
/**
|
|
718
|
+
* Nivel del titular. `h3` por defecto: una tarjeta suelta en una rejilla no
|
|
719
|
+
* gana el nivel que su posición no le da.
|
|
720
|
+
*
|
|
721
|
+
* Acotado a dos valores a propósito. La página de listado —donde las tarjetas
|
|
722
|
+
* SÍ son el encabezado principal de la sección— necesita `h2`, y esa
|
|
723
|
+
* intención se perdía con la constante; abrirlo hasta `h4` o `h5`, en cambio,
|
|
724
|
+
* es invitar a saltarse niveles, que es el fallo que la constante evitaba.
|
|
725
|
+
*/
|
|
726
|
+
headingLevel?: 2 | 3;
|
|
540
727
|
};
|
|
541
728
|
/**
|
|
542
729
|
* La línea de metadatos va en `meta` y no en `eyebrow`: `18 ago 2026 · 8 min de
|
|
@@ -546,7 +733,7 @@ type ArticleCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
|
|
|
546
733
|
* Los tags son la familia CATEGORÍA — píldora de arena en minúscula —, no la de
|
|
547
734
|
* estado. Un slug es lo que se lee `engineering-culture`.
|
|
548
735
|
*/
|
|
549
|
-
declare function ArticleCard({ title, excerpt, date, dateTime, readingMinutes, tags, className, ...props }: ArticleCardProps): react.JSX.Element;
|
|
736
|
+
declare function ArticleCard({ title, excerpt, date, dateTime, readingMinutes, tags, headingLevel, className, ...props }: ArticleCardProps): react.JSX.Element;
|
|
550
737
|
|
|
551
738
|
/**
|
|
552
739
|
* La firma al pie del artículo: avatar 52px, nombre 15/500 y el rol en mono
|
|
@@ -720,6 +907,53 @@ type EmptyStateProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & {
|
|
|
720
907
|
};
|
|
721
908
|
declare function EmptyState({ expresion, title, description, action, basePath, className, ...props }: EmptyStateProps): react.JSX.Element;
|
|
722
909
|
|
|
910
|
+
/**
|
|
911
|
+
* El calendario con eventos: verlos, crearlos, editarlos y borrarlos.
|
|
912
|
+
*
|
|
913
|
+
* `Calendar` es la primitiva y sigue siendo una rejilla de fechas: sirve para
|
|
914
|
+
* ELEGIR un día y no sabe nada de contenido. Esto es la agenda, y por eso vive
|
|
915
|
+
* en `components/` y no al lado de aquella — tiene estado, tiene formulario y
|
|
916
|
+
* codifica cómo se ve un día con cosas dentro.
|
|
917
|
+
*
|
|
918
|
+
* Es presentacional, como el resto de la librería: recibe `events` y emite
|
|
919
|
+
* `onCreateEvent`, `onUpdateEvent` y `onDeleteEvent`. No guarda nada, no llama a
|
|
920
|
+
* ninguna API y no genera ids — el id lo pone quien persiste, porque es quien
|
|
921
|
+
* sabe si viene de una base de datos o de un fichero.
|
|
922
|
+
*
|
|
923
|
+
* DOS COLUMNAS y no un popover sobre el día. El popover es lo que hace todo el
|
|
924
|
+
* mundo y esconde el contenido detrás de un clic: con la lista al lado, un mes
|
|
925
|
+
* con quince eventos se lee de un vistazo y el día seleccionado no tapa la
|
|
926
|
+
* rejilla. En pantalla estrecha se apilan.
|
|
927
|
+
*
|
|
928
|
+
* El borrado pasa por `AlertDialog` y no por un botón directo. Es exactamente el
|
|
929
|
+
* caso para el que existe: una acción destructiva sin deshacer.
|
|
930
|
+
*/
|
|
931
|
+
type EventoCalendario = {
|
|
932
|
+
/** Lo pone el proyecto. La librería nunca lo inventa. */
|
|
933
|
+
id: string;
|
|
934
|
+
/** Cuándo empieza, con hora. */
|
|
935
|
+
start: Date;
|
|
936
|
+
title: string;
|
|
937
|
+
/** `warm` cuando el evento es el problema, igual que en `Stat`. */
|
|
938
|
+
tone?: 'accent' | 'warm' | undefined;
|
|
939
|
+
};
|
|
940
|
+
type EventCalendarProps = Omit<ComponentPropsWithoutRef<'div'>, 'onSelect' | 'children'> & {
|
|
941
|
+
events: readonly EventoCalendario[];
|
|
942
|
+
/** Sin él, la agenda es de solo lectura y no se pinta el formulario. */
|
|
943
|
+
onCreateEvent?: ((evento: Omit<EventoCalendario, 'id'>) => void) | undefined;
|
|
944
|
+
onUpdateEvent?: ((evento: EventoCalendario) => void) | undefined;
|
|
945
|
+
onDeleteEvent?: ((id: string) => void) | undefined;
|
|
946
|
+
/** Día seleccionado, si el proyecto lo controla. Sin él, empieza en hoy. */
|
|
947
|
+
selected?: Date | undefined;
|
|
948
|
+
onSelectDay?: ((dia: Date) => void) | undefined;
|
|
949
|
+
/** Encabezado del panel. Por defecto, `es` de date-fns, como `Calendar`. */
|
|
950
|
+
formatDay?: ((dia: Date) => string) | undefined;
|
|
951
|
+
formatTime?: ((fecha: Date) => string) | undefined;
|
|
952
|
+
/** Texto del panel cuando el día elegido no tiene nada. */
|
|
953
|
+
emptyMessage?: ReactNode;
|
|
954
|
+
};
|
|
955
|
+
declare function EventCalendar({ events, onCreateEvent, onUpdateEvent, onDeleteEvent, selected, onSelectDay, formatDay, formatTime, emptyMessage, className, ...props }: EventCalendarProps): react.JSX.Element;
|
|
956
|
+
|
|
723
957
|
/**
|
|
724
958
|
* El pie, y la firma CLI del sitio: `$ cd ~/eduardoalvarez.dev/2026`.
|
|
725
959
|
*
|
|
@@ -747,8 +981,16 @@ type FooterProps = ComponentPropsWithoutRef<'footer'> & {
|
|
|
747
981
|
year?: number;
|
|
748
982
|
/** Enlaces de texto: aviso legal, RSS, mapa del sitio. */
|
|
749
983
|
children?: ReactNode;
|
|
984
|
+
/**
|
|
985
|
+
* La fila de marca: la aleta y el wordmark, arriba del todo.
|
|
986
|
+
*
|
|
987
|
+
* Existe porque sin ella acababa metida en `children` con un `w-full` para
|
|
988
|
+
* que se llevara su propia línea. Funcionaba y era un apaño: la marca no es
|
|
989
|
+
* un enlace de texto más, y una ranura propia lo dice en el tipo.
|
|
990
|
+
*/
|
|
991
|
+
brand?: ReactNode;
|
|
750
992
|
};
|
|
751
|
-
declare function Footer({ social, year, children, className, ...props }: FooterProps): react.JSX.Element;
|
|
993
|
+
declare function Footer({ social, year, children, brand, className, ...props }: FooterProps): react.JSX.Element;
|
|
752
994
|
type FooterLinkProps = ComponentPropsWithoutRef<'a'> & {
|
|
753
995
|
asChild?: boolean | undefined;
|
|
754
996
|
};
|
|
@@ -759,13 +1001,22 @@ declare function FooterLink({ asChild, className, ...props }: FooterLinkProps):
|
|
|
759
1001
|
* conversión, y por la misma razón: si hay dos, no hay ninguno.
|
|
760
1002
|
*
|
|
761
1003
|
* Degradado, radio de panel, texto al 62 % del ancho y la pose sangrando por el
|
|
762
|
-
* borde inferior derecho.
|
|
763
|
-
* es una ilustración de portada, y esto es una cabecera.
|
|
1004
|
+
* borde inferior derecho. Eso es la variante `cabecera`, que es el defecto.
|
|
764
1005
|
*
|
|
765
1006
|
* En móvil no hay borde por el que sangrar, así que la pose baja al flujo, bajo
|
|
766
1007
|
* los botones. No es un `hidden` en pantalla pequeña: la pose es el 40 % de la
|
|
767
1008
|
* personalidad del hero.
|
|
768
1009
|
*
|
|
1010
|
+
* La otra variante es `centrado`, y existe porque la regla de arriba tiene un
|
|
1011
|
+
* caso donde no aplica. «Nunca centrada» se escribió contra el hero de una
|
|
1012
|
+
* página con más contenido debajo: ahí una mascota centrada bajo el titular es
|
|
1013
|
+
* una ilustración de portada, no una cabecera. Pero una página de enlaces es
|
|
1014
|
+
* centrada de extremo a extremo y la mascota es el protagonista, no el remate.
|
|
1015
|
+
* Ese proyecto se saltaba `Hero` entero por esto, que es peor: una regla
|
|
1016
|
+
* declarada y con nombre se discute; una copia del degradado en otro repo se
|
|
1017
|
+
* desincroniza. La pose va ARRIBA del titular, no debajo, para que siga sin
|
|
1018
|
+
* leerse como la ilustración que cierra un bloque de texto.
|
|
1019
|
+
*
|
|
769
1020
|
* El degradado viene de `--gradient-hero`, así que sigue el modo. No hay ángulo
|
|
770
1021
|
* escrito a mano en ningún proyecto.
|
|
771
1022
|
*/
|
|
@@ -779,8 +1030,13 @@ type HeroProps = Omit<ComponentPropsWithoutRef<'section'>, 'title'> & {
|
|
|
779
1030
|
/** La pose de Tiburoncín. Sin ella el hero es un panel con texto. */
|
|
780
1031
|
pose?: Pose | undefined;
|
|
781
1032
|
basePath?: string | undefined;
|
|
1033
|
+
/**
|
|
1034
|
+
* `cabecera` sangra la pose por la esquina; `centrado` la pone arriba y
|
|
1035
|
+
* centra el texto, para una página que es solo esto.
|
|
1036
|
+
*/
|
|
1037
|
+
variant?: 'cabecera' | 'centrado';
|
|
782
1038
|
};
|
|
783
|
-
declare function Hero({ title, eyebrow, description, action, pose, basePath, className, ...props }: HeroProps): react.JSX.Element;
|
|
1039
|
+
declare function Hero({ title, eyebrow, description, action, pose, basePath, variant, className, ...props }: HeroProps): react.JSX.Element;
|
|
784
1040
|
|
|
785
1041
|
type LinkRowProps = Omit<TarjetaProps, 'children'> & {
|
|
786
1042
|
name: ReactNode;
|
|
@@ -809,11 +1065,17 @@ declare function LinkRow({ name, description, icon, external, className, ...prop
|
|
|
809
1065
|
* Renderiza `<header>` colgando directamente del `body`, así que ES el landmark
|
|
810
1066
|
* `banner` del sitio. Por eso `PageHeader` va dentro de `<main>` y no es
|
|
811
1067
|
* landmark: dos banners en una página son un fallo de accesibilidad.
|
|
1068
|
+
*
|
|
1069
|
+
* Los items van a la DERECHA, pegados a las acciones, no a continuación de la
|
|
1070
|
+
* marca. Con la marca a la izquierda y los items justo detrás, el bloque de
|
|
1071
|
+
* navegación queda flotando en medio de la barra y el ojo tiene que cruzar el
|
|
1072
|
+
* hueco dos veces: una para leer la marca y otra para volver a buscar la
|
|
1073
|
+
* sección. Agrupados a la derecha, marca y navegación son dos anclas y no tres.
|
|
812
1074
|
*/
|
|
813
1075
|
type NavProps = ComponentPropsWithoutRef<'header'> & {
|
|
814
1076
|
/** El logo, a la izquierda. */
|
|
815
1077
|
brand?: ReactNode;
|
|
816
|
-
/** Los items de sección,
|
|
1078
|
+
/** Los items de sección. Van a la derecha, pegados a `actions`. */
|
|
817
1079
|
children?: ReactNode;
|
|
818
1080
|
/** Acciones a la derecha: conversión, cambio de tema, buscar. */
|
|
819
1081
|
actions?: ReactNode;
|
|
@@ -832,6 +1094,13 @@ type NavItemProps = ComponentPropsWithoutRef<'a'> & {
|
|
|
832
1094
|
* no una convención que haya que recordar en cinco proyectos. Va `aria-hidden`,
|
|
833
1095
|
* así que un lector de pantalla anuncia «artículos» y no «punto barra
|
|
834
1096
|
* artículos».
|
|
1097
|
+
*
|
|
1098
|
+
* La sección actual va entre CORCHETES además de en bioluz y subrayada. No es
|
|
1099
|
+
* decoración: el subrayado y el color son la misma señal —«esto destaca»— y en
|
|
1100
|
+
* una barra de seis items en mono, a 13px, esa señal se lee peor de lo que
|
|
1101
|
+
* parece en una maqueta. Los corchetes son la forma en que una terminal marca la
|
|
1102
|
+
* ruta activa, así que dicen «estás aquí» sin depender de que se distinga el
|
|
1103
|
+
* color. Van `aria-hidden`, porque quien escucha ya tiene `aria-current`.
|
|
835
1104
|
*/
|
|
836
1105
|
declare function NavItem({ active, asChild, className, children, ...props }: NavItemProps): react.JSX.Element;
|
|
837
1106
|
|
|
@@ -851,14 +1120,30 @@ declare function NavItem({ active, asChild, className, children, ...props }: Nav
|
|
|
851
1120
|
* receta sutil, las dos líneas se leen como una sola caja.
|
|
852
1121
|
*
|
|
853
1122
|
* Es uno de los sitios donde la mascota puede aparecer: el «sin spam».
|
|
1123
|
+
*
|
|
1124
|
+
* El campo de nombre es OPCIONAL y está apagado por defecto. No es una prop de
|
|
1125
|
+
* estilo: el endpoint de uno de los proyectos valida nombre y correo y responde
|
|
1126
|
+
* 400 si falta el primero, así que un formulario de un solo campo ahí no es un
|
|
1127
|
+
* formulario más pobre — es uno que envía algo que el servidor rechaza. El
|
|
1128
|
+
* nombre viaja como SEGUNDO argumento de `onSubmitEmail`, para que las llamadas
|
|
1129
|
+
* que ya existen —las que solo declaran `(email)`— sigan compilando.
|
|
1130
|
+
*
|
|
1131
|
+
* Lo que la librería NO hace es validar el nombre. El proyecto que lo pide lo
|
|
1132
|
+
* acota entre 2 y 50 caracteres y solo letras y acentos; esa regla es suya y del
|
|
1133
|
+
* servidor que la comprueba de verdad, y copiarla aquí sería tener dos fuentes
|
|
1134
|
+
* que se desincronizan en silencio. `nameInputProps` está para que el proyecto
|
|
1135
|
+
* ponga la suya.
|
|
854
1136
|
*/
|
|
855
1137
|
type NewsletterState = 'reposo' | 'enviando' | 'exito' | 'error';
|
|
856
1138
|
type NewsletterFormProps = Omit<ComponentPropsWithoutRef<'section'>, 'title' | 'onSubmit'> & {
|
|
857
1139
|
title: ReactNode;
|
|
858
1140
|
description?: ReactNode;
|
|
859
1141
|
state?: NewsletterState;
|
|
860
|
-
/**
|
|
861
|
-
|
|
1142
|
+
/**
|
|
1143
|
+
* Se dispara con el correo ya leído del campo, y con el nombre si el campo
|
|
1144
|
+
* está puesto.
|
|
1145
|
+
*/
|
|
1146
|
+
onSubmitEmail?: ((email: string, name?: string) => void) | undefined;
|
|
862
1147
|
successMessage?: ReactNode;
|
|
863
1148
|
errorMessage?: ReactNode;
|
|
864
1149
|
/** La letra pequeña. Es el «sin spam», y por eso admite cara. */
|
|
@@ -868,8 +1153,17 @@ type NewsletterFormProps = Omit<ComponentPropsWithoutRef<'section'>, 'title' | '
|
|
|
868
1153
|
submitLabel?: string;
|
|
869
1154
|
placeholder?: string;
|
|
870
1155
|
fieldLabel?: string;
|
|
1156
|
+
/** Añade el campo de nombre delante del correo. */
|
|
1157
|
+
nameField?: boolean;
|
|
1158
|
+
nameLabel?: string;
|
|
1159
|
+
namePlaceholder?: string;
|
|
1160
|
+
/**
|
|
1161
|
+
* Lo que el proyecto necesite colgar del campo de nombre: `minLength`,
|
|
1162
|
+
* `maxLength`, `pattern`. La librería no impone ninguna de las tres.
|
|
1163
|
+
*/
|
|
1164
|
+
nameInputProps?: Omit<InputProps, 'id' | 'name' | 'disabled'> | undefined;
|
|
871
1165
|
};
|
|
872
|
-
declare function NewsletterForm({ title, description, state, onSubmitEmail, successMessage, errorMessage, disclaimer, expresion, basePath, submitLabel, placeholder, fieldLabel, className, ...props }: NewsletterFormProps): react.JSX.Element;
|
|
1166
|
+
declare function NewsletterForm({ title, description, state, onSubmitEmail, successMessage, errorMessage, disclaimer, expresion, basePath, submitLabel, placeholder, fieldLabel, nameField, nameLabel, namePlaceholder, nameInputProps, className, ...props }: NewsletterFormProps): react.JSX.Element;
|
|
873
1167
|
|
|
874
1168
|
/**
|
|
875
1169
|
* Una sola cabecera en dos escalas, no dos componentes.
|
|
@@ -908,6 +1202,43 @@ type PageHeaderProps = Omit<ComponentPropsWithoutRef<'header'>, 'title'> & Varia
|
|
|
908
1202
|
};
|
|
909
1203
|
declare function PageHeader({ title, eyebrow, description, action, size, as, className, ...props }: PageHeaderProps): react.JSX.Element;
|
|
910
1204
|
|
|
1205
|
+
/**
|
|
1206
|
+
* Cuánto llevas leído. NO es `Progress` con otro nombre.
|
|
1207
|
+
*
|
|
1208
|
+
* `Progress` mide una tarea: hay un total conocido, alguien la empezó y va a
|
|
1209
|
+
* terminar. Esto mide una POSICIÓN en un documento, que se puede recorrer en
|
|
1210
|
+
* los dos sentidos y de la que no hay nada que completar. Por eso no lleva
|
|
1211
|
+
* `role="progressbar"` ni valor accesible: va `aria-hidden`.
|
|
1212
|
+
*
|
|
1213
|
+
* Eso último es deliberado y es la decisión que hay que defender. Un lector de
|
|
1214
|
+
* pantalla ya sabe dónde está en el documento y anunciarle «37 %» cada vez que
|
|
1215
|
+
* se mueve es ruido, no información. La barra es orientación visual, y lo que
|
|
1216
|
+
* es solo visual se declara como tal.
|
|
1217
|
+
*
|
|
1218
|
+
* El ancho se escribe directamente, sin transición: `transition-standard` solo
|
|
1219
|
+
* cubre color y borde, así que la barra sigue al scroll en vez de perseguirlo.
|
|
1220
|
+
*
|
|
1221
|
+
* La medición va dentro de `requestAnimationFrame`. Leer `scrollTop` en el
|
|
1222
|
+
* manejador de scroll fuerza un reflujo síncrono en cada evento, y en un
|
|
1223
|
+
* artículo largo eso se nota en el propio scroll — el efecto contrario al que
|
|
1224
|
+
* busca la pieza.
|
|
1225
|
+
*/
|
|
1226
|
+
type ScrollingProgressBarProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
|
|
1227
|
+
/**
|
|
1228
|
+
* El elemento que se mide. Sin él, el documento entero.
|
|
1229
|
+
*
|
|
1230
|
+
* Se pasa cuando la barra debe seguir SOLO al artículo: si la página tiene
|
|
1231
|
+
* una cabecera alta y un pie con enlaces, medir el documento marca el 100 %
|
|
1232
|
+
* cuando todavía quedan dos párrafos.
|
|
1233
|
+
*/
|
|
1234
|
+
target?: RefObject<HTMLElement | null> | undefined;
|
|
1235
|
+
/** Arena en vez de bioluz, para igualar el progreso de curso. */
|
|
1236
|
+
tone?: 'accent' | 'warm';
|
|
1237
|
+
/** Pega la barra al borde superior de la ventana. */
|
|
1238
|
+
sticky?: boolean;
|
|
1239
|
+
};
|
|
1240
|
+
declare function ScrollingProgressBar({ target, tone, sticky, className, ...props }: ScrollingProgressBarProps): react.JSX.Element;
|
|
1241
|
+
|
|
911
1242
|
/**
|
|
912
1243
|
* La barra lateral del admin del blog.
|
|
913
1244
|
*
|
|
@@ -942,6 +1273,11 @@ declare function SidebarNav({ title, version, branch, children, className, ...pr
|
|
|
942
1273
|
* es un dato; un 0 de design systems es el problema del que trata la charla.
|
|
943
1274
|
* Por eso `tone` no es una paleta abierta — son dos valores y significan cosas
|
|
944
1275
|
* distintas.
|
|
1276
|
+
*
|
|
1277
|
+
* El orden de lectura es icono + título, el número grande, y la bajada debajo.
|
|
1278
|
+
* El número va en MEDIO y no al final a propósito: es lo que se viene a leer, y
|
|
1279
|
+
* una bajada de dos líneas entre el título y la cifra la entierra. Arriba queda
|
|
1280
|
+
* de qué va, en medio cuánto, y abajo el matiz que solo lee quien se para.
|
|
945
1281
|
*/
|
|
946
1282
|
type StatProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & {
|
|
947
1283
|
/** El número, ya formateado. La librería no impone locale. */
|
|
@@ -952,8 +1288,18 @@ type StatProps = Omit<ComponentPropsWithoutRef<'div'>, 'title'> & {
|
|
|
952
1288
|
tone?: 'neutral' | 'alerta';
|
|
953
1289
|
/** Con `progress`, la métrica se lee como avance y añade la barra. */
|
|
954
1290
|
progress?: number | undefined;
|
|
1291
|
+
/**
|
|
1292
|
+
* Glifo al lado del título, a 1em. Hereda `currentColor`, así que sigue al
|
|
1293
|
+
* tono del título y no hay que teñirlo aparte.
|
|
1294
|
+
*/
|
|
1295
|
+
icon?: ReactNode;
|
|
1296
|
+
/**
|
|
1297
|
+
* La bajada: el matiz que el número solo no da. «12 aplicaciones» no dice si
|
|
1298
|
+
* son muchas, y aquí es donde se dice.
|
|
1299
|
+
*/
|
|
1300
|
+
description?: ReactNode;
|
|
955
1301
|
};
|
|
956
|
-
declare function Stat({ value, label, tone, progress, className, ...props }: StatProps): react.JSX.Element;
|
|
1302
|
+
declare function Stat({ value, label, tone, progress, icon, description, className, ...props }: StatProps): react.JSX.Element;
|
|
957
1303
|
|
|
958
1304
|
type TalkCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
|
|
959
1305
|
title: ReactNode;
|
|
@@ -964,8 +1310,63 @@ type TalkCardProps = Omit<TarjetaProps, 'children' | 'title'> & {
|
|
|
964
1310
|
location?: ReactNode;
|
|
965
1311
|
/** Etiqueta corta de estado: «con vídeo», «próxima», «solo audio». */
|
|
966
1312
|
status?: ReactNode;
|
|
1313
|
+
/**
|
|
1314
|
+
* De qué iba la charla. Se corta a dos líneas, igual que el `excerpt` de
|
|
1315
|
+
* `ArticleCard`, para que la rejilla no se desalinee.
|
|
1316
|
+
*/
|
|
1317
|
+
description?: ReactNode;
|
|
1318
|
+
};
|
|
1319
|
+
declare function TalkCard({ title, event, date, dateTime, location, status, description, className, ...props }: TalkCardProps): react.JSX.Element;
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* El control que faltaba. La librería definía todo el sistema de temas y no
|
|
1323
|
+
* exponía lo que lo cambia, así que dos proyectos lo reimplementaban.
|
|
1324
|
+
*
|
|
1325
|
+
* Lo difícil no es el botón: es que la primera pintura no parpadee y que la
|
|
1326
|
+
* elección sobreviva a la navegación. Eso vive en `@eduardoalvarez/arrecife/tema`,
|
|
1327
|
+
* que no importa React —lo consume un Astro que no monta ninguno— y de ahí sale
|
|
1328
|
+
* `scriptTema`, que va inline en el `<head>`. Sin ese script, este botón
|
|
1329
|
+
* funciona y aun así se ve el fogonazo en cada carga.
|
|
1330
|
+
*
|
|
1331
|
+
* Los DOS iconos se renderizan siempre y el que sobra lo esconde el CSS con la
|
|
1332
|
+
* variante `light:`. No es una optimización: es lo que evita que el servidor y
|
|
1333
|
+
* el cliente discrepen. El servidor no sabe qué tema eligió quien va a leer, así
|
|
1334
|
+
* que cualquier icono que elija en el HTML tiene la mitad de probabilidades de
|
|
1335
|
+
* ser el equivocado, y corregirlo al hidratar es el parpadeo otra vez.
|
|
1336
|
+
*
|
|
1337
|
+
* El nombre accesible NO dice a qué modo se va. Sería más informativo y sería
|
|
1338
|
+
* una mentira la mitad del tiempo por lo mismo de arriba: el HTML del servidor
|
|
1339
|
+
* lo fija antes de saber el tema. «Cambiar de tema» es cierto siempre.
|
|
1340
|
+
*/
|
|
1341
|
+
type ThemeToggleProps = Omit<ComponentPropsWithoutRef<'button'>, 'onClick'> & {
|
|
1342
|
+
/** Nombre accesible. El botón no tiene texto visible, así que es lo único que lo nombra. */
|
|
1343
|
+
label?: string;
|
|
1344
|
+
/** Se dispara con el tema que quedó puesto, por si el proyecto quiere anotarlo. */
|
|
1345
|
+
onThemeChange?: ((tema: Tema) => void) | undefined;
|
|
1346
|
+
variant?: ButtonProps['variant'];
|
|
1347
|
+
size?: ButtonProps['size'];
|
|
967
1348
|
};
|
|
968
|
-
declare function
|
|
1349
|
+
declare function ThemeToggle({ label, onThemeChange, variant, size, className, ...props }: ThemeToggleProps): react.JSX.Element;
|
|
1350
|
+
/**
|
|
1351
|
+
* El tema puesto ahora mismo, para un proyecto que necesite ramificar en React
|
|
1352
|
+
* —un logo distinto por modo, una imagen que no tiene versión clara—.
|
|
1353
|
+
*
|
|
1354
|
+
* Es `useSyncExternalStore` y no un `useState` con un efecto detrás porque el
|
|
1355
|
+
* tema es exactamente eso: un estado que vive fuera de React, en un atributo del
|
|
1356
|
+
* `<html>` que puede cambiar sin que React se entere. Escribirlo con un efecto
|
|
1357
|
+
* que llama a `setState` en el montaje es el patrón que dispara un render en
|
|
1358
|
+
* cascada y que la regla `set-state-in-effect` señala con razón.
|
|
1359
|
+
*
|
|
1360
|
+
* `getServerSnapshot` devuelve `'dark'` porque en el servidor no hay `document`.
|
|
1361
|
+
* El primer render del cliente coincide con el del servidor y el valor real
|
|
1362
|
+
* entra después, que es la misma discrepancia de hidratación que `ThemeToggle`
|
|
1363
|
+
* evita renderizando los dos iconos.
|
|
1364
|
+
*
|
|
1365
|
+
* De ahí la regla de uso: si lo que ramifica es SOLO estilo, esto no hace falta
|
|
1366
|
+
* y la variante `light:` es mejor — no re-renderiza nada. Esto es para cuando
|
|
1367
|
+
* cambia el contenido.
|
|
1368
|
+
*/
|
|
1369
|
+
declare function useTema(): Tema;
|
|
969
1370
|
|
|
970
1371
|
/**
|
|
971
1372
|
* «En esta página». El índice del artículo largo.
|
|
@@ -976,6 +1377,20 @@ declare function TalkCard({ title, event, date, dateTime, location, status, clas
|
|
|
976
1377
|
*
|
|
977
1378
|
* El activo se marca con `aria-current`, no solo con color — la sección en la
|
|
978
1379
|
* que estás no puede comunicarse únicamente con bioluz.
|
|
1380
|
+
*
|
|
1381
|
+
* Y ese atributo es además EL GANCHO: las clases del activo se aplican con la
|
|
1382
|
+
* variante `aria-[current]:`, no con un ternario en el render. La diferencia es
|
|
1383
|
+
* la que hay entre servir solo controlado y servir también sin controlar.
|
|
1384
|
+
*
|
|
1385
|
+
* Un sitio Astro resuelve el scroll-spy con quince líneas de script que ponen
|
|
1386
|
+
* `aria-current` en el enlace visible y quitan el del anterior. Con el estado
|
|
1387
|
+
* calculado en el render, ese script no podía hacer nada: había que hidratar el
|
|
1388
|
+
* índice como isla de React en cada artículo para algo que cuesta cero
|
|
1389
|
+
* JavaScript de framework. Ahora el CSS reacciona al atributo y las dos formas
|
|
1390
|
+
* de usarlo dan el mismo resultado.
|
|
1391
|
+
*
|
|
1392
|
+
* El gancho es la PRESENCIA del atributo, así que se quita para desmarcar; no
|
|
1393
|
+
* se pone `aria-current="false"`.
|
|
979
1394
|
*/
|
|
980
1395
|
type Entrada = {
|
|
981
1396
|
/** El ancla, con `#`. */
|
|
@@ -1021,4 +1436,4 @@ declare namespace social {
|
|
|
1021
1436
|
export { social_Correo as Correo, social_Discord as Discord, social_GitHub as GitHub, social_Instagram as Instagram, social_LinkedIn as LinkedIn, social_Rss as Rss, social_X as X, social_YouTube as YouTube };
|
|
1022
1437
|
}
|
|
1023
1438
|
|
|
1024
|
-
export { Alert, type AlertProps, ArticleCard, type ArticleCardProps, AudioPlayer, type AudioPlayerMode, type AudioPlayerProps, AuthorCard, type AuthorCardProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, Badge, type BadgeProps, Blockquote, type BlockquoteProps, Breadcrumb, type BreadcrumbProps, Button, type ButtonProps, Calendar, type CalendarProps, Cara, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryBadge, type CategoryBadgeProps, Checkbox, type CheckboxProps, Code, CodeBlock, type CodeBlockProps, type CodeProps, CourseCard, type CourseCardProps, DateField, type DateFieldProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, type Entrada, Footer, FooterLink, type FooterLinkProps, type FooterProps, HOVER_TARJETA, Hero, type HeroProps, Input, type InputProps,
|
|
1439
|
+
export { Accordion, AccordionContent, AccordionItem, type AccordionProps, AccordionTrigger, type AccordionTriggerProps, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogTitle, AlertDialogTrigger, type AlertProps, ArticleCard, type ArticleCardProps, AudioPlayer, type AudioPlayerMode, type AudioPlayerProps, AuthorCard, type AuthorCardProps, Avatar, AvatarFallback, AvatarImage, type AvatarProps, AvatarUpload, type AvatarUploadProps, Badge, type BadgeProps, Blockquote, type BlockquoteProps, Breadcrumb, type BreadcrumbProps, Button, type ButtonProps, Calendar, type CalendarProps, Cara, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryBadge, type CategoryBadgeProps, Checkbox, type CheckboxProps, Code, CodeBlock, type CodeBlockProps, type CodeProps, CourseCard, type CourseCardProps, DateField, type DateFieldProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, type Entrada, EventCalendar, type EventCalendarProps, type EventoCalendario, Footer, FooterLink, type FooterLinkProps, type FooterProps, HOVER_TARJETA, Hero, type HeroProps, Input, type InputProps, LinkRow, type LinkRowProps, MetricBadge, type MetricBadgeProps, type Migaja, Nav, NavItem, type NavItemProps, type NavProps, NewsletterForm, type NewsletterFormProps, type NewsletterState, PageHeader, type PageHeaderProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, type PopoverContentProps, PopoverTrigger, Pose, Progress, type ProgressProps, RadioGroup, RadioGroupItem, type RadioGroupItemProps, type RadioGroupProps, type Red, SUPERFICIE_TARJETA, ScrollingProgressBar, type ScrollingProgressBarProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeparatorProps, Sheet, SheetBody, SheetClose, SheetContent, type SheetContentProps, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SidebarItem, type SidebarItemProps, SidebarNav, type SidebarNavProps, Skeleton, type SkeletonProps, Stat, type StatProps, Switch, type SwitchProps, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableOfContents, type TableOfContentsProps, TableRow, Tabs, TabsContent, TabsList, type TabsProps, TabsTrigger, TalkCard, type TalkCardProps, Tema, Text, type TextProps, Textarea, type TextareaProps, ThemeToggle, type ThemeToggleProps, ToastAction, type ToastOptions, type ToastVariant, Toaster, type ToasterProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alert as alertVariants, avatar as avatarVariants, badge as badgeVariants, button as buttonVariants, categoria as categoryBadgeVariants, cn, social, texto as textVariants, toast, useTema };
|