@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.
- package/README.md +43 -0
- package/dist/index.css +1 -1
- package/dist/layered.css +1 -1
- package/package.json +4 -2
- package/src/showcases/DataShowcases.tsx +41 -0
- package/src/showcases/FeedbackShowcases.tsx +82 -0
- package/src/showcases/FormShowcases.tsx +311 -0
- package/src/showcases/LayoutPrimitivesShowcases.tsx +110 -0
- package/src/showcases/NavigationShowcases.tsx +94 -0
- package/src/showcases/PageShowcases.tsx +62 -0
- package/src/showcases/StructureShowcases.tsx +48 -0
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.
|
|
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",
|
|
@@ -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
|
};
|
|
@@ -183,6 +183,35 @@ export const ButtonShowcase = () => {
|
|
|
183
183
|
</div>
|
|
184
184
|
</div>
|
|
185
185
|
}
|
|
186
|
+
doc={{
|
|
187
|
+
summary:
|
|
188
|
+
"Botón de acción con colores semánticos, ocho variantes visuales, tres tamaños e icono opcional.",
|
|
189
|
+
description:
|
|
190
|
+
"La apariencia base sale de theme.button[color] y el tamaño de buttonSizes; admite label o children y mantiene sus propios estados de hover y focus.",
|
|
191
|
+
examples: [
|
|
192
|
+
'<ITButton label="Guardar" color="success" onClick={handleSave} />',
|
|
193
|
+
'<ITButton label="Sincronizar" icon={<FaSync />} variant="outlined" color="gray" size="sm" />',
|
|
194
|
+
'<ITButton variant="icon-only" color="danger" ariaLabel="Eliminar">🗑</ITButton>',
|
|
195
|
+
],
|
|
196
|
+
props: [
|
|
197
|
+
{ name: "label", type: "string", description: "Texto del botón. Se ignora si se pasa children." },
|
|
198
|
+
{ name: "children", type: "ReactNode", description: "Contenido propio; tiene prioridad sobre label e icon." },
|
|
199
|
+
{ name: "icon", type: "ReactNode", description: "Icono renderizado antes del label (no se usa con children)." },
|
|
200
|
+
{ name: "onClick", type: "() => void", description: "Handler de click del botón." },
|
|
201
|
+
{ name: "color", type: '"primary" | "secondary" | "success" | "danger" | "warning" | "info" | "purple" | "error" | "gray"', default: '"primary"', description: "Clave de color semántica del tema." },
|
|
202
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del botón (padding y tipografía)." },
|
|
203
|
+
{ name: "variant", type: '"filled" | "outlined" | "raised" | "rounded" | "text" | "raised-text" | "icon-only" | "link"', default: '"filled"', description: "Estilo visual del botón." },
|
|
204
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Deshabilita la interacción y aplica opacidad reducida." },
|
|
205
|
+
{ name: "type", type: '"submit" | "reset" | "button"', default: '"button"', description: "Atributo type del elemento button nativo." },
|
|
206
|
+
{ name: "ariaLabel", type: "string", description: "Nombre accesible para lectores de pantalla; si falta cae en label." },
|
|
207
|
+
{ name: "title", type: "string", description: "Tooltip nativo; si falta cae en ariaLabel o label." },
|
|
208
|
+
{ name: "className", type: "string", description: "Clases CSS adicionales del botón." },
|
|
209
|
+
],
|
|
210
|
+
notes: [
|
|
211
|
+
"Con children no se renderizan label ni icon.",
|
|
212
|
+
'Usa variant="icon-only" junto a ariaLabel cuando el botón no tenga texto visible.',
|
|
213
|
+
],
|
|
214
|
+
}}
|
|
186
215
|
/>
|
|
187
216
|
);
|
|
188
217
|
};
|
|
@@ -712,6 +741,55 @@ export const InputShowcase = () => {
|
|
|
712
741
|
</div>
|
|
713
742
|
) : undefined
|
|
714
743
|
}
|
|
744
|
+
doc={{
|
|
745
|
+
summary:
|
|
746
|
+
"Campo de entrada con label, validación y slots de iconos; cubre text, password, email, number (con moneda o miles), checkbox, radio y textarea.",
|
|
747
|
+
description:
|
|
748
|
+
"Comparte el tema theme.input con el resto del sistema: el error solo se muestra cuando el campo está tocado y los tipos numéricos se reformatean al perder el foco.",
|
|
749
|
+
examples: [
|
|
750
|
+
'<ITInput name="email" label="Correo" value={email} onChange={(e) => setEmail(e.target.value)} onBlur={() => {}} />',
|
|
751
|
+
'<ITInput name="monto" label="Monto" type="number" currencyFormat value={monto} onChange={onChange} onBlur={() => {}} />',
|
|
752
|
+
'<ITInput name="nota" label="Nota" type="textarea" rows={4} maxLength={200} showHintLength value={nota} onChange={onChange} onBlur={() => {}} />',
|
|
753
|
+
],
|
|
754
|
+
props: [
|
|
755
|
+
{ name: "name", type: "string", required: true, description: "Nombre del campo; se usa como id del control y del label." },
|
|
756
|
+
{ name: "type", type: '"text" | "password" | "number" | "email" | "checkbox" | "radio" | "textarea"', default: '"text"', description: "Tipo de control a renderizar." },
|
|
757
|
+
{ name: "label", type: "string", description: "Etiqueta mostrada encima del campo." },
|
|
758
|
+
{ name: "value", type: "any", description: "Valor actual del campo (controlado)." },
|
|
759
|
+
{ name: "onChange", type: "(event: any) => void", required: true, description: "Handler de cambio; recibe un evento con target.name y target.value." },
|
|
760
|
+
{ name: "onBlur", type: "(event: FocusEvent) => void", description: "Handler de blur; también dispara el formateo numérico." },
|
|
761
|
+
{ name: "placeholder", type: "string", description: "Texto de ayuda dentro del campo." },
|
|
762
|
+
{ name: "touched", type: "boolean", description: "Marca el campo como tocado; sin él se usa el blur interno para decidir cuándo mostrar el error." },
|
|
763
|
+
{ name: "error", type: "string | boolean", description: "Mensaje de error propio, o true para el mensaje genérico de campo requerido." },
|
|
764
|
+
{ name: "required", type: "boolean", default: "false", description: "Asterisco y error automático si el campo está vacío." },
|
|
765
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del campo." },
|
|
766
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Deshabilita el campo." },
|
|
767
|
+
{ name: "readOnly", type: "boolean", default: "false", description: "Renderiza el campo en modo solo lectura." },
|
|
768
|
+
{ name: "currencyFormat", type: "boolean", default: "false", description: "Formatea el número como moneda (locale es-MX)." },
|
|
769
|
+
{ name: "decimals", type: "number", default: "currencyFormat ? 2 : 0", description: "Decimales permitidos; 0 restringe a enteros." },
|
|
770
|
+
{ name: "formatNumber", type: "boolean", default: "true", description: "Activa el formateo de miles al perder el foco." },
|
|
771
|
+
{ name: "maxLength", type: "number", description: "Longitud máxima permitida." },
|
|
772
|
+
{ name: "minLength", type: "number", description: "Longitud mínima permitida." },
|
|
773
|
+
{ name: "showHintLength", type: "boolean", default: "false", description: "Muestra el contador de caracteres (requiere minLength o maxLength)." },
|
|
774
|
+
{ name: "iconLeft", type: "ReactNode", description: "Icono a la izquierda del campo." },
|
|
775
|
+
{ name: "iconRight", type: "ReactNode", description: "Icono a la derecha del campo." },
|
|
776
|
+
{ name: "rows", type: "number", default: "4", description: "Filas visibles cuando type es textarea." },
|
|
777
|
+
{ name: "min", type: "number", description: "Valor numérico mínimo; se recorta al salir del campo." },
|
|
778
|
+
{ name: "max", type: "number", description: "Valor numérico máximo; se recorta al salir del campo." },
|
|
779
|
+
{ name: "checked", type: "boolean", description: "Estado marcado para checkbox y radio." },
|
|
780
|
+
{ name: "autoFocus", type: "boolean", default: "false", description: "Enfoca el campo al montar." },
|
|
781
|
+
{ name: "focusContent", type: "boolean", description: "Selecciona todo el contenido al hacer click." },
|
|
782
|
+
{ name: "onKeyDown", type: "(event: KeyboardEvent) => void", description: "Handler de keydown del control." },
|
|
783
|
+
{ name: "containerClassName", type: "string", description: "Clases CSS del contenedor externo." },
|
|
784
|
+
{ name: "labelClassName", type: "string", description: "Clases CSS de la etiqueta." },
|
|
785
|
+
{ name: "className", type: "string", description: "Clases CSS del control." },
|
|
786
|
+
],
|
|
787
|
+
notes: [
|
|
788
|
+
"El error solo aparece cuando el campo está tocado (touched explícito o blur interno); con required y vacío se usa el mensaje genérico del sistema.",
|
|
789
|
+
'En type="number" el control real es un input de texto con inputMode y filtrado de teclas; en blur se recorta a min/max y se reformatea.',
|
|
790
|
+
"El tipo password incluye un botón de ojo con aria-label traducido.",
|
|
791
|
+
],
|
|
792
|
+
}}
|
|
715
793
|
/>
|
|
716
794
|
</div>
|
|
717
795
|
);
|
|
@@ -772,6 +850,39 @@ export const SelectShowcase = () => {
|
|
|
772
850
|
<ITSelect name="s2" label={t("showcase.forms.selectWithError")} options={[]} error={t("showcase.forms.requiredFieldError2")} touched onChange={() => { }} />
|
|
773
851
|
</div>
|
|
774
852
|
}
|
|
853
|
+
doc={{
|
|
854
|
+
summary:
|
|
855
|
+
"Selector desplegable basado en el select nativo, estilizado con los tokens de input del sistema.",
|
|
856
|
+
description:
|
|
857
|
+
"Admite label, opción vacía, validación con touched/error/required y modo solo lectura; los colores y estados se resuelven desde theme.input.",
|
|
858
|
+
examples: [
|
|
859
|
+
'<ITSelect name="role" label="Rol" value={role} options={roles} onChange={(e) => setRole(e.target.value)} />',
|
|
860
|
+
'<ITSelect name="pais" label="País" placeholder="Elige un país" options={countries} required touched error={!pais ? "Requerido" : undefined} onChange={onChange} />',
|
|
861
|
+
],
|
|
862
|
+
props: [
|
|
863
|
+
{ name: "name", type: "string", required: true, description: "Atributo name del select nativo (también su id)." },
|
|
864
|
+
{ name: "options", type: "OptionType[]", required: true, description: "Arreglo de objetos clave-valor con value/label." },
|
|
865
|
+
{ name: "valueField", type: "string", default: '"value"', description: "Clave de cada opción usada como valor." },
|
|
866
|
+
{ name: "labelField", type: "string", default: '"label"', description: "Clave de cada opción usada como etiqueta visible." },
|
|
867
|
+
{ name: "label", type: "string", description: "Etiqueta sobre el select (vinculada por htmlFor)." },
|
|
868
|
+
{ name: "placeholder", type: "string", description: "Texto de la opción vacía; sin él se usa el mensaje traducido del sistema." },
|
|
869
|
+
{ name: "value", type: "string", description: "Valor seleccionado (controlado)." },
|
|
870
|
+
{ name: "onChange", type: "(event: ChangeEvent<HTMLSelectElement>) => void", description: "Se llama al cambiar la selección; recibe el evento nativo." },
|
|
871
|
+
{ name: "onBlur", type: "(event: FocusEvent<HTMLSelectElement>) => void", description: "Se llama al perder el foco." },
|
|
872
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del control." },
|
|
873
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Deshabilita el select." },
|
|
874
|
+
{ name: "readOnly", type: "boolean", default: "false", description: "Muestra el valor actual como una única opción deshabilitada." },
|
|
875
|
+
{ name: "touched", type: "boolean", description: "Marca el campo como tocado para mostrar el error." },
|
|
876
|
+
{ name: "error", type: "string | boolean", description: "Mensaje de error, o true para el error por defecto." },
|
|
877
|
+
{ name: "required", type: "boolean", description: "Marca el campo como requerido (asterisco y error por defecto)." },
|
|
878
|
+
{ name: "className", type: "string", description: "Clases extra aplicadas al elemento select." },
|
|
879
|
+
],
|
|
880
|
+
notes: [
|
|
881
|
+
"Es un select nativo con appearance none y un icono absoluto: conserva el comportamiento del navegador, incluido el teclado.",
|
|
882
|
+
"La opción vacía se renderiza siempre como primera opción con value vacío.",
|
|
883
|
+
"El error solo se muestra cuando el campo está tocado (touched explícito o blur interno).",
|
|
884
|
+
],
|
|
885
|
+
}}
|
|
775
886
|
/>
|
|
776
887
|
);
|
|
777
888
|
};
|
|
@@ -879,6 +990,47 @@ export const SearchSelectShowcase = () => {
|
|
|
879
990
|
</div>
|
|
880
991
|
</>
|
|
881
992
|
}
|
|
993
|
+
doc={{
|
|
994
|
+
summary:
|
|
995
|
+
"Combobox de selección única con búsqueda, filtrado local o remoto y plantilla de opción personalizable.",
|
|
996
|
+
description:
|
|
997
|
+
"En modo local filtra las options en cliente; con onSearch delega la búsqueda al consumidor (debounce de 500 ms) y muestra el estado de carga. El panel se renderiza en un portal.",
|
|
998
|
+
examples: [
|
|
999
|
+
'<ITSearchSelect name="country" label="País" value={country} options={countries} onChange={(value) => setCountry(value)} />',
|
|
1000
|
+
'<ITSearchSelect name="cliente" label="Cliente" options={results} onSearch={fetchClients} isLoading={loading} onChange={setClient} />',
|
|
1001
|
+
'<ITSearchSelect name="pais" label="País" value={country} options={countries} renderOption={(option) => <span>{option.flag} {option.label}</span>} onChange={setCountry} />',
|
|
1002
|
+
],
|
|
1003
|
+
props: [
|
|
1004
|
+
{ name: "name", type: "string", description: "Atributo name para integración con formularios." },
|
|
1005
|
+
{ name: "label", type: "string", description: "Etiqueta mostrada sobre el campo." },
|
|
1006
|
+
{ name: "placeholder", type: "string", description: "Texto cuando no hay valor seleccionado." },
|
|
1007
|
+
{ name: "value", type: "string | number", description: "Valor seleccionado actual." },
|
|
1008
|
+
{ name: "options", type: "ITSearchSelectOption[]", description: "Opciones para el modo local (filtrado en cliente)." },
|
|
1009
|
+
{ name: "valueField", type: "string", default: '"value"', description: "Campo usado como valor de la opción." },
|
|
1010
|
+
{ name: "labelField", type: "string", default: '"label"', description: "Campo usado como etiqueta de la opción." },
|
|
1011
|
+
{ name: "onChange", type: "(value: string | number, option?: ITSearchSelectOption) => void", description: "Se llama al cambiar el valor; recibe el valor y la opción completa." },
|
|
1012
|
+
{ name: "onBlur", type: "(e: FocusEvent) => void", description: "Se llama al perder el foco el input." },
|
|
1013
|
+
{ name: "onSearch", type: "(query: string) => void", description: "Búsqueda en servidor con debounce de 500 ms; desactiva el filtrado local." },
|
|
1014
|
+
{ name: "isLoading", type: "boolean", default: "false", description: "Muestra el spinner mientras llegan las opciones." },
|
|
1015
|
+
{ name: "noResultsMessage", type: "string", description: "Mensaje mostrado cuando no hay resultados." },
|
|
1016
|
+
{ name: "renderOption", type: "(option, state: { isSelected, searchTerm }) => ReactNode", description: "Plantilla personalizada de cada opción del desplegable." },
|
|
1017
|
+
{ name: "clearable", type: "boolean", default: "true", description: "Muestra el botón de limpiar (X) dentro del campo." },
|
|
1018
|
+
{ name: "onClear", type: "() => void", description: "Se llama al pulsar el botón de limpiar." },
|
|
1019
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del campo." },
|
|
1020
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Deshabilita el componente." },
|
|
1021
|
+
{ name: "readOnly", type: "boolean", default: "false", description: "Modo solo lectura." },
|
|
1022
|
+
{ name: "touched", type: "boolean", description: "Indica que el campo fue interactuado (validación)." },
|
|
1023
|
+
{ name: "required", type: "boolean", description: "Marca el campo como requerido (asterisco rojo)." },
|
|
1024
|
+
{ name: "error", type: "string | boolean", description: "Mensaje de error, o true para el estado de error." },
|
|
1025
|
+
{ name: "className", type: "string", description: "Clases adicionales del contenedor." },
|
|
1026
|
+
],
|
|
1027
|
+
notes: [
|
|
1028
|
+
"Con onSearch el componente no filtra options: el consumidor debe devolver la lista ya filtrada.",
|
|
1029
|
+
"El panel vive en un portal sobre document.body, así que no se recorta en contenedores con overflow oculto.",
|
|
1030
|
+
"Los ítems del desplegable son divs clicables sin role de listbox/option ni navegación por teclado declarada.",
|
|
1031
|
+
"Al limpiar se emite onChange con valor vacío y después onClear.",
|
|
1032
|
+
],
|
|
1033
|
+
}}
|
|
882
1034
|
/>
|
|
883
1035
|
);
|
|
884
1036
|
};
|
|
@@ -955,6 +1107,42 @@ export const DatePickerShowcase = () => {
|
|
|
955
1107
|
</div>
|
|
956
1108
|
</>
|
|
957
1109
|
}
|
|
1110
|
+
doc={{
|
|
1111
|
+
summary:
|
|
1112
|
+
"Selector de fecha con modo simple y modo rango, entrada manual DD/MM/AAAA y popover de calendario.",
|
|
1113
|
+
description:
|
|
1114
|
+
"Emite el cambio como un evento con name y value en target (un Date, o una tupla [inicio, fin] en modo rango) y permite acotar los días con minDate y maxDate.",
|
|
1115
|
+
examples: [
|
|
1116
|
+
'<ITDatePicker name="fecha" label="Fecha" value={date} onChange={(e) => setDate(e.target.value)} />',
|
|
1117
|
+
'<ITDatePicker name="rango" label="Periodo" range value={range} minDate={hoy} onChange={(e) => setRange(e.target.value)} />',
|
|
1118
|
+
'<ITDatePicker name="fecha" label="Fecha" value={date} variant="success" size="sm" onChange={onChange} />',
|
|
1119
|
+
],
|
|
1120
|
+
props: [
|
|
1121
|
+
{ name: "name", type: "string", required: true, description: "Atributo name del input subyacente." },
|
|
1122
|
+
{ name: "value", type: "Date | [Date | null, Date | null]", description: "Fecha única, o tupla [inicio, fin] cuando range es true." },
|
|
1123
|
+
{ name: "onChange", type: "(event: { target: { name, value } }) => void", required: true, description: "Se dispara al cambiar la fecha; el evento lleva name y value en target." },
|
|
1124
|
+
{ name: "onBlur", type: "(event: { target: { name, value } }) => void", description: "Se dispara al perder el foco el input." },
|
|
1125
|
+
{ name: "range", type: "boolean", default: "false", description: "Activa la selección por rango; value pasa a ser la tupla inicio-fin." },
|
|
1126
|
+
{ name: "variant", type: "ColorsTypes", default: '"primary"', description: "Variante de color del sistema." },
|
|
1127
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del campo." },
|
|
1128
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Deshabilita el date picker, incluido el popover." },
|
|
1129
|
+
{ name: "placeholder", type: "string", description: "Texto cuando no hay fecha; por defecto dd/mm/aaaa (o el equivalente de rango)." },
|
|
1130
|
+
{ name: "label", type: "string", description: "Etiqueta sobre el input." },
|
|
1131
|
+
{ name: "required", type: "boolean", description: "Marca el campo como requerido." },
|
|
1132
|
+
{ name: "touched", type: "boolean", description: "Indica visualmente que el campo fue interactuado." },
|
|
1133
|
+
{ name: "error", type: "string | boolean", description: "Mensaje de error, o true para el estilo de error por defecto." },
|
|
1134
|
+
{ name: "minDate", type: "Date", description: "Fecha mínima seleccionable." },
|
|
1135
|
+
{ name: "maxDate", type: "Date", description: "Fecha máxima seleccionable." },
|
|
1136
|
+
{ name: "className", type: "string", description: "Clases CSS adicionales del contenedor." },
|
|
1137
|
+
{ name: "calendarClassName", type: "string", description: "Clases CSS adicionales del popover del calendario." },
|
|
1138
|
+
],
|
|
1139
|
+
notes: [
|
|
1140
|
+
"El texto solo acepta dígitos (máximo 8) y se enmascara como DD/MM/AAAA; en modo simple, una fecha inválida se sustituye por hoy al salir del campo.",
|
|
1141
|
+
"En modo rango, al elegir la segunda fecha se emite el cambio y el popover se cierra; con solo la fecha de inicio el valor queda pendiente y se muestra como inicio - ...",
|
|
1142
|
+
"El calendario interno se instancia con selectionMode range/single y recibe minDate, maxDate y variant.",
|
|
1143
|
+
"Limitación conocida: al teclear a mano una fecha válida, onChange se invoca dos veces.",
|
|
1144
|
+
],
|
|
1145
|
+
}}
|
|
958
1146
|
/>
|
|
959
1147
|
);
|
|
960
1148
|
};
|
|
@@ -1014,6 +1202,37 @@ export const TimePickerShowcase = () => {
|
|
|
1014
1202
|
</div>
|
|
1015
1203
|
</>
|
|
1016
1204
|
}
|
|
1205
|
+
doc={{
|
|
1206
|
+
summary:
|
|
1207
|
+
"Campo de hora con formato 24 h y desplegable de doble columna (horas y minutos).",
|
|
1208
|
+
description:
|
|
1209
|
+
"Permite escribir a mano con autoformato HH:MM o elegir del panel; el desplegable se renderiza en un portal para no recortarse.",
|
|
1210
|
+
examples: [
|
|
1211
|
+
'<ITTimePicker name="inicio" label="Hora de inicio" value={time} onChange={(e) => setTime(e.target.value)} />',
|
|
1212
|
+
'<ITTimePicker name="fin" label="Hora de fin" value={time} color="success" size="sm" onBlur={() => {}} onChange={onChange} />',
|
|
1213
|
+
],
|
|
1214
|
+
props: [
|
|
1215
|
+
{ name: "name", type: "string", required: true, description: "Atributo name del campo y clave del evento de cambio." },
|
|
1216
|
+
{ name: "value", type: "string", description: "Hora controlada en formato HH:MM." },
|
|
1217
|
+
{ name: "label", type: "string", description: "Etiqueta mostrada sobre el input." },
|
|
1218
|
+
{ name: "placeholder", type: "string", default: '"HH:MM"', description: "Texto cuando no hay valor." },
|
|
1219
|
+
{ name: "onChange", type: "(e: any) => void", required: true, description: "Se dispara solo con una hora válida; recibe target.name y target.value." },
|
|
1220
|
+
{ name: "onBlur", type: "(e: any) => void", description: "Se llama al perder el foco el input." },
|
|
1221
|
+
{ name: "required", type: "boolean", description: "Marca el campo como requerido." },
|
|
1222
|
+
{ name: "touched", type: "boolean", description: "Indica que el campo fue interactuado." },
|
|
1223
|
+
{ name: "error", type: "string | boolean", description: "Mensaje de error, o true para mostrar el estado de error." },
|
|
1224
|
+
{ name: "disabled", type: "boolean", description: "Deshabilita el time picker." },
|
|
1225
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del campo." },
|
|
1226
|
+
{ name: "variant", type: '"primary" | "secondary" | "danger" | "success" | "warning" | "info" | "purple"', default: '"primary"', description: "Variante de estilo del input." },
|
|
1227
|
+
{ name: "color", type: "string", default: '"primary"', description: "Color del resaltado del desplegable y del botón de confirmación; acepta claves semánticas o un hex." },
|
|
1228
|
+
{ name: "className", type: "string", description: "Clases CSS adicionales del wrapper." },
|
|
1229
|
+
],
|
|
1230
|
+
notes: [
|
|
1231
|
+
"El input solo acepta dígitos (máximo 4) e inserta los dos puntos automáticamente: 0930 se convierte en 09:30.",
|
|
1232
|
+
"Valida el formato 24 h; mientras el texto es inválido muestra Hora inválida y no emite onChange.",
|
|
1233
|
+
"El desplegable se monta en un portal sobre document.body, con columnas de 24 horas y 60 minutos.",
|
|
1234
|
+
],
|
|
1235
|
+
}}
|
|
1017
1236
|
/>
|
|
1018
1237
|
);
|
|
1019
1238
|
};
|
|
@@ -1087,6 +1306,39 @@ export const CalendarShowcase = () => {
|
|
|
1087
1306
|
/>
|
|
1088
1307
|
</>
|
|
1089
1308
|
}
|
|
1309
|
+
doc={{
|
|
1310
|
+
summary:
|
|
1311
|
+
"Calendario y planificador con vistas de mes, semana y día, eventos, selección simple o por rango y arrastre de franjas.",
|
|
1312
|
+
description:
|
|
1313
|
+
"Con events y los callbacks de evento/franja funciona como scheduler; con onChange y selectionMode entra en modo date picker. Usa date-fns con locale es.",
|
|
1314
|
+
examples: [
|
|
1315
|
+
'<ITCalendar events={events} mode="month" onEventClick={(evt) => openEvent(evt)} />',
|
|
1316
|
+
'<ITCalendar events={events} mode="week" variant="success" onSlotClick={(date) => createEvent(date)} />',
|
|
1317
|
+
'<ITCalendar value={date} selectionMode="range" startDate={inicio} endDate={fin} onChange={setDate} />',
|
|
1318
|
+
],
|
|
1319
|
+
props: [
|
|
1320
|
+
{ name: "events", type: "CalendarEvent[]", description: "Eventos del planificador: id, title, start, end, color y data." },
|
|
1321
|
+
{ name: "mode", type: '"week" | "day" | "month"', description: "Vista inicial. Si se omite, es month cuando hay onChange y week en caso contrario." },
|
|
1322
|
+
{ name: "selectionMode", type: '"single" | "range"', default: '"single"', description: "Modo de selección del date picker." },
|
|
1323
|
+
{ name: "value", type: "Date", description: "Fecha seleccionada en modo picker." },
|
|
1324
|
+
{ name: "onChange", type: "(date: Date) => void", description: "Se llama al seleccionar una fecha en modo picker." },
|
|
1325
|
+
{ name: "startDate", type: "Date", description: "Inicio del rango seleccionado." },
|
|
1326
|
+
{ name: "endDate", type: "Date", description: "Fin del rango seleccionado." },
|
|
1327
|
+
{ name: "onEventClick", type: "(event: CalendarEvent) => void", description: "Se llama al hacer click en un evento." },
|
|
1328
|
+
{ name: "onSlotClick", type: "(date: Date) => void", description: "Se llama al hacer click en una franja vacía." },
|
|
1329
|
+
{ name: "onSlotHover", type: "(date: Date) => void", description: "Se llama cuando el cursor entra en una franja." },
|
|
1330
|
+
{ name: "onSelectRange", type: "(start: Date, end: Date) => void", description: "Se llama al confirmar un rango seleccionado por arrastre." },
|
|
1331
|
+
{ name: "minDate", type: "Date", description: "Fecha mínima seleccionable (solo aplica en la vista de mes)." },
|
|
1332
|
+
{ name: "maxDate", type: "Date", description: "Fecha máxima seleccionable (solo aplica en la vista de mes)." },
|
|
1333
|
+
{ name: "variant", type: "ColorsTypes", default: '"primary"', description: "Color de acento de la selección y del día de hoy." },
|
|
1334
|
+
{ name: "className", type: "string", description: "Clases CSS adicionales del contenedor." },
|
|
1335
|
+
],
|
|
1336
|
+
notes: [
|
|
1337
|
+
"Necesita un contenedor con altura definida: el calendario ocupa el 100% de la altura de su padre.",
|
|
1338
|
+
"Las vistas de semana y día cubren de 6:00 a 22:00 en franjas de 30 minutos; la línea de la hora actual solo aparece si el día mostrado es hoy.",
|
|
1339
|
+
"onSelectRange habilita el arrastre y suma 30 minutos al final de la franja elegida.",
|
|
1340
|
+
],
|
|
1341
|
+
}}
|
|
1090
1342
|
/>
|
|
1091
1343
|
);
|
|
1092
1344
|
};
|
|
@@ -1182,6 +1434,33 @@ export const SlideToggleShowcase = () => {
|
|
|
1182
1434
|
/>
|
|
1183
1435
|
</div>
|
|
1184
1436
|
}
|
|
1437
|
+
doc={{
|
|
1438
|
+
summary:
|
|
1439
|
+
"Interruptor estilo iOS con colores del tema, tres tamaños y modos controlado o no controlado.",
|
|
1440
|
+
description:
|
|
1441
|
+
"Expone role switch y aria-checked; funciona con isOn (controlado) o con initialState más onToggle (no controlado).",
|
|
1442
|
+
examples: [
|
|
1443
|
+
'<ITSlideToggle isOn={enabled} onToggle={setEnabled} ariaLabel="Notificaciones" />',
|
|
1444
|
+
'<ITSlideToggle initialState activeColor="primary" size="lg" label="Modo oscuro" onToggle={setDark} />',
|
|
1445
|
+
'<ITSlideToggle isOn={false} disabled size="sm" ariaLabel="No disponible" />',
|
|
1446
|
+
],
|
|
1447
|
+
props: [
|
|
1448
|
+
{ name: "isOn", type: "boolean", description: "Estado controlado; si se pasa, el valor externo manda." },
|
|
1449
|
+
{ name: "initialState", type: "boolean", default: "false", description: "Estado inicial en uso no controlado." },
|
|
1450
|
+
{ name: "onToggle", type: "(value: boolean) => void", description: "Se llama al cambiar el interruptor con el nuevo estado." },
|
|
1451
|
+
{ name: "activeColor", type: "string", default: '"success"', description: "Color activo: clave semántica del tema o valor hex." },
|
|
1452
|
+
{ name: "inactiveColor", type: "string", default: '"#9ca3af"', description: "Color inactivo: clave semántica del tema o valor hex." },
|
|
1453
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño del interruptor." },
|
|
1454
|
+
{ name: "disabled", type: "boolean", default: "false", description: "Deshabilita el interruptor." },
|
|
1455
|
+
{ name: "label", type: "string", description: "Texto visible del interruptor, que también sirve como nombre accesible." },
|
|
1456
|
+
{ name: "ariaLabel", type: "string", description: "Nombre accesible cuando la etiqueta visible vive fuera; tiene prioridad sobre label." },
|
|
1457
|
+
{ name: "className", type: "string", description: "Clases CSS adicionales del contenedor." },
|
|
1458
|
+
],
|
|
1459
|
+
notes: [
|
|
1460
|
+
"Renderiza role switch y responde a Enter y Space; sin label ni ariaLabel el lector de pantalla no sabe qué se está activando.",
|
|
1461
|
+
"Con disabled aplica opacidad reducida y su tabIndex pasa a -1.",
|
|
1462
|
+
],
|
|
1463
|
+
}}
|
|
1185
1464
|
/>
|
|
1186
1465
|
);
|
|
1187
1466
|
};
|
|
@@ -1231,6 +1510,38 @@ export const DropfileShowcase = () => {
|
|
|
1231
1510
|
/>
|
|
1232
1511
|
</>
|
|
1233
1512
|
}
|
|
1513
|
+
doc={{
|
|
1514
|
+
summary:
|
|
1515
|
+
"Zona de carga por arrastrar y soltar con previsualización, badge de estado y flujo seleccionar-confirmar-subir.",
|
|
1516
|
+
description:
|
|
1517
|
+
"Admite tipos MIME configurables, vista previa de imágenes y dos presentaciones: la zona de drop inline o un botón que la abre dentro de un ITDialog.",
|
|
1518
|
+
examples: [
|
|
1519
|
+
'<ITDropfile onFileSelect={setFile} onSubmit={(file) => upload(file)} acceptedFileTypes={[FileTypeEnum.PDF, FileTypeEnum.XLSX]} />',
|
|
1520
|
+
'<ITDropfile onFileSelect={setFile} uploadStatus={status} onStatusChange={setStatus} showStatusBadge />',
|
|
1521
|
+
'<ITDropfile view="button" buttonLabel="Subir archivo" onFileSelect={setFile} />',
|
|
1522
|
+
],
|
|
1523
|
+
props: [
|
|
1524
|
+
{ name: "onFileSelect", type: "(file: File | null) => void", required: true, description: "Se llama al confirmar o limpiar la selección (null al cancelar)." },
|
|
1525
|
+
{ name: "onSubmit", type: "(file: File) => void", description: "Se llama al confirmar el archivo para subirlo." },
|
|
1526
|
+
{ name: "onCancel", type: "() => void", description: "Se llama cuando el usuario cancela la selección actual." },
|
|
1527
|
+
{ name: "acceptedFileTypes", type: "FileTypeEnum[]", default: "[PDF, XLS, XLSX, JPG, PNG, JPEG]", description: "Tipos MIME aceptados." },
|
|
1528
|
+
{ name: "uploadStatus", type: "UploadStatus", description: "Estado controlado; sin él el componente gestiona el suyo (pendiente, subiendo, subido, error)." },
|
|
1529
|
+
{ name: "onStatusChange", type: "(status: UploadStatus) => void", description: "Se llama en cada cambio de estado de subida." },
|
|
1530
|
+
{ name: "showStatusBadge", type: "boolean", default: "true", description: "Muestra el badge de estado sobre la previsualización." },
|
|
1531
|
+
{ name: "initialPreviewUrl", type: "string | null", description: "Previsualización inicial; si existe se asume el estado subido." },
|
|
1532
|
+
{ name: "view", type: '"drop" | "button"', default: '"drop"', description: "Presentación: zona de drop inline o botón que abre la zona en un modal." },
|
|
1533
|
+
{ name: "buttonLabel", type: "string", default: '"Subir archivo"', description: "Etiqueta del botón cuando view es button y aún no hay archivo." },
|
|
1534
|
+
{ name: "modalTitle", type: "string", description: "Título del modal; si falta usa buttonLabel." },
|
|
1535
|
+
{ name: "contentClassName", type: "string", description: "Clases adicionales del área de previsualización." },
|
|
1536
|
+
{ name: "containerClassName", type: "string", description: "Clases adicionales del contenedor externo." },
|
|
1537
|
+
],
|
|
1538
|
+
notes: [
|
|
1539
|
+
"Flujo de tres pasos: al soltar el archivo queda pendiente, al confirmar pasa a subiendo y tras una simulación de 1.5 s a subido.",
|
|
1540
|
+
"Un archivo fuera de acceptedFileTypes muestra un alert y reinicia la selección.",
|
|
1541
|
+
"Con view button, el modal se cierra y la selección se limpia 900 ms después de una subida correcta, listo para el siguiente archivo.",
|
|
1542
|
+
"La vista previa embebida de PDF está deshabilitada: los PDF muestran el icono de documento.",
|
|
1543
|
+
],
|
|
1544
|
+
}}
|
|
1234
1545
|
/>
|
|
1235
1546
|
);
|
|
1236
1547
|
};
|