@studiolxd/brand 49.15.1 → 49.17.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
@@ -7,6 +7,66 @@ 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.17.0] — 2026-10-03
11
+
12
+ > **Minor.** Componente nuevo: `EmbedFrame`, el marco embebido del sistema, y las tres
13
+ > props de alto que lo colocan sin CSS de consumidor.
14
+
15
+ ### `EmbedFrame` — nuevo
16
+
17
+ - Pinta un `<iframe>` que **llena su contenedor**: todo el ancho y todo el alto, sin
18
+ borde ni fondo propios; el desplazamiento ocurre dentro del documento embebido. En
19
+ una columna flexible con alto toma el que dejan sus hermanos.
20
+ - `title` **obligatorio** (nombre accesible del marco), `src`, `ref` al `<iframe>`
21
+ (para `contentWindow` y `postMessage`) y los atributos nativos (`allow`, `sandbox`,
22
+ `loading`, `referrerPolicy`, `name`, `onLoad`, `srcDoc`…). No acepta `width`,
23
+ `height` ni `frameBorder`.
24
+ - `fill?: 'container' | 'viewport'` (por defecto `container`): con `viewport` mide el
25
+ alto de la ventana, para cuando no hay contenedor con alto.
26
+ - Sin tokens propios. Import: `@studiolxd/brand/embed-frame` (también en el barril).
27
+
28
+ ### Props nuevas para ocupar el alto
29
+
30
+ - **`Stack fill`**: la pila ocupa el alto de su contenedor (`block-size: 100%`), y lo que
31
+ crece dentro se queda con lo que dejan las demás piezas.
32
+ - **`ScrollArea fill`**: el área ocupa el alto que dejan sus hermanos en una columna con
33
+ alto. Solo desde `md`; por debajo mide su contenido.
34
+ - **`AppShell contentFlush`**: el contenido principal a sangre, sin relleno de sección
35
+ (respeta la zona segura del dispositivo). Story nueva «Contenido a sangre».
36
+
37
+ ## [49.16.0] — 2026-10-02
38
+
39
+ > **Minor.** Los iconos, en una sola familia de tamaño y trazo: todo glifo cabe en el
40
+ > área útil de 18 (3–21 del lienzo de 24), con caps y uniones redondeadas y marcos de
41
+ > radio 2. Lo que más se nota: el chevron.
42
+
43
+ ### `Icon` — lo que cambia
44
+
45
+ - **Chevron al máximo del área**: `chevron`/`chevron-right` 9×18, `chevron-down`/`up` la
46
+ misma punta girada (18×9), caps `round`. Antes iba de borde a borde (12×24). `chevron`
47
+ pasa a ser alias de `chevron-right` (mismo objeto; ningún nombre público cambia).
48
+ `chevrons-up-down`: dos chevrons de 14×7.
49
+ - **Tamaños a la norma de 18** (regla óptica: cuadrados llenos a 17): bajan `folder` /
50
+ `folder-open` (siguen siendo la misma caja entre sí), `graduation-cap`, `eye` /
51
+ `eye-off`; suben `plus` / `minus`, `check`, `upload` / `download` / `send`, `retry`,
52
+ `play` / `pause` / `stop`; `dashboard`, `layout-kanban`, `layout-sidebar` a 17.
53
+ - `connection` vuelve dentro del lienzo; `inbox` redibujado como bandeja.
54
+ - Uniones y esquinas: `play` y `stop` redondeados como `pause`; radio 2 en `building`,
55
+ `calendar` y los ejes de `chart-bar`.
56
+ - `arrow`, `arrow-left`, `close` y `menu` no cambian (glifos de marca / `MENU_GLYPH`).
57
+
58
+ ### Componentes con chevron
59
+
60
+ Talla del chevron revisada por componente: Pagination, Calendar, CalendarPlanner,
61
+ InputPhone, el orden de Table y la talla `sm` de Select/MultiSelect suben de `xs` a `sm`
62
+ (token `select.sm-icon-size`, `input-phone.country-icon-size`). **Token nuevo**
63
+ `input-phone.sm-country-icon-size` para la caja estrecha del prefijo en `sm`.
64
+
65
+ ### Foundations
66
+
67
+ Iconografía gana la sección y la story «Área útil» (el catálogo sobre la retícula de 18),
68
+ y un test de navegador que falla si un glifo se sale del área (excepciones comentadas).
69
+
10
70
  ## [49.15.1] — 2026-10-02
11
71
 
12
72
  > **Patch.** Dos arreglos vistos en el árbol de contenidos de creator.
@@ -966,10 +966,11 @@ var e = {
966
966
  "--input-phone-content-z-index": "400",
967
967
  "--input-phone-country-gap": "0.25rem",
968
968
  "--input-phone-content-max-height": "17.5rem",
969
- "--input-phone-country-icon-size": "0.625rem",
969
+ "--input-phone-country-icon-size": "16px",
970
970
  "--input-phone-country-icon-transition-easing": "ease",
971
971
  "--input-phone-country-item-padding-block": "0.5rem",
972
972
  "--input-phone-sm-country-width": "3.5rem",
973
+ "--input-phone-sm-country-icon-size": "0.625rem",
973
974
  "--input-phone-lg-country-width": "5.5rem",
974
975
  "--input-phone-country-item-highlighted-bg": "#111e30",
975
976
  "--input-phone-country-item-highlighted-color": "#ffffff",
@@ -1210,7 +1211,7 @@ var e = {
1210
1211
  "--multi-select-icon-transition-easing": "ease",
1211
1212
  "--multi-select-selection-gap": "0.25rem",
1212
1213
  "--multi-select-icon-size": "16px",
1213
- "--multi-select-sm-icon-size": "8px",
1214
+ "--multi-select-sm-icon-size": "16px",
1214
1215
  "--multi-select-lg-icon-size": "24px",
1215
1216
  "--multi-select-sm-font-size": "0.875rem",
1216
1217
  "--multi-select-sm-padding-inline": "0.5rem",
@@ -1486,7 +1487,7 @@ var e = {
1486
1487
  "--select-item-highlighted-bg": "#111e30",
1487
1488
  "--select-item-highlighted-color": "#ffffff",
1488
1489
  "--select-icon-size": "16px",
1489
- "--select-sm-icon-size": "8px",
1490
+ "--select-sm-icon-size": "16px",
1490
1491
  "--select-lg-icon-size": "24px",
1491
1492
  "--select-sm-font-size": "0.875rem",
1492
1493
  "--select-sm-padding-inline": "0.5rem",
@@ -0,0 +1,33 @@
1
+ import './EmbedFrame.css';
2
+ export interface EmbedFrameProps extends Omit<React.ComponentPropsWithoutRef<'iframe'>, 'title' | 'width' | 'height' | 'frameBorder'> {
3
+ /**
4
+ * Nombre accesible del marco: lo que anuncia el lector de pantalla al entrar
5
+ * en él. Obligatorio — un `<iframe>` sin título es un hueco sin nombre.
6
+ */
7
+ title: string;
8
+ /** Dirección del documento que se pinta dentro. */
9
+ src?: string;
10
+ /**
11
+ * Qué alto llena. `container` (por defecto): el de la caja que le dan.
12
+ * `viewport`: el de la ventana, para cuando el marco no tiene un contenedor
13
+ * con alto —solo en la página, o como celda de `Columns`—.
14
+ */
15
+ fill?: 'container' | 'viewport';
16
+ /** Se añade DESPUÉS de las clases propias. */
17
+ className?: string;
18
+ }
19
+ /**
20
+ * Un documento dentro de la página: el `<iframe>` del sistema. Ocupa todo el
21
+ * ancho y todo el alto de su contenedor, sin borde ni fondo propios, y el
22
+ * desplazamiento ocurre dentro del documento embebido, no en la página.
23
+ *
24
+ * **El alto lo pone el contenedor**: el marco llena la caja que le dan. Dentro
25
+ * de `AppShell` esa caja es el contenido principal, que ya mide el resto de la
26
+ * ventana bajo la cabecera y junto a la barra lateral. Sin contenedor con alto,
27
+ * `fill="viewport"` le da el de la ventana.
28
+ *
29
+ * `ref` apunta al `<iframe>` —de ahí sale `contentWindow` para `postMessage`—
30
+ * y `{...rest}` (`allow`, `sandbox`, `loading`, `referrerPolicy`, `name`,
31
+ * `onLoad`, `aria-*`, `data-*`…) se reenvía tal cual.
32
+ */
33
+ export declare const EmbedFrame: import("react").ForwardRefExoticComponent<EmbedFrameProps & import("react").RefAttributes<HTMLIFrameElement>>;
@@ -24,8 +24,8 @@ declare const ICONS: {
24
24
  readonly render: () => import("react/jsx-runtime").JSX.Element;
25
25
  };
26
26
  readonly chevron: {
27
- readonly viewBox: "0 0 24 24";
28
- readonly render: () => import("react/jsx-runtime").JSX.Element;
27
+ viewBox: string;
28
+ render: () => import("react/jsx-runtime").JSX.Element;
29
29
  };
30
30
  readonly close: {
31
31
  readonly viewBox: "0 0 24 24";
@@ -248,8 +248,8 @@ declare const ICONS: {
248
248
  readonly render: () => import("react/jsx-runtime").JSX.Element;
249
249
  };
250
250
  readonly 'chevron-right': {
251
- readonly viewBox: "0 0 24 24";
252
- readonly render: () => import("react/jsx-runtime").JSX.Element;
251
+ viewBox: string;
252
+ render: () => import("react/jsx-runtime").JSX.Element;
253
253
  };
254
254
  readonly 'chevron-down': {
255
255
  readonly viewBox: "0 0 24 24";
@@ -12,6 +12,13 @@ export interface ScrollAreaProps extends Omit<React.ComponentPropsWithoutRef<typ
12
12
  * que decir de qué es; sin él el lector solo anuncia «región desplazable».
13
13
  */
14
14
  label?: string;
15
+ /**
16
+ * Ocupa el alto que dejan sus hermanos en una columna con alto (`Stack fill`,
17
+ * la celda estirada de `Columns`), en vez de medirlo quien lo usa. Desde
18
+ * `md`; por debajo mide su contenido, porque ahí la columna ya no tiene alto
19
+ * propio (en móvil `Columns` apila).
20
+ */
21
+ fill?: boolean;
15
22
  children: ReactNode;
16
23
  /** Se añade DESPUÉS de las clases propias. */
17
24
  className?: string;
@@ -21,8 +28,8 @@ export interface ScrollAreaProps extends Omit<React.ComponentPropsWithoutRef<typ
21
28
  * Area): la lista de un panel lateral, el resultado largo de una consulta, el
22
29
  * lienzo de un editor.
23
30
  *
24
- * **El alto lo pone quien lo usa** (`className` o el contenedor): el componente
25
- * no decide cuánto mide el hueco, solo cómo se desplaza dentro.
31
+ * **El alto lo pone quien lo usa** (`className`, el contenedor o `fill`): el
32
+ * componente no decide cuánto mide el hueco, solo cómo se desplaza dentro.
26
33
  *
27
34
  * Base UI hace la zona alcanzable con el teclado cuando de verdad hay
28
35
  * desplazamiento, y la barra aparece al usarla.
@@ -23,6 +23,12 @@ export interface StackProps extends React.ComponentPropsWithoutRef<'div'> {
23
23
  * (un enlace de vuelta sobre la cabecera).
24
24
  */
25
25
  mobileOrder?: 'normal' | 'reverse';
26
+ /**
27
+ * Ocupa el alto de su contenedor. Con él, la pila es una columna con alto y
28
+ * una pieza que crece (`EmbedFrame`, `ScrollArea fill`) se queda con lo que
29
+ * dejan las demás. Para la raíz del contenido de `AppShell`.
30
+ */
31
+ fill?: boolean;
26
32
  children: ReactNode;
27
33
  /** Se añade DESPUÉS de las clases propias. */
28
34
  className?: string;
@@ -36,4 +42,4 @@ export interface StackProps extends React.ComponentPropsWithoutRef<'div'> {
36
42
  * pila puede ser el grupo con nombre accesible que pide un formulario sin
37
43
  * tener que envolverla en un elemento nativo aparte.
38
44
  */
39
- export declare function Stack({ gap, align, mobileOrder, children, className, ...rest }: StackProps): import("react/jsx-runtime").JSX.Element;
45
+ export declare function Stack({ gap, align, mobileOrder, fill, children, className, ...rest }: StackProps): import("react/jsx-runtime").JSX.Element;
@@ -20,6 +20,14 @@ export interface AppShellProps {
20
20
  /** La barra lateral: un `Sidebar`. Columna en escritorio, cajón en móvil. */
21
21
  sidebar: ReactNode;
22
22
  children: ReactNode;
23
+ /**
24
+ * Contenido a sangre: sin el relleno de sección del contenido principal, que
25
+ * llega al borde de la cabecera y de la sidebar. Para lo que trae sus propios
26
+ * márgenes —un `EmbedFrame` con el reproductor de un curso, cuyos márgenes
27
+ * son del tema del curso y no del armazón—. Respeta la zona segura del
28
+ * dispositivo.
29
+ */
30
+ contentFlush?: boolean;
23
31
  /** Estado inicial de la sidebar en escritorio (en móvil siempre arranca cerrada). */
24
32
  defaultSidebar?: SidebarState;
25
33
  /** Estado controlado de la sidebar en escritorio. */
@@ -51,4 +59,4 @@ export interface AppShellMessages {
51
59
  * modo y su asa) y `SidebarNav` (rail). Persistir el estado es del producto:
52
60
  * `onSidebarChange` / `onSidebarWidthChange` avisan de cada cambio.
53
61
  */
54
- export declare function AppShell({ banner, header, sidebar, children, defaultSidebar, sidebarState, onSidebarChange, defaultSidebarWidth, onSidebarWidthChange, skipLabel, }: AppShellProps): import("react/jsx-runtime").JSX.Element;
62
+ export declare function AppShell({ banner, header, sidebar, children, contentFlush, defaultSidebar, sidebarState, onSidebarChange, defaultSidebarWidth, onSidebarWidthChange, skipLabel, }: AppShellProps): 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__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)}
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)}
package/dist/app-shell.js CHANGED
@@ -17,63 +17,63 @@ function p() {
17
17
  return n(), e.addEventListener("change", n), () => e.removeEventListener("change", n);
18
18
  }, []), e;
19
19
  }
20
- function m({ banner: i, header: f, sidebar: m, children: h, defaultSidebar: g = "open", sidebarState: _, onSidebarChange: v, defaultSidebarWidth: y, onSidebarWidthChange: b, skipLabel: x }) {
21
- let S = e("appShell"), C = p(), [w, T] = l(g), [E, D] = l(!1), [O, k] = l(y), A = C ? _ ?? w : E ? "open" : "closed", j = o((e) => {
22
- C ? (T(e), v?.(e)) : D(e === "open");
23
- }, [C, v]), M = o(() => j(A === "open" ? "closed" : "open"), [j, A]), N = o(() => j("closed"), [j]), P = o((e) => {
24
- k(e), b?.(e);
25
- }, [b]);
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
26
  s(() => {
27
- if (C || !E) return;
27
+ if (w || !D) return;
28
28
  let e = (e) => {
29
- e.key === "Escape" && D(!1);
29
+ e.key === "Escape" && O(!1);
30
30
  };
31
31
  document.addEventListener("keydown", e);
32
32
  let t = document.body.style.overflow;
33
33
  return document.body.style.overflow = "hidden", () => {
34
34
  document.removeEventListener("keydown", e), document.body.style.overflow = t;
35
35
  };
36
- }, [C, E]);
37
- let F = c(() => ({
38
- sidebar: A,
39
- setSidebar: j,
40
- sidebarWidth: O ?? 0,
41
- setSidebarWidth: P,
42
- toggleSidebar: M,
43
- closeSidebar: N,
44
- isDesktop: C
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
45
45
  }), [
46
- A,
47
46
  j,
48
- O,
49
- P,
50
47
  M,
48
+ k,
49
+ F,
51
50
  N,
52
- C
53
- ]), [I, L] = l(0), R = o((e) => {
51
+ P,
52
+ w
53
+ ]), [L, R] = l(0), z = o((e) => {
54
54
  if (!e) return;
55
- let t = () => L(e.getBoundingClientRect().height);
55
+ let t = () => R(e.getBoundingClientRect().height);
56
56
  if (t(), typeof ResizeObserver > "u") return;
57
57
  let n = new ResizeObserver(t);
58
58
  return n.observe(e), () => {
59
- n.disconnect(), L(0);
59
+ n.disconnect(), R(0);
60
60
  };
61
- }, []), z = n({
62
- "--app-shell-sidebar-width": O ? `${O}px` : void 0,
63
- "--app-shell-banner-height": i ? `${I}px` : void 0
64
- }), B = !C && E;
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
65
  return /* @__PURE__ */ u(a.Provider, {
66
- value: F,
66
+ value: I,
67
67
  children: /* @__PURE__ */ d(r, { children: [/* @__PURE__ */ u(t, {
68
68
  href: "#main-content",
69
- children: S("skipToContent", x)
69
+ children: C("skipToContent", S)
70
70
  }), /* @__PURE__ */ d("div", {
71
- ref: z,
71
+ ref: B,
72
72
  className: "app-shell",
73
- "data-sidebar": A,
73
+ "data-sidebar": j,
74
74
  children: [
75
75
  i && /* @__PURE__ */ u("div", {
76
- ref: R,
76
+ ref: z,
77
77
  className: "app-shell__banner",
78
78
  children: i
79
79
  }),
@@ -82,16 +82,16 @@ function m({ banner: i, header: f, sidebar: m, children: h, defaultSidebar: g =
82
82
  className: "app-shell__body",
83
83
  children: [
84
84
  m,
85
- B && /* @__PURE__ */ u("div", {
85
+ V && /* @__PURE__ */ u("div", {
86
86
  className: "app-shell__backdrop",
87
- onClick: N,
87
+ onClick: P,
88
88
  "aria-hidden": "true"
89
89
  }),
90
90
  /* @__PURE__ */ u("main", {
91
91
  id: "main-content",
92
92
  tabIndex: -1,
93
- className: "app-shell__content",
94
- inert: B || void 0,
93
+ className: g ? "app-shell__content app-shell__content--flush" : "app-shell__content",
94
+ inert: V || void 0,
95
95
  children: h
96
96
  })
97
97
  ]