@jotis-me/react 0.0.0-stage → 0.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.
Files changed (179) hide show
  1. package/README.md +77 -2
  2. package/dist/components/ActivityFeed.d.ts +29 -0
  3. package/dist/components/ActivityFeed.js +10 -0
  4. package/dist/components/Alert.d.ts +34 -0
  5. package/dist/components/Alert.js +27 -0
  6. package/dist/components/AppIcon.d.ts +27 -0
  7. package/dist/components/AppIcon.js +9 -0
  8. package/dist/components/Avatar.d.ts +20 -0
  9. package/dist/components/Avatar.js +27 -0
  10. package/dist/components/AvatarGroup.d.ts +10 -0
  11. package/dist/components/AvatarGroup.js +7 -0
  12. package/dist/components/AvatarLabel.d.ts +10 -0
  13. package/dist/components/AvatarLabel.js +7 -0
  14. package/dist/components/BackgroundPattern.d.ts +35 -0
  15. package/dist/components/BackgroundPattern.js +73 -0
  16. package/dist/components/Badge.d.ts +38 -0
  17. package/dist/components/Badge.js +82 -0
  18. package/dist/components/Banner.d.ts +48 -0
  19. package/dist/components/Banner.js +60 -0
  20. package/dist/components/BarChart.d.ts +25 -0
  21. package/dist/components/BarChart.js +30 -0
  22. package/dist/components/Breadcrumbs.d.ts +19 -0
  23. package/dist/components/Breadcrumbs.js +14 -0
  24. package/dist/components/BrowserFrame.d.ts +18 -0
  25. package/dist/components/BrowserFrame.js +13 -0
  26. package/dist/components/Button.d.ts +51 -0
  27. package/dist/components/Button.js +54 -0
  28. package/dist/components/ButtonGroup.d.ts +21 -0
  29. package/dist/components/ButtonGroup.js +14 -0
  30. package/dist/components/CalendarView.d.ts +30 -0
  31. package/dist/components/CalendarView.js +43 -0
  32. package/dist/components/CamFrame.d.ts +21 -0
  33. package/dist/components/CamFrame.js +11 -0
  34. package/dist/components/CardHeader.d.ts +32 -0
  35. package/dist/components/CardHeader.js +12 -0
  36. package/dist/components/ChannelBanner.d.ts +24 -0
  37. package/dist/components/ChannelBanner.js +13 -0
  38. package/dist/components/ChannelPanel.d.ts +36 -0
  39. package/dist/components/ChannelPanel.js +12 -0
  40. package/dist/components/ChartLegend.d.ts +12 -0
  41. package/dist/components/ChartLegend.js +7 -0
  42. package/dist/components/ChatBadge.d.ts +15 -0
  43. package/dist/components/ChatBadge.js +39 -0
  44. package/dist/components/ChatBox.d.ts +42 -0
  45. package/dist/components/ChatBox.js +35 -0
  46. package/dist/components/Checkbox.d.ts +17 -0
  47. package/dist/components/Checkbox.js +18 -0
  48. package/dist/components/CloseButton.d.ts +7 -0
  49. package/dist/components/CloseButton.js +8 -0
  50. package/dist/components/CodeSnippet.d.ts +23 -0
  51. package/dist/components/CodeSnippet.js +75 -0
  52. package/dist/components/ColorPicker.d.ts +15 -0
  53. package/dist/components/ColorPicker.js +42 -0
  54. package/dist/components/CommandMenu.d.ts +72 -0
  55. package/dist/components/CommandMenu.js +102 -0
  56. package/dist/components/ContentDivider.d.ts +17 -0
  57. package/dist/components/ContentDivider.js +13 -0
  58. package/dist/components/CreditCard.d.ts +28 -0
  59. package/dist/components/CreditCard.js +63 -0
  60. package/dist/components/DatePicker.d.ts +48 -0
  61. package/dist/components/DatePicker.js +105 -0
  62. package/dist/components/Dropdown.d.ts +39 -0
  63. package/dist/components/Dropdown.js +39 -0
  64. package/dist/components/EmptyState.d.ts +18 -0
  65. package/dist/components/EmptyState.js +7 -0
  66. package/dist/components/FeaturedIcon.d.ts +17 -0
  67. package/dist/components/FeaturedIcon.js +32 -0
  68. package/dist/components/FileUpload.d.ts +39 -0
  69. package/dist/components/FileUpload.js +50 -0
  70. package/dist/components/FilterBar.d.ts +46 -0
  71. package/dist/components/FilterBar.js +24 -0
  72. package/dist/components/Icon.d.ts +5 -0
  73. package/dist/components/Icon.js +9 -0
  74. package/dist/components/InlineCTA.d.ts +19 -0
  75. package/dist/components/InlineCTA.js +7 -0
  76. package/dist/components/Input.d.ts +16 -0
  77. package/dist/components/Input.js +10 -0
  78. package/dist/components/LineChart.d.ts +25 -0
  79. package/dist/components/LineChart.js +23 -0
  80. package/dist/components/Message.d.ts +22 -0
  81. package/dist/components/Message.js +10 -0
  82. package/dist/components/MessageInput.d.ts +17 -0
  83. package/dist/components/MessageInput.js +25 -0
  84. package/dist/components/MetricCard.d.ts +28 -0
  85. package/dist/components/MetricCard.js +11 -0
  86. package/dist/components/Modal.d.ts +45 -0
  87. package/dist/components/Modal.js +15 -0
  88. package/dist/components/Notification.d.ts +15 -0
  89. package/dist/components/Notification.js +33 -0
  90. package/dist/components/NowPlaying.d.ts +25 -0
  91. package/dist/components/NowPlaying.js +16 -0
  92. package/dist/components/PageHeader.d.ts +22 -0
  93. package/dist/components/PageHeader.js +8 -0
  94. package/dist/components/Pagination.d.ts +29 -0
  95. package/dist/components/Pagination.js +33 -0
  96. package/dist/components/PieChart.d.ts +32 -0
  97. package/dist/components/PieChart.js +30 -0
  98. package/dist/components/ProgressBar.d.ts +11 -0
  99. package/dist/components/ProgressBar.js +8 -0
  100. package/dist/components/Radio.d.ts +15 -0
  101. package/dist/components/Radio.js +10 -0
  102. package/dist/components/RadioGroup.d.ts +26 -0
  103. package/dist/components/RadioGroup.js +9 -0
  104. package/dist/components/SectionFooter.d.ts +12 -0
  105. package/dist/components/SectionFooter.js +7 -0
  106. package/dist/components/SectionHeader.d.ts +24 -0
  107. package/dist/components/SectionHeader.js +7 -0
  108. package/dist/components/Select.d.ts +22 -0
  109. package/dist/components/Select.js +14 -0
  110. package/dist/components/SidebarNav.d.ts +38 -0
  111. package/dist/components/SidebarNav.js +26 -0
  112. package/dist/components/Skeleton.d.ts +15 -0
  113. package/dist/components/Skeleton.js +10 -0
  114. package/dist/components/Slideout.d.ts +28 -0
  115. package/dist/components/Slideout.js +15 -0
  116. package/dist/components/Slider.d.ts +15 -0
  117. package/dist/components/Slider.js +17 -0
  118. package/dist/components/Spinner.d.ts +11 -0
  119. package/dist/components/Spinner.js +12 -0
  120. package/dist/components/StreamAlert.d.ts +34 -0
  121. package/dist/components/StreamAlert.js +10 -0
  122. package/dist/components/StreamCountdown.d.ts +30 -0
  123. package/dist/components/StreamCountdown.js +44 -0
  124. package/dist/components/StreamEvents.d.ts +40 -0
  125. package/dist/components/StreamEvents.js +20 -0
  126. package/dist/components/StreamGoal.d.ts +22 -0
  127. package/dist/components/StreamGoal.js +21 -0
  128. package/dist/components/StreamScene.d.ts +35 -0
  129. package/dist/components/StreamScene.js +33 -0
  130. package/dist/components/StreamStatus.d.ts +28 -0
  131. package/dist/components/StreamStatus.js +26 -0
  132. package/dist/components/Table.d.ts +36 -0
  133. package/dist/components/Table.js +15 -0
  134. package/dist/components/Tabs.d.ts +38 -0
  135. package/dist/components/Tabs.js +34 -0
  136. package/dist/components/Tag.d.ts +18 -0
  137. package/dist/components/Tag.js +15 -0
  138. package/dist/components/TextEditor.d.ts +37 -0
  139. package/dist/components/TextEditor.js +103 -0
  140. package/dist/components/Textarea.d.ts +6 -0
  141. package/dist/components/Textarea.js +10 -0
  142. package/dist/components/Toggle.d.ts +22 -0
  143. package/dist/components/Toggle.js +12 -0
  144. package/dist/components/Tooltip.d.ts +15 -0
  145. package/dist/components/Tooltip.js +27 -0
  146. package/dist/components/TreeView.d.ts +33 -0
  147. package/dist/components/TreeView.js +48 -0
  148. package/dist/components/VideoPlayer.d.ts +25 -0
  149. package/dist/components/VideoPlayer.js +52 -0
  150. package/dist/index.d.ts +80 -0
  151. package/dist/index.js +76 -0
  152. package/dist/internal/chart.d.ts +38 -0
  153. package/dist/internal/chart.js +33 -0
  154. package/dist/internal/choice.d.ts +18 -0
  155. package/dist/internal/choice.js +17 -0
  156. package/dist/internal/cx.d.ts +3 -0
  157. package/dist/internal/cx.js +26 -0
  158. package/dist/internal/field.d.ts +29 -0
  159. package/dist/internal/field.js +23 -0
  160. package/dist/internal/format.d.ts +12 -0
  161. package/dist/internal/format.js +40 -0
  162. package/dist/internal/kbd.d.ts +6 -0
  163. package/dist/internal/kbd.js +6 -0
  164. package/dist/internal/layer.d.ts +21 -0
  165. package/dist/internal/layer.js +27 -0
  166. package/dist/internal/line.d.ts +2 -0
  167. package/dist/internal/line.js +5 -0
  168. package/dist/internal/overlay.d.ts +2 -0
  169. package/dist/internal/overlay.js +4 -0
  170. package/dist/internal/stream-stage.d.ts +18 -0
  171. package/dist/internal/stream-stage.js +38 -0
  172. package/dist/internal/stream.d.ts +56 -0
  173. package/dist/internal/stream.js +112 -0
  174. package/dist/internal/strings.d.ts +100 -0
  175. package/dist/internal/strings.js +102 -0
  176. package/dist/internal/use-controllable.d.ts +5 -0
  177. package/dist/internal/use-controllable.js +17 -0
  178. package/package.json +66 -5
  179. package/styles.css +7 -0
package/README.md CHANGED
@@ -1,3 +1,78 @@
1
- # Temporary Holding Version
1
+ # @jotis-me/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Los componentes de la suite jotis.me para Next.js y Astro, con Tailwind CSS v4. Trae consigo `@jotis-me/tokens` (colores, tipografía, fuentes) y `@jotis-me/icons`.
4
+
5
+ Documentación con ejemplos de cada componente: [regula.jotis.me](https://regula.jotis.me).
6
+
7
+ ## Next.js (App Router)
8
+
9
+ ```sh
10
+ bun add @jotis-me/react tailwindcss @tailwindcss/postcss
11
+ ```
12
+
13
+ `postcss.config.mjs`:
14
+
15
+ ```js
16
+ export default { plugins: { "@tailwindcss/postcss": {} } };
17
+ ```
18
+
19
+ `app/globals.css`:
20
+
21
+ ```css
22
+ @import "tailwindcss";
23
+ @import "@jotis-me/react/styles.css";
24
+ ```
25
+
26
+ Impórtalo en `app/layout.tsx` y úsalos donde quieras:
27
+
28
+ ```tsx
29
+ import { Button } from "@jotis-me/react";
30
+
31
+ export default function Page() {
32
+ return <Button>Guardar</Button>;
33
+ }
34
+ ```
35
+
36
+ Los componentes sin estado funcionan como Server Components. Los interactivos ya llevan `"use client"`: impórtalos también desde páginas de servidor.
37
+
38
+ ## Astro
39
+
40
+ ```sh
41
+ bunx astro add react
42
+ bun add @jotis-me/react tailwindcss @tailwindcss/vite
43
+ ```
44
+
45
+ `astro.config.mjs`:
46
+
47
+ ```js
48
+ import react from "@astrojs/react";
49
+ import tailwindcss from "@tailwindcss/vite";
50
+ import { defineConfig } from "astro/config";
51
+
52
+ export default defineConfig({ integrations: [react()], vite: { plugins: [tailwindcss()] } });
53
+ ```
54
+
55
+ `src/styles/global.css`, importado desde tu layout:
56
+
57
+ ```css
58
+ @import "tailwindcss";
59
+ @import "@jotis-me/react/styles.css";
60
+ ```
61
+
62
+ ```astro
63
+ ---
64
+ import { Button, ButtonGroup } from "@jotis-me/react";
65
+ ---
66
+ <Button>Guardar</Button> <!-- HTML estático, sin JavaScript -->
67
+ <ButtonGroup client:load items={[…]} /> <!-- interactivo: necesita client:* -->
68
+ ```
69
+
70
+ Las props que son funciones (`onChange`…) no pueden pasar de un `.astro` a una isla: envuelve el componente en un `.tsx` y pásale ahí la función.
71
+
72
+ ## Modo oscuro
73
+
74
+ Clase `dark-mode` en `<html>`. La variante `dark:` responde a esa clase.
75
+
76
+ ## Licencia
77
+
78
+ MIT.
@@ -0,0 +1,29 @@
1
+ import type { ReactNode } from "react";
2
+ import { type AvatarProps } from "./Avatar.js";
3
+ export interface ActivityItem {
4
+ author: ReactNode;
5
+ /** Por defecto, las dos primeras letras del autor. */
6
+ avatar?: AvatarProps;
7
+ time?: ReactNode;
8
+ /** Qué pasó. Admite `<strong>` para destacar el objeto. */
9
+ text: ReactNode;
10
+ /** Adjunto, etiquetas… bajo el texto. */
11
+ content?: ReactNode;
12
+ /** Punto `fg-success-secondary` a la derecha. */
13
+ unread?: boolean;
14
+ /** Punto de estado en línea en el avatar. */
15
+ online?: boolean;
16
+ }
17
+ export interface ActivityFeedProps {
18
+ items: ActivityItem[];
19
+ /** Línea vertical entre avatares en lugar de separadores (historial de un elemento). */
20
+ connector?: boolean;
21
+ /**
22
+ * Avatar de 40px (`md`) o 32px (`sm`).
23
+ * @default "md"
24
+ */
25
+ size?: "sm" | "md";
26
+ className?: string;
27
+ }
28
+ /** Lista cronológica de lo que ha pasado: quién hizo qué y cuándo. */
29
+ export declare function ActivityFeed({ items, connector, size, className }: ActivityFeedProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { defaultStrings } from "../internal/strings.js";
4
+ import { Avatar } from "./Avatar.js";
5
+ /** Lista cronológica de lo que ha pasado: quién hizo qué y cuándo. */
6
+ export function ActivityFeed({ items, connector, size = "md", className }) {
7
+ return (_jsx("ol", { className: cx("m-0 flex max-w-[420px] list-none flex-col p-0 font-body", connector ? "gap-0" : "gap-4 [&>li+li]:border-t [&>li+li]:border-secondary [&>li+li]:pt-4", className), children: items.map((it, i) => (_jsxs("li", { className: "relative flex gap-3", children: [_jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx(Avatar, { size: size, status: it.online ? "online" : undefined, ...(it.avatar ?? {
8
+ initials: typeof it.author === "string" ? it.author.slice(0, 2) : "?",
9
+ }) }), connector && i < items.length - 1 ? (_jsx("span", { "aria-hidden": "true", className: "mb-1 min-h-4 w-0.5 flex-1 rounded-xs bg-[var(--border-secondary)]" })) : null] }), _jsxs("div", { className: cx("flex min-w-0 flex-1 flex-col gap-0.5 pr-4", connector && "pb-8"), children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-sm font-semibold text-secondary", children: it.author }), it.time ? _jsx("span", { className: "text-xs text-tertiary", children: it.time }) : null] }), _jsx("div", { className: "text-sm text-tertiary [&_b]:font-semibold [&_b]:text-secondary [&_strong]:font-semibold [&_strong]:text-secondary", children: it.text }), it.content ? _jsx("div", { className: "mt-2", children: it.content }) : null] }), it.unread ? (_jsx("span", { className: "absolute top-1 right-0 size-2 rounded-full bg-fg-success-secondary", children: _jsx("span", { className: "sr-only", children: defaultStrings.unread }) })) : null] }, i))) }));
10
+ }
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StatusColor } from "./Badge.js";
3
+ import type { IconName } from "./Icon.js";
4
+ export interface AlertProps {
5
+ /**
6
+ * El color va solo en el icono: el texto sigue en `text-secondary` / `text-tertiary`.
7
+ * @default "gray"
8
+ */
9
+ color?: StatusColor;
10
+ title: ReactNode;
11
+ description?: ReactNode;
12
+ /** Botones `link-gray` + `link-color`. */
13
+ actions?: ReactNode;
14
+ /** Muestra la X para descartarlo. */
15
+ onClose?: () => void;
16
+ /** Nombre de la X. @default "Descartar" */
17
+ closeLabel?: string;
18
+ /** Icono propio. Por defecto, uno según el color (`alert-circle` en error, `check-circle` en éxito…). */
19
+ icon?: IconName;
20
+ /**
21
+ * `floating`: tarjeta con borde y radio. `full`: a todo el ancho, sin radio y con línea inferior.
22
+ * @default "floating"
23
+ */
24
+ variant?: "floating" | "full";
25
+ className?: string;
26
+ }
27
+ /** Clases y contenido comunes a Alert y Notification. */
28
+ export declare function alertBody({ color, title, description, actions, onClose, closeLabel, icon, }: AlertProps): import("react").JSX.Element;
29
+ export declare const ALERT_BASE = "relative flex gap-4 p-4 font-body bg-primary_alt";
30
+ /**
31
+ * Aviso en la página sobre algo que afecta a lo que el usuario está viendo.
32
+ * Los errores se anuncian como `alert`; el resto, como `status`.
33
+ */
34
+ export declare function Alert(props: AlertProps): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { defaultStrings } from "../internal/strings.js";
4
+ import { CloseButton } from "./CloseButton.js";
5
+ import { FeaturedIcon } from "./FeaturedIcon.js";
6
+ const ICON = {
7
+ gray: "info-circle",
8
+ brand: "info-circle",
9
+ error: "alert-circle",
10
+ warning: "alert-triangle",
11
+ success: "check-circle",
12
+ };
13
+ /** Clases y contenido comunes a Alert y Notification. */
14
+ export function alertBody({ color = "gray", title, description, actions, onClose, closeLabel, icon, }) {
15
+ return (_jsxs(_Fragment, { children: [_jsx(FeaturedIcon, { icon: icon ?? ICON[color], color: color, size: "md", variant: "outline" }), _jsxs("div", { className: "flex flex-1 flex-col gap-3 pr-8", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-semibold text-secondary", children: title }), description ? _jsx("div", { className: "mt-1 text-sm text-tertiary", children: description }) : null] }), actions ? _jsx("div", { className: "flex gap-3", children: actions }) : null] }), onClose ? (_jsx(CloseButton, { label: closeLabel ?? defaultStrings.dismiss, onClick: onClose, className: "absolute top-2 right-2" })) : null] }));
16
+ }
17
+ export const ALERT_BASE = "relative flex gap-4 p-4 font-body bg-primary_alt";
18
+ /**
19
+ * Aviso en la página sobre algo que afecta a lo que el usuario está viendo.
20
+ * Los errores se anuncian como `alert`; el resto, como `status`.
21
+ */
22
+ export function Alert(props) {
23
+ const { color = "gray", variant = "floating", className } = props;
24
+ return (_jsx("div", { role: color === "error" ? "alert" : "status", className: cx(ALERT_BASE, variant === "full"
25
+ ? "rounded-none shadow-[inset_0_-1px_0_var(--border-primary)]"
26
+ : "rounded-xl shadow-[var(--shadow-xs),inset_0_0_0_1px_var(--border-primary)]", className), children: alertBody(props) }));
27
+ }
@@ -0,0 +1,27 @@
1
+ import { type AppIconHue, type AppIconShape, type AppIconVariant, type AppName } from "@jotis-me/icons/app-icon";
2
+ import type { CSSProperties } from "react";
3
+ export type { AppIconHue, AppIconShape, AppIconVariant, AppName };
4
+ /** Apps de la suite con su nombre, letras y tono. */
5
+ export declare const appIcons: Record<"emptor" | "ornatus" | "pecunia" | "vinculum", import("@jotis-me/icons/app-icon").AppInfo>;
6
+ export interface AppIconProps {
7
+ /** App de la suite. Para una nueva, `letters` + `hue`. */
8
+ app?: AppName;
9
+ /** Dos letras: mayúscula + minúscula. */
10
+ letters?: string;
11
+ /** Tono de la paleta para una app nueva. Nunca rojo, amarillo ni grises. */
12
+ hue?: AppIconHue;
13
+ /** Por debajo de 20px, si no lo pasas, sale `solid`. */
14
+ variant?: AppIconVariant;
15
+ /** Lado en px. Por defecto, 48. La virgulilla solo se dibuja desde 32px. */
16
+ size?: number;
17
+ /** `square` para el icono maskable de PWA. */
18
+ shape?: AppIconShape;
19
+ /** Fuerza o quita la virgulilla. */
20
+ tilde?: boolean;
21
+ /** Nombre accesible. Por defecto, el de la app. */
22
+ label?: string;
23
+ className?: string;
24
+ style?: CSSProperties;
25
+ }
26
+ /** Icono de una app de la suite: baldosa con dos letras en curvas y la virgulilla, en el tono de la app. */
27
+ export declare function AppIcon({ size, className, style, ...options }: AppIconProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { appIcon, apps, } from "@jotis-me/icons/app-icon";
3
+ /** Apps de la suite con su nombre, letras y tono. */
4
+ export const appIcons = apps;
5
+ /** Icono de una app de la suite: baldosa con dos letras en curvas y la virgulilla, en el tono de la app. */
6
+ export function AppIcon({ size = 48, className, style, ...options }) {
7
+ const { label, parts } = appIcon({ ...options, size });
8
+ return (_jsx("svg", { width: size, height: size, viewBox: "0 0 128 128", role: "img", "aria-label": label, className: className, style: style, children: parts.map((p, i) => (_jsx("path", { d: p.d, fill: p.fill, stroke: p.stroke, strokeWidth: p.strokeWidth, strokeLinecap: p.strokeLinecap, vectorEffect: p.vectorEffect }, i))) }));
9
+ }
@@ -0,0 +1,20 @@
1
+ export type AvatarSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
2
+ export type AvatarStatus = "online" | "offline";
3
+ export interface AvatarProps {
4
+ /** Foto. Sin `src` ni `initials` se muestra el icono de usuario. */
5
+ src?: string;
6
+ /** Texto alternativo de la foto, o nombre accesible de las iniciales. */
7
+ alt?: string;
8
+ initials?: string;
9
+ /** Diámetro: `xs` 24 · `sm` 32 · `md` 40 · `lg` 48 · `xl` 56 · `2xl` 64. */
10
+ size?: AvatarSize;
11
+ /** Punto de estado abajo a la derecha. */
12
+ status?: AvatarStatus;
13
+ /** Texto del estado para lectores de pantalla. Por defecto, "En línea" o "Desconectado". */
14
+ statusLabel?: string;
15
+ /** Anillo `bg-primary` para apilar avatares. */
16
+ ring?: boolean;
17
+ className?: string;
18
+ }
19
+ /** Foto o iniciales de una persona en círculo, de 24 a 64px. */
20
+ export declare function Avatar({ src, alt, initials, size, status, statusLabel, ring, className, }: AvatarProps): import("react").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { defaultStrings } from "../internal/strings.js";
4
+ import { Icon } from "./Icon.js";
5
+ const SIZE = {
6
+ xs: "size-6 text-xs",
7
+ sm: "size-8 text-sm",
8
+ md: "size-10 text-md",
9
+ lg: "size-12 text-lg",
10
+ xl: "size-14 text-xl",
11
+ "2xl": "size-16 text-display-xs",
12
+ };
13
+ const ICON_SIZE = { xs: 16, sm: 20, md: 24, lg: 28, xl: 32, "2xl": 32 };
14
+ // color-fijo: borde interior de 0,5px al 16 % negro para que la foto no se funda con el fondo (ficha de Avatar)
15
+ const EDGE = "shadow-[inset_0_0_0_0.5px_#00000029]";
16
+ // color-fijo: el mismo borde interior bajo el anillo para apilar (ficha de Avatar)
17
+ const RING = "shadow-[0_0_0_1.5px_var(--bg-primary),inset_0_0_0_0.5px_#00000029]";
18
+ /** Foto o iniciales de una persona en círculo, de 24 a 64px. */
19
+ export function Avatar({ src, alt, initials, size = "md", status, statusLabel, ring, className, }) {
20
+ const statusText = status ? (statusLabel ?? defaultStrings[status]) : undefined;
21
+ // Con iniciales y `alt`, el avatar es una imagen con nombre: el estado entra en el nombre
22
+ const named = Boolean(alt && !src);
23
+ const a11y = named ? { role: "img", "aria-label": [alt, statusText].filter(Boolean).join(", ") } : {};
24
+ return (_jsxs("span", { ...a11y, className: cx("relative inline-flex shrink-0 items-center justify-center rounded-full bg-avatar-styles-bg-neutral font-semibold text-(color:--bg-secondary-solid)", SIZE[size],
25
+ // Interlineado de la fuente, como la guía: con el de la escala las iniciales bajan un píxel
26
+ "leading-[normal]", ring ? RING : EDGE, className), children: [src ? (_jsx("img", { src: src, alt: alt ?? "", className: "size-full rounded-[inherit] object-cover" })) : initials ? (_jsx("span", { "aria-hidden": alt ? true : undefined, children: initials })) : (_jsx(Icon, { name: "user-01", size: ICON_SIZE[size] })), status ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: cx("absolute right-0 bottom-0 size-[30%] min-h-1.5 min-w-1.5 rounded-full shadow-[0_0_0_1.5px_var(--bg-primary)]", status === "online" ? "bg-fg-success-secondary" : "bg-fg-quaternary") }), named ? null : _jsx("span", { className: "sr-only", children: statusText })] })) : null] }));
27
+ }
@@ -0,0 +1,10 @@
1
+ import { type AvatarProps } from "./Avatar.js";
2
+ export interface AvatarGroupProps {
3
+ /** Props de cada `Avatar`. El último puede ser "+N" en `initials`. */
4
+ items: AvatarProps[];
5
+ /** Tamaño de todos los avatares. Por defecto, `sm`. */
6
+ size?: AvatarProps["size"];
7
+ className?: string;
8
+ }
9
+ /** Avatares solapados 8px, cada uno con su anillo, para mostrar quién participa. */
10
+ export declare function AvatarGroup({ items, size, className }: AvatarGroupProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { Avatar } from "./Avatar.js";
4
+ /** Avatares solapados 8px, cada uno con su anillo, para mostrar quién participa. */
5
+ export function AvatarGroup({ items, size = "sm", className }) {
6
+ return (_jsx("span", { className: cx("inline-flex [&>*+*]:-ml-2", className), children: items.map((item, i) => (_jsx(Avatar, { size: size, ring: true, ...item }, i))) }));
7
+ }
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ import { type AvatarProps } from "./Avatar.js";
3
+ export interface AvatarLabelProps extends AvatarProps {
4
+ /** Nombre, en `text-sm` semibold y `text-primary`. */
5
+ name: ReactNode;
6
+ /** Línea de apoyo (correo, rol), en `text-tertiary`. */
7
+ supporting?: ReactNode;
8
+ }
9
+ /** Avatar con nombre y una línea de apoyo, para menús, filas de tabla y el sidebar. */
10
+ export declare function AvatarLabel({ name, supporting, className, ...avatar }: AvatarLabelProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { Avatar } from "./Avatar.js";
4
+ /** Avatar con nombre y una línea de apoyo, para menús, filas de tabla y el sidebar. */
5
+ export function AvatarLabel({ name, supporting, className, ...avatar }) {
6
+ return (_jsxs("span", { className: cx("inline-flex items-center gap-2 font-body", className), children: [_jsx(Avatar, { ...avatar }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-primary", children: name }), supporting ? _jsx("span", { className: "block text-sm text-tertiary", children: supporting }) : null] })] }));
7
+ }
@@ -0,0 +1,35 @@
1
+ import { type CSSProperties } from "react";
2
+ export type BackgroundPatternName = "grid" | "grid-dot" | "grid-check" | "circles" | "squares";
3
+ export type BackgroundPatternMask = "radial" | "radial-down" | "radial-up" | "linear-down" | "linear-up" | "none";
4
+ export interface BackgroundPatternProps {
5
+ /**
6
+ * `grid` rejilla · `grid-dot` puntos cada 32px · `grid-check` rejilla con casillas · `circles` y `squares`
7
+ * concéntricos.
8
+ * @default "grid"
9
+ */
10
+ pattern?: BackgroundPatternName;
11
+ /**
12
+ * Celda de 80/96/96px en las rejillas; lado de 336/480/768px en los concéntricos.
13
+ * @default "md"
14
+ */
15
+ size?: "sm" | "md" | "lg";
16
+ /**
17
+ * Desvanecido hacia los bordes.
18
+ * @default "radial"
19
+ */
20
+ mask?: BackgroundPatternMask;
21
+ /** Ancho del área de la rejilla en px. @default 1536 */
22
+ width?: number;
23
+ /** Alto del área de la rejilla en px. @default 960 */
24
+ height?: number;
25
+ /** Reparto de las casillas en `grid-check`. @default 7 */
26
+ seed?: number;
27
+ className?: string;
28
+ style?: CSSProperties;
29
+ }
30
+ /**
31
+ * Fondo decorativo detrás de cabeceras, estados vacíos o tarjetas de bienvenida. Ocupa todo su contenedor
32
+ * (`absolute inset-0`): el padre necesita `relative` y el contenido, su propio `relative` para quedar encima.
33
+ * Lleva `aria-hidden`.
34
+ */
35
+ export declare function BackgroundPattern({ pattern, size, mask, width, height, seed, className, style, }: BackgroundPatternProps): import("react").JSX.Element;
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useId } from "react";
3
+ import { cx } from "../internal/cx.js";
4
+ const CONCENTRIC = { sm: 336, md: 480, lg: 768 };
5
+ const GRID_CELL = { sm: 80, md: 96, lg: 96 };
6
+ // Máscaras con negro y transparente: solo cuenta el alfa, no es un color de la interfaz.
7
+ const MASK = {
8
+ radial: "radial-gradient(closest-side, black, transparent)",
9
+ "radial-down": "radial-gradient(ellipse 60% 100% at 50% 0, black, transparent)",
10
+ "radial-up": "radial-gradient(ellipse 60% 100% at 50% 100%, black, transparent)",
11
+ "linear-down": "linear-gradient(black, transparent)",
12
+ "linear-up": "linear-gradient(transparent, black)",
13
+ };
14
+ const LINE = {
15
+ stroke: "var(--border-secondary)",
16
+ strokeWidth: 1,
17
+ fill: "none",
18
+ vectorEffect: "non-scaling-stroke",
19
+ };
20
+ /**
21
+ * Fondo decorativo detrás de cabeceras, estados vacíos o tarjetas de bienvenida. Ocupa todo su contenedor
22
+ * (`absolute inset-0`): el padre necesita `relative` y el contenido, su propio `relative` para quedar encima.
23
+ * Lleva `aria-hidden`.
24
+ */
25
+ export function BackgroundPattern({ pattern = "grid", size = "md", mask = "radial", width = 1536, height = 960, seed = 7, className, style, }) {
26
+ const pid = `jm-bgp${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
27
+ let w;
28
+ let h;
29
+ const kids = [];
30
+ if (pattern === "circles" || pattern === "squares") {
31
+ w = h = CONCENTRIC[size];
32
+ const scale = w / 480;
33
+ const c = w / 2;
34
+ for (const d of [96, 160, 224, 288, 352, 416, 480]) {
35
+ const r = (d * scale) / 2 - 0.5;
36
+ kids.push(pattern === "circles" ? (_jsx("circle", { cx: c, cy: c, r: r, ...LINE }, d)) : (_jsx("rect", { x: c - r, y: c - r, width: r * 2, height: r * 2, rx: 2, ...LINE }, d)));
37
+ }
38
+ }
39
+ else {
40
+ const cell = pattern === "grid-dot" ? 32 : GRID_CELL[size];
41
+ const cols = Math.ceil(width / cell);
42
+ const rows = Math.ceil(height / cell);
43
+ w = cols * cell;
44
+ h = rows * cell;
45
+ if (pattern === "grid-dot") {
46
+ kids.push(_jsx("defs", { children: _jsx("pattern", { id: pid, width: cell, height: cell, patternUnits: "userSpaceOnUse", children: _jsx("circle", { cx: cell / 2, cy: cell / 2, r: 1.5, fill: "var(--utility-neutral-300)" }) }) }, "defs"), _jsx("rect", { width: w, height: h, fill: `url(#${pid})` }, "dots"));
47
+ }
48
+ else {
49
+ if (pattern === "grid-check") {
50
+ // Reparto determinista, cargado hacia el centro para que la máscara no lo esconda
51
+ let s = seed;
52
+ const rnd = () => {
53
+ s = (s * 16807) % 2147483647;
54
+ return s / 2147483647;
55
+ };
56
+ const n = Math.round(cols * rows * 0.09);
57
+ for (let i = 0; i < n; i++) {
58
+ const x = Math.floor(cols / 2 + ((rnd() + rnd() - 1) * cols) / 2);
59
+ const y = Math.floor(rows / 2 + ((rnd() + rnd() - 1) * rows) / 2);
60
+ kids.push(_jsx("rect", { x: x * cell, y: y * cell, width: cell, height: cell, fill: "var(--bg-secondary)" }, `c${i}`));
61
+ }
62
+ }
63
+ let d = "";
64
+ for (let gx = 0; gx <= cols; gx++)
65
+ d += `M${gx * cell + 0.5} 0V${h}`;
66
+ for (let gy = 0; gy <= rows; gy++)
67
+ d += `M0 ${gy * cell + 0.5}H${w}`;
68
+ kids.push(_jsx("path", { d: d, ...LINE }, "grid"));
69
+ }
70
+ }
71
+ const maskImage = mask !== "none" ? MASK[mask] : undefined;
72
+ return (_jsx("div", { "aria-hidden": "true", className: cx("pointer-events-none absolute inset-0 z-0 overflow-hidden", className), style: maskImage ? { maskImage, WebkitMaskImage: maskImage, ...style } : style, children: _jsx("svg", { "aria-hidden": "true", width: w, height: h, viewBox: `0 0 ${w} ${h}`, className: "absolute top-1/2 left-1/2 block max-w-none -translate-1/2", children: kids }) }));
73
+ }
@@ -0,0 +1,38 @@
1
+ import type { ReactNode } from "react";
2
+ import { type IconName } from "./Icon.js";
3
+ export type StatusColor = "gray" | "brand" | "error" | "warning" | "success";
4
+ export type BadgeColor = StatusColor | "slate" | "sky" | "blue" | "indigo" | "purple" | "pink" | "orange";
5
+ export type BadgeType = "pill" | "badge" | "modern";
6
+ export type BadgeSize = "sm" | "md" | "lg";
7
+ export interface BadgeProps {
8
+ /** Escala utility: fondo `-50`, borde `-200`, texto `-700`, punto `-500`. */
9
+ color?: BadgeColor;
10
+ /** `pill` redonda · `badge` con `radius-md` · `modern` blanca con sombra. */
11
+ type?: BadgeType;
12
+ /** Alto: `sm` 22 · `md` 24 · `lg` 28. */
13
+ size?: BadgeSize;
14
+ /** Punto de color antes del texto. */
15
+ dot?: boolean;
16
+ /** Icono de 12px antes del texto. */
17
+ icon?: IconName;
18
+ /** Muestra una X para quitarlo. */
19
+ onRemove?: () => void;
20
+ /** Nombre accesible de la X. Por defecto, "Quitar". */
21
+ removeLabel?: string;
22
+ /** Una o dos palabras: el estado no puede depender solo del color. */
23
+ children: ReactNode;
24
+ className?: string;
25
+ }
26
+ interface Palette {
27
+ /** Fondo, texto y borde del badge. */
28
+ badge: string;
29
+ /** Fondo del punto. */
30
+ dot: string;
31
+ /** Color de la X y fondo al pasar por encima. */
32
+ remove: string;
33
+ }
34
+ /** Clases por color (también para los chips de eventos de CalendarView). */
35
+ export declare const COLOR: Record<BadgeColor, Palette>;
36
+ /** Etiqueta de estado o categoría de una o dos palabras. */
37
+ export declare function Badge({ color, type, size, dot, icon, onRemove, removeLabel, children, className, }: BadgeProps): import("react").JSX.Element;
38
+ export {};
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { defaultStrings } from "../internal/strings.js";
4
+ import { Icon } from "./Icon.js";
5
+ // Clases enteras, no compuestas: Tailwind solo genera las que encuentra escritas.
6
+ /** Clases por color (también para los chips de eventos de CalendarView). */
7
+ export const COLOR = {
8
+ gray: {
9
+ badge: "bg-utility-neutral-50 text-utility-neutral-700 inset-ring-utility-neutral-200",
10
+ dot: "bg-utility-neutral-500",
11
+ remove: "text-utility-neutral-500 hover:bg-utility-neutral-200",
12
+ },
13
+ brand: {
14
+ badge: "bg-utility-brand-50 text-utility-brand-700 inset-ring-utility-brand-200",
15
+ dot: "bg-utility-brand-500",
16
+ remove: "text-utility-brand-500 hover:bg-utility-brand-200",
17
+ },
18
+ error: {
19
+ badge: "bg-utility-red-50 text-utility-red-700 inset-ring-utility-red-200",
20
+ dot: "bg-utility-red-500",
21
+ remove: "text-utility-red-500 hover:bg-utility-red-200",
22
+ },
23
+ warning: {
24
+ badge: "bg-utility-yellow-50 text-utility-yellow-700 inset-ring-utility-yellow-200",
25
+ dot: "bg-utility-yellow-500",
26
+ remove: "text-utility-yellow-500 hover:bg-utility-yellow-200",
27
+ },
28
+ success: {
29
+ badge: "bg-utility-green-50 text-utility-green-700 inset-ring-utility-green-200",
30
+ dot: "bg-utility-green-500",
31
+ remove: "text-utility-green-500 hover:bg-utility-green-200",
32
+ },
33
+ slate: {
34
+ badge: "bg-utility-slate-50 text-utility-slate-700 inset-ring-utility-slate-200",
35
+ dot: "bg-utility-slate-500",
36
+ remove: "text-utility-slate-500 hover:bg-utility-slate-200",
37
+ },
38
+ sky: {
39
+ badge: "bg-utility-sky-50 text-utility-sky-700 inset-ring-utility-sky-200",
40
+ dot: "bg-utility-sky-500",
41
+ remove: "text-utility-sky-500 hover:bg-utility-sky-200",
42
+ },
43
+ blue: {
44
+ badge: "bg-utility-blue-50 text-utility-blue-700 inset-ring-utility-blue-200",
45
+ dot: "bg-utility-blue-500",
46
+ remove: "text-utility-blue-500 hover:bg-utility-blue-200",
47
+ },
48
+ indigo: {
49
+ badge: "bg-utility-indigo-50 text-utility-indigo-700 inset-ring-utility-indigo-200",
50
+ dot: "bg-utility-indigo-500",
51
+ remove: "text-utility-indigo-500 hover:bg-utility-indigo-200",
52
+ },
53
+ purple: {
54
+ badge: "bg-utility-purple-50 text-utility-purple-700 inset-ring-utility-purple-200",
55
+ dot: "bg-utility-purple-500",
56
+ remove: "text-utility-purple-500 hover:bg-utility-purple-200",
57
+ },
58
+ pink: {
59
+ badge: "bg-utility-pink-50 text-utility-pink-700 inset-ring-utility-pink-200",
60
+ dot: "bg-utility-pink-500",
61
+ remove: "text-utility-pink-500 hover:bg-utility-pink-200",
62
+ },
63
+ orange: {
64
+ badge: "bg-utility-orange-50 text-utility-orange-700 inset-ring-utility-orange-200",
65
+ dot: "bg-utility-orange-500",
66
+ remove: "text-utility-orange-500 hover:bg-utility-orange-200",
67
+ },
68
+ };
69
+ // `modern` es blanca con sombra en todos los colores; el punto conserva el color
70
+ const MODERN = "bg-primary text-secondary inset-ring-primary shadow-xs";
71
+ const MODERN_REMOVE = "hover:bg-(--border-primary)";
72
+ const PADDING = {
73
+ pill: { sm: "px-2 py-0.5", md: "px-2.5 py-0.5", lg: "px-3 py-1" },
74
+ badge: { sm: "px-1.5 py-0.5", md: "px-2 py-0.5", lg: "px-2.5 py-1 rounded-lg" },
75
+ modern: { sm: "px-2 py-0.5", md: "px-2 py-0.5", lg: "px-2.5 py-1 rounded-lg" },
76
+ };
77
+ /** Etiqueta de estado o categoría de una o dos palabras. */
78
+ export function Badge({ color = "gray", type = "pill", size = "md", dot, icon, onRemove, removeLabel = defaultStrings.remove, children, className, }) {
79
+ const palette = COLOR[color];
80
+ const modern = type === "modern";
81
+ return (_jsxs("span", { className: cx("inline-flex items-center gap-1.5 whitespace-nowrap font-body text-sm font-medium inset-ring", type === "pill" ? "rounded-full" : "rounded-md", size === "sm" && "gap-1 text-xs", palette.badge, modern && MODERN, PADDING[type][size], dot && type === "pill" && "pl-2", className), children: [dot ? (_jsx("span", { "aria-hidden": "true", className: cx("m-px size-1.5 shrink-0 rounded-full", palette.dot) })) : null, icon ? _jsx(Icon, { name: icon, size: 12 }) : null, children, onRemove ? (_jsx("button", { type: "button", onClick: onRemove, "aria-label": removeLabel, className: cx("-my-0.5 -mr-1 -ml-0.5 inline-flex cursor-pointer rounded-sm bg-transparent p-0.5 transition-colors duration-100 focus-visible:shadow-focus-ring focus-visible:outline-none", palette.remove, modern && MODERN_REMOVE), children: _jsx(Icon, { name: "x-close", size: 12 }) })) : null] }));
82
+ }
@@ -0,0 +1,48 @@
1
+ import { type ReactNode } from "react";
2
+ import type { IconName } from "./Icon.js";
3
+ export interface BannerEmail {
4
+ /** @default "Tu correo" */
5
+ placeholder?: string;
6
+ /** @default "Suscribirme" */
7
+ buttonLabel?: string;
8
+ /** Texto bajo el campo, como "Puedes darte de baja cuando quieras." */
9
+ hint?: ReactNode;
10
+ onSubmit?: (value: string) => void;
11
+ }
12
+ export interface BannerProps {
13
+ title: ReactNode;
14
+ description?: ReactNode;
15
+ /** Icono destacado de 48px: `modern` en el tema por defecto y `light` en `brand`. */
16
+ icon?: IconName;
17
+ actions?: ReactNode;
18
+ /** Campo de correo de 360px con botón, para captar suscripciones. */
19
+ email?: BannerEmail;
20
+ /** Fecha objetivo de una cuenta atrás (días, horas, minutos y segundos). */
21
+ countdown?: Date;
22
+ /** Congela el reloj de la cuenta atrás en esta fecha (capturas y vistas previas). */
23
+ now?: Date;
24
+ /**
25
+ * `default`: `bg-secondary`. `brand`: `bg-brand-section_subtle` con textos `on-brand`.
26
+ * @default "default"
27
+ */
28
+ theme?: "default" | "brand";
29
+ /**
30
+ * `floating`: con radio y sombra. `full`: pegado al borde superior, a todo el ancho y sin radio.
31
+ * @default "floating"
32
+ */
33
+ layout?: "floating" | "full";
34
+ /** Una línea, centrada y sin icono. */
35
+ slim?: boolean;
36
+ /** Muestra la X. Un banner siempre se puede descartar salvo que el aviso bloquee algo de verdad. */
37
+ onClose?: () => void;
38
+ /** Nombre de la X. @default "Cerrar aviso" */
39
+ closeLabel?: string;
40
+ /** Nombre de la región. Por defecto, el título si es texto, o "Aviso". */
41
+ "aria-label"?: string;
42
+ className?: string;
43
+ }
44
+ /**
45
+ * Aviso de ancho de página o flotante para anunciar algo a toda la app: novedades, mantenimiento, captación de
46
+ * correo, cuenta atrás. Uno a la vez. En móvil se apila y oculta el icono.
47
+ */
48
+ export declare function Banner({ title, description, icon, actions, email, countdown, now, theme, layout, slim, onClose, closeLabel, className, ...rest }: BannerProps): import("react").JSX.Element;