@axzydev/axzy_ui_system 1.5.0 → 1.5.2

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/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "packageManager": "pnpm@10.34.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "version": "1.5.0",
7
+ "version": "1.5.2",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
10
  "*.css"
@@ -42,7 +42,9 @@
42
42
  "ext:build": "node vscode-extension/esbuild.mjs",
43
43
  "ext:typecheck": "tsc --noEmit -p vscode-extension/tsconfig.json",
44
44
  "ext:package": "node vscode-extension/esbuild.mjs && cd vscode-extension && npx --yes @vscode/vsce package --no-dependencies",
45
- "audit:sandbox": "node scripts/audit-sandbox.mjs"
45
+ "audit:sandbox": "node scripts/audit-sandbox.mjs",
46
+ "docs:llm": "pnpm ext:catalog && node scripts/build-llm-docs.mjs && node scripts/build-ai-bundle.mjs",
47
+ "docs:ai": "node scripts/build-ai-bundle.mjs"
46
48
  },
47
49
  "dependencies": {
48
50
  "clsx": "^2.1.1",
package/src/dev.css CHANGED
@@ -116,19 +116,3 @@ html {
116
116
  animation: none;
117
117
  }
118
118
  }
119
-
120
- /* ──────────────────────────────────────────────
121
- PANEL DE PROPIEDADES A ANCHO COMPLETO (dev.css)
122
- Cuando la vitrina ocupa las 12 columnas, las filas `justify-between` de los
123
- controles dejaban la etiqueta a un lado y el control a 800px del otro. Aquí
124
- el control se agrupa junto a su etiqueta.
125
- ────────────────────────────────────────────── */
126
- .it-panel-ancho .justify-between {
127
- justify-content: flex-start;
128
- gap: 0.75rem;
129
- }
130
-
131
- /* Las notas del panel no se estiran a 100+ caracteres por línea. */
132
- .it-panel-ancho p {
133
- max-width: 72ch;
134
- }
@@ -72,6 +72,11 @@ export const sandboxMessages: ITMessagesByLocale = {
72
72
  "showcase.layout.docs": "Documentación",
73
73
  "showcase.layout.eyebrow": "Vitrina de componentes",
74
74
  "showcase.layout.gallery": "Variaciones y estados (Galería)",
75
+ "showcase.layout.galleryTitle": "Variaciones y estados",
76
+ "showcase.layout.viewCode": "Código",
77
+ "showcase.layout.hideCode": "Ocultar",
78
+ "showcase.layout.controls": "Controles",
79
+ "showcase.layout.api": "API del componente",
75
80
  "showcase.layout.jsxCode": "Código JSX",
76
81
  "showcase.layout.notes": "Notas",
77
82
  "showcase.layout.overview": "Descripción general",
@@ -1013,6 +1018,11 @@ export const sandboxMessages: ITMessagesByLocale = {
1013
1018
  "showcase.layout.docs": "Docs",
1014
1019
  "showcase.layout.eyebrow": "Component showcase",
1015
1020
  "showcase.layout.gallery": "Variations & States (Gallery)",
1021
+ "showcase.layout.galleryTitle": "Variations & states",
1022
+ "showcase.layout.viewCode": "Code",
1023
+ "showcase.layout.hideCode": "Hide",
1024
+ "showcase.layout.controls": "Controls",
1025
+ "showcase.layout.api": "Component API",
1016
1026
  "showcase.layout.jsxCode": "JSX Code",
1017
1027
  "showcase.layout.notes": "Notes",
1018
1028
  "showcase.layout.overview": "Overview",
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { useState, type ReactNode } from "react";
16
- import { FaCheck, FaCopy } from "react-icons/fa";
16
+ import { FaCheck, FaCode, FaCopy } from "react-icons/fa";
17
17
  import { useITTranslation } from "@/i18n";
18
18
 
19
19
  /** Eyebrow mono apagado: encabezados de sección y etiquetas de metadato. */
@@ -367,6 +367,7 @@ export const BloqueCodigo = ({
367
367
  />
368
368
  </div>
369
369
  <pre
370
+ tabIndex={0}
370
371
  className={`overflow-x-auto whitespace-pre-wrap p-4 font-mono text-xs leading-relaxed text-slate-200 ${
371
372
  compact ? "max-h-[420px] overflow-y-auto" : ""
372
373
  }`}
@@ -375,3 +376,99 @@ export const BloqueCodigo = ({
375
376
  </pre>
376
377
  </div>
377
378
  );
379
+
380
+ /** Props de `EjemploContenedor`. */
381
+ export interface EjemploContenedorProps {
382
+ /** Número del ejemplo (`01`, `02`…). Opcional. */
383
+ numero?: string;
384
+ /** Título del ejemplo (mono, mayúsculas). */
385
+ titulo: string;
386
+ /** El demo. */
387
+ children: ReactNode;
388
+ /** Código fuente del ejemplo: si se pasa, aparece el botón «Código». */
389
+ codigo?: string;
390
+ /** Nota al pie, bajo el demo. */
391
+ nota?: string;
392
+ /** Superficie del demo sin padding (layouts completos). */
393
+ sangrado?: boolean;
394
+ /** Clases extra de la superficie del demo. */
395
+ className?: string;
396
+ }
397
+
398
+ /**
399
+ * Contenedor de un ejemplo del sandbox: cabecera con número, título y la acción
400
+ * de ver el código (siempre visible), superficie del demo y nota opcional. El
401
+ * código se despliega **dentro** del mismo contenedor, no en una pestaña aparte.
402
+ *
403
+ * @example
404
+ * ```tsx
405
+ * <EjemploContenedor numero="01" titulo="Con icono" codigo={code}>
406
+ * <ITButton icon={<FaSave />}>Guardar</ITButton>
407
+ * </EjemploContenedor>
408
+ * ```
409
+ */
410
+ export const EjemploContenedor = ({
411
+ numero,
412
+ titulo,
413
+ children,
414
+ codigo,
415
+ nota,
416
+ sangrado = false,
417
+ className = "",
418
+ }: EjemploContenedorProps) => {
419
+ const { t } = useITTranslation();
420
+ const [verCodigo, setVerCodigo] = useState(false);
421
+
422
+ return (
423
+ <section className={`overflow-hidden rounded-[10px] border ${FILETE}`}>
424
+ <header
425
+ className={`flex flex-wrap items-center gap-x-3 gap-y-2 border-b bg-slate-50/70 px-4 py-2.5 dark:bg-slate-950/40 ${FILETE}`}
426
+ >
427
+ {numero && (
428
+ <span className="font-mono text-[10px] tabular-nums text-slate-400 dark:text-slate-500">
429
+ {numero}
430
+ </span>
431
+ )}
432
+ <h2 className="min-w-0 flex-1 truncate font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:text-slate-200">
433
+ {titulo}
434
+ </h2>
435
+
436
+ {codigo && (
437
+ <button
438
+ type="button"
439
+ onClick={() => setVerCodigo(!verCodigo)}
440
+ aria-expanded={verCodigo}
441
+ className={`inline-flex items-center gap-1.5 rounded-[8px] border px-2.5 py-1 font-mono text-[10px] uppercase tracking-[0.12em] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/60 ${
442
+ verCodigo
443
+ ? "border-primary-700 text-primary-700 dark:border-primary-500 dark:text-primary-500"
444
+ : "border-slate-200 text-slate-500 hover:text-slate-900 dark:border-slate-700 dark:text-slate-400 dark:hover:text-white"
445
+ }`}
446
+ >
447
+ <FaCode size={10} />
448
+ {verCodigo ? t("showcase.layout.hideCode") : t("showcase.layout.viewCode")}
449
+ </button>
450
+ )}
451
+ </header>
452
+
453
+ <div
454
+ className={`bg-white dark:bg-slate-950 ${sangrado ? "" : "p-5 sm:p-6"} ${className}`}
455
+ >
456
+ {children}
457
+ </div>
458
+
459
+ {nota && (
460
+ <p
461
+ className={`border-t px-4 py-2.5 font-mono text-[11px] leading-relaxed text-slate-500 dark:text-slate-400 ${FILETE}`}
462
+ >
463
+ {nota}
464
+ </p>
465
+ )}
466
+
467
+ {codigo && verCodigo && (
468
+ <div className={`border-t p-3 ${FILETE}`}>
469
+ <BloqueCodigo code={codigo} compact />
470
+ </div>
471
+ )}
472
+ </section>
473
+ );
474
+ };
@@ -418,7 +418,7 @@ export const TableShowcase = () => {
418
418
  gallery={
419
419
  <div className="space-y-6">
420
420
  <div>
421
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCustomCardsHover")}</h3>
421
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryCustomCardsHover")}</h3>
422
422
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
423
423
  {TABLE_DATA.slice(0, 4).map((row, i) => {
424
424
  const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
@@ -455,7 +455,7 @@ export const TableShowcase = () => {
455
455
  </div>
456
456
 
457
457
  <div>
458
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDefaultCards")}</h3>
458
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryDefaultCards")}</h3>
459
459
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
460
460
  <ITTable
461
461
  columns={columns}
@@ -469,7 +469,7 @@ export const TableShowcase = () => {
469
469
  </div>
470
470
 
471
471
  <div>
472
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCustomCardsPagination")}</h3>
472
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryCustomCardsPagination")}</h3>
473
473
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
474
474
  <ITTable
475
475
  columns={columns}
@@ -501,7 +501,7 @@ export const TableShowcase = () => {
501
501
  </div>
502
502
 
503
503
  <div>
504
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCompactFixed")}</h3>
504
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryCompactFixed")}</h3>
505
505
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 560 }}>
506
506
  <ITTable
507
507
  columns={fixedColumns}
@@ -515,7 +515,7 @@ export const TableShowcase = () => {
515
515
  </div>
516
516
 
517
517
  <div>
518
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryPinnedNameActions")}</h3>
518
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryPinnedNameActions")}</h3>
519
519
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 520 }}>
520
520
  <ITTable
521
521
  columns={pinnedColumns}
@@ -532,7 +532,7 @@ export const TableShowcase = () => {
532
532
  </div>
533
533
 
534
534
  <div>
535
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryAutoCards")}</h3>
535
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryAutoCards")}</h3>
536
536
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 420 }}>
537
537
  <ITTable
538
538
  columns={columns}
@@ -545,7 +545,7 @@ export const TableShowcase = () => {
545
545
  </div>
546
546
 
547
547
  <div>
548
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryVirtualized")}</h3>
548
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryVirtualized")}</h3>
549
549
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
550
550
  <ITTable
551
551
  columns={virtualColumns}
@@ -563,7 +563,7 @@ export const TableShowcase = () => {
563
563
  </div>
564
564
 
565
565
  <div>
566
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryColumnFilters")}</h3>
566
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryColumnFilters")}</h3>
567
567
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
568
568
  <ITTable
569
569
  columns={filterDemoColumns}
@@ -579,7 +579,7 @@ export const TableShowcase = () => {
579
579
  </div>
580
580
 
581
581
  <div>
582
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDesignOverrides")}</h3>
582
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryDesignOverrides")}</h3>
583
583
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
584
584
  <ITTable
585
585
  columns={columns}
@@ -609,7 +609,7 @@ export const TableShowcase = () => {
609
609
  </div>
610
610
 
611
611
  <div>
612
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryPerColumnDesign")}</h3>
612
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryPerColumnDesign")}</h3>
613
613
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
614
614
  <ITTable
615
615
  columns={columns.map((col: any) =>
@@ -1062,7 +1062,7 @@ export const DataTableShowcase = () => {
1062
1062
  gallery={
1063
1063
  <div className="space-y-6">
1064
1064
  <div>
1065
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryCustomCardsHover")}</h3>
1065
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryCustomCardsHover")}</h3>
1066
1066
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1067
1067
  <GalleryDataTable
1068
1068
  title={t("showcase.data.teamMembers")}
@@ -1105,7 +1105,7 @@ export const DataTableShowcase = () => {
1105
1105
  </div>
1106
1106
 
1107
1107
  <div>
1108
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDefaultCardsPagination")}</h3>
1108
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryDefaultCardsPagination")}</h3>
1109
1109
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1110
1110
  <GalleryDataTable
1111
1111
  size="sm"
@@ -1116,7 +1116,7 @@ export const DataTableShowcase = () => {
1116
1116
  </div>
1117
1117
 
1118
1118
  <div>
1119
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryDesignOverrides")}</h3>
1119
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryDesignOverrides")}</h3>
1120
1120
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1121
1121
  <ITDataTable
1122
1122
  columns={userColumns}
@@ -1140,7 +1140,7 @@ export const DataTableShowcase = () => {
1140
1140
  </div>
1141
1141
 
1142
1142
  <div>
1143
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryPinnedIdActive")}</h3>
1143
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryPinnedIdActive")}</h3>
1144
1144
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800" style={{ maxWidth: 520 }}>
1145
1145
  <ITDataTable
1146
1146
  columns={pinnedColumns}
@@ -1158,7 +1158,7 @@ export const DataTableShowcase = () => {
1158
1158
  </div>
1159
1159
 
1160
1160
  <div>
1161
- <h3 className="mb-3 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">{t("showcase.data.galleryServerVirtualization", { page: dtVirtualized ? 200 : 10 })}</h3>
1161
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.data.galleryServerVirtualization", { page: dtVirtualized ? 200 : 10 })}</h3>
1162
1162
  <div className="overflow-hidden rounded-[10px] border border-slate-200 dark:border-slate-800">
1163
1163
  <ITDataTable
1164
1164
  key={dtVirtualized ? "dt-virt-on" : "dt-virt-off"}
@@ -1309,6 +1309,27 @@ export const BadgetShowcase = () => {
1309
1309
  <ITBadget label="Large" color="primary" size="lg" />
1310
1310
  </div>
1311
1311
  }
1312
+ doc={{
1313
+ summary: "Etiqueta compacta de estado o categoría, con colores del tema y variantes filled/outlined.",
1314
+ examples: [
1315
+ '<ITBadget label="Activo" color="success" />',
1316
+ '<ITBadget label="Pendiente" color="warning" variant="outlined" size="sm" />',
1317
+ '<ITBadget color="danger" size="lg">\n <span className="flex items-center gap-1">3 nuevos</span>\n</ITBadget>',
1318
+ ],
1319
+ props: [
1320
+ { name: "label", type: "string", description: "Texto mostrado dentro del badge. Se ignora si se pasa children." },
1321
+ { name: "children", type: "ReactNode", description: "Contenido personalizado; tiene prioridad sobre label." },
1322
+ { name: "color", type: "ColorsTypes", default: '"primary"', description: 'Clave de color semántica: "primary", "secondary", "success", "danger", "warning", "info", "purple", "error" o "gray".' },
1323
+ { name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del badge (padding y tamaño de fuente)." },
1324
+ { name: "variant", type: '"filled" | "outlined"', default: '"filled"', description: "Estilo visual: fondo sólido del tema o fondo transparente con borde y texto del color." },
1325
+ { name: "className", type: "string", description: "Clases CSS adicionales del elemento." },
1326
+ ],
1327
+ notes: [
1328
+ "Renderiza un <span> inline-flex centrado; la apariencia sale de theme.badge[color] (radio completo, borde de 1px), no de clases Tailwind. Una clave inexistente en el tema cae en la configuración de primary.",
1329
+ "Para color=\"primary\" el color se resuelve con utilidades (primary-700 en claro, primary-500 en oscuro) para que el contraste pase AA; el resto de colores del tema se respetan tal cual.",
1330
+ "No declara role ni atributos ARIA propios: es un span textual/decorativo, no un control.",
1331
+ ],
1332
+ }}
1312
1333
  />
1313
1334
  );
1314
1335
  };
@@ -1349,6 +1370,26 @@ export const ImageShowcase = () => {
1349
1370
  </div>
1350
1371
  </>
1351
1372
  }
1373
+ doc={{
1374
+ summary: "Imagen con fallback automático: si el src principal falla al cargar, muestra la imagen de respaldo.",
1375
+ description: "El tamaño y la forma se controlan por completo desde className; las imágenes internas ocupan el 100% del contenedor con object-cover.",
1376
+ examples: [
1377
+ '<ITImage src="/foto.jpg" alt="Foto de usuario" />',
1378
+ '<ITImage\n src={user.avatar}\n alt={user.name}\n fallback="/placeholder.png"\n className="h-32 w-32 rounded-full"\n/>',
1379
+ '<ITImage src={logo} alt="Logo" fallback="/logo-fallback.svg" className="h-40 w-full rounded-xl object-cover" />',
1380
+ ],
1381
+ props: [
1382
+ { name: "src", type: "string", required: true, description: "URL de la imagen principal." },
1383
+ { name: "alt", type: "string", description: "Texto alternativo de la imagen principal (accesibilidad)." },
1384
+ { name: "fallback", type: "string", default: '""', description: "URL de la imagen mostrada cuando el src principal dispara onError." },
1385
+ { name: "className", type: "string", default: '""', description: "Clases del contenedor externo; definen el tamaño de la imagen." },
1386
+ ],
1387
+ notes: [
1388
+ "El className se aplica al <div> contenedor (flex items-center justify-center) y las <img> internas usan w-full h-full object-cover: sin alto/ancho en className la imagen no toma tamaño propio.",
1389
+ "El fallback se renderiza con alt fijo traducido (image.fallbackAlt, \"Fallback\") en lugar del alt del componente, y el cambio a fallback es permanente: si el src vuelve a estar disponible hay que remontar el componente.",
1390
+ "No reenvía atributos nativos del <img> (loading, width, height, onClick…): onClick está declarado en las props, pero la implementación actual no lo conecta.",
1391
+ ],
1392
+ }}
1352
1393
  />
1353
1394
  );
1354
1395
  };
@@ -74,6 +74,28 @@ export const DialogShowcase = () => {
74
74
  </div>
75
75
  </>
76
76
  }
77
+ doc={{
78
+ summary: "Modal montado en un portal sobre `document.body` para confirmaciones, formularios o detalle, con cierre por Escape o clic fuera.",
79
+ description: "Con `title` dibuja una barra propia o un `ITFormHeader` si `useFormHeader` está activo, y con `fullScreen` ocupa todo el viewport.",
80
+ examples: [
81
+ '<ITDialog isOpen={open} onClose={() => setOpen(false)} title="Confirmación de auditoría"><p>¿Registrar la auditoría?</p></ITDialog>',
82
+ '<ITDialog isOpen={open} onClose={close} title="Nuevo usuario" useFormHeader>…</ITDialog>',
83
+ '<ITDialog isOpen={open} onClose={close} fullScreen><ITDataTable columns={cols} fetchData={api.page} /></ITDialog>',
84
+ ],
85
+ props: [
86
+ { name: "isOpen", type: "boolean", required: true, description: "Controla la visibilidad; con `false` no renderiza nada." },
87
+ { name: "onClose", type: "() => void", required: true, description: "Se dispara al pulsar Escape, hacer clic fuera del panel o usar el botón de cerrar." },
88
+ { name: "children", type: "React.ReactNode", required: true, description: "Contenido del cuerpo del diálogo." },
89
+ { name: "className", type: "string", description: "Clases adicionales del panel; se ignoran en `fullScreen`." },
90
+ { name: "title", type: "string", description: "Encabezado opcional mostrado en la parte superior." },
91
+ { name: "useFormHeader", type: "boolean", default: "false", description: "Con `title`, renderiza un `ITFormHeader` en lugar de la barra por defecto." },
92
+ { name: "fullScreen", type: "boolean", default: "false", description: "Hace que el diálogo ocupe todo el viewport (sin radio, borde ni sombra)." },
93
+ ],
94
+ notes: [
95
+ "Los paneles flotantes que abre su contenido (selects, pickers, menús) viven en su propio portal, así que elegir una opción no cierra el diálogo.",
96
+ "No trae `role=\"dialog\"`, `aria-modal`, focus trap ni retorno de foco: esa gestión queda en el consumidor.",
97
+ ],
98
+ }}
77
99
  />
78
100
  );
79
101
  };
@@ -130,6 +152,24 @@ export const ToastShowcase = () => {
130
152
  <ITButton label={t("showcase.feedback.toastInfo")} color="info" size="sm" onClick={() => { setMsg(t("showcase.feedback.msgInfo")); setType("info"); setVisible(true); }} />
131
153
  </div>
132
154
  }
155
+ doc={{
156
+ summary: "Notificación flotante que se autocierra y colorea según su severidad, colocada en una de seis posiciones.",
157
+ examples: [
158
+ '<ITToast message="Cambios guardados" type="success" duration={2000} position="bottom-right" onClose={() => setVisible(false)} />',
159
+ '<ITToast message="No se pudo conectar" type="error" />',
160
+ ],
161
+ props: [
162
+ { name: "message", type: "string", required: true, description: "Texto que muestra el toast." },
163
+ { name: "type", type: '"success" | "error" | "warning" | "info" | "primary" | "danger" | string', default: '"info"', description: "Severidad; define el icono y el color de fondo." },
164
+ { name: "duration", type: "number", default: "1500", description: "Milisegundos antes del autocierre." },
165
+ { name: "position", type: '"top-right" | "top-center" | "top-left" | "bottom-right" | "bottom-center" | "bottom-left"', default: '"top-right"', description: "Posición fija en pantalla." },
166
+ { name: "onClose", type: "() => void", description: "Se invoca 300 ms después del autocierre o del cierre manual, al terminar la transición." },
167
+ ],
168
+ notes: [
169
+ "`type` acepta cualquier string: se resuelve contra `theme.colors`; si la clave no existe, cae al `primary[500]` del tema.",
170
+ "El contenedor lleva `role=\"alert\"`, `fixed z-[80]` y un ancho mínimo de 300 px. Sin `onClose` el toast se desvanece igual, pero el padre no puede desmontarlo.",
171
+ ],
172
+ }}
133
173
  />
134
174
  );
135
175
  };
@@ -204,6 +244,25 @@ export const LoaderShowcase = () => {
204
244
  <ITLoader variant="pulse" size="md" color="purple" />
205
245
  </div>
206
246
  }
247
+ doc={{
248
+ summary: "Indicador de carga con cuatro animaciones (spinner, dots, bar y pulse) y color semántico del tema o valor CSS.",
249
+ examples: [
250
+ '<ITLoader variant="spinner" size="md" color="primary" />',
251
+ '<ITLoader variant="dots" size="sm" color="success" />',
252
+ '<div className="w-48"><ITLoader variant="bar" color="#06b6d4" /></div>',
253
+ ],
254
+ props: [
255
+ { name: "size", type: '"sm" | "md" | "lg" | "xl"', default: '"md"', description: "Tamaño del indicador." },
256
+ { name: "variant", type: '"spinner" | "dots" | "bar" | "pulse"', default: '"spinner"', description: "Tipo de animación." },
257
+ { name: "color", type: "string", default: '"primary"', description: "Clave semántica del tema, valor CSS (`#rrggbb`, `rgb(...)`) o clase CSS por compatibilidad." },
258
+ { name: "className", type: "string", default: '""', description: "Clases adicionales (en `bar` define el ancho)." },
259
+ ],
260
+ notes: [
261
+ "Resolución de color: si es clave de `theme.colors` usa el tono 500; si empieza por `#` o `rgb` lo usa tal cual; cualquier otro string se inyecta como clase.",
262
+ "`spinner` lleva `role=\"status\"` con un texto oculto para lectores de pantalla; `bar` ocupa todo el ancho de su contenedor, así que conviene acotarlo con `className`.",
263
+ "En `dots` el tamaño se deriva quitando la primera `l`/`g` a `size`, por lo que solo `sm` y `md` producen clases válidas.",
264
+ ],
265
+ }}
207
266
  />
208
267
  );
209
268
  };
@@ -285,6 +344,29 @@ export const ThemeProviderShowcase = () => {
285
344
  </p>
286
345
  </ITStack>
287
346
  }
347
+ doc={{
348
+ summary: "Provider raíz del tema: inyecta las variables CSS de color, resuelve el modo claro/oscuro y aplica densidad, radio y sombras globales.",
349
+ description: "Envuelve la app (o la parte que usa componentes AXZY) una sola vez. Expone `useITTheme()` con `palette`, `colors`, `setPalette`, `updateColor`, `resetTheme`, `applyPreset`, `resolvedTheme`, `darkModeMode`, `setDarkModeMode` y `appearance`, y opcionalmente un FAB con diseñador de temas en vivo.",
350
+ examples: [
351
+ '<ITThemeProvider><App /></ITThemeProvider>',
352
+ '<ITThemeProvider theme={{ primary: "#3b82f6", layout: { sidebarBg: "#0f172a" } }} showFab={false}><App /></ITThemeProvider>',
353
+ '<ITThemeProvider density={0.9} radius={8} shadow={1} appearance="flat"><App /></ITThemeProvider>',
354
+ ],
355
+ props: [
356
+ { name: "theme", type: "Partial<ITThemePalette>", description: "Overrides de paleta fusionados (deep merge) con el tema por defecto: `primary`, `secondary`, `ternary`, `danger`, `success`, `info`, `alert`, `warning`, `layout` y `table`." },
357
+ { name: "children", type: "React.ReactNode", required: true, description: "Subárbol que recibe el contexto y las variables CSS." },
358
+ { name: "showFab", type: "boolean", default: "true", description: "Muestra el botón flotante que abre el diseñador de temas y persiste la paleta; en producción se suele poner en `false`." },
359
+ { name: "density", type: "number", default: "1", description: "Factor de densidad global: escala el `font-size` raíz para compactar todo lo basado en `rem`. Se limita a `0.5`–`1.5`." },
360
+ { name: "radius", type: "number", default: "—", description: "Radio global en px aplicado a toda la escala `--radius-*`; `0` deja las esquinas totalmente cuadradas." },
361
+ { name: "shadow", type: "number", default: "—", description: "Fuerza global de sombras: `0` ninguna, `1` sutiles, `2` look actual, `3` marcadas." },
362
+ { name: "appearance", type: '"soft" | "flat"', default: '"soft"', description: "Lenguaje visual del sistema; `\"flat\"` usa superficies planas con bordes de 1 px y fija `data-it-appearance` en `<html>`." },
363
+ ],
364
+ notes: [
365
+ "`useITTheme()` lanza error si se usa fuera del provider; `useITThemeSafe()` devuelve `undefined` en ese caso.",
366
+ "Con `showFab` la paleta, el modo y los presets se persisten en `localStorage` (`it-theme-palette`, `it-theme-dark-mode`, `it-theme-custom-presets`); con `showFab={false}` el estado se sincroniza con la prop `theme`.",
367
+ "`radius={0}` no significa «sin override»: deja todas las esquinas cuadradas. Para no tocar la escala hay que omitir la prop.",
368
+ ],
369
+ }}
288
370
  />
289
371
  );
290
372
  };