@axzydev/axzy_ui_system 1.5.1 → 1.5.3

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.
@@ -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
  };
@@ -5,7 +5,8 @@ export const buttonVariants: Record<string, string> = {
5
5
  rounded: "border-transparent shadow-sm rounded-full",
6
6
  text: "bg-transparent border-transparent shadow-none hover:bg-black/5 dark:hover:bg-white/10",
7
7
  "raised-text": "bg-white border border-gray-200 shadow-sm hover:shadow-md",
8
- "icon-only": "p-2 aspect-square flex items-center justify-center border-transparent shadow-sm",
8
+ // No lleva utilidades de padding: el cuadrado lo define `buttonIconPadding` por tamaño.
9
+ "icon-only": "aspect-square flex items-center justify-center border-transparent shadow-sm",
9
10
  link: "bg-transparent border-transparent shadow-none hover:underline px-0",
10
11
  };
11
12
 
@@ -14,3 +15,20 @@ export const buttonSizes: Record<string, string> = {
14
15
  md: "text-[13px] px-3.5 py-1.5",
15
16
  lg: "text-sm px-4 py-2",
16
17
  };
18
+
19
+ /**
20
+ * Tipografía de `icon-only`: solo el tamaño de fuente, sin padding horizontal/vertical,
21
+ * para que el padding simétrico de `buttonIconPadding` no compita con `buttonSizes`.
22
+ */
23
+ export const buttonIconSizes: Record<string, string> = {
24
+ sm: "text-xs",
25
+ md: "text-[13px]",
26
+ lg: "text-sm",
27
+ };
28
+
29
+ /** Padding simétrico (px) del botón `icon-only` por tamaño; define el cuadrado con `aspect-square`. */
30
+ export const buttonIconPadding: Record<string, string> = {
31
+ sm: "0.5rem",
32
+ md: "0.625rem",
33
+ lg: "0.75rem",
34
+ };