@astra-kit/ui 1.0.0 → 1.1.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 CHANGED
@@ -4,6 +4,13 @@ Todos los cambios relevantes de `@astra-kit/ui` se documentan en este archivo co
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.1.0] - 2026-10-19
8
+
9
+ ### Added
10
+
11
+ - `ButtonLink`: un link con el aspecto de `Button` (`variant` y `size`) que rutea con el adaptador del provider y sigue siendo un enlace, para navegar sin estilos propios en cada app. También se exporta `buttonClassName({ variant, size, className })`, con las mismas clases para otros elementos, como un `<label>` que abre un selector de archivos.
12
+ - `RadioGroup` acepta `hideLabel` (por defecto `false`), como `Select`: la leyenda sigue nombrando el grupo para lectores de pantalla pero no se ve ni ocupa espacio, para layouts que ya muestran su propio título. Ya no hace falta ocultarla con CSS propio.
13
+
7
14
  ## [1.0.0] - 2026-10-18
8
15
 
9
16
  ### Changed
package/README.md CHANGED
@@ -47,7 +47,16 @@ 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 `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`. 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
+
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
+
54
+ ```tsx
55
+ import { ButtonLink, buttonClassName } from "@astra-kit/ui";
56
+
57
+ <ButtonLink href="/" variant="secondary">Volver al inicio</ButtonLink>;
58
+ <label className={buttonClassName({ variant: "ghost", size: "sm" })}>Adjuntar<input type="file" hidden /></label>;
59
+ ```
51
60
 
52
61
  Agrupar los imports de componentes desde la raíz. El paquete publica JavaScript ESM y declaraciones TypeScript en `dist`; el consumidor no necesita transpilar el código fuente de la librería. La gramática de filtros y los helpers de mes-año son independientes de React.
53
62
 
@@ -0,0 +1,15 @@
1
+ import { type ButtonSize, type ButtonVariant } from "./button-recipe.js";
2
+ import { type LinkProps } from "./link.js";
3
+ export type ButtonLinkProps = LinkProps & {
4
+ /** Visual weight, as in `Button`. Defaults to `primary`. */
5
+ variant?: ButtonVariant;
6
+ /** Height and padding, as in `Button`. Defaults to `md`. */
7
+ size?: ButtonSize;
8
+ };
9
+ /**
10
+ * A link styled as a button, such as "Volver al inicio" on an error page.
11
+ * @param props - Variant, size and every `Link` prop, including `href`, `prefetch` and `component`.
12
+ * @returns The link.
13
+ */
14
+ declare function ButtonLink({ variant, size, className, ...props }: ButtonLinkProps): import("react").JSX.Element;
15
+ export { ButtonLink };
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { c as _c } from "react/compiler-runtime";
3
+ /**
4
+ * Navigation that looks like Arc's Button (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md): the
5
+ * provider's `Link`, so it routes through the configured adapter, with the button recipe. A link stays a link for
6
+ * assistive technology and the browser (it opens in a new tab, it can be copied), which a `Button` cannot.
7
+ */
8
+ import { cn } from "../lib/utils.js";
9
+ import { buttonClassName } from "./button-recipe.js";
10
+ import { Link } from "./link.js";
11
+ import { jsx as _jsx } from "react/jsx-runtime";
12
+ /**
13
+ * A link styled as a button, such as "Volver al inicio" on an error page.
14
+ * @param props - Variant, size and every `Link` prop, including `href`, `prefetch` and `component`.
15
+ * @returns The link.
16
+ */
17
+ function ButtonLink(t0) {
18
+ const $ = _c(14);
19
+ let className;
20
+ let props;
21
+ let t1;
22
+ let t2;
23
+ if ($[0] !== t0) {
24
+ ({variant: t1, size: t2, className, ...props} = t0);
25
+ $[0] = t0;
26
+ $[1] = className;
27
+ $[2] = props;
28
+ $[3] = t1;
29
+ $[4] = t2;
30
+ } else {
31
+ className = $[1];
32
+ props = $[2];
33
+ t1 = $[3];
34
+ t2 = $[4];
35
+ }
36
+ const variant = t1 === undefined ? "primary" : t1;
37
+ const size = t2 === undefined ? "md" : t2;
38
+ let t3;
39
+ if ($[5] !== className || $[6] !== size || $[7] !== variant) {
40
+ t3 = cn(buttonClassName({
41
+ variant,
42
+ size
43
+ }), className);
44
+ $[5] = className;
45
+ $[6] = size;
46
+ $[7] = variant;
47
+ $[8] = t3;
48
+ } else {
49
+ t3 = $[8];
50
+ }
51
+ let t4;
52
+ if ($[9] !== props || $[10] !== size || $[11] !== t3 || $[12] !== variant) {
53
+ t4 = /* @__PURE__ */ _jsx(Link, {
54
+ "data-slot": "button-link",
55
+ "data-variant": variant,
56
+ "data-size": size,
57
+ ...props,
58
+ className: t3
59
+ });
60
+ $[9] = props;
61
+ $[10] = size;
62
+ $[11] = t3;
63
+ $[12] = variant;
64
+ $[13] = t4;
65
+ } else {
66
+ t4 = $[13];
67
+ }
68
+ return t4;
69
+ }
70
+ export { ButtonLink };
@@ -0,0 +1,27 @@
1
+ /** Visual weight, as in Arc: one primary action per surface. */
2
+ export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
3
+ /** Height and padding, from Arc's control heights. */
4
+ export type ButtonSize = "sm" | "md" | "lg";
5
+ /**
6
+ * Arc's button recipe on its tokens. Press scale is driven by Motion alone, so `transform` never gets a CSS
7
+ * transition, and anchors of floating layers never scale because Radix measures them on pointerdown. Keyboard focus
8
+ * uses the foundation's shared outline; pointer focus draws no ring.
9
+ */
10
+ export declare const buttonVariants: (props?: ({
11
+ variant?: "danger" | "ghost" | "primary" | "secondary" | null | undefined;
12
+ size?: "lg" | "md" | "sm" | null | undefined;
13
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
14
+ /** Options of {@link buttonClassName}. */
15
+ export type ButtonClassNameOptions = {
16
+ variant?: ButtonVariant;
17
+ size?: ButtonSize;
18
+ /** Extra classes merged after the recipe, so they win on conflicts. */
19
+ className?: string;
20
+ };
21
+ /**
22
+ * Arc's button classes for an element that is not a `Button`, such as a `<label>` that opens a file picker. Links
23
+ * use {@link ButtonLink} instead. The press scale and the loading label are effects of `Button` and are not included.
24
+ * @param options - Variant, size and extra classes. Defaults to `primary` and `md`.
25
+ * @returns The class list.
26
+ */
27
+ export declare function buttonClassName({ variant, size, className }?: ButtonClassNameOptions): string;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Arc's Button recipe (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md): variants, sizes and the helper
3
+ * that applies them. It has no client directive, so `buttonClassName` also runs in React Server Components; the
4
+ * `Button` and `ButtonLink` components import it from here.
5
+ */
6
+ import { cva } from "class-variance-authority";
7
+ import { cn } from "../lib/utils.js";
8
+ /**
9
+ * Arc's button recipe on its tokens. Press scale is driven by Motion alone, so `transform` never gets a CSS
10
+ * transition, and anchors of floating layers never scale because Radix measures them on pointerdown. Keyboard focus
11
+ * uses the foundation's shared outline; pointer focus draws no ring.
12
+ */
13
+ export const buttonVariants = cva("relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-(--radius-control) border border-transparent px-4 font-sans text-sm leading-(--leading-body) font-medium whitespace-nowrap select-none [-webkit-tap-highlight-color:transparent] transition-[color,background-color,border-color,opacity,box-shadow] duration-(--duration-fast) ease-(--ease-standard) disabled:cursor-not-allowed disabled:opacity-[.52] aria-busy:cursor-progress motion-reduce:transition-none [&:is([aria-haspopup]:not([aria-haspopup=false]),[data-state],[role=combobox])]:transform-none! [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
14
+ variants: {
15
+ variant: {
16
+ primary: "border-foreground bg-foreground text-background hover-fine:not-disabled:opacity-[.91] hover-fine:not-disabled:shadow-(--shadow-resting) active:not-disabled:opacity-[.84]",
17
+ secondary: "border-border bg-surface text-foreground hover-fine:not-disabled:bg-surface-muted hover-fine:not-disabled:shadow-(--shadow-resting) active:not-disabled:bg-surface-muted disabled:bg-surface-muted disabled:text-text-muted disabled:opacity-100",
18
+ ghost: "bg-transparent text-text-secondary hover-fine:not-disabled:bg-surface-muted hover-fine:not-disabled:text-foreground active:not-disabled:bg-surface-muted active:not-disabled:text-foreground disabled:text-text-muted disabled:opacity-100",
19
+ danger: "border-border bg-surface text-danger hover-fine:not-disabled:border-danger hover-fine:not-disabled:bg-surface-muted active:not-disabled:border-danger active:not-disabled:bg-surface-muted disabled:bg-surface-muted disabled:text-[color-mix(in_oklab,var(--danger)_55%,var(--surface-muted))] disabled:opacity-100"
20
+ },
21
+ size: {
22
+ sm: "min-h-(--control-height-sm) px-3",
23
+ md: "min-h-(--control-height-md)",
24
+ lg: "min-h-(--control-height-lg) px-5"
25
+ }
26
+ },
27
+ defaultVariants: {
28
+ variant: "primary",
29
+ size: "md"
30
+ }
31
+ });
32
+ /**
33
+ * Arc's button classes for an element that is not a `Button`, such as a `<label>` that opens a file picker. Links
34
+ * use {@link ButtonLink} instead. The press scale and the loading label are effects of `Button` and are not included.
35
+ * @param options - Variant, size and extra classes. Defaults to `primary` and `md`.
36
+ * @returns The class list.
37
+ */
38
+ export function buttonClassName({ variant = "primary", size = "md", className } = {}) {
39
+ return cn(buttonVariants({
40
+ variant,
41
+ size,
42
+ className
43
+ }));
44
+ }
@@ -1,13 +1,10 @@
1
1
  /**
2
2
  * Arc's Button (https://uiarc.dev, MIT License, see THIRD_PARTY_NOTICES.md): its variants, sizes, tokens and motion,
3
3
  * with width-aware press feedback, a label that springs to its new width and a loading spinner. It renders a native
4
- * button; style links as links instead. Composite components that need icon sizes use the internal `ControlButton`.
4
+ * button: navigation that looks like a button is a `ButtonLink`, and other elements take `buttonClassName`. Composite components that need icon sizes use the internal `ControlButton`.
5
5
  */
6
6
  import type { ComponentProps } from "react";
7
- /** Visual weight, as in Arc: one primary action per surface. */
8
- export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
9
- /** Height and padding, from Arc's control heights. */
10
- export type ButtonSize = "sm" | "md" | "lg";
7
+ import { type ButtonSize, type ButtonVariant } from "./button-recipe.js";
11
8
  export type ButtonProps = ComponentProps<"button"> & {
12
9
  variant?: ButtonVariant;
13
10
  size?: ButtonSize;
@@ -21,3 +18,4 @@ export type ButtonProps = ComponentProps<"button"> & {
21
18
  */
22
19
  declare function Button({ className, variant, size, loading, type, children, ref, ...props }: ButtonProps): import("react").JSX.Element;
23
20
  export { Button };
21
+ export type { ButtonSize, ButtonVariant } from "./button-recipe.js";
@@ -1,39 +1,15 @@
1
1
  "use client";
2
2
  import { c as _c } from "react/compiler-runtime";
3
- import { cva } from "class-variance-authority";
4
3
  import { useIsPresent } from "motion/react";
5
4
  import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
6
5
  import { cn } from "../lib/utils.js";
6
+ import { buttonVariants } from "./button-recipe.js";
7
7
  import { useArcPress } from "../motion/arc-press.js";
8
8
  import { useAttachedElement } from "../motion/use-attached-element.js";
9
9
  import { ButtonLabel } from "./button-label.js";
10
10
  import { LEAVING_ATTRIBUTES, LOADING_INERT_HANDLERS } from "./button-interaction.js";
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
12
  /**
13
- * Arc's button recipe on its tokens. Press scale is driven by Motion alone, so `transform` never gets a CSS
14
- * transition, and anchors of floating layers never scale because Radix measures them on pointerdown. Keyboard focus
15
- * uses the foundation's shared outline; pointer focus draws no ring.
16
- */
17
- const buttonVariants = cva("relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-(--radius-control) border border-transparent px-4 font-sans text-sm leading-(--leading-body) font-medium whitespace-nowrap select-none [-webkit-tap-highlight-color:transparent] transition-[color,background-color,border-color,opacity,box-shadow] duration-(--duration-fast) ease-(--ease-standard) disabled:cursor-not-allowed disabled:opacity-[.52] aria-busy:cursor-progress motion-reduce:transition-none [&:is([aria-haspopup]:not([aria-haspopup=false]),[data-state],[role=combobox])]:transform-none! [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
18
- variants: {
19
- variant: {
20
- primary: "border-foreground bg-foreground text-background hover-fine:not-disabled:opacity-[.91] hover-fine:not-disabled:shadow-(--shadow-resting) active:not-disabled:opacity-[.84]",
21
- secondary: "border-border bg-surface text-foreground hover-fine:not-disabled:bg-surface-muted hover-fine:not-disabled:shadow-(--shadow-resting) active:not-disabled:bg-surface-muted disabled:bg-surface-muted disabled:text-text-muted disabled:opacity-100",
22
- ghost: "bg-transparent text-text-secondary hover-fine:not-disabled:bg-surface-muted hover-fine:not-disabled:text-foreground active:not-disabled:bg-surface-muted active:not-disabled:text-foreground disabled:text-text-muted disabled:opacity-100",
23
- danger: "border-border bg-surface text-danger hover-fine:not-disabled:border-danger hover-fine:not-disabled:bg-surface-muted active:not-disabled:border-danger active:not-disabled:bg-surface-muted disabled:bg-surface-muted disabled:text-[color-mix(in_oklab,var(--danger)_55%,var(--surface-muted))] disabled:opacity-100"
24
- },
25
- size: {
26
- sm: "min-h-(--control-height-sm) px-3",
27
- md: "min-h-(--control-height-md)",
28
- lg: "min-h-(--control-height-lg) px-5"
29
- }
30
- },
31
- defaultVariants: {
32
- variant: "primary",
33
- size: "md"
34
- }
35
- });
36
- /**
37
13
  * A single action on a page, form or dialog. Defaults to `type="button"`, so it only submits a form when asked to.
38
14
  * @param props - Variant, size, loading state and native button attributes, including `ref`.
39
15
  * @returns The native button.
@@ -10,8 +10,10 @@ export type RadioGroupOption = {
10
10
  disabled?: boolean;
11
11
  };
12
12
  export type RadioGroupProps = {
13
- /** Visible legend of the fieldset, which names the group. */
13
+ /** Legend of the fieldset, which names the group. */
14
14
  label: string;
15
+ /** Keeps `label` for assistive technology only, for layouts that already show their own label. */
16
+ hideLabel?: boolean;
15
17
  /** Rows in order. Values double as keys, so keep them unique. */
16
18
  options: RadioGroupOption[];
17
19
  /** Selected value when controlled. */
@@ -27,8 +29,8 @@ export type RadioGroupProps = {
27
29
  };
28
30
  /**
29
31
  * A choice of one option from a visible set, controlled with `value` or uncontrolled with `defaultValue`.
30
- * @param props - Legend, options, value, change handler, form name and classes.
32
+ * @param props - Legend, its visibility, options, value, change handler, form name and classes.
31
33
  * @returns The fieldset with its legend and option rows.
32
34
  */
33
- declare function RadioGroup({ label, options, value, defaultValue, onValueChange, name, className }: RadioGroupProps): import("react").JSX.Element;
35
+ declare function RadioGroup({ label, hideLabel, options, value, defaultValue, onValueChange, name, className, }: RadioGroupProps): import("react").JSX.Element;
34
36
  export { RadioGroup };
@@ -208,12 +208,13 @@ function _temp(event) {
208
208
  }
209
209
  /**
210
210
  * A choice of one option from a visible set, controlled with `value` or uncontrolled with `defaultValue`.
211
- * @param props - Legend, options, value, change handler, form name and classes.
211
+ * @param props - Legend, its visibility, options, value, change handler, form name and classes.
212
212
  * @returns The fieldset with its legend and option rows.
213
213
  */
214
214
  function RadioGroup(t0) {
215
- const $ = _c(32);
216
- const { label, options, value, defaultValue, onValueChange, name, className } = t0;
215
+ const $ = _c(33);
216
+ const { label, hideLabel: t1, options, value, defaultValue, onValueChange, name, className } = t0;
217
+ const hideLabel = t1 === undefined ? false : t1;
217
218
  const reduceMotion = usePrefersReducedMotion();
218
219
  const generatedId = useId();
219
220
  const legendId = `${generatedId}-legend`;
@@ -224,36 +225,36 @@ function RadioGroup(t0) {
224
225
  const highlightRef = useRef(null);
225
226
  const shownValue = useRef(null);
226
227
  const placedAt = useRef("");
227
- let t1;
228
+ let t2;
228
229
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
229
- t1 = () => optionsRef.current?.querySelector(CHECKED_OPTION_SELECTOR) ?? null;
230
- $[0] = t1;
230
+ t2 = () => optionsRef.current?.querySelector(CHECKED_OPTION_SELECTOR) ?? null;
231
+ $[0] = t2;
231
232
  } else {
232
- t1 = $[0];
233
+ t2 = $[0];
233
234
  }
234
- const findCheckedRow = t1;
235
- let t2;
235
+ const findCheckedRow = t2;
236
236
  let t3;
237
+ let t4;
237
238
  if ($[1] !== reduceMotion || $[2] !== selectedValue) {
238
- t2 = () => {
239
+ t3 = () => {
239
240
  const hasChanged = shownValue.current !== null && shownValue.current !== selectedValue;
240
241
  placeHighlight(highlightRef.current, findCheckedRow(), placedAt, hasChanged && !reduceMotion);
241
242
  shownValue.current = selectedValue;
242
243
  groupRef.current?.setAttribute(RADIO_GROUP_READY_ATTRIBUTE, "");
243
244
  };
244
- t3 = [selectedValue, reduceMotion];
245
+ t4 = [selectedValue, reduceMotion];
245
246
  $[1] = reduceMotion;
246
247
  $[2] = selectedValue;
247
- $[3] = t2;
248
- $[4] = t3;
248
+ $[3] = t3;
249
+ $[4] = t4;
249
250
  } else {
250
- t2 = $[3];
251
- t3 = $[4];
251
+ t3 = $[3];
252
+ t4 = $[4];
252
253
  }
253
- useLayoutEffect(t2, t3);
254
- let t4;
254
+ useLayoutEffect(t3, t4);
255
+ let t5;
255
256
  if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
256
- t4 = () => {
257
+ t5 = () => {
257
258
  const list = optionsRef.current;
258
259
  if (!list || typeof ResizeObserver === "undefined") {
259
260
  return;
@@ -263,15 +264,15 @@ function RadioGroup(t0) {
263
264
  list.querySelectorAll(OPTION_SELECTOR).forEach((row) => observer.observe(row));
264
265
  return () => observer.disconnect();
265
266
  };
266
- $[5] = t4;
267
+ $[5] = t5;
267
268
  } else {
268
- t4 = $[5];
269
+ t5 = $[5];
269
270
  }
270
- useEffect(t4);
271
- const t5 = name ?? generatedId;
272
- let t6;
271
+ useEffect(t5);
272
+ const t6 = name ?? generatedId;
273
+ let t7;
273
274
  if ($[6] !== onValueChange || $[7] !== value) {
274
- t6 = (nextValue) => {
275
+ t7 = (nextValue) => {
275
276
  if (value === undefined) {
276
277
  setUncontrolledValue(nextValue);
277
278
  }
@@ -279,112 +280,114 @@ function RadioGroup(t0) {
279
280
  };
280
281
  $[6] = onValueChange;
281
282
  $[7] = value;
282
- $[8] = t6;
283
+ $[8] = t7;
283
284
  } else {
284
- t6 = $[8];
285
+ t7 = $[8];
285
286
  }
286
- let t7;
287
+ let t8;
287
288
  if ($[9] !== className) {
288
- t7 = cn("m-0 grid min-w-0 border-0 p-0", className);
289
+ t8 = cn("m-0 grid min-w-0 border-0 p-0", className);
289
290
  $[9] = className;
290
- $[10] = t7;
291
+ $[10] = t8;
291
292
  } else {
292
- t7 = $[10];
293
+ t8 = $[10];
293
294
  }
294
- let t8;
295
- if ($[11] !== label || $[12] !== legendId) {
296
- t8 = /* @__PURE__ */ _jsx("legend", {
295
+ const t9 = hideLabel ? "sr-only" : "mb-(--space-2) p-0 text-sm font-medium text-foreground";
296
+ let t10;
297
+ if ($[11] !== label || $[12] !== legendId || $[13] !== t9) {
298
+ t10 = /* @__PURE__ */ _jsx("legend", {
297
299
  id: legendId,
298
300
  "data-slot": "radio-group-label",
299
- className: "mb-(--space-2) p-0 text-sm font-medium text-foreground",
301
+ className: t9,
300
302
  children: label
301
303
  });
302
304
  $[11] = label;
303
305
  $[12] = legendId;
304
- $[13] = t8;
306
+ $[13] = t9;
307
+ $[14] = t10;
305
308
  } else {
306
- t8 = $[13];
309
+ t10 = $[14];
307
310
  }
308
- let t9;
309
- if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
310
- t9 = /* @__PURE__ */ _jsx("span", {
311
+ let t11;
312
+ if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
313
+ t11 = /* @__PURE__ */ _jsx("span", {
311
314
  ref: highlightRef,
312
315
  "data-slot": "radio-group-highlight",
313
316
  "aria-hidden": "true",
314
317
  className: "pointer-events-none absolute top-0 left-0 z-1 box-border rounded-(--radius-control) border border-(--control-on) bg-[color-mix(in_oklab,var(--control-on)_5%,transparent)] opacity-0"
315
318
  });
316
- $[14] = t9;
319
+ $[15] = t11;
317
320
  } else {
318
- t9 = $[14];
321
+ t11 = $[15];
319
322
  }
320
- let t10;
321
- if ($[15] !== options) {
322
- t10 = options.map(_temp2);
323
- $[15] = options;
324
- $[16] = t10;
323
+ let t12;
324
+ if ($[16] !== options) {
325
+ t12 = options.map(_temp2);
326
+ $[16] = options;
327
+ $[17] = t12;
325
328
  } else {
326
- t10 = $[16];
329
+ t12 = $[17];
327
330
  }
328
- let t11;
329
- if ($[17] !== t10) {
330
- t11 = /* @__PURE__ */ _jsxs("div", {
331
+ let t13;
332
+ if ($[18] !== t12) {
333
+ t13 = /* @__PURE__ */ _jsxs("div", {
331
334
  ref: optionsRef,
332
335
  "data-slot": "radio-group-options",
333
336
  className: "relative grid w-full gap-(--space-2)",
334
- children: [t9, t10]
337
+ children: [t11, t12]
335
338
  });
336
- $[17] = t10;
337
- $[18] = t11;
339
+ $[18] = t12;
340
+ $[19] = t13;
338
341
  } else {
339
- t11 = $[18];
342
+ t13 = $[19];
340
343
  }
341
- let t12;
342
- if ($[19] !== legendId || $[20] !== t11 || $[21] !== t7 || $[22] !== t8) {
343
- t12 = /* @__PURE__ */ _jsxs("fieldset", {
344
+ let t14;
345
+ if ($[20] !== legendId || $[21] !== t10 || $[22] !== t13 || $[23] !== t8) {
346
+ t14 = /* @__PURE__ */ _jsxs("fieldset", {
344
347
  ref: groupRef,
345
348
  "data-slot": "radio-group",
346
349
  "aria-labelledby": legendId,
347
- className: t7,
348
- children: [t8, t11]
350
+ className: t8,
351
+ children: [t10, t13]
349
352
  });
350
- $[19] = legendId;
351
- $[20] = t11;
352
- $[21] = t7;
353
- $[22] = t8;
354
- $[23] = t12;
353
+ $[20] = legendId;
354
+ $[21] = t10;
355
+ $[22] = t13;
356
+ $[23] = t8;
357
+ $[24] = t14;
355
358
  } else {
356
- t12 = $[23];
359
+ t14 = $[24];
357
360
  }
358
- let t13;
359
- if ($[24] !== selectedValue || $[25] !== t12 || $[26] !== t5 || $[27] !== t6) {
360
- t13 = /* @__PURE__ */ _jsx(RadioGroupPrimitive.Root, {
361
+ let t15;
362
+ if ($[25] !== selectedValue || $[26] !== t14 || $[27] !== t6 || $[28] !== t7) {
363
+ t15 = /* @__PURE__ */ _jsx(RadioGroupPrimitive.Root, {
361
364
  asChild: true,
362
- name: t5,
365
+ name: t6,
363
366
  value: selectedValue,
364
- onValueChange: t6,
365
- children: t12
367
+ onValueChange: t7,
368
+ children: t14
366
369
  });
367
- $[24] = selectedValue;
368
- $[25] = t12;
369
- $[26] = t5;
370
+ $[25] = selectedValue;
371
+ $[26] = t14;
370
372
  $[27] = t6;
371
- $[28] = t13;
373
+ $[28] = t7;
374
+ $[29] = t15;
372
375
  } else {
373
- t13 = $[28];
376
+ t15 = $[29];
374
377
  }
375
- let t14;
376
- if ($[29] !== selectedValue || $[30] !== t13) {
377
- t14 = /* @__PURE__ */ _jsx(RadioGroupValueContext.Provider, {
378
+ let t16;
379
+ if ($[30] !== selectedValue || $[31] !== t15) {
380
+ t16 = /* @__PURE__ */ _jsx(RadioGroupValueContext.Provider, {
378
381
  value: selectedValue,
379
- children: t13
382
+ children: t15
380
383
  });
381
- $[29] = selectedValue;
382
- $[30] = t13;
383
- $[31] = t14;
384
+ $[30] = selectedValue;
385
+ $[31] = t15;
386
+ $[32] = t16;
384
387
  } else {
385
- t14 = $[31];
388
+ t16 = $[32];
386
389
  }
387
- return t14;
390
+ return t16;
388
391
  }
389
392
  function _temp2(option) {
390
393
  return /* @__PURE__ */ _jsx(RadioGroupOptionRow, { ...option }, option.value);
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@ export * from "./components/action-button.js";
7
7
  export * from "./components/avatar.js";
8
8
  export * from "./components/badge.js";
9
9
  export * from "./components/button.js";
10
+ export { buttonClassName, type ButtonClassNameOptions } from "./components/button-recipe.js";
11
+ export * from "./components/button-link.js";
10
12
  export * from "./components/card.js";
11
13
  export * from "./components/carousel.js";
12
14
  export * from "./components/checkbox.js";
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ export * from "./components/action-button.js";
6
6
  export * from "./components/avatar.js";
7
7
  export * from "./components/badge.js";
8
8
  export * from "./components/button.js";
9
+ // From the recipe module, which has no client directive, so it also runs in Server Components.
10
+ export { buttonClassName } from "./components/button-recipe.js";
11
+ export * from "./components/button-link.js";
9
12
  export * from "./components/card.js";
10
13
  export * from "./components/carousel.js";
11
14
  export * from "./components/checkbox.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astra-kit/ui",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Shared UI components with the customizations extracted from TuTribu.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -38,6 +38,11 @@
38
38
  "import": "./dist/components/button.js",
39
39
  "default": "./dist/components/button.js"
40
40
  },
41
+ "./button-link": {
42
+ "types": "./dist/components/button-link.d.ts",
43
+ "import": "./dist/components/button-link.js",
44
+ "default": "./dist/components/button-link.js"
45
+ },
41
46
  "./card": {
42
47
  "types": "./dist/components/card.d.ts",
43
48
  "import": "./dist/components/card.js",