@axzydev/axzy_ui_system 1.5.1 → 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.
@@ -669,6 +669,37 @@ export const FlexShowcase = () => {
669
669
  title="ITFlex"
670
670
  description={t("showcase.layoutPrimitives.flexDesc")}
671
671
  code={buildCode()}
672
+ doc={{
673
+ summary:
674
+ "Contenedor flexbox completo: dirección, alineación, distribución, wrap y factores flex (grow/shrink/basis) en un solo componente.",
675
+ description:
676
+ "Es el primitivo de layout con más control; úsalo cuando ITStack no llegue (reparto por ejes, hijos que crecen o se encogen, wrap con alineación). La separación se mide en unidades de 0.25rem y se emite como `gap`.",
677
+ examples: [
678
+ '<ITFlex justify="between" align="center" gap={3}>\n <h2>Panel</h2>\n <ITButton label="Exportar" variant="outlined" />\n</ITFlex>',
679
+ '<ITFlex direction="column" gap={2} as="section">\n <header>Título</header>\n <main>Contenido</main>\n</ITFlex>',
680
+ '<ITFlex gap={2}>\n <ITFlex grow basis={56}>Contenido flexible</ITFlex>\n <span>Fijo</span>\n</ITFlex>',
681
+ ],
682
+ props: [
683
+ { name: "children", type: "ReactNode", description: "Elementos hijos del contenedor." },
684
+ { name: "direction", type: '"row" | "column" | "row-reverse" | "column-reverse"', default: '"row"', description: "Dirección del eje principal." },
685
+ { name: "align", type: '"start" | "end" | "center" | "stretch" | "baseline"', description: "Alineación en el eje transversal." },
686
+ { name: "justify", type: '"start" | "end" | "center" | "between" | "around" | "evenly"', description: "Reparto de los hijos en el eje principal." },
687
+ { name: "wrap", type: '"nowrap" | "wrap" | "wrap-reverse"', description: "Si los hijos saltan a la siguiente línea." },
688
+ { name: "gap", type: "number", default: "0", description: "Separación entre hijos en unidades de 0.25rem." },
689
+ { name: "grow", type: "boolean | number", description: "Factor flex-grow: `true` = 1, `false` = 0, un número se usa tal cual." },
690
+ { name: "shrink", type: "boolean | number", description: "Factor flex-shrink: `true` = 1, `false` = 0, un número se usa tal cual." },
691
+ { name: "basis", type: "string | number", description: "flex-basis: los números se multiplican por 0.25rem, los strings se usan tal cual." },
692
+ { name: "className", type: "string", description: "Clases CSS adicionales." },
693
+ { name: "style", type: "CSSProperties", description: "Estilos inline; se aplican después de gap/grow/shrink/basis, así que pueden sobrescribirlos." },
694
+ { name: "as", type: "ElementType", default: '"div"', description: "Elemento HTML a renderizar en lugar del `div` por defecto." },
695
+ { name: "onClick", type: "(e: React.MouseEvent) => void", description: "Handler de click del contenedor." },
696
+ ],
697
+ notes: [
698
+ "gap, grow, shrink y basis se resuelven como estilos inline, no como clases Tailwind: el `style` propio tiene la última palabra.",
699
+ "align, justify y wrap no traen valor por defecto: si se omiten no se emite clase y valen los valores iniciales de CSS (stretch, flex-start, nowrap).",
700
+ "El contenedor siempre es `display: flex`; para una sola dirección con separación uniforme, ITStack es más corto.",
701
+ ],
702
+ }}
672
703
  demo={
673
704
  <div className="w-full max-w-xl flex flex-col">
674
705
  <div className="min-h-[360px]">
@@ -939,6 +970,37 @@ export const GridShowcase = () => {
939
970
  title="ITGrid"
940
971
  description={t("showcase.layoutPrimitives.gridDesc")}
941
972
  code={buildCode()}
973
+ doc={{
974
+ summary:
975
+ "Sistema de grid CSS responsivo de 12 columnas con el patrón container/item: el contenedor fija columnas y gap, y cada item declara su span por breakpoint.",
976
+ description:
977
+ "Como `container` renderiza `display: grid` con `grid-cols-{columns}`; como `item` calcula el `col-span` base y los spans de sm/md/lg/xl. Si no se pasa ninguno de los dos, renderiza un `div` normal.",
978
+ examples: [
979
+ '<ITGrid container spacing={4}>\n <ITGrid item xs={12} md={6}>Sidebar</ITGrid>\n <ITGrid item xs={12} md={6}>Contenido</ITGrid>\n</ITGrid>',
980
+ '<ITGrid container columns={3} spacing={2}>\n {plans.map((plan) => (\n <ITGrid key={plan.id} item>{plan.name}</ITGrid>\n ))}\n</ITGrid>',
981
+ '<ITGrid container spacing={4} as="section">\n <ITGrid item xs={12} lg={8}>Gráfico</ITGrid>\n <ITGrid item xs={12} lg={4}>Actividad</ITGrid>\n</ITGrid>',
982
+ ],
983
+ props: [
984
+ { name: "children", type: "ReactNode", description: "Elementos hijos." },
985
+ { name: "container", type: "boolean", description: "Renderiza el padre: `display: grid` + `grid-cols-{columns}`." },
986
+ { name: "item", type: "boolean", description: "Renderiza un hijo con `col-span` según los breakpoints. Si `container` también está activo, gana `container`." },
987
+ { name: "spacing", type: "number", default: "0", description: "Gap entre hijos en unidades de 0.25rem; solo aplica con `container` y si es mayor que 0." },
988
+ { name: "columns", type: "number", default: "12", description: "Columnas del contenedor (se acota a 1–12); también limita el span de los items." },
989
+ { name: "xs", type: "number", description: "Span en el breakpoint base; si falta se usa `sm` y, si tampoco hay, el item ocupa todo (`col-span-full`)." },
990
+ { name: "sm", type: "number", description: "Span a partir del breakpoint `sm`." },
991
+ { name: "md", type: "number", description: "Span a partir del breakpoint `md`." },
992
+ { name: "lg", type: "number", description: "Span a partir del breakpoint `lg`." },
993
+ { name: "xl", type: "number", description: "Span a partir del breakpoint `xl`." },
994
+ { name: "className", type: "string", description: "Clases CSS adicionales." },
995
+ { name: "style", type: "CSSProperties", description: "Estilos inline." },
996
+ { name: "as", type: "ElementType", default: '"div"', description: "Elemento HTML a renderizar en lugar del `div` por defecto." },
997
+ ],
998
+ notes: [
999
+ "El span base es `xs ?? sm` y, si no se define ninguno, cae en `col-span-full` (las 12 columnas).",
1000
+ "Los spans se redondean y se acotan entre 1 y `columns`: con 3 columnas, `md={99}` acaba en `md:col-span-3`.",
1001
+ "Tailwind no ve clases construidas en runtime: `grid-cols-{n}` y `col-span-{n}` están en el safelist `@source inline(...)` de src/index.css.",
1002
+ ],
1003
+ }}
942
1004
  demo={
943
1005
  <div className="w-full max-w-xl flex flex-col">
944
1006
  <div className="min-h-[360px]">
@@ -1351,6 +1413,30 @@ export const GridShowcase = () => {
1351
1413
  title={t("showcase.layoutPrimitives.screensTitle")}
1352
1414
  description={t("showcase.layoutPrimitives.screensSubtitle")}
1353
1415
  code={code}
1416
+ doc={{
1417
+ summary:
1418
+ "Receta de pantalla: un dashboard de una columna que compone ITStack, ITFlex, ITGrid, ITStatCard, ITAvatar e ITButton para armar cabecera, KPIs y bloque de gráfico + actividad.",
1419
+ description:
1420
+ "No es un componente de la librería, sino un patrón de composición: primero la cabecera con su acción, después la fila de métricas y por último el contenido a dos columnas, apilado en móvil.",
1421
+ examples: [
1422
+ '<ITGrid container spacing={3}>\n <ITGrid item xs={12} sm={6} lg={3}>\n <ITStatCard label="Usuarios activos" value="1,245" trend="+12%" trendDirection="up" />\n </ITGrid>\n</ITGrid>',
1423
+ '<ITFlex gap={3} align="center">\n <ITAvatar initials="AL" size="sm" color="bg-primary-500" />\n <p>Ana López creó un reporte</p>\n</ITFlex>',
1424
+ ],
1425
+ props: [
1426
+ { name: "ITStack", type: "componente", description: "Columna raíz (`spacing={6}`) que separa cabecera, métricas y gráficos." },
1427
+ { name: "ITFlex", type: "componente", description: "Cabecera con `justify=\"between\" align=\"center\"` y cada fila de actividad con `gap={3} align=\"center\"`." },
1428
+ { name: "ITGrid (container)", type: "componente", description: "Rejilla de 12 columnas (`spacing={3}`) para la fila de KPIs y para el bloque de gráfico/actividad." },
1429
+ { name: "ITGrid (item)", type: "componente", description: "Reparto responsive: `xs={12} sm={6} lg={3}` en las métricas y `xs={12} lg={8}` / `lg={4}` en el contenido." },
1430
+ { name: "ITStatCard", type: "componente", description: "Cada KPI, con `label`, `value`, `trend`, `trendDirection` y `color` para el fondo." },
1431
+ { name: "ITAvatar", type: "componente", description: "Iniciales del usuario en la actividad reciente, con `size=\"sm\"` y `color`." },
1432
+ { name: "ITButton", type: "componente", description: "Acción de la cabecera (“Exportar”), con `variant=\"outlined\" size=\"sm\"`." },
1433
+ ],
1434
+ notes: [
1435
+ "Es una receta, no un componente: no tiene props propias; en la tabla se documentan las piezas que la componen.",
1436
+ "Criterio de composición: cabecera → KPIs en 4 columnas (`lg={3}`) → contenido 8/4 (`lg={8}` + `lg={4}`); todo a ancho completo por debajo de `sm` y `lg`.",
1437
+ "El demo usa `demoMinAncho={780}` y textos de `useITTranslation()`; los colores salen de tokens (`bg-primary-500`), no de valores sueltos.",
1438
+ ],
1439
+ }}
1354
1440
  demo={
1355
1441
  <div className="w-full">
1356
1442
  <p className="mb-4 font-mono text-[11px] uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">
@@ -1462,6 +1548,30 @@ export const ScreenFormShowcase = () => {
1462
1548
  title={t("showcase.layoutPrimitives.screensTitle")}
1463
1549
  description={t("showcase.layoutPrimitives.screensSubtitle")}
1464
1550
  code={formCode}
1551
+ doc={{
1552
+ summary:
1553
+ "Receta de pantalla: un formulario responsive que compone ITStack, ITGrid, ITInput, ITSelect, ITFlex e ITButton para armar encabezado, campos y barra de acciones.",
1554
+ description:
1555
+ "Tampoco es un componente: es el patrón de un formulario controlado dentro de una tarjeta. La rejilla de 12 columnas coloca los campos cortos en pareja y los largos a fila completa.",
1556
+ examples: [
1557
+ '<ITGrid container spacing={4}>\n <ITGrid item xs={12} md={6}>\n <ITInput name="nombre" label="Nombre" onChange={() => {}} />\n </ITGrid>\n <ITGrid item xs={12}>\n <ITInput name="email" label="Email" onChange={() => {}} />\n </ITGrid>\n</ITGrid>',
1558
+ '<ITFlex justify="end" gap={3}>\n <ITButton label="Cancelar" variant="outlined" size="sm" />\n <ITButton label="Guardar" size="sm" />\n</ITFlex>',
1559
+ ],
1560
+ props: [
1561
+ { name: "ITStack", type: "componente", description: "Columna raíz (`spacing={6}`) que separa el encabezado de la rejilla de campos." },
1562
+ { name: "ITGrid (container)", type: "componente", description: "Rejilla de 12 columnas con `spacing={4}` para repartir los campos." },
1563
+ { name: "ITGrid (item)", type: "componente", description: "`xs={12} md={6}` para los campos en pareja y `xs={12}` para los largos (email) y la fila de botones." },
1564
+ { name: "ITInput", type: "componente", description: "Campos de texto controlados con `name`, `label`, `placeholder` y `onChange`." },
1565
+ { name: "ITSelect", type: "componente", description: "Listas de opciones con `name`, `label` y `options` (`{ label, value }`)." },
1566
+ { name: "ITFlex", type: "componente", description: "Barra de acciones alineada a la derecha (`justify=\"end\" gap={3}`)." },
1567
+ { name: "ITButton", type: "componente", description: "“Cancelar” (`variant=\"outlined\"`) y “Guardar”; ambos con `size=\"sm\"`." },
1568
+ ],
1569
+ notes: [
1570
+ "Es una receta de composición: las props de la tabla son las piezas que usa, no una API propia.",
1571
+ "Formulario controlado: cada ITInput/ITSelect recibe `name` y `onChange`; ITInput los exige, aunque en el ejemplo el handler sea un no-op.",
1572
+ "Mismo criterio responsive que el dashboard: 1 columna en móvil (`xs={12}`) y 2 a partir de `md` (`md={6}`), con las acciones a ancho completo al final.",
1573
+ ],
1574
+ }}
1465
1575
  demo={
1466
1576
  <div className="w-full">
1467
1577
  <p className="mb-4 font-mono text-[11px] uppercase tracking-[0.14em] text-slate-500 dark:text-slate-400">
@@ -65,6 +65,26 @@ export const TabsShowcase = () => {
65
65
  />
66
66
  </>
67
67
  }
68
+ doc={{
69
+ summary:
70
+ "Navegación por pestañas con dos variantes visuales (`line` y `pill`) que gestiona internamente cuál está activa.",
71
+ examples: [
72
+ '<ITTabs items={[{ id: "general", label: "General", content: <GeneralPanel /> }]} />',
73
+ '<ITTabs variant="pill" defaultActiveId="security" onChange={setActive} items={tabs} />',
74
+ ],
75
+ props: [
76
+ { name: "items", type: "ITTabItem[]", required: true, description: "Pestañas a renderizar: { id, label, content, icon?, disabled? }." },
77
+ { name: "defaultActiveId", type: "string", default: "primer item", description: "`id` de la pestaña activa al montar." },
78
+ { name: "onChange", type: "(id: string) => void", description: "Se dispara al cambiar de pestaña, con el nuevo `id`." },
79
+ { name: "variant", type: '"line" | "pill"', default: '"line"', description: "`line` = indicador inferior; `pill` = fondo redondeado." },
80
+ { name: "className", type: "string", default: '""', description: "Clases adicionales de la fila de pestañas." },
81
+ { name: "containerClassName", type: "string", default: '""', description: "Clases adicionales del contenedor externo." },
82
+ ],
83
+ notes: [
84
+ "Estado no controlado: no existe `activeId`; si el `id` activo desaparece de `items`, el contenido queda vacío.",
85
+ 'Los botones son `<button>` nativos, pero sin roles de pestañas (`role="tab"`, `aria-selected`) ni navegación con flechas.',
86
+ ],
87
+ }}
68
88
  />
69
89
  );
70
90
  };
@@ -115,6 +135,35 @@ export const StepperShowcase = () => {
115
135
  </div>
116
136
  </>
117
137
  }
138
+ doc={{
139
+ summary:
140
+ "Asistente paso a paso (wizard) con indicador de progreso, panel de contenido y botones Atrás / Siguiente (Finalizar en el último paso).",
141
+ description:
142
+ "El paso activo es controlado (`currentStep`): cada paso define su propio contenido y el panel lo monta con transición.",
143
+ examples: [
144
+ "<ITStepper steps={steps} currentStep={step} onStepChange={setStep} onFinish={save} />",
145
+ '<ITStepper steps={steps} currentStep={0} useIcons scrollableContent maxContentHeight="320px" />',
146
+ ],
147
+ props: [
148
+ { name: "steps", type: "Step[]", required: true, description: "Pasos del flujo: { label, content, icon? }." },
149
+ { name: "currentStep", type: "number", required: true, description: "Índice base 0 del paso activo." },
150
+ { name: "onFinish", type: "() => void", description: "Se llama al pulsar «Finalizar» en el último paso." },
151
+ { name: "onStepChange", type: "(step: number) => void", description: "Se dispara en cada cambio de paso, con el nuevo índice." },
152
+ { name: "allowClickToJump", type: "boolean", default: "true", description: "Permite saltar a pasos completados o al actual desde sus indicadores." },
153
+ { name: "useIcons", type: "boolean", default: "false", description: "Muestra el icono de cada paso en lugar del número." },
154
+ { name: "disableNext", type: "boolean", default: "false", description: "Deshabilita «Siguiente» / «Finalizar»." },
155
+ { name: "scrollableContent", type: "boolean", default: "false", description: "Hace desplazable el panel de contenido." },
156
+ { name: "maxContentHeight", type: "string", default: '"400px"', description: "Altura máxima del panel desplazable (valor CSS)." },
157
+ { name: "color", type: "string", default: '"primary"', description: "Color semántico del tema para paso activo y botones; acepta también un color crudo." },
158
+ { name: "containerClassName", type: "string", description: "Clases adicionales del contenedor externo." },
159
+ { name: "stepClassName", type: "string", description: "Clases adicionales del panel de contenido." },
160
+ ],
161
+ notes: [
162
+ "«Atrás» se deshabilita en el primer paso; en el último, «Siguiente» pasa a «Finalizar» y dispara `onFinish`.",
163
+ "El contador «Paso N de M» se oculta por debajo del breakpoint `sm`.",
164
+ 'El panel usa `role="region"` con `aria-labelledby` y los indicadores `aria-current="step"`.',
165
+ ],
166
+ }}
118
167
  />
119
168
  );
120
169
  };
@@ -162,6 +211,32 @@ export const PaginationShowcase = () => {
162
211
  />
163
212
  </>
164
213
  }
214
+ doc={{
215
+ summary:
216
+ "Paginador con anterior/siguiente, números de página (con elipsis si hay muchas) y selector opcional de filas por página.",
217
+ examples: [
218
+ "<ITPagination currentPage={page} totalPages={10} onPageChange={setPage} />",
219
+ '<ITPagination currentPage={page} totalPages={20} onPageChange={setPage} color="secondary" siblingCount={2} />',
220
+ "<ITPagination currentPage={page} totalPages={20} onPageChange={setPage} itemsPerPageOptions={[10, 25, 50]} itemsPerPage={10} onItemsPerPageChange={setSize} totalItems={95} />",
221
+ ],
222
+ props: [
223
+ { name: "currentPage", type: "number", required: true, description: "Página activa (indexada en 1)." },
224
+ { name: "totalPages", type: "number", required: true, description: "Número total de páginas." },
225
+ { name: "onPageChange", type: "(page: number) => void", required: true, description: "Se dispara al pulsar una página o anterior/siguiente." },
226
+ { name: "siblingCount", type: "number", default: "1", description: "Páginas visibles antes y después de la actual." },
227
+ { name: "color", type: "string", default: '"primary"', description: "Color semántico del tema (primary, secondary, success, danger, warning, info, purple) o un valor crudo." },
228
+ { name: "className", type: "string", description: "Clases adicionales del contenedor." },
229
+ { name: "itemsPerPageOptions", type: "number[]", description: "Opciones del selector de filas por página; activa la variante con resumen." },
230
+ { name: "itemsPerPage", type: "number", description: "Valor actual de filas por página. Requerido junto a `itemsPerPageOptions`." },
231
+ { name: "onItemsPerPageChange", type: "(value: number) => void", description: "Se dispara al cambiar las filas por página." },
232
+ { name: "totalItems", type: "number", description: "Total de elementos; alimenta el resumen «X - Y de Z»." },
233
+ ],
234
+ notes: [
235
+ "Se auto-oculta (devuelve `null`) si `currentPage === 0` o si, sin selector, el rango tiene menos de 2 páginas.",
236
+ "La variante con `itemsPerPageOptions` monta un `ITSelect` y el resumen «X - Y de Z», que solo aparece si se pasa `totalItems`.",
237
+ 'Se envuelve en `<nav aria-label>`; anterior/siguiente usan `role="button"` con `aria-disabled` en los extremos.',
238
+ ],
239
+ }}
165
240
  />
166
241
  );
167
242
  };
@@ -197,6 +272,25 @@ export const TripleFilterShowcase = () => {
197
272
  <p className="text-slate-500">{t("showcase.nav.tripleFilterHint")}</p>
198
273
  </div>
199
274
  }
275
+ doc={{
276
+ summary:
277
+ "Filtro segmentado genérico estilo pill: solo una opción está activa y admite valores `string` o `boolean`.",
278
+ examples: [
279
+ '<ITTripleFilter value={filter} onChange={setFilter} options={[{ label: "Todos", value: "all" }, { label: "Activos", value: "active" }]} />',
280
+ '<ITTripleFilter value={onlyActive} onChange={setOnlyActive} options={[{ label: "Sí", value: true }, { label: "No", value: false }]} color="success" />',
281
+ ],
282
+ props: [
283
+ { name: "value", type: "T", required: true, description: "Valor seleccionado; debe coincidir con uno de `options[].value`." },
284
+ { name: "onChange", type: "(value: T) => void", required: true, description: "Se dispara al elegir otra opción, con el nuevo valor." },
285
+ { name: "options", type: "ITTripleFilterOption<T>[]", required: true, description: "Opciones a renderizar: { label, value }." },
286
+ { name: "color", type: "ColorsTypes", default: '"primary"', description: "Color del botón activo (primary, secondary, success, danger, warning, info, purple, error, gray)." },
287
+ { name: "className", type: "string", description: "Clases adicionales del contenedor." },
288
+ ],
289
+ notes: [
290
+ "Genérico sobre `T extends string | boolean`, así que el mismo componente sirve para estados y para banderas true/false.",
291
+ "Los botones no exponen `aria-pressed` ni `role`, por lo que el estado activo no se anuncia a lectores de pantalla.",
292
+ ],
293
+ }}
200
294
  />
201
295
  );
202
296
  };
@@ -389,6 +389,30 @@ export const PageHeaderShowcase = () => {
389
389
  </div>
390
390
  </ITStack>
391
391
  }
392
+ doc={{
393
+ summary: "Barra de título de página con breadcrumbs, descripción, botón de volver y acciones.",
394
+ description:
395
+ "Es la cabecera que ITPage delega por defecto. Cuando hay breadcrumbs o backAction dibuja una fila superior con el botón de volver y las migas a la izquierda, y mueve `actions` a esa fila; sin fila superior las acciones quedan en la fila del título.",
396
+ examples: [
397
+ '<ITPageHeader title="Team members" description="Manage invitations, roles and permissions" />',
398
+ '<ITPageHeader\n title="Usuarios"\n icon={<FaUsers size={20} />}\n iconColor="#6366f1"\n breadcrumbs={[{ label: "Admin", href: "#" }, { label: "Usuarios" }]}\n backAction={() => history.back()}\n actions={<ITButton label="Invitar" size="sm" />}\n/>',
399
+ '<ITPageHeader title="Reportes" actions={<ITButton label="Exportar" variant="outlined" size="sm" />} />',
400
+ ],
401
+ props: [
402
+ { name: "title", type: "string", required: true, description: "Título principal, renderizado como <h1>." },
403
+ { name: "description", type: "string", description: "Texto descriptivo bajo el título." },
404
+ { name: "breadcrumbs", type: "ITBreadcrumbItem[]", description: "Migas de pan ({ label, href?, onClick? }); el último ítem se pinta como texto plano." },
405
+ { name: "actions", type: "ReactNode", description: "Botones o elementos alineados a la derecha." },
406
+ { name: "backAction", type: "() => void", description: "Se dispara al pulsar el botón de volver; su presencia basta para mostrar la fila superior." },
407
+ { name: "icon", type: "ReactNode", description: "Icono mostrado dentro del recuadro redondo junto al título." },
408
+ { name: "iconColor", type: "string", description: "Color CSS aplicado al recuadro del icono (style.color)." },
409
+ { name: "className", type: "string", description: "Clases adicionales del contenedor." },
410
+ ],
411
+ notes: [
412
+ "El recuadro del icono (w-12 h-12 redondo) no se renderiza en aspecto flat (`useITFlatAppearance()`); con ese aspecto el título se reduce y `iconColor` deja de tener efecto.",
413
+ "El botón de volver es un círculo de 9×9 con aria-label \"Volver\" (i18n `pageHeader.back`).",
414
+ ],
415
+ }}
392
416
  />
393
417
  );
394
418
  };
@@ -957,6 +981,44 @@ export const PageShowcase = () => {
957
981
  </div>
958
982
  </ITStack>
959
983
  }
984
+ doc={{
985
+ summary: "Plantilla de página: ancho máximo, cabecera opcional (ITPageHeader) y estados integrados de carga, error y vacío.",
986
+ description:
987
+ "Envuelve el contenido con el ancho y el padding de página y, si hay algo que mostrar, delega la cabecera en ITPageHeader. Los tres estados sustituyen a los children y también conservan la cabecera.",
988
+ examples: [
989
+ '<ITPage title="Team members" description="Manage your organization workspace" actions={<ITButton label="Invite" size="sm" />}>\n <ITTable columns={columns} data={users} />\n</ITPage>',
990
+ '<ITPage title="Reportes" loading={loading} error={error} errorTitle="Error de carga" onRetry={refetch} maxWidth="6xl">\n <ReportBody />\n</ITPage>',
991
+ '<ITPage title="Tablero" horizontalPadding="px-0">\n <div>Contenido a sangre</div>\n</ITPage>',
992
+ ],
993
+ props: [
994
+ { name: "title", type: "string", description: "Título de la página, se pasa a la cabecera." },
995
+ { name: "description", type: "string", description: "Descripción bajo el título." },
996
+ { name: "breadcrumbs", type: "ITBreadcrumbItem[]", description: "Migas de pan para dar contexto de navegación." },
997
+ { name: "actions", type: "ReactNode", description: "Botones de acción de la cabecera." },
998
+ { name: "backAction", type: "() => void", description: "Callback del botón de volver." },
999
+ { name: "icon", type: "ReactNode", description: "Icono opcional junto al título." },
1000
+ { name: "iconColor", type: "string", description: "Color CSS del icono." },
1001
+ { name: "loading", type: "boolean", default: "false", description: "Muestra placeholders de esqueleto (tres skeletons rectangulares)." },
1002
+ { name: "error", type: "string | null", default: "null", description: "Mensaje del estado de error; dibuja un ITEmptyState con botón de reintento si hay onRetry." },
1003
+ { name: "errorTitle", type: "string", default: 'i18n common.error ("Error")', description: "Título del estado de error." },
1004
+ { name: "errorActionLabel", type: "string", default: 'i18n common.retry ("Reintentar")', description: "Etiqueta del botón de reintento." },
1005
+ { name: "onRetry", type: "() => void", description: "Callback del botón de reintento; solo se pinta si hay error." },
1006
+ { name: "empty", type: "boolean", default: "false", description: "Muestra el estado vacío." },
1007
+ { name: "emptyTitle", type: "string", default: 'i18n common.noData ("Sin datos")', description: "Título del estado vacío." },
1008
+ { name: "emptyDescription", type: "string", default: 'i18n common.empty ("No hay información para mostrar")', description: "Descripción del estado vacío." },
1009
+ { name: "emptyAction", type: "ReactNode", description: "Acción personalizada del estado vacío." },
1010
+ { name: "children", type: "ReactNode", required: true, description: "Contenido renderizado dentro del wrapper." },
1011
+ { name: "maxWidth", type: '"2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "7xl"', default: '"7xl"', description: "Ancho máximo del contenido." },
1012
+ { name: "noPadding", type: "boolean", default: "false", description: "Quita el padding por defecto y gana sobre horizontalPadding (también omite el py-6)." },
1013
+ { name: "horizontalPadding", type: "string", default: '"px-4 sm:px-6 lg:px-8"', description: "Clases Tailwind del gutter horizontal; debe ser un literal que Tailwind pueda escanear." },
1014
+ { name: "className", type: "string", description: "Clases adicionales del wrapper." },
1015
+ ],
1016
+ notes: [
1017
+ "Prioridad de estados: loading > error > empty; si ninguno aplica se renderizan los children. La cabecera se incluye en los cuatro casos si hay título, descripción, breadcrumbs, acciones, backAction o icono.",
1018
+ "Con noPadding se omiten a la vez el gutter horizontal y el py-6; horizontalPadding montado en runtime no llega al CSS generado por Tailwind, así que pásalo siempre literal.",
1019
+ "maxWidth se compone como `max-w-${maxWidth}` en runtime; los literales max-w-2xl…max-w-7xl están en el código fuente y Tailwind los genera.",
1020
+ ],
1021
+ }}
960
1022
  />
961
1023
  );
962
1024
  };
@@ -534,6 +534,33 @@ export const CardShowcase = () => {
534
534
  </GallerySection>
535
535
  </ITStack>
536
536
  }
537
+ doc={{
538
+ summary: "Contenedor de tarjeta con imagen, título, cuerpo y pie de acciones opcionales; con onClick se vuelve interactivo.",
539
+ description: "Pieza base de composición para agrupar contenido relacionado. Borde, radio y sombra se adaptan al aspecto del tema (flat / soft).",
540
+ examples: [
541
+ '<ITCard title="Ingresos del mes" actions={<ITButton label="Ver detalle" size="sm" />}>$48.230</ITCard>',
542
+ '<ITCard title="Curso" image="/courses/ts.jpg" imageClassName="h-32" onClick={() => open(course.id)}>…</ITCard>',
543
+ '<ITCard className="max-w-sm"><ITText muted>Tarjeta estática, sin cabecera ni acciones.</ITText></ITCard>',
544
+ ],
545
+ props: [
546
+ { name: "onClick", type: "() => void", default: "—", description: "Al recibirlo la tarjeta se vuelve clicable: cursor pointer, transición de sombra y elevación en hover. Sin él es estática." },
547
+ { name: "title", type: "string", default: "—", description: "Título de la cabecera; se renderiza como h3 mediante ITText." },
548
+ { name: "image", type: "string", default: "—", description: "URL de la imagen superior, a ancho completo con h-48 object-cover." },
549
+ { name: "alt", type: "string", default: "t(\"card.imageAlt\")", description: "Texto alternativo de la imagen; si se omite usa la key i18n card.imageAlt." },
550
+ { name: "children", type: "ReactNode", default: "—", description: "Contenido del cuerpo, debajo del título." },
551
+ { name: "actions", type: "ReactNode", default: "—", description: "Pie de acciones separado por un borde superior y anclado al fondo con mt-auto." },
552
+ { name: "className", type: "string", default: "—", description: "Clases del contenedor de la tarjeta." },
553
+ { name: "imageClassName", type: "string", default: "—", description: "Clases de la imagen; permite sobreescribir el h-48 por defecto." },
554
+ { name: "titleClassName", type: "string", default: "—", description: "Clases del título de la cabecera." },
555
+ { name: "contentClassName", type: "string", default: "—", description: "Clases del contenedor del cuerpo." },
556
+ { name: "actionClassName", type: "string", default: "—", description: "Clases del pie de acciones." },
557
+ ],
558
+ notes: [
559
+ "El aspecto del tema decide borde y sombra: en flat el borde es un hairline y solo hay sombra en hover si la tarjeta es clicable; en soft flota con --shadow-sm y sube a --shadow-lg con translateY(-2px).",
560
+ "Variables CSS: --it-card-bg, --it-card-border, --it-card-flat-border y --it-card-radius.",
561
+ "El padding es fijo (cuerpo 1.25rem, pie 1rem): no hay prop de tamaño.",
562
+ ],
563
+ }}
537
564
  />
538
565
  );
539
566
  };
@@ -910,6 +937,27 @@ export const TextShowcase = () => {
910
937
  </GallerySection>
911
938
  </ITStack>
912
939
  }
940
+ doc={{
941
+ summary: "Wrapper tipográfico semántico que renderiza cualquier elemento HTML (p, span, h1, label…) y reenvía los atributos estándar.",
942
+ description: "Aporta la capa de texto del sistema con el token muted; no impone clases tipográficas, así que tamaño, peso y color los define el className del consumidor.",
943
+ examples: [
944
+ '<ITText as="h1" className="text-2xl font-bold">Bienvenido</ITText>',
945
+ '<ITText as="p" muted className="text-sm">Descripción secundaria del panel.</ITText>',
946
+ '<ITText as="label" htmlFor="email">Correo electrónico</ITText>',
947
+ ],
948
+ props: [
949
+ { name: "children", type: "ReactNode", default: "—", description: "Contenido renderizado dentro del elemento." },
950
+ { name: "as", type: "ElementType", default: '"p"', description: "Elemento HTML a renderizar: p, span, h1, h2, label, code, etc." },
951
+ { name: "muted", type: "boolean", default: "false", description: "Aplica el color de texto atenuado (--color-text-muted) mediante style inline." },
952
+ { name: "htmlFor", type: "string", default: "—", description: "Asocia el texto con un campo de formulario; se reenvía como htmlFor cuando as es label." },
953
+ { name: "className", type: "string", default: "—", description: "Clases del elemento: definen tamaño, peso y color, porque el componente no añade ninguna." },
954
+ ],
955
+ notes: [
956
+ "Extiende HTMLAttributes<HTMLElement>, así que id, style, title, onClick, role y aria-* se reenvían al elemento renderizado.",
957
+ "muted se combina con el style propio: si el consumidor define color, gana el del consumidor.",
958
+ "Lo usan internamente otros componentes (avatar, button, badget, chip, checkbox, loader, radio, segmented-control, slider, textarea, tooltip) para etiquetas y textos.",
959
+ ],
960
+ }}
913
961
  />
914
962
  );
915
963
  };