@axzydev/axzy_ui_system 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/dist/index.css +1 -1
- package/dist/layered.css +1 -1
- package/package.json +4 -2
- package/src/dev.css +0 -16
- package/src/sandbox/i18n.ts +10 -0
- package/src/sandbox/ui.tsx +98 -1
- package/src/showcases/DataShowcases.tsx +56 -15
- package/src/showcases/FeedbackShowcases.tsx +82 -0
- package/src/showcases/FormShowcases.tsx +316 -5
- package/src/showcases/LayoutPrimitivesShowcases.tsx +112 -2
- package/src/showcases/NavigationShowcases.tsx +94 -0
- package/src/showcases/PageShowcases.tsx +72 -10
- package/src/showcases/ShowcaseLayout.tsx +145 -162
- package/src/showcases/StructureShowcases.tsx +107 -20
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Marco de las páginas de componente del sandbox (`#ui-system/<grupo>/<item>`)
|
|
3
|
-
* cabecera de la hoja, vitrina interactiva (vista previa / código), panel de
|
|
4
|
-
* propiedades y documentación, y galería de variaciones.
|
|
2
|
+
* Marco de las páginas de componente del sandbox (`#ui-system/<grupo>/<item>`).
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Diseño «documentación viva»: cada ejemplo es un **contenedor con cabecera
|
|
5
|
+
* propia** (número, título y la acción de ver el código siempre a la vista), el
|
|
6
|
+
* código se despliega dentro del mismo contenedor, los controles viven en un
|
|
7
|
+
* grid compacto bajo el demo y la API (props, ejemplos, notas) está **siempre
|
|
8
|
+
* visible** en la columna derecha, sin pestañas.
|
|
9
|
+
*
|
|
10
|
+
* Sigue `DESIGN.md` (AXZY Console): filetes de 1px, mono para metadatos y un
|
|
11
|
+
* solo acento.
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
import React, { useState } from "react";
|
|
13
|
-
import { FaBookOpen, FaCheck, FaCode,
|
|
15
|
+
import { FaBookOpen, FaCheck, FaCode, FaCopy, FaEye } from "react-icons/fa";
|
|
14
16
|
import ITGrid from "../components/atoms/grid/grid";
|
|
15
17
|
import { useITTheme } from "../components/theme-provider/themeProvider";
|
|
16
18
|
import { ControlSegmentado } from "@/sandbox/controls";
|
|
17
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
BotonCopiar,
|
|
21
|
+
EYEBROW,
|
|
22
|
+
EjemploContenedor,
|
|
23
|
+
FILETE,
|
|
24
|
+
FOCO_CTA,
|
|
25
|
+
} from "@/sandbox/ui";
|
|
18
26
|
import { useITTranslation } from "@/i18n";
|
|
19
27
|
|
|
20
28
|
/**
|
|
@@ -52,6 +60,7 @@ export const CodeViewer: React.FC<{ code: string; compact?: boolean }> = ({ code
|
|
|
52
60
|
</button>
|
|
53
61
|
</div>
|
|
54
62
|
<pre
|
|
63
|
+
tabIndex={0}
|
|
55
64
|
className={`overflow-x-auto whitespace-pre-wrap p-4 font-mono text-xs leading-relaxed ${
|
|
56
65
|
compact ? "max-h-[420px]" : ""
|
|
57
66
|
} overflow-y-auto`}
|
|
@@ -98,32 +107,27 @@ export interface ShowcaseLayoutProps {
|
|
|
98
107
|
description: string;
|
|
99
108
|
/** Demo interactiva. */
|
|
100
109
|
demo: React.ReactNode;
|
|
101
|
-
/**
|
|
102
|
-
* Panel de controles (lo construye cada showcase). Si se omite, la vitrina
|
|
103
|
-
* ocupa todo el ancho y no se pinta panel: para páginas que solo muestran un
|
|
104
|
-
* ejemplo o una receta.
|
|
105
|
-
*/
|
|
110
|
+
/** Controles del ejemplo (lo construye cada showcase). */
|
|
106
111
|
controls?: React.ReactNode;
|
|
107
|
-
/** Código fuente
|
|
112
|
+
/** Código fuente del ejemplo principal. */
|
|
108
113
|
code: string;
|
|
109
114
|
/** Galería de variaciones y estados. */
|
|
110
115
|
gallery?: React.ReactNode;
|
|
111
|
-
/** Documentación opcional (
|
|
116
|
+
/** Documentación opcional (columna derecha). */
|
|
112
117
|
doc?: ComponentDoc;
|
|
113
118
|
/**
|
|
114
|
-
* Ancho de la vitrina.
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
119
|
+
* Ancho mínimo (px) del demo dentro de la vitrina. Los demos que son un layout
|
|
120
|
+
* entero (`ITLayout`, pantallas de ejemplo) lo necesitan: por debajo de ~780px
|
|
121
|
+
* su propio contenido se recorta. La vitrina desplaza en horizontal lo que
|
|
122
|
+
* sobre.
|
|
123
|
+
* @default 0
|
|
118
124
|
*/
|
|
119
|
-
|
|
125
|
+
demoMinAncho?: number;
|
|
120
126
|
}
|
|
121
127
|
|
|
122
|
-
type SandboxView = "preview" | "code";
|
|
123
|
-
|
|
124
128
|
/**
|
|
125
|
-
* Página de un componente del sandbox: cabecera,
|
|
126
|
-
*
|
|
129
|
+
* Página de un componente del sandbox: cabecera, ejemplo principal con su código,
|
|
130
|
+
* controles en grid, API en la columna derecha y galería de variaciones.
|
|
127
131
|
*
|
|
128
132
|
* @example
|
|
129
133
|
* ```tsx
|
|
@@ -139,20 +143,18 @@ export const ShowcaseLayout: React.FC<ShowcaseLayoutProps> = ({
|
|
|
139
143
|
code,
|
|
140
144
|
gallery,
|
|
141
145
|
doc,
|
|
142
|
-
|
|
146
|
+
demoMinAncho = 0,
|
|
143
147
|
}) => {
|
|
144
|
-
const completo = demoAncho === "completo";
|
|
145
148
|
const { t } = useITTranslation();
|
|
146
149
|
const { resolvedTheme } = useITTheme();
|
|
147
|
-
const [
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const hayPanel = Boolean(controls) || Boolean(doc);
|
|
150
|
+
const [sandboxView, setSandboxView] = useState<"preview" | "code">("preview");
|
|
151
|
+
|
|
152
|
+
const hayApi = Boolean(doc);
|
|
151
153
|
|
|
152
154
|
return (
|
|
153
155
|
<div className="animate-fadeIn pb-4">
|
|
154
|
-
{/* ─── CABECERA
|
|
155
|
-
<header className={`border-b pb-
|
|
156
|
+
{/* ─── CABECERA ─── */}
|
|
157
|
+
<header className={`border-b pb-6 ${FILETE}`}>
|
|
156
158
|
<p className={EYEBROW}>{t("showcase.layout.eyebrow")}</p>
|
|
157
159
|
<h1 className="mt-2.5 font-display text-[32px] font-bold tracking-[-0.02em] text-slate-900 dark:text-white sm:text-[40px]">
|
|
158
160
|
{title}
|
|
@@ -162,18 +164,21 @@ export const ShowcaseLayout: React.FC<ShowcaseLayoutProps> = ({
|
|
|
162
164
|
</p>
|
|
163
165
|
</header>
|
|
164
166
|
|
|
165
|
-
<div className="pt-
|
|
167
|
+
<div className="pt-6">
|
|
166
168
|
<ITGrid container columns={12} spacing={8}>
|
|
167
|
-
{/* ──
|
|
168
|
-
<ITGrid item xs={12} md={
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
{/* ── EJEMPLO PRINCIPAL + CONTROLES ── */}
|
|
170
|
+
<ITGrid item xs={12} md={hayApi ? 8 : 12}>
|
|
171
|
+
<EjemploContenedor
|
|
172
|
+
numero="01"
|
|
173
|
+
titulo={t("showcase.layout.sandbox")}
|
|
174
|
+
nota={doc?.summary}
|
|
175
|
+
sangrado
|
|
172
176
|
>
|
|
173
|
-
{/* Barra
|
|
174
|
-
<div
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
{/* Barra del ejemplo: vista previa / código, siempre visible */}
|
|
178
|
+
<div
|
|
179
|
+
className={`flex flex-wrap items-center justify-between gap-3 border-b px-4 py-2.5 ${FILETE}`}
|
|
180
|
+
>
|
|
181
|
+
<span className={EYEBROW}>{t("showcase.layout.preview")}</span>
|
|
177
182
|
<ControlSegmentado
|
|
178
183
|
etiqueta={t("showcase.layout.sandbox")}
|
|
179
184
|
valor={sandboxView}
|
|
@@ -189,145 +194,118 @@ export const ShowcaseLayout: React.FC<ShowcaseLayoutProps> = ({
|
|
|
189
194
|
/>
|
|
190
195
|
</div>
|
|
191
196
|
|
|
192
|
-
{/* Contenido */}
|
|
193
197
|
{sandboxView === "preview" ? (
|
|
194
198
|
<div
|
|
195
199
|
data-theme={resolvedTheme}
|
|
196
|
-
|
|
200
|
+
tabIndex={0}
|
|
201
|
+
className="flex min-h-[220px] items-center justify-center overflow-x-auto bg-white p-6 dark:bg-slate-950 md:p-8"
|
|
197
202
|
>
|
|
198
|
-
<div
|
|
203
|
+
<div
|
|
204
|
+
className="flex w-full justify-center"
|
|
205
|
+
style={demoMinAncho ? { minWidth: `${demoMinAncho}px` } : undefined}
|
|
206
|
+
>
|
|
207
|
+
{demo}
|
|
208
|
+
</div>
|
|
199
209
|
</div>
|
|
200
210
|
) : (
|
|
201
|
-
<div className="p-
|
|
211
|
+
<div className="p-4">
|
|
202
212
|
<CodeViewer code={code} compact />
|
|
203
213
|
</div>
|
|
204
214
|
)}
|
|
205
|
-
</
|
|
215
|
+
</EjemploContenedor>
|
|
216
|
+
|
|
217
|
+
{controls && (
|
|
218
|
+
<section className={`mt-6 overflow-hidden rounded-[10px] border ${FILETE}`}>
|
|
219
|
+
<header
|
|
220
|
+
className={`flex items-center justify-between gap-3 border-b bg-slate-50/70 px-4 py-2.5 dark:bg-slate-950/40 ${FILETE}`}
|
|
221
|
+
>
|
|
222
|
+
<h2 className={EYEBROW}>{t("showcase.layout.controls")}</h2>
|
|
223
|
+
</header>
|
|
224
|
+
<div className="grid gap-4 p-4 sm:grid-cols-2 xl:grid-cols-3">{controls}</div>
|
|
225
|
+
</section>
|
|
226
|
+
)}
|
|
206
227
|
</ITGrid>
|
|
207
228
|
|
|
208
|
-
{/* ──
|
|
209
|
-
{
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
completo ? "max-w-[880px]" : ""
|
|
216
|
-
}`}
|
|
217
|
-
>
|
|
218
|
-
{doc ? (
|
|
219
|
-
<div
|
|
220
|
-
role="tablist"
|
|
221
|
-
aria-label={t("showcase.layout.properties")}
|
|
222
|
-
className={`flex items-stretch gap-1 border-b px-2 ${FILETE}`}
|
|
229
|
+
{/* ── API (siempre visible, sin pestañas) ── */}
|
|
230
|
+
{hayApi && (
|
|
231
|
+
<ITGrid item xs={12} md={4}>
|
|
232
|
+
<div className="md:sticky md:top-4">
|
|
233
|
+
<section className={`overflow-hidden rounded-[10px] border ${FILETE}`}>
|
|
234
|
+
<header
|
|
235
|
+
className={`flex items-center gap-2 border-b bg-slate-50/70 px-4 py-2.5 dark:bg-slate-950/40 ${FILETE}`}
|
|
223
236
|
>
|
|
224
|
-
{
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
{ id: "docs", label: t("showcase.layout.docs"), icono: <FaBookOpen size={10} /> },
|
|
228
|
-
] as const
|
|
229
|
-
).map((tab) => {
|
|
230
|
-
const activa = activeTab === tab.id;
|
|
231
|
-
return (
|
|
232
|
-
<button
|
|
233
|
-
key={tab.id}
|
|
234
|
-
type="button"
|
|
235
|
-
role="tab"
|
|
236
|
-
aria-selected={activa}
|
|
237
|
-
onClick={() => setActiveTab(tab.id)}
|
|
238
|
-
className={`-mb-px inline-flex items-center gap-2 border-b-2 px-3 py-2.5 font-mono text-[10px] font-medium uppercase tracking-[0.14em] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/60 ${
|
|
239
|
-
activa
|
|
240
|
-
? "border-primary-700 text-primary-700 dark:border-primary-500 dark:text-primary-500"
|
|
241
|
-
: "border-transparent text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white"
|
|
242
|
-
}`}
|
|
243
|
-
>
|
|
244
|
-
{tab.icono}
|
|
245
|
-
{tab.label}
|
|
246
|
-
</button>
|
|
247
|
-
);
|
|
248
|
-
})}
|
|
249
|
-
</div>
|
|
250
|
-
) : null}
|
|
237
|
+
<FaBookOpen size={11} className="text-primary-700 dark:text-primary-500" />
|
|
238
|
+
<h2 className={EYEBROW}>{t("showcase.layout.api")}</h2>
|
|
239
|
+
</header>
|
|
251
240
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}`}
|
|
257
|
-
>
|
|
258
|
-
{controls}
|
|
259
|
-
</div>
|
|
260
|
-
)}
|
|
261
|
-
|
|
262
|
-
{doc && activeTab === "docs" && (
|
|
263
|
-
<div
|
|
264
|
-
className={`max-h-[calc(100vh-200px)] space-y-6 overflow-y-auto p-5 ${
|
|
265
|
-
completo ? "it-panel-ancho max-w-[880px]" : ""
|
|
266
|
-
}`}
|
|
267
|
-
>
|
|
268
|
-
<div>
|
|
269
|
-
<h2 className={EYEBROW}>{t("showcase.layout.overview")}</h2>
|
|
270
|
-
<p className="mt-2.5 text-[13px] leading-relaxed text-slate-700 dark:text-slate-300">
|
|
271
|
-
{doc.summary}
|
|
241
|
+
<div tabIndex={0} className="max-h-[calc(100vh-220px)] space-y-6 overflow-y-auto p-4">
|
|
242
|
+
{doc?.description && (
|
|
243
|
+
<p className="text-[12.5px] leading-relaxed text-slate-600 dark:text-slate-300">
|
|
244
|
+
{doc.description}
|
|
272
245
|
</p>
|
|
273
|
-
|
|
274
|
-
<p className="mt-2 text-[12px] leading-relaxed text-slate-500 dark:text-slate-400">
|
|
275
|
-
{doc.description}
|
|
276
|
-
</p>
|
|
277
|
-
)}
|
|
278
|
-
</div>
|
|
246
|
+
)}
|
|
279
247
|
|
|
280
|
-
|
|
281
|
-
<
|
|
282
|
-
|
|
283
|
-
{
|
|
284
|
-
|
|
285
|
-
<
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
248
|
+
{!!doc?.props.length && (
|
|
249
|
+
<div>
|
|
250
|
+
<h3 className={EYEBROW}>{t("showcase.layout.props")}</h3>
|
|
251
|
+
<dl className={`mt-2.5 border-t ${FILETE}`}>
|
|
252
|
+
{doc.props.map((prop) => (
|
|
253
|
+
<div key={prop.name} className={`border-b py-2.5 ${FILETE}`}>
|
|
254
|
+
<dt className="flex flex-wrap items-baseline gap-x-2 font-mono text-[11px] font-semibold text-primary-700 dark:text-primary-500">
|
|
255
|
+
{prop.name}
|
|
256
|
+
{prop.required ? (
|
|
257
|
+
<span
|
|
258
|
+
className="text-danger-500"
|
|
259
|
+
title={t("showcase.common.required")}
|
|
260
|
+
>
|
|
261
|
+
*
|
|
262
|
+
</span>
|
|
263
|
+
) : null}
|
|
264
|
+
<span className="font-mono text-[10px] font-normal text-slate-500 dark:text-slate-400">
|
|
265
|
+
{prop.type}
|
|
290
266
|
</span>
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
</
|
|
295
|
-
<
|
|
296
|
-
{
|
|
297
|
-
</
|
|
298
|
-
</
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
))}
|
|
304
|
-
</dl>
|
|
305
|
-
</div>
|
|
267
|
+
<span className="font-mono text-[10px] font-normal text-slate-500 dark:text-slate-400">
|
|
268
|
+
{`= ${prop.default || "—"}`}
|
|
269
|
+
</span>
|
|
270
|
+
</dt>
|
|
271
|
+
<dd className="mt-1 text-[12px] leading-relaxed text-slate-600 dark:text-slate-300">
|
|
272
|
+
{prop.description}
|
|
273
|
+
</dd>
|
|
274
|
+
</div>
|
|
275
|
+
))}
|
|
276
|
+
</dl>
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
306
279
|
|
|
307
|
-
{doc
|
|
280
|
+
{doc?.examples && doc.examples.length > 0 && (
|
|
308
281
|
<div>
|
|
309
|
-
<
|
|
310
|
-
<div className="mt-
|
|
282
|
+
<h3 className={EYEBROW}>{t("showcase.layout.usageExamples")}</h3>
|
|
283
|
+
<div className="mt-2.5 space-y-2">
|
|
311
284
|
{doc.examples.map((example, i) => (
|
|
312
|
-
<
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
<
|
|
317
|
-
|
|
285
|
+
<div key={i} className="relative">
|
|
286
|
+
<pre tabIndex={0} className="overflow-x-auto rounded-[10px] border border-slate-800 bg-slate-950 p-3 pr-10 font-mono text-[10px] leading-relaxed text-slate-200">
|
|
287
|
+
<code>{example}</code>
|
|
288
|
+
</pre>
|
|
289
|
+
<span className="absolute right-1.5 top-1.5">
|
|
290
|
+
<BotonCopiar
|
|
291
|
+
texto={example}
|
|
292
|
+
className="text-slate-500 hover:bg-slate-800 hover:text-white dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-white"
|
|
293
|
+
/>
|
|
294
|
+
</span>
|
|
295
|
+
</div>
|
|
318
296
|
))}
|
|
319
297
|
</div>
|
|
320
298
|
</div>
|
|
321
299
|
)}
|
|
322
300
|
|
|
323
|
-
{doc
|
|
301
|
+
{doc?.notes && doc.notes.length > 0 && (
|
|
324
302
|
<div>
|
|
325
|
-
<
|
|
326
|
-
<ul className={`mt-
|
|
303
|
+
<h3 className={EYEBROW}>{t("showcase.layout.notes")}</h3>
|
|
304
|
+
<ul className={`mt-2.5 border-t ${FILETE}`}>
|
|
327
305
|
{doc.notes.map((note, i) => (
|
|
328
306
|
<li
|
|
329
307
|
key={i}
|
|
330
|
-
className={`border-b py-2
|
|
308
|
+
className={`border-b py-2 text-[12px] leading-relaxed text-slate-600 dark:text-slate-300 ${FILETE}`}
|
|
331
309
|
>
|
|
332
310
|
{note}
|
|
333
311
|
</li>
|
|
@@ -336,19 +314,24 @@ export const ShowcaseLayout: React.FC<ShowcaseLayoutProps> = ({
|
|
|
336
314
|
</div>
|
|
337
315
|
)}
|
|
338
316
|
</div>
|
|
339
|
-
|
|
340
|
-
</
|
|
341
|
-
</
|
|
342
|
-
</ITGrid>
|
|
317
|
+
</section>
|
|
318
|
+
</div>
|
|
319
|
+
</ITGrid>
|
|
343
320
|
)}
|
|
344
321
|
</ITGrid>
|
|
345
322
|
</div>
|
|
346
323
|
|
|
347
|
-
{/* ───
|
|
324
|
+
{/* ─── MÁS EJEMPLOS ─── */}
|
|
348
325
|
{gallery && (
|
|
349
|
-
<section
|
|
350
|
-
|
|
351
|
-
|
|
326
|
+
<section
|
|
327
|
+
className={`mt-12 border-t pt-8 ${FILETE}`}
|
|
328
|
+
aria-label={t("showcase.layout.gallery")}
|
|
329
|
+
>
|
|
330
|
+
<p className={EYEBROW}>{t("showcase.layout.gallery")}</p>
|
|
331
|
+
<h2 className="mt-2 font-display text-[22px] font-bold tracking-[-0.02em] text-slate-900 dark:text-white">
|
|
332
|
+
{t("showcase.layout.galleryTitle")}
|
|
333
|
+
</h2>
|
|
334
|
+
<div className="mt-6 space-y-6">{gallery}</div>
|
|
352
335
|
</section>
|
|
353
336
|
)}
|
|
354
337
|
</div>
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
} from "../index";
|
|
36
36
|
import type { ITSidebarProps } from "../index";
|
|
37
37
|
import { ShowcaseLayout, CodeViewer } from "./ShowcaseLayout";
|
|
38
|
+
import { EjemploContenedor } from "@/sandbox/ui";
|
|
38
39
|
import { useITTranslation } from "@/i18n";
|
|
39
40
|
|
|
40
41
|
// ─────────────────────────────────────────
|
|
@@ -533,6 +534,33 @@ export const CardShowcase = () => {
|
|
|
533
534
|
</GallerySection>
|
|
534
535
|
</ITStack>
|
|
535
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
|
+
}}
|
|
536
564
|
/>
|
|
537
565
|
);
|
|
538
566
|
};
|
|
@@ -909,6 +937,27 @@ export const TextShowcase = () => {
|
|
|
909
937
|
</GallerySection>
|
|
910
938
|
</ITStack>
|
|
911
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
|
+
}}
|
|
912
961
|
/>
|
|
913
962
|
);
|
|
914
963
|
};
|
|
@@ -1050,13 +1099,13 @@ export const LayoutShowcase = () => {
|
|
|
1050
1099
|
|
|
1051
1100
|
return (
|
|
1052
1101
|
<ShowcaseLayout
|
|
1053
|
-
|
|
1102
|
+
demoMinAncho={780}
|
|
1054
1103
|
title="ITLayout & ITNavbar"
|
|
1055
1104
|
description="Chasis estructural del portal con barra superior y lateral colapsable. Totalmente responsivo con drawer móvil."
|
|
1056
1105
|
code={code}
|
|
1057
1106
|
demo={
|
|
1058
1107
|
<div className="w-full h-[640px] rounded-xl overflow-hidden border border-slate-200 dark:border-slate-700 shadow-md">
|
|
1059
|
-
<ITLayout topBar={topBar} sidebar={sidebar}>
|
|
1108
|
+
<ITLayout className="!h-full" topBar={topBar} sidebar={sidebar}>
|
|
1060
1109
|
<ITStack spacing={5}>
|
|
1061
1110
|
<ITPageHeader
|
|
1062
1111
|
title={t("showcase.structure.navDashboard")}
|
|
@@ -1102,11 +1151,23 @@ export const LayoutShowcase = () => {
|
|
|
1102
1151
|
}
|
|
1103
1152
|
gallery={
|
|
1104
1153
|
<ITStack spacing={8}>
|
|
1105
|
-
<
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1154
|
+
<EjemploContenedor
|
|
1155
|
+
numero="02"
|
|
1156
|
+
titulo={t("showcase.structure.galleryNavbarStandalone")}
|
|
1157
|
+
codigo={`<ITNavbar
|
|
1158
|
+
logoText="AXZY"
|
|
1159
|
+
navigationItems={[
|
|
1160
|
+
{ id: "1", label: "Inicio", icon: <FaHome />, isActive: true },
|
|
1161
|
+
{ id: "2", label: "Documentos", icon: <FaFileAlt /> },
|
|
1162
|
+
{ id: "3", label: "Alertas", icon: <FaBell /> },
|
|
1163
|
+
]}
|
|
1164
|
+
userMenu={{ userName: "Auditor AXZY", userEmail: "auditor@axzy.dev" }}
|
|
1165
|
+
>
|
|
1166
|
+
<Dashboard />
|
|
1167
|
+
</ITNavbar>`}
|
|
1168
|
+
sangrado
|
|
1169
|
+
>
|
|
1170
|
+
<div className="h-[380px] overflow-hidden [&>div]:!h-full">
|
|
1110
1171
|
<ITNavbar
|
|
1111
1172
|
logoText="AXZY"
|
|
1112
1173
|
navigationItems={navbarItems}
|
|
@@ -1130,13 +1191,29 @@ export const LayoutShowcase = () => {
|
|
|
1130
1191
|
</div>
|
|
1131
1192
|
</ITNavbar>
|
|
1132
1193
|
</div>
|
|
1133
|
-
</
|
|
1194
|
+
</EjemploContenedor>
|
|
1134
1195
|
|
|
1135
|
-
<
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1196
|
+
<EjemploContenedor
|
|
1197
|
+
numero="03"
|
|
1198
|
+
titulo={t("showcase.structure.galleryNavbarSubmenus")}
|
|
1199
|
+
codigo={`<ITNavbar
|
|
1200
|
+
logoText="Admin"
|
|
1201
|
+
navigationItems={[
|
|
1202
|
+
{ id: "u", label: "Usuarios", icon: <FaUsers /> },
|
|
1203
|
+
{
|
|
1204
|
+
id: "s",
|
|
1205
|
+
label: "Ventas",
|
|
1206
|
+
icon: <FaShoppingCart />,
|
|
1207
|
+
subitems: [
|
|
1208
|
+
{ id: "o", label: "Órdenes", action: () => {} },
|
|
1209
|
+
{ id: "i", label: "Facturas", action: () => {} },
|
|
1210
|
+
],
|
|
1211
|
+
},
|
|
1212
|
+
]}
|
|
1213
|
+
/>`}
|
|
1214
|
+
sangrado
|
|
1215
|
+
>
|
|
1216
|
+
<div className="h-[380px] overflow-hidden [&>div]:!h-full">
|
|
1140
1217
|
<ITNavbar
|
|
1141
1218
|
logoText="Admin"
|
|
1142
1219
|
navigationItems={[
|
|
@@ -1165,13 +1242,23 @@ export const LayoutShowcase = () => {
|
|
|
1165
1242
|
</div>
|
|
1166
1243
|
</ITNavbar>
|
|
1167
1244
|
</div>
|
|
1168
|
-
</
|
|
1245
|
+
</EjemploContenedor>
|
|
1169
1246
|
|
|
1170
|
-
<
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1247
|
+
<EjemploContenedor
|
|
1248
|
+
numero="04"
|
|
1249
|
+
titulo={t("showcase.structure.gallerySidebarBadges")}
|
|
1250
|
+
codigo={`<ITSidebar
|
|
1251
|
+
subitemConnector="dot"
|
|
1252
|
+
navigationItems={[
|
|
1253
|
+
{ id: "home", label: "Inicio", icon: <FaHome />, isActive: true },
|
|
1254
|
+
{ id: "inbox", label: "Bandeja", icon: <FaUsers />, badge: 12 },
|
|
1255
|
+
{ id: "alerts", label: "Alertas", icon: <FaBell />, badge: 3 },
|
|
1256
|
+
]}
|
|
1257
|
+
notification={{ count: 5, icon: <FaBell /> }}
|
|
1258
|
+
/>`}
|
|
1259
|
+
sangrado
|
|
1260
|
+
>
|
|
1261
|
+
<div className="flex h-[320px] overflow-hidden [&>div]:!h-full">
|
|
1175
1262
|
<ITSidebar
|
|
1176
1263
|
visibleOnMobile
|
|
1177
1264
|
className="h-full"
|
|
@@ -1200,7 +1287,7 @@ export const LayoutShowcase = () => {
|
|
|
1200
1287
|
</p>
|
|
1201
1288
|
</div>
|
|
1202
1289
|
</div>
|
|
1203
|
-
</
|
|
1290
|
+
</EjemploContenedor>
|
|
1204
1291
|
</ITStack>
|
|
1205
1292
|
}
|
|
1206
1293
|
doc={{
|