@axzydev/axzy_ui_system 1.4.0 → 1.4.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.
@@ -0,0 +1,497 @@
1
+ import { useState, type ReactNode } from "react";
2
+ import {
3
+ FaBook,
4
+ FaBug,
5
+ FaCheck,
6
+ FaCheckCircle,
7
+ FaClipboardList,
8
+ FaExclamationTriangle,
9
+ FaLifeRing,
10
+ FaLightbulb,
11
+ FaQuestionCircle,
12
+ FaSyncAlt,
13
+ FaTerminal,
14
+ FaWrench,
15
+ } from "react-icons/fa";
16
+ import { ITAccordion, ITBadget, ITButton, ITCard, ITFlex, ITGrid, ITStack, ITText } from "../index";
17
+ import type { ITAccordionItem } from "../index";
18
+
19
+ const GITHUB_URL = "https://github.com/maag070208/AXZY_UI_SYSTEM";
20
+ const ISSUE_URL = `${GITHUB_URL}/issues/new`;
21
+
22
+ /** Clipboard button that flips to a "Copiado" confirmation for 2s. */
23
+ const CopyButton = ({ text }: { text: string }) => {
24
+ const [copied, setCopied] = useState(false);
25
+ return (
26
+ <ITButton
27
+ variant="text"
28
+ color="gray"
29
+ size="sm"
30
+ onClick={() => {
31
+ navigator.clipboard?.writeText(text);
32
+ setCopied(true);
33
+ setTimeout(() => setCopied(false), 2000);
34
+ }}
35
+ >
36
+ <ITFlex align="center" gap={1.5}>
37
+ {copied ? <FaCheck size={10} className="text-emerald-400" /> : <FaClipboardList size={10} />}
38
+ {copied ? "Copiado" : "Copiar"}
39
+ </ITFlex>
40
+ </ITButton>
41
+ );
42
+ };
43
+
44
+ /** Terminal/code surface reused across the page. */
45
+ const CodeBlock = ({ code, filename }: { code: string; filename?: string }) => (
46
+ <div className="overflow-hidden rounded-xl border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900">
47
+ {filename && (
48
+ <div className="px-4 py-2 border-b border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-800/80">
49
+ <ITText as="span" className="!text-xs !font-mono !text-slate-400">
50
+ {filename}
51
+ </ITText>
52
+ </div>
53
+ )}
54
+ <div className="relative bg-slate-950">
55
+ <pre className="p-4 text-xs font-mono overflow-x-auto leading-relaxed text-slate-200 whitespace-pre-wrap">
56
+ <code>{code}</code>
57
+ </pre>
58
+ </div>
59
+ </div>
60
+ );
61
+
62
+ /** Result pill used to mark the fix of a common error. */
63
+ const FixPill = ({ children }: { children: ReactNode }) => (
64
+ <span className="inline-flex items-center gap-1.5 rounded-full bg-success-100 dark:bg-success-950/40 px-2.5 py-0.5 text-[11px] font-semibold text-success-700 dark:text-success-400">
65
+ <FaCheckCircle size={10} />
66
+ {children}
67
+ </span>
68
+ );
69
+
70
+ /** Body of one common-error accordion entry: symptom, cause and the fix. */
71
+ const ErrorBody = ({
72
+ symptom,
73
+ cause,
74
+ fix,
75
+ bad,
76
+ good,
77
+ }: {
78
+ symptom: string;
79
+ cause: string;
80
+ fix: ReactNode;
81
+ bad?: string;
82
+ good?: string;
83
+ }) => (
84
+ <ITStack spacing={3} className="pt-1">
85
+ <div className="flex items-start gap-2">
86
+ <FaExclamationTriangle size={12} className="mt-0.5 shrink-0 text-danger-500" />
87
+ <span className="text-xs text-slate-700 dark:text-slate-300">{symptom}</span>
88
+ </div>
89
+ <div className="flex items-start gap-2">
90
+ <FaLightbulb size={12} className="mt-0.5 shrink-0 text-warning-500" />
91
+ <span className="text-xs text-slate-500 dark:text-slate-400">{cause}</span>
92
+ </div>
93
+ <div className="flex items-start gap-2">
94
+ <FaWrench size={12} className="mt-0.5 shrink-0 text-primary-500" />
95
+ <span className="text-xs text-slate-700 dark:text-slate-300">{fix}</span>
96
+ </div>
97
+ {bad && (
98
+ <CodeBlock
99
+ filename="❌ Incorrecto"
100
+ code={bad}
101
+ />
102
+ )}
103
+ {good && (
104
+ <CodeBlock
105
+ filename="✅ Correcto"
106
+ code={good}
107
+ />
108
+ )}
109
+ </ITStack>
110
+ );
111
+
112
+ const COMMON_ERRORS: ITAccordionItem[] = [
113
+ {
114
+ id: "dynamic-classes",
115
+ title: "Tailwind no aplica clases construidas en runtime",
116
+ content: (
117
+ <ErrorBody
118
+ symptom="Un elemento con `grid-cols-${n}` o `col-span-${n}` se renderiza sin columnas, aunque el valor sea correcto."
119
+ cause="Tailwind v4 escanea el código de forma estática y no puede ver clases construidas con template strings."
120
+ fix={
121
+ <>
122
+ Declara las clases en el safelist con <code>@source inline(...)</code> en{" "}
123
+ <code>src/index.css</code>.
124
+ </>
125
+ }
126
+ bad={`// grid.tsx\nclassName={\`grid-cols-\${columns}\`}`}
127
+ good={`/* src/index.css */\n@source inline("grid-cols-1 grid-cols-2 ... col-span-full");`}
128
+ />
129
+ ),
130
+ },
131
+ {
132
+ id: "col-span-full",
133
+ title: "col-span-full pisa el span base de un item",
134
+ content: (
135
+ <ErrorBody
136
+ symptom="Un grid item ocupa todo el ancho aunque tenga definido un `col-span-N`."
137
+ cause="`col-span-full` se emite después de `col-span-N`, así que gana en la cascada cuando ambas están presentes."
138
+ fix="No combines `col-span-full` con un span base en el mismo item: usa la clase solo cuando quieras el ancho completo."
139
+ />
140
+ ),
141
+ },
142
+ {
143
+ id: "component-css",
144
+ title: "El CSS de un componente no llega al bundle publicado",
145
+ content: (
146
+ <ErrorBody
147
+ symptom="El estilo funciona en `pnpm dev` pero desaparece en `dist/index.css` tras `pnpm bundle`."
148
+ cause="Los archivos `.css` por componente no se recogen al construir el paquete."
149
+ fix={
150
+ <>
151
+ Mueve los keyframes estilos a <code>src/index.css</code> dentro de{" "}
152
+ <code>@layer components</code>, con prefijo <code>it-</code> y selectores{" "}
153
+ <code>:where(.it-*)</code>.
154
+ </>
155
+ }
156
+ />
157
+ ),
158
+ },
159
+ {
160
+ id: "styles-missing",
161
+ title: "Los componentes se ven sin estilos en mi app",
162
+ content: (
163
+ <ErrorBody
164
+ symptom="El markup aparece pero sin colores, espaciados ni tipografía."
165
+ cause="Falta la hoja de estilos del paquete o el plugin de Tailwind v4 en el consumidor."
166
+ fix={
167
+ <>
168
+ Instala Tailwind v4, impórtalo con <code>@import "tailwindcss"</code> y carga{" "}
169
+ <code>@axzydev/axzy_ui_system/dist/index.css</code>.
170
+ </>
171
+ }
172
+ good={`import { ITThemeProvider } from "@axzydev/axzy_ui_system"\nimport "@axzydev/axzy_ui_system/dist/index.css"`}
173
+ />
174
+ ),
175
+ },
176
+ {
177
+ id: "dark-mode",
178
+ title: "El dark mode no reacciona al toggle",
179
+ content: (
180
+ <ErrorBody
181
+ symptom="Los componentes mantienen los colores claros al cambiar de tema."
182
+ cause="La variante `dark` de Tailwind v4 debe apuntar a la clase `.dark`, y la app debe estar envuelta en el provider."
183
+ fix={
184
+ <>
185
+ Añade <code>@variant dark (&:is(.dark &));</code> a tu CSS y envuelve la app en{" "}
186
+ <code>ITThemeProvider</code>.
187
+ </>
188
+ }
189
+ />
190
+ ),
191
+ },
192
+ {
193
+ id: "atomic-deps",
194
+ title: "`pnpm check:atomic` falla con un import hacia arriba",
195
+ content: (
196
+ <ErrorBody
197
+ symptom="El check reporta que un átomo importa a una molécula (o similar)."
198
+ cause="Las capas deben ser unidireccionales: `atoms < molecules < organisms < templates`."
199
+ fix="Mueve el componente compartido a una capa inferior o resuelve la composición en el consumidor, no dentro del componente."
200
+ />
201
+ ),
202
+ },
203
+ {
204
+ id: "hardcoded-colors",
205
+ title: "Los colores no respetan el tema",
206
+ content: (
207
+ <ErrorBody
208
+ symptom="Un componente ignora la paleta y muestra siempre los mismos colores."
209
+ cause="Hay colores hexadecimales fijos en lugar de tokens del tema."
210
+ fix={
211
+ <>
212
+ Usa <code>theme</code> desde <code>@/theme/theme</code> y clases semánticas (
213
+ <code>text-primary-600</code>) en vez de valores hardcodeados.
214
+ </>
215
+ }
216
+ bad={`style={{ color: "#3b82f6" }}`}
217
+ good={`className="text-primary-600"`}
218
+ />
219
+ ),
220
+ },
221
+ {
222
+ id: "lint-errors",
223
+ title: "`pnpm lint` termina con errores",
224
+ content: (
225
+ <ErrorBody
226
+ symptom="La verificación local no deja avanzar aunque los tests pasen."
227
+ cause="ESLint, `check:atomic` o `check:css` detectan convenciones incumplidas."
228
+ fix={
229
+ <>
230
+ Ejecuta <code>pnpm lint</code> y corrige hasta que termine con{" "}
231
+ <code>0 errors</code>. Las advertencias de <code>no-explicit-any</code> son
232
+ intencionales.
233
+ </>
234
+ }
235
+ />
236
+ ),
237
+ },
238
+ ];
239
+
240
+ const FAQ: ITAccordionItem[] = [
241
+ {
242
+ id: "faq-theme",
243
+ title: "¿Cómo personalizo los colores?",
244
+ content:
245
+ "Pasa un objeto parcial a ITThemeProvider con la clave `colors`. Solo define lo que quieras sobrescribir; el resto se hereda.",
246
+ },
247
+ {
248
+ id: "faq-dark",
249
+ title: "¿Cómo activo el dark mode?",
250
+ content:
251
+ "ITThemeProvider gestiona la clase `dark` en el árbol. Asegúrate de añadir `@variant dark (&:is(.dark &));` en tu CSS de Tailwind v4.",
252
+ },
253
+ {
254
+ id: "faq-examples",
255
+ title: "¿Dónde están los ejemplos de cada componente?",
256
+ content:
257
+ "En el sandbox: ejecuta `pnpm dev` y navega a `#ui-system`. Las historias de Storybook existen y deben compilar, pero la superficie principal de ejemplos es el sandbox.",
258
+ },
259
+ {
260
+ id: "faq-new-component",
261
+ title: "¿Cómo agrego un componente nuevo?",
262
+ content:
263
+ "Crea `src/components/<layer>/<kebab-name>/` con `<name>.tsx`, `<name>.props.ts` y `<name>.stories.tsx`. Documenta cada prop con JSDoc, regístralo en `src/index.ts` y actualiza los documentos y el showcase correspondiente.",
264
+ },
265
+ {
266
+ id: "faq-changes-not-visible",
267
+ title: "¿Por qué mis cambios no se ven en el paquete?",
268
+ content:
269
+ "El consumidor usa `dist/`. Regenera con `pnpm bundle` (tsup + CSS) o `pnpm build:app` para el sandbox. No edites `dist/` a mano.",
270
+ },
271
+ {
272
+ id: "faq-tree-shaking",
273
+ title: "¿La librería es tree-shakeable?",
274
+ content:
275
+ "Sí. Importa named exports (`import { ITButton } from \"@axzydev/axzy_ui_system\"`) y el bundler descarta lo que no usas. No hay dependencias de UI de terceros.",
276
+ },
277
+ {
278
+ id: "faq-storybook",
279
+ title: "¿Necesito Storybook para desarrollar?",
280
+ content:
281
+ "No es obligatorio. `pnpm storybook` es útil para aislar un componente, pero los ejemplos de referencia viven en el sandbox y las historias solo deben seguir compilando.",
282
+ },
283
+ {
284
+ id: "faq-browser-support",
285
+ title: "¿Qué navegadores están soportados?",
286
+ content:
287
+ "Navegadores modernos con soporte de variables CSS y `:where()`. El copy-to-clipboard incluye un fallback para contextos no seguros o webviews embebidos.",
288
+ },
289
+ ];
290
+
291
+ const REPORT_STEPS = [
292
+ "Confirma que el bug ocurre en la última versión publicada y anota el número de versión.",
293
+ "Reproduce el problema en una app mínima: elimina todo lo que no sea necesario para verlo.",
294
+ "Revisa la consola del navegador y la terminal; copia el error completo con su stack.",
295
+ "Registra el resultado de `pnpm lint` y `pnpm bundle` si el fallo es del build.",
296
+ "Abre un issue con la plantilla y adjunta los pasos para reproducir.",
297
+ ];
298
+
299
+ const REPORT_CHECKLIST = [
300
+ "Versión de @axzydev/axzy_ui_system",
301
+ "Pasos exactos para reproducir",
302
+ "Resultado esperado vs. obtenido",
303
+ "Sistema operativo, Node y gestor de paquetes",
304
+ "Código o repo mínimo reproducible",
305
+ "Capturas o mensajes de error completos",
306
+ ];
307
+
308
+ const DIAGNOSTICS = `pnpm lint # ESLint + check:atomic + check:css (debe dar 0 errors)
309
+ pnpm bundle # regenera dist/ (tsup + CSS)
310
+ pnpm check:css # valida prefijos it-, tokens --it-* y duplicados
311
+ pnpm check:atomic # valida la dirección de dependencias entre capas`;
312
+
313
+ /**
314
+ * Página de ayuda del sandbox (`#ui-system/general/help`): resolución de bugs
315
+ * frecuentes, preguntas frecuentes y guía para reportar incidencias.
316
+ */
317
+ export const HelpShowcase = () => {
318
+ return (
319
+ <ITStack spacing={10} className="animate-fadeIn">
320
+ {/* ─── HERO ─── */}
321
+ <ITCard className="overflow-hidden border-0 bg-gradient-to-br from-primary-50 via-white to-purple-50 dark:from-primary-950/20 dark:via-slate-900 dark:to-purple-950/20 shadow-sm">
322
+ <div className="relative">
323
+ <div className="absolute -top-40 -right-40 w-[500px] h-[500px] bg-primary-500/10 rounded-full blur-[120px] pointer-events-none" />
324
+ <div className="absolute -bottom-40 -left-40 w-[500px] h-[500px] bg-purple-500/10 rounded-full blur-[120px] pointer-events-none" />
325
+ <ITStack spacing={5} className="relative z-10">
326
+ <ITStack spacing={2}>
327
+ <ITBadget label="Soporte" color="purple" variant="outlined" className="w-fit" />
328
+ <ITText as="h1" className="!text-4xl !font-bold !tracking-tight">
329
+ Ayuda & Solución de Bugs
330
+ </ITText>
331
+ <ITText as="p" muted className="!text-base !leading-relaxed max-w-2xl">
332
+ Guía para diagnosticar y resolver los problemas más frecuentes al usar AXZY UI System,
333
+ más una checklist para reportar incidencias con todo el contexto necesario.
334
+ </ITText>
335
+ </ITStack>
336
+ <ITFlex gap={3} wrap="wrap" align="center">
337
+ <ITButton
338
+ variant="filled"
339
+ color="primary"
340
+ icon={<FaBug size={13} />}
341
+ onClick={() => window.open(ISSUE_URL, "_blank", "noopener,noreferrer")}
342
+ >
343
+ Reportar un bug
344
+ </ITButton>
345
+ <ITButton
346
+ variant="outlined"
347
+ color="secondary"
348
+ icon={<FaBook size={13} />}
349
+ onClick={() => window.open(GITHUB_URL, "_blank", "noopener,noreferrer")}
350
+ >
351
+ Ver repositorio
352
+ </ITButton>
353
+ </ITFlex>
354
+ </ITStack>
355
+ </div>
356
+ </ITCard>
357
+
358
+ {/* ─── DIAGNÓSTICO RÁPIDO ─── */}
359
+ <section>
360
+ <ITFlex align="center" gap={2.5} className="!mb-4">
361
+ <span className="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300">
362
+ <FaTerminal size={13} />
363
+ </span>
364
+ <ITStack spacing={0}>
365
+ <ITText as="h2" className="!text-lg !font-bold">
366
+ Diagnóstico rápido
367
+ </ITText>
368
+ <ITText as="p" muted className="!text-xs">
369
+ Ejecuta estos comandos antes de reportar cualquier fallo.
370
+ </ITText>
371
+ </ITStack>
372
+ </ITFlex>
373
+ <div className="relative">
374
+ <div className="absolute top-2 right-2 z-10">
375
+ <CopyButton text={DIAGNOSTICS} />
376
+ </div>
377
+ <CodeBlock filename="terminal" code={DIAGNOSTICS} />
378
+ </div>
379
+ </section>
380
+
381
+ {/* ─── ERRORES COMUNES ─── */}
382
+ <section>
383
+ <ITFlex align="center" gap={2.5} className="!mb-4">
384
+ <span className="flex h-8 w-8 items-center justify-center rounded-lg bg-danger-100 dark:bg-danger-950/40 text-danger-600 dark:text-danger-400">
385
+ <FaExclamationTriangle size={13} />
386
+ </span>
387
+ <ITStack spacing={0}>
388
+ <ITText as="h2" className="!text-lg !font-bold">
389
+ Errores comunes y su solución
390
+ </ITText>
391
+ <ITText as="p" muted className="!text-xs">
392
+ Síntoma, causa y arreglo de los tropiezos más habituales del stack.
393
+ </ITText>
394
+ </ITStack>
395
+ </ITFlex>
396
+ <ITAccordion items={COMMON_ERRORS} allowMultiple variant="separated" />
397
+ </section>
398
+
399
+ {/* ─── FAQ ─── */}
400
+ <section>
401
+ <ITFlex align="center" gap={2.5} className="!mb-4">
402
+ <span className="flex h-8 w-8 items-center justify-center rounded-lg bg-info-100 dark:bg-info-950/40 text-info-600 dark:text-info-400">
403
+ <FaQuestionCircle size={13} />
404
+ </span>
405
+ <ITStack spacing={0}>
406
+ <ITText as="h2" className="!text-lg !font-bold">
407
+ Preguntas frecuentes
408
+ </ITText>
409
+ <ITText as="p" muted className="!text-xs">
410
+ Respuestas cortas a las dudas más repetidas.
411
+ </ITText>
412
+ </ITStack>
413
+ </ITFlex>
414
+ <ITAccordion items={FAQ} allowMultiple variant="bordered" />
415
+ </section>
416
+
417
+ {/* ─── CÓMO REPORTAR UN BUG ─── */}
418
+ <ITCard title="Cómo reportar un bug" className="border-purple-200/60 dark:border-purple-800/60 shadow-sm">
419
+ <ITStack spacing={6}>
420
+ <ITText as="p" className="!text-sm" muted>
421
+ Un buen reporte se puede reproducir sin preguntas extra. Sigue estos pasos y completa la
422
+ checklist antes de abrir el issue.
423
+ </ITText>
424
+
425
+ <ITGrid container spacing={6}>
426
+ <ITGrid item xs={12} md={6}>
427
+ <ITStack spacing={3}>
428
+ <ITText as="h3" className="!text-[11px] !font-bold uppercase !tracking-wider text-slate-400 dark:text-slate-500">
429
+ Pasos
430
+ </ITText>
431
+ {REPORT_STEPS.map((step, index) => (
432
+ <ITFlex key={step} align="start" gap={3}>
433
+ <span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary-100 dark:bg-primary-950/40 text-[11px] font-bold text-primary-600 dark:text-primary-400">
434
+ {index + 1}
435
+ </span>
436
+ <ITText as="p" className="!text-sm text-slate-600 dark:text-slate-300">
437
+ {step}
438
+ </ITText>
439
+ </ITFlex>
440
+ ))}
441
+ </ITStack>
442
+ </ITGrid>
443
+
444
+ <ITGrid item xs={12} md={6}>
445
+ <div className="h-full rounded-2xl border border-slate-200 dark:border-slate-800 bg-slate-50 dark:bg-slate-800/40 p-5">
446
+ <ITText as="h3" className="!mb-4 !text-[11px] !font-bold uppercase !tracking-wider text-slate-400 dark:text-slate-500">
447
+ Incluye en el reporte
448
+ </ITText>
449
+ <ul className="space-y-3">
450
+ {REPORT_CHECKLIST.map((item) => (
451
+ <li key={item} className="flex items-start gap-3">
452
+ <span className="mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-success-100 dark:bg-success-950/40 text-success-600 dark:text-success-400">
453
+ <FaCheck size={10} />
454
+ </span>
455
+ <span className="text-sm text-slate-600 dark:text-slate-300">{item}</span>
456
+ </li>
457
+ ))}
458
+ </ul>
459
+ </div>
460
+ </ITGrid>
461
+ </ITGrid>
462
+
463
+ <ITFlex align="center" justify="between" gap={4} wrap="wrap" className="border-t border-slate-200 dark:border-slate-800 pt-5">
464
+ <ITFlex align="center" gap={2}>
465
+ <FaLifeRing size={14} className="text-primary-500" />
466
+ <ITText as="p" className="!text-xs" muted>
467
+ ¿Dudas de uso en lugar de un bug? Abre una discusión en el repositorio.
468
+ </ITText>
469
+ </ITFlex>
470
+ <ITFlex gap={3} wrap="wrap">
471
+ <ITButton
472
+ variant="filled"
473
+ color="primary"
474
+ icon={<FaBug size={13} />}
475
+ onClick={() => window.open(ISSUE_URL, "_blank", "noopener,noreferrer")}
476
+ >
477
+ Abrir issue
478
+ </ITButton>
479
+ <ITButton
480
+ variant="outlined"
481
+ color="secondary"
482
+ icon={<FaSyncAlt size={13} />}
483
+ onClick={() => window.open(`${GITHUB_URL}/blob/main/AGENTS.md`, "_blank", "noopener,noreferrer")}
484
+ >
485
+ Ver AGENTS.md
486
+ </ITButton>
487
+ </ITFlex>
488
+ </ITFlex>
489
+
490
+ <ITFlex align="center" gap={2} wrap="wrap">
491
+ <FixPill>Pasos reproducibles + versión + entorno = reporte accionable</FixPill>
492
+ </ITFlex>
493
+ </ITStack>
494
+ </ITCard>
495
+ </ITStack>
496
+ );
497
+ };
@@ -22,6 +22,7 @@ import {
22
22
  ITText,
23
23
  ITNavbar,
24
24
  ITLayout,
25
+ ITSidebar,
25
26
  ITStack,
26
27
  ITStatCard,
27
28
  ITGrid,
@@ -32,6 +33,7 @@ import {
32
33
  ITSelect,
33
34
  ITAccordion,
34
35
  } from "../index";
36
+ import type { ITSidebarProps } from "../index";
35
37
  import { ShowcaseLayout, CodeViewer } from "./ShowcaseLayout";
36
38
 
37
39
  // ─────────────────────────────────────────
@@ -931,10 +933,10 @@ export const LayoutShowcase = () => {
931
933
  },
932
934
  };
933
935
 
934
- const sidebar = {
936
+ const sidebar: ITSidebarProps = {
935
937
  navigationItems: [
936
938
  { id: "dashboard", label: "Dashboard", icon: <FaChartLine />, isActive: activeId === "dashboard", action: () => setActiveId("dashboard") },
937
- { id: "users", label: "Usuarios", icon: <FaUsers />, isActive: activeId === "users", action: () => setActiveId("users") },
939
+ { id: "users", label: "Usuarios", icon: <FaUsers />, isActive: activeId === "users", action: () => setActiveId("users"), badge: 4 },
938
940
  {
939
941
  id: "sales",
940
942
  label: "Ventas",
@@ -958,8 +960,8 @@ export const LayoutShowcase = () => {
958
960
  },
959
961
  ],
960
962
  },
961
- { id: "products", label: "Productos", icon: <FaBoxOpen />, isActive: activeId === "products", action: () => setActiveId("products"), badge: "3" },
962
- { id: "settings", label: "Configuración", icon: <FaCog />, isActive: activeId === "settings", action: () => setActiveId("settings") },
963
+ { id: "products", label: "Productos", icon: <FaBoxOpen />, isActive: activeId === "products", action: () => setActiveId("products"), badge: 12 },
964
+ { id: "settings", label: "Configuración", icon: <FaCog />, isActive: activeId === "settings", action: () => setActiveId("settings"), badge: 3, badgeProps: { color: "warning", variant: "outlined" } },
963
965
  ],
964
966
  isCollapsed: collapsed,
965
967
  header: (
@@ -970,6 +972,14 @@ export const LayoutShowcase = () => {
970
972
  AXZY v1.3
971
973
  </ITText>
972
974
  ),
975
+ // Fila de notificación (icono + ITBadget con número). Sigue visible al colapsar.
976
+ notification: {
977
+ count: 5,
978
+ icon: <FaBell />,
979
+ label: "Notificaciones",
980
+ onClick: () => setActiveId("notifications"),
981
+ badgeProps: { color: "danger" },
982
+ },
973
983
  };
974
984
 
975
985
  const code = `<ITLayout
@@ -995,10 +1005,22 @@ export const LayoutShowcase = () => {
995
1005
  ]},
996
1006
  ]
997
1007
  },
1008
+ // badge: number | string -> se dibuja con ITBadget; badgeProps lo estiliza
1009
+ { id: "products", label: "Productos", icon: <FaBoxOpen />, badge: 12 },
1010
+ { id: "settings", label: "Configuración", icon: <FaCog />, badge: 3,
1011
+ badgeProps: { color: "warning", variant: "outlined" } },
998
1012
  ],
999
1013
  isCollapsed: ${collapsed},
1000
1014
  // Slot opcional sobre la navegación (oculto al colapsar)
1001
- header: <span>AXZY v1.3</span>
1015
+ header: <span>AXZY v1.3</span>,
1016
+ // Fila de notificación (icono + label + ITBadget). Visible también al colapsar.
1017
+ notification: {
1018
+ count: 5,
1019
+ icon: <FaBell />,
1020
+ label: "Notificaciones",
1021
+ onClick: () => {},
1022
+ badgeProps: { color: "danger" }, // className para padding
1023
+ }
1002
1024
  }}
1003
1025
  >
1004
1026
  {/* Tu contenido */}
@@ -1130,6 +1152,32 @@ export const LayoutShowcase = () => {
1130
1152
  </ITNavbar>
1131
1153
  </div>
1132
1154
  </div>
1155
+
1156
+ <div>
1157
+ <h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">ITSidebar — badges + notificación</h4>
1158
+ <div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden h-[420px] flex">
1159
+ <ITSidebar
1160
+ visibleOnMobile
1161
+ className="h-full"
1162
+ subitemConnector="dot"
1163
+ navigationItems={[
1164
+ { id: "home", label: "Inicio", icon: <FaHome />, isActive: true, action: () => {} },
1165
+ { id: "inbox", label: "Bandeja", icon: <FaUsers />, badge: 12, action: () => {} },
1166
+ { id: "alerts", label: "Alertas", icon: <FaBell />, badge: 3, badgeProps: { color: "warning", variant: "outlined" }, action: () => {} },
1167
+ { id: "settings", label: "Configuración", icon: <FaCog />, action: () => {} },
1168
+ ]}
1169
+ notification={{
1170
+ count: 5,
1171
+ icon: <FaBell />,
1172
+ label: "Notificaciones",
1173
+ onClick: () => {},
1174
+ }}
1175
+ />
1176
+ <div className="flex-1 p-6 text-sm text-slate-500">
1177
+ Cada ítem puede llevar un contador con <code className="font-mono text-xs">badge</code> + <code className="font-mono text-xs">badgeProps</code>, y el sidebar admite una fila <code className="font-mono text-xs">notification</code> con icono + ITBadget.
1178
+ </div>
1179
+ </div>
1180
+ </div>
1133
1181
  </ITStack>
1134
1182
  }
1135
1183
  doc={{
@@ -1138,6 +1186,7 @@ export const LayoutShowcase = () => {
1138
1186
  '<ITLayout topBar={{ centerContent: <SearchBox /> }} sidebar={{ navigationItems }}>',
1139
1187
  " <Dashboard />",
1140
1188
  "</ITLayout>",
1189
+ '<ITSidebar navigationItems={[{ id: "inbox", label: "Bandeja", badge: 12, badgeProps: { color: "primary" } }]} notification={{ count: 5, icon: <FaBell /> }} />',
1141
1190
  ],
1142
1191
  props: [
1143
1192
  { name: "topBar", type: "ITTopBarProps", required: true, description: "Configuración de la barra superior." },
@@ -1146,10 +1195,14 @@ export const LayoutShowcase = () => {
1146
1195
  { name: "className", type: "string", description: "Clases del wrapper externo." },
1147
1196
  { name: "contentClassName", type: "string", description: "Clases del contenedor de contenido." },
1148
1197
  { name: "sidebar.header", type: "React.ReactNode", description: "Slot opcional sobre la navegación (ej. wordmark o filtro). Oculto al colapsar." },
1198
+ { name: "sidebar.notification", type: "ITSidebarNotification", description: "Fila de notificación (icono + label + ITBadget) sobre la navegación: { count, icon?, label?, onClick?, badgeProps? }. Visible también al colapsar." },
1199
+ { name: "sidebar.navigationItems[].badge", type: "number | string", description: "Contador que se renderiza con ITBadget en el ítem." },
1200
+ { name: "sidebar.navigationItems[].badgeProps", type: "ITBadgetProps", description: "Estilo del badge del ítem (color, size, variant, className para padding)." },
1149
1201
  ],
1150
1202
  notes: [
1151
1203
  "`subitems` acepta entradas planas o grupos con `label`+`items`; el título del grupo no es foco ni un tercer nivel y cada grupo se nombra por `aria-labelledby`. En el riel colapsado el flyout aplana los grupos (sin títulos).",
1152
1204
  "El sidebar hereda aria-current/aria-expanded y acepta un slot header opcional.",
1205
+ "Los badges por ítem (`badge` + `badgeProps`) y la fila `notification` se dibujan con el átomo `ITBadget`; el padding se ajusta con `badgeProps.className`.",
1153
1206
  "Con `sidebar.isCollapsed` omitido el contenido reserva 88px (hover expande sobre el contenido). Si lo controlas a `false`, reserva 280px y no tapa el contenido.",
1154
1207
  "El riel colapsado se expande automáticamente al pasar el cursor por encima; no hay botón de colapso.",
1155
1208
  ],