@astra-kit/ui 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +5 -3
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/dist/components/empty-state.d.ts +20 -0
  5. package/dist/components/empty-state.js +357 -0
  6. package/dist/components/metric-card.d.ts +22 -0
  7. package/dist/components/metric-card.js +378 -0
  8. package/dist/components/stepper.d.ts +39 -0
  9. package/dist/components/stepper.js +693 -0
  10. package/dist/components/timeline.d.ts +51 -0
  11. package/dist/components/timeline.js +1013 -0
  12. package/dist/components/usage-meter.d.ts +39 -0
  13. package/dist/components/usage-meter.js +1093 -0
  14. package/dist/constants/empty-state.d.ts +12 -0
  15. package/dist/constants/empty-state.js +12 -0
  16. package/dist/constants/metric-card.d.ts +12 -0
  17. package/dist/constants/metric-card.js +12 -0
  18. package/dist/constants/stepper.d.ts +52 -0
  19. package/dist/constants/stepper.js +23 -0
  20. package/dist/constants/timeline.d.ts +62 -0
  21. package/dist/constants/timeline.js +31 -0
  22. package/dist/constants/usage-meter.d.ts +100 -0
  23. package/dist/constants/usage-meter.js +38 -0
  24. package/dist/hooks/use-has-entered-view.d.ts +9 -0
  25. package/dist/hooks/use-has-entered-view.js +46 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +5 -0
  28. package/dist/motion/arc-odometer.d.ts +53 -0
  29. package/dist/motion/arc-odometer.js +424 -0
  30. package/dist/motion/arc-text-swap.d.ts +22 -0
  31. package/dist/motion/arc-text-swap.js +142 -0
  32. package/dist/motion/arc-tokens.d.ts +1 -0
  33. package/dist/motion/arc-tokens.js +2 -1
  34. package/dist/styles.css +1 -1
  35. package/package.json +26 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,31 @@ Todos los cambios relevantes de `@astra-kit/ui` se documentan en este archivo co
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.2.1] - 2026-10-19
8
+
9
+ ### Fixed
10
+
11
+ - `EmptyState` ya no puede trabar la página en WebKit (Safari y navegadores basados en WebKit, sobre todo en Linux) mientras entra su ícono: la animación de entrada solo anima opacidad y escala, sin desenfoque.
12
+ - `UsageMeter` redondea cada segmento a la precisión de `decimals` antes de calcular todo, así la barra, la insignia, la leyenda y el resumen accesible coinciden (por ejemplo, 200,4 con límite 200 y sin decimales ya no dibuja un excedente que la insignia no muestra).
13
+ - `UsageMeter` acepta un segmento con `id="free"` sin confundirlo con el espacio libre de la leyenda.
14
+ - La leyenda de `UsageMeter` sigue alcanzable con Tab cuando se quitan segmentos después de mover el foco entre sus ítems.
15
+ - La insignia de estado de `UsageMeter` con un texto largo se recorta dentro del ancho del medidor en lugar de desbordarlo.
16
+ - `Stepper` muestra el contorno de foco del teclado en el paso actual al llegar con las flechas o End.
17
+ - `Timeline` etiqueta como "Ayer" el día calendario anterior en la zona horaria elegida, también en los cambios de horario de verano.
18
+ - `Timeline` vuelve a anunciar una actualización nueva aunque su resumen sea igual al anterior.
19
+ - `Timeline` ya no vuelve al principio del historial cuando solo cambia la preferencia de movimiento reducido; solo lo hace ante una actualización nueva.
20
+ - Los números de `MetricCard` y `UsageMeter` se detienen en su valor si se activa el movimiento reducido mientras ruedan.
21
+
22
+ ## [1.2.0] - 2026-10-19
23
+
24
+ ### Added
25
+
26
+ - `UsageMeter`: barra segmentada de cupo (por ejemplo, códigos y avisos enviados hoy contra el límite diario) con total animado, insignia de estado (libre, casi lleno, excedido), leyenda navegable con teclado y anuncios para lectores de pantalla. Todo el copy viene en español y se reemplaza con `freeLabel`, `overLabel` y `messages`; los números usan `locale` (por defecto `es-AR`).
27
+ - `MetricCard`: tarjeta compacta para un número con contexto, con dígitos que ruedan al cambiar y un chip de variación (`change`) que se tiñe según el signo. Acepta `locale` (por defecto `es-AR`).
28
+ - `EmptyState`: estado vacío centrado con ícono, título, descripción y acción opcional; al cambiar el copy, el bloque se ajusta de alto sin saltos.
29
+ - `Timeline`: historial de eventos agrupado por día, del más reciente al más antiguo, con encabezados fijos, filas que se expanden en el lugar, tiempos relativos y anuncios de nuevas actualizaciones. Fechas en `es-AR` por defecto; el copy se reemplaza con `messages`.
30
+ - `Stepper`: indicador de pasos horizontal o vertical, con navegación opcional a pasos completados (`onStepSelect`), estado de error por paso y modo compacto automático en contenedores angostos. Textos de accesibilidad en español, reemplazables con `label`, `completeLabel` y `messages`.
31
+
7
32
  ## [1.1.0] - 2026-10-19
8
33
 
9
34
  ### Added
package/README.md CHANGED
@@ -47,7 +47,7 @@ import { Avatar, Button, Calendar, DataTable } from "@astra-kit/ui";
47
47
  <Avatar name="Ana Núñez" src="/ana.jpg" />;
48
48
  ```
49
49
 
50
- Los componentes adoptados de [Arc](https://uiarc.dev) usan la API pública de Arc: `Switch`, `Checkbox`, `RadioGroup`, `Badge`, `Avatar`, `AvatarGroup`, `Skeleton`, `Card`, `Alert`, `Tooltip`, `HoverCard`, `DropdownMenu`, `Select`, `Pagination`, `Calendar` y `Carousel` son componentes cerrados que se configuran por props (`items`, `options`, `members`, `content`, `title`); `Dialog` (`Dialog`, `DialogTrigger`, `DialogContent` con `title` y `description`, `DialogClose`), `Popover` y `Tabs` conservan las partes de Arc. Cada uno suma solo las extensiones que Arc no cubre, con copy en español por defecto, por ejemplo `getHref` en `Pagination`, `trigger` e ítems con `href` en `DropdownMenu`, `onOpenChange` en `HoverCard`, `footer` y el modo `alert` en `Dialog` (reemplaza a `AlertDialog`), `hideLabel` en `Select` y `RadioGroup`, y `onSettle` en `Carousel`. La tabla de migración desde `beez-ui` 2.x está en [`docs/history/beez-ui-changelog.md`](https://github.com/guidomodarelli/astra-ui/blob/main/docs/history/beez-ui-changelog.md).
50
+ Los componentes adoptados de [Arc](https://uiarc.dev) usan la API pública de Arc: `Switch`, `Checkbox`, `RadioGroup`, `Badge`, `Avatar`, `AvatarGroup`, `Skeleton`, `Card`, `Alert`, `Tooltip`, `HoverCard`, `DropdownMenu`, `Select`, `Pagination`, `Calendar` y `Carousel` son componentes cerrados que se configuran por props (`items`, `options`, `members`, `content`, `title`); `Dialog` (`Dialog`, `DialogTrigger`, `DialogContent` con `title` y `description`, `DialogClose`), `Popover` y `Tabs` conservan las partes de Arc. Cada uno suma solo las extensiones que Arc no cubre, con copy en español por defecto, por ejemplo `getHref` en `Pagination`, `trigger` e ítems con `href` en `DropdownMenu`, `onOpenChange` en `HoverCard`, `footer` y el modo `alert` en `Dialog` (reemplaza a `AlertDialog`), `hideLabel` en `Select` y `RadioGroup`, y `onSettle` en `Carousel`. Los displays de datos de Arc `UsageMeter`, `MetricCard`, `EmptyState`, `Timeline` y `Stepper` también son cerrados y se configuran por props; sus textos visibles y anunciados vienen en español y se reemplazan con `messages` (y con `freeLabel`/`overLabel`, `label` o `completeLabel` donde Arc ya los define), y `locale` formatea números y fechas (`es-AR` por defecto). La tabla de migración desde `beez-ui` 2.x está en [`docs/history/beez-ui-changelog.md`](https://github.com/guidomodarelli/astra-ui/blob/main/docs/history/beez-ui-changelog.md).
51
51
 
52
52
  `Button` es para acciones y no tiene `asChild`. Una navegación con aspecto de botón es un `ButtonLink`: el `Link` del provider (rutea con el adaptador configurado y sigue siendo un enlace, que se puede abrir en otra pestaña o copiar) con la receta de `Button` y sus `variant` y `size`. Para otro elemento con aspecto de botón, como un `<label>` que abre un selector de archivos, `buttonClassName({ variant, size, className })` devuelve las mismas clases. Ninguno de los dos incluye la presión ni el spinner de `Button`.
53
53
 
@@ -108,8 +108,10 @@ no tiemblan al pasar a `aria-invalid="true"`.
108
108
  a 0.97 (un poco más los de solo ícono y menos los anchos) y vuelven con `snappy`; los
109
109
  disparadores de menús, popovers y diálogos responden solo con color.
110
110
  - `Checkbox`, `RadioGroup`, `Switch`, `Tabs`, `Calendar`, `Carousel`, `Card`, `Avatar`,
111
- `AvatarGroup`, `Badge`, `Alert`, `ActionButton` y `SplitButton` usan el movimiento de sus
112
- componentes de Arc.
111
+ `AvatarGroup`, `Badge`, `Alert`, `ActionButton`, `SplitButton`, `UsageMeter`, `MetricCard`,
112
+ `EmptyState`, `Timeline` y `Stepper` usan el movimiento de sus componentes de Arc. Los
113
+ números de `UsageMeter` y `MetricCard` ruedan como un odómetro y sus segmentos o dígitos
114
+ entran la primera vez que el componente se ve; con movimiento reducido saltan al valor final.
113
115
  - Una `Card` interactiva se eleva 2 px con el borde fuerte y la sombra `--shadow-raised` de
114
116
  Arc al pasar un puntero fino.
115
117
  - `Tooltip`, `Popover` y `HoverCard` se acercan desde su trigger con escala y fundido.
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Arc
6
6
 
7
- The theme tokens (`theme.css`, from Arc's `foundation.css`) and the `Button` API, styles and motion (`src/components/button.tsx`, `src/components/button-label.tsx`, `src/components/control-button.tsx`, `src/motion/arc-press.ts`, `src/motion/arc-tokens.ts`, `src/motion/use-morph-width.ts`), and the `ActionButton` and `SplitButton` components and their label morph (`src/components/action-button.tsx`, `src/components/split-button.tsx`, `src/components/morph-text.tsx`), the `Input` and `Textarea` fields with their animated helper and error rows (`src/components/input.tsx`, `src/components/textarea.tsx`, `src/components/field.tsx`), the `Switch`, `Checkbox` and `RadioGroup` controls (`src/components/switch.tsx`, `src/components/checkbox.tsx`, `src/components/radio-group.tsx`, `src/constants/switch.ts`, `src/constants/checkbox.ts`, `src/constants/radio-group.ts`), the `Badge`, `Avatar`, `AvatarGroup`, `Skeleton`, `Card` and `Alert` displays (`src/components/badge.tsx`, `src/components/avatar.tsx`, `src/components/skeleton.tsx`, `src/components/card.tsx`, `src/components/alert.tsx`, `src/constants/avatar.ts`, `src/constants/card.ts`, `src/constants/alert.ts`), the `Tooltip`, `Popover` and `HoverCard` overlays and their motion (`src/components/tooltip.tsx`, `src/components/popover.tsx`, `src/components/hover-card.tsx`, `src/motion/arc-floating-slot.tsx`, `src/motion/overlay-warmth.ts`, `src/constants/tooltip.ts`, `src/constants/popover.ts`, `src/constants/hover-card.ts`), the `DropdownMenu` and `Select` menus (`src/components/dropdown-menu.tsx`, `src/components/select.tsx`), the `Tabs` and `Pagination` navigation (`src/components/tabs.tsx`, `src/components/pagination.tsx`, `src/constants/tabs.ts`), the `Dialog` surface and its `alert` mode (`src/components/dialog.tsx`, `src/components/dialog-surface.tsx`, `src/constants/dialog.ts`), the `Calendar` and `Carousel` (`src/components/calendar.tsx`, `src/components/carousel.tsx`, `src/constants/calendar.ts`, `src/constants/carousel.ts`, `src/hooks/use-today.ts`), and the internal compositions those public components are built on (`src/components/primitives/`) are adapted from the free Arc library: https://uiarc.dev, https://github.com/kuratlielia/arc-library
7
+ The theme tokens (`theme.css`, from Arc's `foundation.css`) and the `Button` API, styles and motion (`src/components/button.tsx`, `src/components/button-label.tsx`, `src/components/control-button.tsx`, `src/motion/arc-press.ts`, `src/motion/arc-tokens.ts`, `src/motion/use-morph-width.ts`), and the `ActionButton` and `SplitButton` components and their label morph (`src/components/action-button.tsx`, `src/components/split-button.tsx`, `src/components/morph-text.tsx`), the `Input` and `Textarea` fields with their animated helper and error rows (`src/components/input.tsx`, `src/components/textarea.tsx`, `src/components/field.tsx`), the `Switch`, `Checkbox` and `RadioGroup` controls (`src/components/switch.tsx`, `src/components/checkbox.tsx`, `src/components/radio-group.tsx`, `src/constants/switch.ts`, `src/constants/checkbox.ts`, `src/constants/radio-group.ts`), the `Badge`, `Avatar`, `AvatarGroup`, `Skeleton`, `Card` and `Alert` displays (`src/components/badge.tsx`, `src/components/avatar.tsx`, `src/components/skeleton.tsx`, `src/components/card.tsx`, `src/components/alert.tsx`, `src/constants/avatar.ts`, `src/constants/card.ts`, `src/constants/alert.ts`), the `Tooltip`, `Popover` and `HoverCard` overlays and their motion (`src/components/tooltip.tsx`, `src/components/popover.tsx`, `src/components/hover-card.tsx`, `src/motion/arc-floating-slot.tsx`, `src/motion/overlay-warmth.ts`, `src/constants/tooltip.ts`, `src/constants/popover.ts`, `src/constants/hover-card.ts`), the `DropdownMenu` and `Select` menus (`src/components/dropdown-menu.tsx`, `src/components/select.tsx`), the `Tabs` and `Pagination` navigation (`src/components/tabs.tsx`, `src/components/pagination.tsx`, `src/constants/tabs.ts`), the `Dialog` surface and its `alert` mode (`src/components/dialog.tsx`, `src/components/dialog-surface.tsx`, `src/constants/dialog.ts`), the `Calendar` and `Carousel` (`src/components/calendar.tsx`, `src/components/carousel.tsx`, `src/constants/calendar.ts`, `src/constants/carousel.ts`, `src/hooks/use-today.ts`), the `EmptyState`, `MetricCard`, `UsageMeter`, `Timeline` and `Stepper` data displays and their shared text swap and rolling digits (`src/components/empty-state.tsx`, `src/components/metric-card.tsx`, `src/components/usage-meter.tsx`, `src/components/timeline.tsx`, `src/components/stepper.tsx`, `src/constants/empty-state.ts`, `src/constants/metric-card.ts`, `src/constants/usage-meter.ts`, `src/constants/timeline.ts`, `src/constants/stepper.ts`, `src/motion/arc-text-swap.tsx`, `src/motion/arc-odometer.tsx`), and the internal compositions those public components are built on (`src/components/primitives/`) are adapted from the free Arc library: https://uiarc.dev, https://github.com/kuratlielia/arc-library
8
8
 
9
9
  ```
10
10
  MIT License
@@ -0,0 +1,20 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ export type EmptyStateProps = Omit<ComponentProps<"section">, "title" | "children"> & {
3
+ /** Headline. A new title rises in while the old one leaves. */
4
+ title: string;
5
+ /** One or two sentences on why it is empty and what to do. */
6
+ description: string;
7
+ /** Call to action, usually a button. */
8
+ action?: ReactNode;
9
+ /** Leading icon; defaults to a folder. A different icon component crossfades in. */
10
+ icon?: ReactNode;
11
+ /** Accessible name of the section region. */
12
+ label?: string;
13
+ };
14
+ /**
15
+ * A centered placeholder for empty views with an icon, a title, a description and an optional action.
16
+ * @param props - Copy, icon, action, accessible name and native section attributes.
17
+ * @returns The empty state section.
18
+ */
19
+ declare function EmptyState({ title, description, action, icon, label, className, ...props }: EmptyStateProps): import("react").JSX.Element;
20
+ export { EmptyState };
@@ -0,0 +1,357 @@
1
+ "use client";
2
+ import { c as _c } from "react/compiler-runtime";
3
+ /**
4
+ * Arc's Empty state (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md): a useful next step when there is
5
+ * nothing to show yet. A centered icon, title, description and optional action. When the copy changes it rolls in place
6
+ * while the block height springs to fit, and a different icon component crossfades in.
7
+ */
8
+ import { isValidElement, useEffect, useLayoutEffect, useRef } from "react";
9
+ import { AnimatePresence, animate, motion, useMotionValue } from "motion/react";
10
+ import { Folder } from "lucide-react";
11
+ import { EMPTY_STATE_HEIGHT_MORPH_WINDOW_MS, EMPTY_STATE_ICON_ENTER_SCALE, EMPTY_STATE_ICON_SIZE_PX, EMPTY_STATE_ICON_STROKE_WIDTH } from "../constants/empty-state.js";
12
+ import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
13
+ import { cn } from "../lib/utils.js";
14
+ import { ArcTextSwap } from "../motion/arc-text-swap.js";
15
+ import { ARC_BLUR, ARC_DURATION, ARC_EASE, ARC_SPRING_SMOOTH, ARC_SPRING_SNAPPY } from "../motion/arc-tokens.js";
16
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
+ const iconEnter = {
18
+ opacity: 0,
19
+ scale: EMPTY_STATE_ICON_ENTER_SCALE,
20
+ filter: `blur(${ARC_BLUR.subtle}px)`
21
+ };
22
+ const iconRest = {
23
+ opacity: 1,
24
+ scale: 1,
25
+ filter: "blur(0px)"
26
+ };
27
+ const iconExit = {
28
+ ...iconEnter,
29
+ transition: {
30
+ duration: ARC_DURATION.fast,
31
+ ease: [...ARC_EASE.standard]
32
+ }
33
+ };
34
+ const reducedFade = { opacity: 0 };
35
+ const reducedExit = {
36
+ opacity: 0,
37
+ transition: { duration: ARC_DURATION.instant }
38
+ };
39
+ const reducedTransition = { duration: ARC_DURATION.instant };
40
+ /**
41
+ * Keys an icon by its component, so a new icon component crossfades in while re-rendering the same icon stays still.
42
+ * @param icon - The icon element.
43
+ * @returns A stable key for the icon's component.
44
+ */
45
+ function iconKey(icon) {
46
+ if (!isValidElement(icon)) return "icon";
47
+ const type = icon.type;
48
+ return typeof type === "string" ? type : type.displayName ?? type.name ?? "icon";
49
+ }
50
+ /**
51
+ * Follows its content height. After `morphKey` changes the height springs from the old size to the new one and then
52
+ * returns to auto, so passive reflows follow instantly. It clips only while moving, so focus outlines stay visible.
53
+ */
54
+ function HeightFrame(t0) {
55
+ const $ = _c(14);
56
+ const { reduceMotion, morphKey, children } = t0;
57
+ const frame = useRef(null);
58
+ const content = useRef(null);
59
+ const height = useMotionValue("auto");
60
+ const changedAt = useRef(0);
61
+ let t1;
62
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
63
+ t1 = () => {
64
+ changedAt.current = performance.now();
65
+ };
66
+ $[0] = t1;
67
+ } else {
68
+ t1 = $[0];
69
+ }
70
+ let t2;
71
+ if ($[1] !== morphKey) {
72
+ t2 = [morphKey];
73
+ $[1] = morphKey;
74
+ $[2] = t2;
75
+ } else {
76
+ t2 = $[2];
77
+ }
78
+ useLayoutEffect(t1, t2);
79
+ let t3;
80
+ let t4;
81
+ if ($[3] !== height || $[4] !== reduceMotion) {
82
+ t3 = () => {
83
+ const node = content.current;
84
+ if (!node || typeof ResizeObserver === "undefined") {
85
+ return;
86
+ }
87
+ let lastHeight;
88
+ let controls;
89
+ const settle = () => {
90
+ height.jump("auto");
91
+ if (frame.current) {
92
+ Object.assign(frame.current.style, {
93
+ overflow: "",
94
+ height: "auto"
95
+ });
96
+ }
97
+ };
98
+ const observer = new ResizeObserver((t5) => {
99
+ const [entry] = t5;
100
+ if (!entry) {
101
+ return;
102
+ }
103
+ const next = entry.borderBoxSize?.[0]?.blockSize ?? node.offsetHeight;
104
+ const current = height.get();
105
+ const from = typeof current === "number" ? current : lastHeight;
106
+ lastHeight = next;
107
+ controls?.stop();
108
+ if (reduceMotion || from === undefined || from === next || performance.now() - changedAt.current > EMPTY_STATE_HEIGHT_MORPH_WINDOW_MS) {
109
+ settle();
110
+ return;
111
+ }
112
+ if (frame.current) {
113
+ Object.assign(frame.current.style, {
114
+ overflow: "hidden",
115
+ height: `${from}px`
116
+ });
117
+ }
118
+ controls = animate(height, [from, next], {
119
+ ...ARC_SPRING_SMOOTH,
120
+ onComplete: settle
121
+ });
122
+ });
123
+ observer.observe(node);
124
+ return () => {
125
+ observer.disconnect();
126
+ controls?.stop();
127
+ };
128
+ };
129
+ t4 = [height, reduceMotion];
130
+ $[3] = height;
131
+ $[4] = reduceMotion;
132
+ $[5] = t3;
133
+ $[6] = t4;
134
+ } else {
135
+ t3 = $[5];
136
+ t4 = $[6];
137
+ }
138
+ useEffect(t3, t4);
139
+ let t5;
140
+ if ($[7] !== height) {
141
+ t5 = { height };
142
+ $[7] = height;
143
+ $[8] = t5;
144
+ } else {
145
+ t5 = $[8];
146
+ }
147
+ let t6;
148
+ if ($[9] !== children) {
149
+ t6 = /* @__PURE__ */ _jsx("div", {
150
+ ref: content,
151
+ className: "flow-root",
152
+ children
153
+ });
154
+ $[9] = children;
155
+ $[10] = t6;
156
+ } else {
157
+ t6 = $[10];
158
+ }
159
+ let t7;
160
+ if ($[11] !== t5 || $[12] !== t6) {
161
+ t7 = /* @__PURE__ */ _jsx(motion.div, {
162
+ ref: frame,
163
+ "data-slot": "empty-state-copy",
164
+ className: "w-full max-w-full",
165
+ style: t5,
166
+ children: t6
167
+ });
168
+ $[11] = t5;
169
+ $[12] = t6;
170
+ $[13] = t7;
171
+ } else {
172
+ t7 = $[13];
173
+ }
174
+ return t7;
175
+ }
176
+ /**
177
+ * A centered placeholder for empty views with an icon, a title, a description and an optional action.
178
+ * @param props - Copy, icon, action, accessible name and native section attributes.
179
+ * @returns The empty state section.
180
+ */
181
+ function EmptyState(t0) {
182
+ const $ = _c(40);
183
+ let action;
184
+ let className;
185
+ let description;
186
+ let icon;
187
+ let label;
188
+ let props;
189
+ let title;
190
+ if ($[0] !== t0) {
191
+ ({title, description, action, icon, label, className, ...props} = t0);
192
+ $[0] = t0;
193
+ $[1] = action;
194
+ $[2] = className;
195
+ $[3] = description;
196
+ $[4] = icon;
197
+ $[5] = label;
198
+ $[6] = props;
199
+ $[7] = title;
200
+ } else {
201
+ action = $[1];
202
+ className = $[2];
203
+ description = $[3];
204
+ icon = $[4];
205
+ label = $[5];
206
+ props = $[6];
207
+ title = $[7];
208
+ }
209
+ const reduceMotion = usePrefersReducedMotion();
210
+ let t1;
211
+ if ($[8] !== icon) {
212
+ t1 = icon ?? /* @__PURE__ */ _jsx(Folder, {
213
+ width: EMPTY_STATE_ICON_SIZE_PX,
214
+ height: EMPTY_STATE_ICON_SIZE_PX,
215
+ strokeWidth: EMPTY_STATE_ICON_STROKE_WIDTH
216
+ });
217
+ $[8] = icon;
218
+ $[9] = t1;
219
+ } else {
220
+ t1 = $[9];
221
+ }
222
+ const glyph = t1;
223
+ let t2;
224
+ if ($[10] !== className) {
225
+ t2 = cn("flex w-full flex-col items-center px-(--space-5) py-[clamp(var(--space-8),8vw,var(--space-12))] text-center text-foreground", className);
226
+ $[10] = className;
227
+ $[11] = t2;
228
+ } else {
229
+ t2 = $[11];
230
+ }
231
+ let t3;
232
+ if ($[12] !== glyph) {
233
+ t3 = iconKey(glyph);
234
+ $[12] = glyph;
235
+ $[13] = t3;
236
+ } else {
237
+ t3 = $[13];
238
+ }
239
+ const t4 = reduceMotion ? reducedFade : iconEnter;
240
+ const t5 = reduceMotion ? reducedExit : iconExit;
241
+ const t6 = reduceMotion ? reducedTransition : ARC_SPRING_SNAPPY;
242
+ let t7;
243
+ if ($[14] !== glyph || $[15] !== t3 || $[16] !== t4 || $[17] !== t5 || $[18] !== t6) {
244
+ t7 = /* @__PURE__ */ _jsx("div", {
245
+ "data-slot": "empty-state-icon",
246
+ "aria-hidden": "true",
247
+ className: "relative grid size-(--space-12) place-items-center rounded-(--radius-panel) border border-border bg-surface-muted text-text-secondary animate-[astra-empty-state-icon-enter_var(--duration-considered)_var(--ease-enter)_both] motion-reduce:animate-none",
248
+ children: /* @__PURE__ */ _jsx(AnimatePresence, {
249
+ mode: "popLayout",
250
+ initial: false,
251
+ children: /* @__PURE__ */ _jsx(motion.span, {
252
+ className: "grid place-items-center",
253
+ initial: t4,
254
+ animate: iconRest,
255
+ exit: t5,
256
+ transition: t6,
257
+ children: glyph
258
+ }, t3)
259
+ })
260
+ });
261
+ $[14] = glyph;
262
+ $[15] = t3;
263
+ $[16] = t4;
264
+ $[17] = t5;
265
+ $[18] = t6;
266
+ $[19] = t7;
267
+ } else {
268
+ t7 = $[19];
269
+ }
270
+ const t8 = `${title}\n${description}`;
271
+ let t9;
272
+ if ($[20] !== reduceMotion || $[21] !== title) {
273
+ t9 = /* @__PURE__ */ _jsx("h3", {
274
+ "data-slot": "empty-state-title",
275
+ className: "relative mt-(--space-5) text-base leading-(--leading-body) font-medium text-foreground",
276
+ children: /* @__PURE__ */ _jsx(ArcTextSwap, {
277
+ text: title,
278
+ reduceMotion,
279
+ lineClassName: "text-balance"
280
+ })
281
+ });
282
+ $[20] = reduceMotion;
283
+ $[21] = title;
284
+ $[22] = t9;
285
+ } else {
286
+ t9 = $[22];
287
+ }
288
+ let t10;
289
+ if ($[23] !== description || $[24] !== reduceMotion) {
290
+ t10 = /* @__PURE__ */ _jsx("p", {
291
+ "data-slot": "empty-state-description",
292
+ className: "relative mx-auto mt-(--space-2) max-w-72 text-sm leading-(--leading-body) text-text-secondary",
293
+ children: /* @__PURE__ */ _jsx(ArcTextSwap, {
294
+ text: description,
295
+ reduceMotion,
296
+ lineClassName: "text-balance"
297
+ })
298
+ });
299
+ $[23] = description;
300
+ $[24] = reduceMotion;
301
+ $[25] = t10;
302
+ } else {
303
+ t10 = $[25];
304
+ }
305
+ let t11;
306
+ if ($[26] !== reduceMotion || $[27] !== t10 || $[28] !== t8 || $[29] !== t9) {
307
+ t11 = /* @__PURE__ */ _jsxs(HeightFrame, {
308
+ reduceMotion,
309
+ morphKey: t8,
310
+ children: [t9, t10]
311
+ });
312
+ $[26] = reduceMotion;
313
+ $[27] = t10;
314
+ $[28] = t8;
315
+ $[29] = t9;
316
+ $[30] = t11;
317
+ } else {
318
+ t11 = $[30];
319
+ }
320
+ let t12;
321
+ if ($[31] !== action) {
322
+ t12 = action ? /* @__PURE__ */ _jsx("div", {
323
+ "data-slot": "empty-state-action",
324
+ className: "mt-(--space-5) flex flex-wrap justify-center gap-(--space-3)",
325
+ children: action
326
+ }) : null;
327
+ $[31] = action;
328
+ $[32] = t12;
329
+ } else {
330
+ t12 = $[32];
331
+ }
332
+ let t13;
333
+ if ($[33] !== label || $[34] !== props || $[35] !== t11 || $[36] !== t12 || $[37] !== t2 || $[38] !== t7) {
334
+ t13 = /* @__PURE__ */ _jsxs("section", {
335
+ "data-slot": "empty-state",
336
+ "aria-label": label,
337
+ className: t2,
338
+ ...props,
339
+ children: [
340
+ t7,
341
+ t11,
342
+ t12
343
+ ]
344
+ });
345
+ $[33] = label;
346
+ $[34] = props;
347
+ $[35] = t11;
348
+ $[36] = t12;
349
+ $[37] = t2;
350
+ $[38] = t7;
351
+ $[39] = t13;
352
+ } else {
353
+ t13 = $[39];
354
+ }
355
+ return t13;
356
+ }
357
+ export { EmptyState };
@@ -0,0 +1,22 @@
1
+ import type { ComponentProps } from "react";
2
+ export type MetricCardProps = Omit<ComponentProps<"article">, "children"> & {
3
+ /** What is measured. */
4
+ label: string;
5
+ /** The number. Rolls up from zero on first view, then between values. */
6
+ value: number;
7
+ /** Text after the number, such as "%" or " ms". */
8
+ suffix?: string;
9
+ /** Line under the number, such as "vs. la semana pasada". */
10
+ context: string;
11
+ /** Chip in the top corner, such as "+12%". Write the sign: it sets the colour and is what screen readers hear. */
12
+ change?: string;
13
+ /** Locale of the number's separators. Defaults to `"es-AR"`. */
14
+ locale?: string;
15
+ };
16
+ /**
17
+ * A compact card with a label, an odometer style number, a context line and an optional change chip.
18
+ * @param props - Label, value, suffix, context, change, locale and native article attributes.
19
+ * @returns The metric card.
20
+ */
21
+ declare function MetricCard({ label, value, suffix, context, change, locale, className, ...props }: MetricCardProps): import("react").JSX.Element;
22
+ export { MetricCard };