@studiolxd/brand 49.24.0 → 49.25.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 (36) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/dist/_shared/media-query.js +12 -0
  3. package/dist/_shared/tokens.js +19 -0
  4. package/dist/_types/atoms/Button/Button.d.ts +14 -0
  5. package/dist/_types/constants/media-query.d.ts +19 -0
  6. package/dist/_types/molecules/LoadingRegion/LoadingRegion.d.ts +53 -0
  7. package/dist/_types/molecules/LoadingState/LoadingState.d.ts +39 -0
  8. package/dist/app-shell.css +1 -1
  9. package/dist/app-shell.js +53 -59
  10. package/dist/brand.css +33 -1
  11. package/dist/button.css +1 -1
  12. package/dist/button.js +57 -45
  13. package/dist/chat-shell.css +1 -1
  14. package/dist/chat-shell.js +21 -24
  15. package/dist/loading-region.css +1 -0
  16. package/dist/loading-region.js +61 -0
  17. package/dist/loading-state.css +1 -0
  18. package/dist/loading-state.js +46 -0
  19. package/dist/toaster.css +1 -1
  20. package/dist/tokens.css +37 -1
  21. package/package.json +11 -1
  22. package/src/tokens/components/button.css +2 -0
  23. package/src/tokens/index.css +2 -0
  24. package/src/tokens/molecules/loading-region.css +16 -0
  25. package/src/tokens/molecules/loading-region.ts +4 -0
  26. package/src/tokens/molecules/loading-state.css +13 -0
  27. package/src/tokens/molecules/loading-state.ts +4 -0
  28. package/src/tokens/scss/_index.legacy.scss +2 -0
  29. package/src/tokens/scss/_index.scss +2 -0
  30. package/src/tokens/scss/components/_button.scss +2 -0
  31. package/src/tokens/scss/molecules/_loading-region.scss +13 -0
  32. package/src/tokens/scss/molecules/_loading-state.scss +10 -0
  33. package/src/tokens/surface-dark-derived.css +1 -0
  34. package/src/tokens/surface-invert.css +1 -0
  35. package/src/tokens/surface-light.css +1 -0
  36. package/src/tokens/tokens.json +19 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,84 @@ El paquete sigue [semver](https://semver.org/lang/es/): **patch** para bug fixes
7
7
  regeneración de `dist`, **minor** para componentes/props/variantes/tokens nuevos, **major**
8
8
  para breaking changes.
9
9
 
10
+ ## [49.25.0] — 2026-10-05
11
+
12
+ > **Minor.** Las esperas pasan a ser piezas del sistema: las aplicaciones dejan de
13
+ > componerlas a mano (`LoadingRegion`/`SkeletonLines`/`SkeletonTable` de lmsmcp, `LoadingState`/
14
+ > `LoadingList`/`LoadingGrid` de creator, los botones con `Spinner` metido a mano). Criterio de la
15
+ > suite: esqueleto cuando se conoce la forma de lo que llega, girador cuando no o cuando se espera
16
+ > una acción, y la espera la anuncia el contenedor. Sin cambios incompatibles.
17
+
18
+ ### `LoadingState` — la espera de un bloque sin forma conocida (`@studiolxd/brand/loading-state`)
19
+
20
+ - Un girador centrado en los dos ejes, en una caja que reserva alto: nunca un `Spinner` suelto.
21
+ - **Solo se ve el girador.** El texto de la espera es el nombre accesible de la caja (`role="status"`,
22
+ `aria-busy`, `aria-labelledby` al texto oculto) y no se pinta. `label` lo cambia; sin él sale de
23
+ `spinner.label` del `BrandMessagesProvider` (sin espacio de textos nuevo). Fuera del proveedor hay
24
+ que pasar `label`: sin él ni catálogo, el error explícito de siempre.
25
+ - Tallas `md` (página o zona) y `sm` (diálogo, hoja, popover); `action` opcional (p. ej. «Cancelar»),
26
+ con el mismo `EmptyStateAction` que `EmptyState`.
27
+ - El alto: en una zona con alto definido la ocupa entera; en un flujo sin alto reserva el mínimo de
28
+ la talla; con **`fill`**, a página completa, toma el alto visible bajo la cabecera del `AppShell` y
29
+ centra sin provocar desplazamiento (para el `loading.tsx` de una ruta).
30
+
31
+ ### `LoadingRegion` y los esqueletos compuestos (`@studiolxd/brand/loading-region`)
32
+
33
+ - `LoadingRegion` envuelve esqueletos y anuncia la espera (`role="status"`, `aria-busy`, texto oculto
34
+ de `label` o `spinner.label`). `announce={false}` la deja solo a la vista, con `aria-hidden`, cuando
35
+ ya anuncia otra región viva.
36
+ - `SkeletonText` (`lines`, la última más corta), `SkeletonList` (`rows`), `SkeletonTable` (`rows`,
37
+ más la cabecera) y `SkeletonGrid` (`columns` 2|3|4, `rows`). Decorativos, sin atributo `style`: el
38
+ alto de cada barra sale de su token.
39
+
40
+ ### `Button loading`
41
+
42
+ - El girador entra delante del texto, que se queda; mide `1em`, como el icono que acompaña al
43
+ texto. Con `iconOnly` sustituye al icono y el botón no cambia de ancho.
44
+ - Inactivo y dicho: `aria-busy="true"` + `aria-disabled="true"`, **sin** `disabled` nativo (no
45
+ pierde el foco); el clic y el envío del formulario se cortan en el manejador. Con `href` quita el
46
+ `href`, como `disabled`. Con `render={<Link />}` corta el clic normal; el `href` del `Link` sigue
47
+ ahí (clic con modificador). No cambia dónde se puede usar `render` (solo desde cliente).
48
+ - Nativo: `loading` entra en `excluded` de la ficha de paridad de `Button` hasta que una app nativa
49
+ lo pida.
50
+
51
+ ### Tokens nuevos
52
+
53
+ - `loading-state.*`: `min-block-size`, `sm-min-block-size`, `gap`, `padding-block`, `padding-inline`,
54
+ `sm-padding-block`, `spinner-color` (en cascada desde `empty-state.*`).
55
+ - `loading-region.*`: `gap`, `text-gap`, `text-last-line-width`, `list-gap`, `list-row-height`,
56
+ `table-gap`, `table-header-height`, `table-row-height`, `grid-gap`, `grid-item-height`.
57
+ - `button.loading-gap`, `button.loading-spinner-size`.
58
+
59
+ ## [49.24.1] — 2026-10-04
60
+
61
+ > **Patch.** `AppShell` y `ChatShell` ya no rompen la hidratación por debajo del punto de ruptura de
62
+ > escritorio (encargo de Homenize: Next.js 16 con SSR, a 1000px de ancho, React 19 daba «Hydration
63
+ > failed because the server rendered HTML didn't match the client» en cada carga). Sin cambios de API.
64
+
65
+ ### El ancho se lee sin desajustar el HTML del servidor
66
+
67
+ - El servidor no conoce el ancho y pinta la versión de escritorio; el primer render del cliente leía
68
+ `matchMedia` en el estado inicial y, por debajo de `breakpoint.lg`, pintaba el cajón: otro
69
+ `aria-label`/`aria-expanded` en el botón de menú, otra clase, `data-state`, `inert` y `tabIndex` en
70
+ el `aside` de la `Sidebar`, y el asa de redimensión de menos.
71
+ - Nuevo hook interno `useMediaQuery` (`src/stories/constants/media-query.ts`) sobre
72
+ `useSyncExternalStore`, con una instantánea de servidor fija (`null`, «todavía no se sabe»): servidor
73
+ y render de hidratación pintan lo mismo, y React vuelve a pintar con el valor real nada más hidratar.
74
+ Un render solo de cliente lee el valor real desde el principio.
75
+ - `AppShell` y `ChatShell` publican la forma que han tomado en **`data-layout`** (`column`/`drawer`);
76
+ sin el atributo, aún no la saben. Por debajo de lg, su hoja esconde la columna mientras falte el
77
+ atributo —y el `AppShell` deja el glifo de menú en «menú»—, así que el HTML del servidor ya se ve
78
+ como el cajón cerrado: ni parpadeo ni salto de maquetación al hidratar.
79
+ - Una vez montado, todo igual: en escritorio la barra abierta y redimensionable; en móvil el cajón
80
+ cerrado, con `inert`, foco, `aria-expanded` y `aria-controls`.
81
+ - Revisados sin cambios: `SiteHeader` (el panel lo resuelve el CSS), `OrgSwitcher`, `Carousel` y
82
+ `ConversationThread` (solo leen `prefers-reduced-motion` dentro de efectos) y `Chart` (mide con
83
+ `ResizeObserver` tras montar). Las versiones nativas no hidratan y no tienen `AppShell`.
84
+ - Test nuevo (`AppShell.hydration.test.tsx`): `renderToString` + `hydrateRoot` del `AppShell` con
85
+ `AppHeader` y `Sidebar`, y del `ChatShell`, a 1000px y a 1280px, sin ningún `console.error` ni
86
+ error recuperable.
87
+
10
88
  ## [49.24.0] — 2026-10-04
11
89
 
12
90
  > **Minor.** Disparador propio y ancla sin disparador en `ColorPicker`, a petición de creator (la barra
@@ -0,0 +1,12 @@
1
+ import { useCallback as e, useSyncExternalStore as t } from "react";
2
+ //#region src/stories/constants/media-query.ts
3
+ var n = () => null;
4
+ function r(r) {
5
+ return t(e((e) => {
6
+ if (typeof window.matchMedia != "function") return () => {};
7
+ let t = window.matchMedia(r);
8
+ return t.addEventListener("change", e), () => t.removeEventListener("change", e);
9
+ }, [r]), () => typeof window.matchMedia == "function" ? window.matchMedia(r).matches : null, n);
10
+ }
11
+ //#endregion
12
+ export { r as t };
@@ -407,6 +407,8 @@ var e = {
407
407
  "--button-height": "2.5rem",
408
408
  "--button-sm-height": "2rem",
409
409
  "--button-lg-height": "3rem",
410
+ "--button-loading-gap": "0.5rem",
411
+ "--button-loading-spinner-size": "1em",
410
412
  "--button-padding-inline": "2rem",
411
413
  "--button-focus-ring-width": "2px",
412
414
  "--button-focus-ring-style": "solid",
@@ -1103,6 +1105,23 @@ var e = {
1103
1105
  "--link-accent-1-hover-color": "#baabff",
1104
1106
  "--link-accent-1-underline-width": "1px",
1105
1107
  "--link-accent-1-hover-underline-width": "0px",
1108
+ "--loading-region-gap": "1.5rem",
1109
+ "--loading-region-text-gap": "0.5rem",
1110
+ "--loading-region-text-last-line-width": "60%",
1111
+ "--loading-region-list-gap": "0.5rem",
1112
+ "--loading-region-list-row-height": "2.5rem",
1113
+ "--loading-region-table-gap": "0.5rem",
1114
+ "--loading-region-table-header-height": "2.5rem",
1115
+ "--loading-region-table-row-height": "2rem",
1116
+ "--loading-region-grid-gap": "1rem",
1117
+ "--loading-region-grid-item-height": "6rem",
1118
+ "--loading-state-min-block-size": "16rem",
1119
+ "--loading-state-sm-min-block-size": "8rem",
1120
+ "--loading-state-gap": "1rem",
1121
+ "--loading-state-padding-block": "3rem",
1122
+ "--loading-state-padding-inline": "1.5rem",
1123
+ "--loading-state-sm-padding-block": "1.5rem",
1124
+ "--loading-state-spinner-color": "#6b6b6b",
1106
1125
  "--logo-height-sm": "2rem",
1107
1126
  "--logo-height-md": "2.5rem",
1108
1127
  "--logo-height-lg": "3rem",
@@ -60,6 +60,20 @@ export interface ButtonBaseProps extends Omit<React.ComponentPropsWithoutRef<'bu
60
60
  */
61
61
  type?: 'button' | 'submit' | 'reset';
62
62
  onClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>;
63
+ /**
64
+ * La acción está en marcha: el girador entra delante del texto (que se queda
65
+ * a la vista) y el botón queda inactivo y lo dice — `aria-busy="true"` y
66
+ * `aria-disabled="true"`. No usa `disabled` nativo a propósito: un botón que
67
+ * se deshabilita justo después del clic pierde el foco, y el lector de
68
+ * pantalla con él. El clic (y el envío del formulario, si es `submit`) se
69
+ * corta en el manejador.
70
+ *
71
+ * Con texto, el botón crece lo que ocupan el girador y su aire; con
72
+ * `iconOnly` el girador **sustituye** al icono y el botón no cambia de ancho.
73
+ * El resultado de la acción no lo anuncia el botón: lo anuncia quien lo
74
+ * enseña (el `Toast`, el error del campo).
75
+ */
76
+ loading?: boolean;
63
77
  /** Renders as <a> when provided */
64
78
  href?: string;
65
79
  /** Adds target="_blank" rel="noopener noreferrer" (solo con href) */
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Lee una media query **sin romper la hidratación**.
3
+ *
4
+ * El servidor no conoce el ancho de la ventana. Si el primer render del
5
+ * cliente leyera `matchMedia` (en el `useState` inicial, por ejemplo), pintaría
6
+ * otro marcado que el servidor y React 19 tiraría la hidratación con «server
7
+ * rendered HTML didn't match the client». Con `useSyncExternalStore` el
8
+ * servidor y el render de hidratación leen la misma instantánea fija —`null`,
9
+ * «todavía no se sabe»— y React vuelve a pintar con el valor real nada más
10
+ * hidratar. Un render solo de cliente (sin SSR) lee el valor real desde el
11
+ * primer momento.
12
+ *
13
+ * Mientras valga `null`, el componente pinta su forma de partida y es su CSS,
14
+ * con la misma media query, quien la corrige a la vista: así no hay parpadeo
15
+ * entre el HTML del servidor y el render hidratado.
16
+ *
17
+ * Sin `matchMedia` en el entorno, `null` siempre.
18
+ */
19
+ export declare function useMediaQuery(query: string): boolean | null;
@@ -0,0 +1,53 @@
1
+ import './LoadingRegion.css';
2
+ export interface LoadingRegionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'role'> {
3
+ /**
4
+ * Lo que se espera, con puntos suspensivos («Cargando bancos…»). Se anuncia,
5
+ * no se ve. **Sin default**: sin él, sale de `spinner.label` del
6
+ * `BrandMessagesProvider`. No se lee cuando `announce` es `false`.
7
+ */
8
+ label?: string;
9
+ /**
10
+ * `false` cuando la espera ya la anuncia una región viva que existe antes y
11
+ * después de cargar: la región queda solo a la vista (`aria-hidden`), para
12
+ * no anunciarla dos veces.
13
+ */
14
+ announce?: boolean;
15
+ /** Los esqueletos: `SkeletonText`, `SkeletonList`, `SkeletonTable`, `SkeletonGrid` o `Skeleton` sueltos. */
16
+ children: React.ReactNode;
17
+ }
18
+ /**
19
+ * La espera de un contenido cuya forma se conoce: reserva su sitio con
20
+ * esqueletos y la página no salta al llegar. El `Skeleton` es decorativo; quien
21
+ * anuncia es esta región, con `role="status"`, `aria-busy` y el texto de la
22
+ * espera oculto a la vista. Apila sus hijos con aire entre bloques.
23
+ */
24
+ export declare function LoadingRegion({ label, announce, children, className, ...rest }: LoadingRegionProps): import("react/jsx-runtime").JSX.Element;
25
+ export interface SkeletonTextProps {
26
+ /** Líneas del párrafo; la última sale más corta. */
27
+ lines?: number;
28
+ }
29
+ /** Un bloque de texto —un párrafo, una lista de definiciones— que aún no llegó. Decorativo. */
30
+ export declare function SkeletonText({ lines }: SkeletonTextProps): import("react/jsx-runtime").JSX.Element;
31
+ export interface SkeletonListProps {
32
+ /** Filas de la lista. */
33
+ rows?: number;
34
+ }
35
+ /** Una lista que aún no llegó: una barra por fila, del alto de un control. Decorativo. */
36
+ export declare function SkeletonList({ rows }: SkeletonListProps): import("react/jsx-runtime").JSX.Element;
37
+ export interface SkeletonTableProps {
38
+ /** Filas de cuerpo, sin contar la cabecera. */
39
+ rows?: number;
40
+ }
41
+ /**
42
+ * Una tabla que aún no llegó: la cabecera y sus filas. No calca el diseño —no
43
+ * hace falta—, solo ocupa un sitio parecido. Decorativo.
44
+ */
45
+ export declare function SkeletonTable({ rows }: SkeletonTableProps): import("react/jsx-runtime").JSX.Element;
46
+ export interface SkeletonGridProps {
47
+ /** Columnas de la rejilla. */
48
+ columns?: 2 | 3 | 4;
49
+ /** Filas de la rejilla: se pintan `columns × rows` celdas. */
50
+ rows?: number;
51
+ }
52
+ /** Una rejilla de tarjetas o miniaturas que aún no llegó. Decorativo. */
53
+ export declare function SkeletonGrid({ columns, rows }: SkeletonGridProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,39 @@
1
+ import type { EmptyStateAction } from '../EmptyState/EmptyState';
2
+ import './LoadingState.css';
3
+ export interface LoadingStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'role' | 'children'> {
4
+ /**
5
+ * Lo que se espera, con puntos suspensivos («Cargando revisión…»: ver
6
+ * Foundations → Redacción). **No se ve**: es el nombre accesible de la
7
+ * espera y lo que anuncian los lectores de pantalla. **Sin default**: sin
8
+ * él, sale de `spinner.label` del `BrandMessagesProvider` — el mismo texto
9
+ * que el `Spinner`.
10
+ */
11
+ label?: string;
12
+ /**
13
+ * `md` para una página o una zona; `sm` para el cuerpo de un diálogo, una
14
+ * hoja, un popover o una barra lateral.
15
+ */
16
+ size?: 'sm' | 'md';
17
+ /**
18
+ * A página completa: la caja toma el alto visible bajo la cabecera del
19
+ * `AppShell` (la ventana menos el cromo del armazón y el relleno del
20
+ * contenido) y centra el girador en él, sin provocar desplazamiento. Para el
21
+ * `loading.tsx` de una ruta cuya forma no se conoce. Sin `fill`, la caja ya
22
+ * ocupa el alto de una zona que lo tenga definido y, si no, reserva el
23
+ * mínimo de su talla.
24
+ */
25
+ fill?: boolean;
26
+ /** Una salida mientras se espera (cancelar un proceso largo). */
27
+ action?: EmptyStateAction;
28
+ }
29
+ /**
30
+ * La espera de un bloque cuya forma no se conoce: un girador centrado en una
31
+ * caja que **reserva alto** y, si hace falta, una salida. A la vista solo está
32
+ * el girador; el texto de la espera es solo para los lectores de pantalla. Cuando la forma sí se conoce (tabla, lista, ficha), la respuesta es
33
+ * `LoadingRegion` con esqueletos.
34
+ *
35
+ * Anuncia: la caja es `role="status"` con `aria-busy`, y su nombre es el texto
36
+ * de la espera, oculto a la vista. El girador va decorativo, para no
37
+ * anunciarla dos veces.
38
+ */
39
+ export declare function LoadingState({ label, size, fill, action, className, ...rest }: LoadingStateProps): import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- .app-shell{--app-shell-sidebar-width:var(--sidebar-width);--app-shell-banner-height:0px;--app-shell-chrome-block-start:calc(var(--app-shell-banner-height) + var(--app-header-height) + env(safe-area-inset-top));flex-direction:column;block-size:100dvh;display:flex;overflow:clip}.app-shell__banner{flex-shrink:0}.app-shell__body{flex:1;min-block-size:0;display:flex;position:relative}.app-shell__content{min-inline-size:0;padding-inline:var(--section-padding-inline);padding-block:var(--section-padding-block-lg);flex:1;padding-block-end:max(var(--section-padding-block-lg), env(safe-area-inset-bottom));padding-inline-end:max(var(--section-padding-inline), env(safe-area-inset-right));overflow:auto}.app-shell__content.app-shell__content--flush{padding-block:0;padding-inline:0;padding-block-end:env(safe-area-inset-bottom);padding-inline-end:env(safe-area-inset-right)}.app-shell__backdrop{inset:0;background-color:var(--sidebar-backdrop-bg);opacity:var(--sidebar-backdrop-opacity);z-index:var(--sidebar-backdrop-z-index);position:fixed;inset-block-start:var(--app-shell-chrome-block-start)}.app-shell:has(.app-header__logo) .sidebar__header{display:none}
1
+ .app-shell{--app-shell-sidebar-width:var(--sidebar-width);--app-shell-banner-height:0px;--app-shell-chrome-block-start:calc(var(--app-shell-banner-height) + var(--app-header-height) + env(safe-area-inset-top));flex-direction:column;block-size:100dvh;display:flex;overflow:clip}.app-shell__banner{flex-shrink:0}.app-shell__body{flex:1;min-block-size:0;display:flex;position:relative}.app-shell__content{min-inline-size:0;padding-inline:var(--section-padding-inline);padding-block:var(--section-padding-block-lg);flex:1;padding-block-end:max(var(--section-padding-block-lg), env(safe-area-inset-bottom));padding-inline-end:max(var(--section-padding-inline), env(safe-area-inset-right));overflow:auto}.app-shell__content.app-shell__content--flush{padding-block:0;padding-inline:0;padding-block-end:env(safe-area-inset-bottom);padding-inline-end:env(safe-area-inset-right)}.app-shell__backdrop{inset:0;background-color:var(--sidebar-backdrop-bg);opacity:var(--sidebar-backdrop-opacity);z-index:var(--sidebar-backdrop-z-index);position:fixed;inset-block-start:var(--app-shell-chrome-block-start)}.app-shell:has(.app-header__logo) .sidebar__header{display:none}@media (width<=1023.98px){.app-shell:not([data-layout]) .sidebar{display:none}.app-shell:not([data-layout]) .app-header .menu-button .icon__line{opacity:1;transform:none}}
package/dist/app-shell.js CHANGED
@@ -5,94 +5,88 @@ import { SkipLink as t } from "./skip-link.js";
5
5
  import { t as n } from "./_shared/css-properties.js";
6
6
  import { TooltipProvider as r } from "./tooltip.js";
7
7
  import { n as i, t as a } from "./_shared/appshellcontext.js";
8
- import { useCallback as o, useEffect as s, useMemo as c, useState as l } from "react";
9
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
8
+ import { t as o } from "./_shared/media-query.js";
9
+ import { useCallback as s, useEffect as c, useMemo as l, useState as u } from "react";
10
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
10
11
  //#region src/stories/sections/AppShell/AppShell.tsx
11
- var f = "(min-width: 1024px)";
12
- function p() {
13
- let [e, t] = l(() => typeof window > "u" || typeof window.matchMedia != "function" ? !0 : window.matchMedia(f).matches);
14
- return s(() => {
15
- if (typeof window.matchMedia != "function") return;
16
- let e = window.matchMedia(f), n = () => t(e.matches);
17
- return n(), e.addEventListener("change", n), () => e.removeEventListener("change", n);
18
- }, []), e;
19
- }
20
- function m({ banner: i, header: f, sidebar: m, children: h, contentFlush: g = !1, defaultSidebar: _ = "open", sidebarState: v, onSidebarChange: y, defaultSidebarWidth: b, onSidebarWidthChange: x, skipLabel: S }) {
21
- let C = e("appShell"), w = p(), [T, E] = l(_), [D, O] = l(!1), [k, A] = l(b), j = w ? v ?? T : D ? "open" : "closed", M = o((e) => {
22
- w ? (E(e), y?.(e)) : O(e === "open");
23
- }, [w, y]), N = o(() => M(j === "open" ? "closed" : "open"), [M, j]), P = o(() => M("closed"), [M]), F = o((e) => {
24
- A(e), x?.(e);
25
- }, [x]);
26
- s(() => {
27
- if (w || !D) return;
12
+ var p = "(min-width: 1024px)";
13
+ function m({ banner: i, header: m, sidebar: h, children: g, contentFlush: _ = !1, defaultSidebar: v = "open", sidebarState: y, onSidebarChange: b, defaultSidebarWidth: x, onSidebarWidthChange: S, skipLabel: C }) {
14
+ let w = e("appShell"), T = o(p), E = T ?? !0, [D, O] = u(v), [k, A] = u(!1), [j, M] = u(x), N = E ? y ?? D : k ? "open" : "closed", P = s((e) => {
15
+ E ? (O(e), b?.(e)) : A(e === "open");
16
+ }, [E, b]), F = s(() => P(N === "open" ? "closed" : "open"), [P, N]), I = s(() => P("closed"), [P]), L = s((e) => {
17
+ M(e), S?.(e);
18
+ }, [S]);
19
+ c(() => {
20
+ if (E || !k) return;
28
21
  let e = (e) => {
29
- e.key === "Escape" && O(!1);
22
+ e.key === "Escape" && A(!1);
30
23
  };
31
24
  document.addEventListener("keydown", e);
32
25
  let t = document.body.style.overflow;
33
26
  return document.body.style.overflow = "hidden", () => {
34
27
  document.removeEventListener("keydown", e), document.body.style.overflow = t;
35
28
  };
36
- }, [w, D]);
37
- let I = c(() => ({
38
- sidebar: j,
39
- setSidebar: M,
40
- sidebarWidth: k ?? 0,
41
- setSidebarWidth: F,
42
- toggleSidebar: N,
43
- closeSidebar: P,
44
- isDesktop: w
29
+ }, [E, k]);
30
+ let R = l(() => ({
31
+ sidebar: N,
32
+ setSidebar: P,
33
+ sidebarWidth: j ?? 0,
34
+ setSidebarWidth: L,
35
+ toggleSidebar: F,
36
+ closeSidebar: I,
37
+ isDesktop: E
45
38
  }), [
46
- j,
47
- M,
48
- k,
49
- F,
50
39
  N,
51
40
  P,
52
- w
53
- ]), [L, R] = l(0), z = o((e) => {
41
+ j,
42
+ L,
43
+ F,
44
+ I,
45
+ E
46
+ ]), [z, B] = u(0), V = s((e) => {
54
47
  if (!e) return;
55
- let t = () => R(e.getBoundingClientRect().height);
48
+ let t = () => B(e.getBoundingClientRect().height);
56
49
  if (t(), typeof ResizeObserver > "u") return;
57
50
  let n = new ResizeObserver(t);
58
51
  return n.observe(e), () => {
59
- n.disconnect(), R(0);
52
+ n.disconnect(), B(0);
60
53
  };
61
- }, []), B = n({
62
- "--app-shell-sidebar-width": k ? `${k}px` : void 0,
63
- "--app-shell-banner-height": i ? `${L}px` : void 0
64
- }), V = !w && D;
65
- return /* @__PURE__ */ u(a.Provider, {
66
- value: I,
67
- children: /* @__PURE__ */ d(r, { children: [/* @__PURE__ */ u(t, {
54
+ }, []), H = n({
55
+ "--app-shell-sidebar-width": j ? `${j}px` : void 0,
56
+ "--app-shell-banner-height": i ? `${z}px` : void 0
57
+ }), U = !E && k;
58
+ return /* @__PURE__ */ d(a.Provider, {
59
+ value: R,
60
+ children: /* @__PURE__ */ f(r, { children: [/* @__PURE__ */ d(t, {
68
61
  href: "#main-content",
69
- children: C("skipToContent", S)
70
- }), /* @__PURE__ */ d("div", {
71
- ref: B,
62
+ children: w("skipToContent", C)
63
+ }), /* @__PURE__ */ f("div", {
64
+ ref: H,
72
65
  className: "app-shell",
73
- "data-sidebar": j,
66
+ "data-sidebar": N,
67
+ "data-layout": T === null ? void 0 : E ? "column" : "drawer",
74
68
  children: [
75
- i && /* @__PURE__ */ u("div", {
76
- ref: z,
69
+ i && /* @__PURE__ */ d("div", {
70
+ ref: V,
77
71
  className: "app-shell__banner",
78
72
  children: i
79
73
  }),
80
- f,
81
- /* @__PURE__ */ d("div", {
74
+ m,
75
+ /* @__PURE__ */ f("div", {
82
76
  className: "app-shell__body",
83
77
  children: [
84
- m,
85
- V && /* @__PURE__ */ u("div", {
78
+ h,
79
+ U && /* @__PURE__ */ d("div", {
86
80
  className: "app-shell__backdrop",
87
- onClick: P,
81
+ onClick: I,
88
82
  "aria-hidden": "true"
89
83
  }),
90
- /* @__PURE__ */ u("main", {
84
+ /* @__PURE__ */ d("main", {
91
85
  id: "main-content",
92
86
  tabIndex: -1,
93
- className: g ? "app-shell__content app-shell__content--flush" : "app-shell__content",
94
- inert: V || void 0,
95
- children: h
87
+ className: _ ? "app-shell__content app-shell__content--flush" : "app-shell__content",
88
+ inert: U || void 0,
89
+ children: g
96
90
  })
97
91
  ]
98
92
  })