@axzydev/axzy_ui_system 1.3.1 → 1.3.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.
- package/README.md +6 -2
- package/dist/index.cjs +957 -554
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +191 -9
- package/dist/index.d.ts +191 -9
- package/dist/index.js +908 -505
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/App.tsx +150 -154
- package/src/components/molecules/table/table.doc.mdx +64 -21
- package/src/components/molecules/table/table.props.ts +81 -2
- package/src/components/molecules/table/table.stories.tsx +155 -0
- package/src/components/molecules/table/table.tsx +178 -36
- package/src/components/organisms/data-table/ITDataTable.doc.mdx +37 -3
- package/src/components/organisms/data-table/ITDataTable.stories.tsx +43 -0
- package/src/components/organisms/data-table/dataTable.props.ts +64 -0
- package/src/components/organisms/data-table/dataTable.tsx +241 -82
- package/src/components/organisms/sidebar/sidebar.doc.mdx +5 -0
- package/src/components/organisms/sidebar/sidebar.props.ts +5 -0
- package/src/components/organisms/sidebar/sidebar.stories.tsx +1 -1
- package/src/components/organisms/sidebar/sidebar.tsx +27 -7
- package/src/components/organisms/topbar/topbar.doc.mdx +3 -0
- package/src/components/organisms/topbar/topbar.props.ts +16 -0
- package/src/components/organisms/topbar/topbar.tsx +133 -98
- package/src/components/templates/layout/layout.doc.mdx +2 -0
- package/src/components/templates/layout/layout.tsx +7 -1
- package/src/components/templates/page/page.doc.mdx +8 -2
- package/src/components/templates/page/page.props.ts +11 -1
- package/src/components/templates/page/page.tsx +11 -1
- package/src/components/theme-provider/themePresets.ts +6 -0
- package/src/components/theme-provider/themeProvider.doc.mdx +1 -0
- package/src/components/theme-provider/themeProvider.props.ts +2 -0
- package/src/components/theme-provider/themeProvider.tsx +13 -2
- package/src/dev.css +58 -1
- package/src/hooks/useElementSize.ts +46 -0
- package/src/hooks/useVirtualRows.ts +123 -0
- package/src/index.ts +2 -0
- package/src/sandbox/LandingShowcase.tsx +654 -0
- package/src/sandbox/NotFoundShowcase.tsx +70 -0
- package/src/sandbox/TopbarSearch.tsx +85 -0
- package/src/sandbox/navigation.ts +148 -0
- package/src/sandbox/useHashRoute.ts +149 -0
- package/src/showcases/DataShowcases.tsx +321 -24
- package/src/showcases/PageShowcases.tsx +31 -0
- package/src/showcases/StructureShowcases.tsx +42 -4
- package/src/theme/theme.types.ts +2 -0
- package/src/types/table.types.ts +48 -0
- package/src/utils/styles.ts +12 -5
- package/src/utils/table.utils.ts +31 -0
|
@@ -32,6 +32,21 @@ const TABLE_DATA: Record<string, unknown>[] = [
|
|
|
32
32
|
{ id: 15, name: "Florencia Díaz", email: "florencia@axzy.dev", role: "Admin", status: "active", lastLogin: "2026-07-18T07:15:00", sales: 48900 },
|
|
33
33
|
];
|
|
34
34
|
|
|
35
|
+
const VIRTUAL_USERS = Array.from({ length: 240 }, (_, i) => ({
|
|
36
|
+
id: i + 1,
|
|
37
|
+
name: `User ${String(i + 1).padStart(3, "0")}`,
|
|
38
|
+
email: `user${i + 1}@axzy.dev`,
|
|
39
|
+
role: (["Admin", "Editor", "Viewer"] as const)[i % 3],
|
|
40
|
+
active: i % 2 === 0,
|
|
41
|
+
}));
|
|
42
|
+
const virtualUserColumns: any = [
|
|
43
|
+
{ key: "id", label: "ID", type: "number", width: 64 },
|
|
44
|
+
{ key: "name", label: "Name", type: "string", width: "26%", truncate: true },
|
|
45
|
+
{ key: "email", label: "Email", type: "string", width: "28%", truncate: true },
|
|
46
|
+
{ key: "role", label: "Role", type: "string", width: "16%" },
|
|
47
|
+
{ key: "active", label: "Active", type: "boolean", width: "12%" },
|
|
48
|
+
];
|
|
49
|
+
|
|
35
50
|
const nameColumn = {
|
|
36
51
|
key: "name",
|
|
37
52
|
label: "Name",
|
|
@@ -67,6 +82,7 @@ export const TableShowcase = () => {
|
|
|
67
82
|
const [variant, setVariant] = useState<any>("default");
|
|
68
83
|
const [size, setSize] = useState<any>("sm");
|
|
69
84
|
const [useCustomCard, setUseCustomCard] = useState(true);
|
|
85
|
+
const [selectedRow, setSelectedRow] = useState<Record<string, unknown> | null>(null);
|
|
70
86
|
|
|
71
87
|
const customCard = (row: Record<string, unknown>) => {
|
|
72
88
|
const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
|
|
@@ -157,6 +173,31 @@ export const TableShowcase = () => {
|
|
|
157
173
|
}
|
|
158
174
|
];
|
|
159
175
|
|
|
176
|
+
const fixedColumns: any = columns.map((col: any, i: number) => ({
|
|
177
|
+
...col,
|
|
178
|
+
width: [64, "18%", "18%", "14%", "20%", "10%"][i],
|
|
179
|
+
truncate: col.key === "email" || col.key === "name",
|
|
180
|
+
}));
|
|
181
|
+
|
|
182
|
+
// Single-line columns for virtualization: no avatar, no actions, no custom
|
|
183
|
+
// render, so every row is a deterministic one-line box.
|
|
184
|
+
const virtualColumns: any = [
|
|
185
|
+
{ key: "id", label: "ID", type: "number", width: 64 },
|
|
186
|
+
{ key: "name", label: "Name", type: "string", width: "28%", truncate: true },
|
|
187
|
+
{ key: "email", label: "Email", type: "string", width: "28%", truncate: true },
|
|
188
|
+
{ key: "role", label: "Role", type: "string", width: "16%" },
|
|
189
|
+
{ key: "sales", label: "Sales", type: "number", width: "18%" },
|
|
190
|
+
];
|
|
191
|
+
|
|
192
|
+
const virtualData = useMemo(
|
|
193
|
+
() =>
|
|
194
|
+
Array.from({ length: 300 }, (_, i) => ({
|
|
195
|
+
...TABLE_DATA[i % TABLE_DATA.length],
|
|
196
|
+
id: i + 1,
|
|
197
|
+
})),
|
|
198
|
+
[]
|
|
199
|
+
);
|
|
200
|
+
|
|
160
201
|
const code = `<ITTable
|
|
161
202
|
columns={columns}
|
|
162
203
|
data={data}
|
|
@@ -173,16 +214,24 @@ export const TableShowcase = () => {
|
|
|
173
214
|
description="Tabla con vista responsive Table / Cards, paginación, filtros y template de card personalizable."
|
|
174
215
|
code={code}
|
|
175
216
|
demo={
|
|
176
|
-
<div className="w-full
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
217
|
+
<div className="w-full space-y-2">
|
|
218
|
+
<div className="text-xs font-medium text-slate-500 dark:text-slate-400 px-1">
|
|
219
|
+
{selectedRow
|
|
220
|
+
? `Selected: ${selectedRow.name as string}`
|
|
221
|
+
: "Click a row/card or press Enter/Space to select"}
|
|
222
|
+
</div>
|
|
223
|
+
<div className="w-full bg-white dark:bg-slate-900 rounded-xl overflow-hidden shadow-sm">
|
|
224
|
+
<ITTable
|
|
225
|
+
columns={columns}
|
|
226
|
+
data={TABLE_DATA}
|
|
227
|
+
title="Team members"
|
|
228
|
+
variant={variant}
|
|
229
|
+
size={size}
|
|
230
|
+
defaultItemsPerPage={5}
|
|
231
|
+
renderCard={useCustomCard ? customCard : undefined}
|
|
232
|
+
onRowClick={(row) => setSelectedRow(row)}
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
186
235
|
</div>
|
|
187
236
|
}
|
|
188
237
|
controls={
|
|
@@ -195,8 +244,7 @@ export const TableShowcase = () => {
|
|
|
195
244
|
options={[
|
|
196
245
|
{ label: "Default", value: "default" },
|
|
197
246
|
{ label: "Striped", value: "striped" },
|
|
198
|
-
{ label: "Bordered", value: "bordered" }
|
|
199
|
-
{ label: "Clean", value: "clean" }
|
|
247
|
+
{ label: "Bordered", value: "bordered" }
|
|
200
248
|
]}
|
|
201
249
|
/>
|
|
202
250
|
<ITSelect
|
|
@@ -221,6 +269,48 @@ export const TableShowcase = () => {
|
|
|
221
269
|
)}
|
|
222
270
|
</>
|
|
223
271
|
}
|
|
272
|
+
doc={{
|
|
273
|
+
summary: "Tabla de datos con vistas Table/Cards, paginación, filtros, ordenamiento y activación de fila.",
|
|
274
|
+
examples: [
|
|
275
|
+
'<ITTable columns={columns} data={data} title="Users" />',
|
|
276
|
+
'<ITTable columns={columns} data={data} defaultView="cards" onRowClick={(row) => open(row)} />',
|
|
277
|
+
'<ITTable columns={fixedColumns} data={data} layout="fixed" density="compact" />',
|
|
278
|
+
'<ITTable columns={cols} data={rows} virtualized stickyHeader layout="fixed" density="compact" defaultItemsPerPage={300} itemsPerPageOptions={[50, 100, 300]} />',
|
|
279
|
+
],
|
|
280
|
+
props: [
|
|
281
|
+
{ name: "columns", type: "Column<T>[]", description: "Definiciones de columna: key, label, type, sortable, filter, render, actions." },
|
|
282
|
+
{ name: "data", type: "T[]", default: "[]", description: "Arreglo de filas a renderizar." },
|
|
283
|
+
{ name: "containerClassName", type: "string", description: "Clases del contenedor externo." },
|
|
284
|
+
{ name: "className", type: "string", description: "Declarado por compatibilidad, pero ITTable no lo aplica; usar containerClassName." },
|
|
285
|
+
{ name: "variant", type: '"default" | "striped" | "bordered"', default: '"default"', description: "Variante visual." },
|
|
286
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño de fuente; no cambia padding (usar density)." },
|
|
287
|
+
{ name: "itemsPerPageOptions", type: "number[]", default: "[5, 10, 20]", description: "Opciones del selector de filas por página." },
|
|
288
|
+
{ name: "defaultItemsPerPage", type: "number", default: "10", description: "Filas por página iniciales." },
|
|
289
|
+
{ name: "title", type: "string", description: "Título opcional sobre la tabla." },
|
|
290
|
+
{ name: "renderCard", type: "(row: T) => ReactNode", description: "Renderer custom de card para la vista cards." },
|
|
291
|
+
{ name: "defaultView", type: '"table" | "cards"', default: '"table"', description: "Vista inicial." },
|
|
292
|
+
{ name: "showVerticalBorder", type: "boolean", default: "true", description: "Bordes verticales entre columnas." },
|
|
293
|
+
{ name: "verticalBorderClassname", type: "string", description: "Clases custom de los bordes verticales." },
|
|
294
|
+
{ name: "onRowClick", type: "(row: T, event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>) => void", description: "Activa fila/card. MouseEvent en puntero, KeyboardEvent en Enter/Space. Se ignora si la interacción inicia en un hijo interactivo." },
|
|
295
|
+
{ name: "layout", type: '"auto" | "fixed"', default: '"auto"', description: "fixed respeta width de columna y elimina min-w, evitando scroll horizontal." },
|
|
296
|
+
{ name: "density", type: '"compact" | "normal" | "comfortable"', default: '"normal"', description: "Padding / alto de fila (independiente de size, que solo cambia font-size)." },
|
|
297
|
+
{ name: "autoCardBreakpoint", type: "number", default: "0", description: "Ancho de contenedor (px) bajo el cual cae a la vista cards. 0 desactiva." },
|
|
298
|
+
{ name: "virtualized", type: "boolean", default: "false", description: "Vista tabla: renderiza solo la ventana visible con spacer rows. Cards nunca virtualiza." },
|
|
299
|
+
{ name: "virtualizedMaxHeight", type: "number", default: "400", description: "Alto máx (px) del contenedor con scroll virtual." },
|
|
300
|
+
{ name: "rowHeight", type: "number", default: "getRowHeight(size, density)", description: "Alto de fila asumido (px). Size-aware: baseline size=\"md\" = 33/45/53 según densidad; size lo desplaza por el line box (sm 16 / md 20 / lg 28 px)." },
|
|
301
|
+
{ name: "overscan", type: "number", default: "5", description: "Filas extra renderizadas arriba/abajo del viewport." },
|
|
302
|
+
{ name: "stickyHeader", type: "boolean", default: "false", description: "Header sticky mientras scrollea el cuerpo virtualizado." },
|
|
303
|
+
],
|
|
304
|
+
notes: [
|
|
305
|
+
"onRowClick funciona en ambas vistas (tabla y cards).",
|
|
306
|
+
"Cuando se omite no se agrega cursor-pointer, foco, tabIndex ni role.",
|
|
307
|
+
"Column acepta width (number=px | string CSS), minWidth (px), align (left/center/right) y truncate (ellipsis + title nativo).",
|
|
308
|
+
"Anti scroll horizontal: layout=\"fixed\" + width por columna + truncate + density.",
|
|
309
|
+
"Virtualización: solo vista tabla; rinde con page size grande; filas de alto uniforme; el scroll no se reinicia al cambiar filtros.",
|
|
310
|
+
"Virtualización exige alto de fila uniforme. El default es size-aware (getRowHeight(size, density)): con size=\"md\" el baseline es compact 33 / normal 45 / comfortable 53 (padding de celda + line box text-sm de 20px + 1px del divide-y); size lo desplaza por el line box (sm 16 / md 20 / lg 28 px, es decir −4 / 0 / +8 px). Una celda de dos líneas (avatar + texto) mide ~50px real, distinto de ese alto asumido, así que los spacers se desalinean: usar celdas de una sola línea o pasar un rowHeight explícito.",
|
|
311
|
+
"ITSearchTable no soporta layout/density/autoCardBreakpoint/virtualized/width — no asumir paridad.",
|
|
312
|
+
],
|
|
313
|
+
}}
|
|
224
314
|
gallery={
|
|
225
315
|
<div className="space-y-6">
|
|
226
316
|
<div>
|
|
@@ -269,6 +359,7 @@ export const TableShowcase = () => {
|
|
|
269
359
|
size="sm"
|
|
270
360
|
defaultItemsPerPage={6}
|
|
271
361
|
defaultView="cards"
|
|
362
|
+
onRowClick={(row) => setSelectedRow(row)}
|
|
272
363
|
/>
|
|
273
364
|
</div>
|
|
274
365
|
</div>
|
|
@@ -304,6 +395,51 @@ export const TableShowcase = () => {
|
|
|
304
395
|
/>
|
|
305
396
|
</div>
|
|
306
397
|
</div>
|
|
398
|
+
|
|
399
|
+
<div>
|
|
400
|
+
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Compact fixed layout — no horizontal scroll</h4>
|
|
401
|
+
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden" style={{ maxWidth: 560 }}>
|
|
402
|
+
<ITTable
|
|
403
|
+
columns={fixedColumns}
|
|
404
|
+
data={TABLE_DATA}
|
|
405
|
+
title="Fixed layout"
|
|
406
|
+
layout="fixed"
|
|
407
|
+
density="compact"
|
|
408
|
+
defaultItemsPerPage={5}
|
|
409
|
+
/>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<div>
|
|
414
|
+
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Auto cards fallback (< 640px container)</h4>
|
|
415
|
+
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden" style={{ maxWidth: 420 }}>
|
|
416
|
+
<ITTable
|
|
417
|
+
columns={columns}
|
|
418
|
+
data={TABLE_DATA.slice(0, 6)}
|
|
419
|
+
title="Narrow container"
|
|
420
|
+
autoCardBreakpoint={640}
|
|
421
|
+
defaultItemsPerPage={6}
|
|
422
|
+
/>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<div>
|
|
427
|
+
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Virtualized table — 300 rows (only visible window mounted)</h4>
|
|
428
|
+
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
429
|
+
<ITTable
|
|
430
|
+
columns={virtualColumns}
|
|
431
|
+
data={virtualData}
|
|
432
|
+
title="Virtualized · 300 rows"
|
|
433
|
+
virtualized
|
|
434
|
+
stickyHeader
|
|
435
|
+
layout="fixed"
|
|
436
|
+
density="compact"
|
|
437
|
+
virtualizedMaxHeight={360}
|
|
438
|
+
defaultItemsPerPage={300}
|
|
439
|
+
itemsPerPageOptions={[50, 100, 300]}
|
|
440
|
+
/>
|
|
441
|
+
</div>
|
|
442
|
+
</div>
|
|
307
443
|
</div>
|
|
308
444
|
}
|
|
309
445
|
/>
|
|
@@ -317,6 +453,13 @@ export const DataTableShowcase = () => {
|
|
|
317
453
|
const [reloadKey, setReloadKey] = useState(0);
|
|
318
454
|
const [showVBorder, setShowVBorder] = useState(true);
|
|
319
455
|
const [borderStyle, setBorderStyle] = useState<"default" | "blue" | "rose" | "none">("default");
|
|
456
|
+
const [selectedRow, setSelectedRow] = useState<any>(null);
|
|
457
|
+
const [dtSize, setDtSize] = useState<"sm" | "md" | "lg">("md");
|
|
458
|
+
const [dtDensity, setDtDensity] = useState<"compact" | "normal" | "comfortable">("normal");
|
|
459
|
+
const [dtLayout, setDtLayout] = useState<"auto" | "fixed">("auto");
|
|
460
|
+
const [dtAutoCardBreakpoint, setDtAutoCardBreakpoint] = useState(0);
|
|
461
|
+
const [dtVirtualized, setDtVirtualized] = useState(true);
|
|
462
|
+
const [dtStickyHeader, setDtStickyHeader] = useState(false);
|
|
320
463
|
|
|
321
464
|
const userColumns = useMemo(() => [
|
|
322
465
|
{ key: "id", label: "ID", type: "number" as const },
|
|
@@ -403,6 +546,12 @@ export const DataTableShowcase = () => {
|
|
|
403
546
|
};
|
|
404
547
|
}, [apiState, MOCK_USERS]);
|
|
405
548
|
|
|
549
|
+
const virtualFetchData = useCallback(async (params: ITDataTableFetchParams): Promise<ITDataTableResponse<any>> => {
|
|
550
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
551
|
+
const start = (params.page - 1) * params.limit;
|
|
552
|
+
return { data: VIRTUAL_USERS.slice(start, start + params.limit), total: VIRTUAL_USERS.length };
|
|
553
|
+
}, []);
|
|
554
|
+
|
|
406
555
|
const handleStateChange = (s: typeof apiState) => {
|
|
407
556
|
setApiState(s);
|
|
408
557
|
setReloadKey(k => k + 1);
|
|
@@ -412,7 +561,10 @@ export const DataTableShowcase = () => {
|
|
|
412
561
|
columns={columns}
|
|
413
562
|
fetchData={api.fetchUsers}
|
|
414
563
|
title="Users"
|
|
415
|
-
defaultItemsPerPage={
|
|
564
|
+
defaultItemsPerPage={4}
|
|
565
|
+
size="${dtSize}"
|
|
566
|
+
layout="${dtLayout}"
|
|
567
|
+
density="${dtDensity}"${dtAutoCardBreakpoint > 0 ? `\n autoCardBreakpoint={${dtAutoCardBreakpoint}}` : ""}
|
|
416
568
|
showVerticalBorder={${showVBorder}}${borderStyle !== "default" ? `\n verticalBorderClassname="${borderStyle === "blue" ? "[&_th]:border-blue-200 [&_td]:border-blue-100" : "[&_th]:border-rose-200 [&_td]:border-rose-100"}"` : ""}
|
|
417
569
|
renderCard={(row) => <CustomCard row={row} />}
|
|
418
570
|
/>`;
|
|
@@ -425,16 +577,27 @@ export const DataTableShowcase = () => {
|
|
|
425
577
|
description="Tabla auto-suficiente con carga dinámica, filtros, paginación y vista responsive Cards/Table."
|
|
426
578
|
code={code}
|
|
427
579
|
demo={
|
|
428
|
-
<div className="w-full
|
|
429
|
-
<
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
580
|
+
<div className="w-full space-y-2">
|
|
581
|
+
<div className="text-xs font-medium text-slate-500 dark:text-slate-400 px-1">
|
|
582
|
+
{selectedRow
|
|
583
|
+
? `Selected: ${selectedRow.name as string} (id ${selectedRow.id as number})`
|
|
584
|
+
: "Click a row/card or press Enter/Space to select"}
|
|
585
|
+
</div>
|
|
586
|
+
<div className="w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl overflow-hidden shadow-md">
|
|
587
|
+
<ITDataTable
|
|
588
|
+
key={reloadKey}
|
|
589
|
+
columns={userColumns}
|
|
590
|
+
fetchData={fetchData}
|
|
591
|
+
title={apiState === "error" ? "Connection error" : apiState === "empty" ? "No data" : "Users"}
|
|
592
|
+
defaultItemsPerPage={4}
|
|
593
|
+
size={dtSize}
|
|
594
|
+
layout={dtLayout}
|
|
595
|
+
density={dtDensity}
|
|
596
|
+
autoCardBreakpoint={dtAutoCardBreakpoint}
|
|
597
|
+
showVerticalBorder={showVBorder}
|
|
598
|
+
verticalBorderClassname={borderClass}
|
|
599
|
+
onRowClick={(row) => setSelectedRow(row)}
|
|
600
|
+
renderCard={useCustomCard ? (row: any) => {
|
|
438
601
|
const initials = (row.name as string).split(" ").map((w: string) => w[0]).join("");
|
|
439
602
|
return (
|
|
440
603
|
<div className="group bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 p-3 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md hover:border-slate-300 dark:hover:border-slate-600">
|
|
@@ -458,7 +621,8 @@ export const DataTableShowcase = () => {
|
|
|
458
621
|
</div>
|
|
459
622
|
);
|
|
460
623
|
} : undefined}
|
|
461
|
-
|
|
624
|
+
/>
|
|
625
|
+
</div>
|
|
462
626
|
</div>
|
|
463
627
|
}
|
|
464
628
|
controls={
|
|
@@ -511,8 +675,120 @@ export const DataTableShowcase = () => {
|
|
|
511
675
|
))}
|
|
512
676
|
</div>
|
|
513
677
|
</div>
|
|
678
|
+
<div className="space-y-3">
|
|
679
|
+
<p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">Table capabilities</p>
|
|
680
|
+
<ITSelect
|
|
681
|
+
name="dt_size"
|
|
682
|
+
label="Size"
|
|
683
|
+
value={dtSize}
|
|
684
|
+
onChange={(e: any) => setDtSize(e.target.value as "sm" | "md" | "lg")}
|
|
685
|
+
options={[
|
|
686
|
+
{ label: "Small", value: "sm" },
|
|
687
|
+
{ label: "Medium", value: "md" },
|
|
688
|
+
{ label: "Large", value: "lg" },
|
|
689
|
+
]}
|
|
690
|
+
/>
|
|
691
|
+
<ITSelect
|
|
692
|
+
name="dt_density"
|
|
693
|
+
label="Density"
|
|
694
|
+
value={dtDensity}
|
|
695
|
+
onChange={(e: any) => setDtDensity(e.target.value as "compact" | "normal" | "comfortable")}
|
|
696
|
+
options={[
|
|
697
|
+
{ label: "Compact", value: "compact" },
|
|
698
|
+
{ label: "Normal", value: "normal" },
|
|
699
|
+
{ label: "Comfortable", value: "comfortable" },
|
|
700
|
+
]}
|
|
701
|
+
/>
|
|
702
|
+
<ITSelect
|
|
703
|
+
name="dt_layout"
|
|
704
|
+
label="Layout"
|
|
705
|
+
value={dtLayout}
|
|
706
|
+
onChange={(e: any) => setDtLayout(e.target.value as "auto" | "fixed")}
|
|
707
|
+
options={[
|
|
708
|
+
{ label: "Auto", value: "auto" },
|
|
709
|
+
{ label: "Fixed", value: "fixed" },
|
|
710
|
+
]}
|
|
711
|
+
/>
|
|
712
|
+
<ITSelect
|
|
713
|
+
name="dt_autocards"
|
|
714
|
+
label="Auto cards below (px)"
|
|
715
|
+
value={String(dtAutoCardBreakpoint)}
|
|
716
|
+
onChange={(e: any) => setDtAutoCardBreakpoint(Number(e.target.value))}
|
|
717
|
+
options={[
|
|
718
|
+
{ label: "Off", value: "0" },
|
|
719
|
+
{ label: "640px", value: "640" },
|
|
720
|
+
{ label: "900px", value: "900" },
|
|
721
|
+
{ label: "1200px", value: "1200" },
|
|
722
|
+
]}
|
|
723
|
+
/>
|
|
724
|
+
<div className="flex items-center justify-between pt-2">
|
|
725
|
+
<span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Virtualized (gallery)</span>
|
|
726
|
+
<ITSlideToggle isOn={dtVirtualized} onToggle={setDtVirtualized} size="sm" />
|
|
727
|
+
</div>
|
|
728
|
+
<div className="flex items-center justify-between">
|
|
729
|
+
<span className="text-sm font-semibold text-gray-700 dark:text-slate-300">Sticky header</span>
|
|
730
|
+
<ITSlideToggle isOn={dtStickyHeader} onToggle={setDtStickyHeader} size="sm" disabled={!dtVirtualized} />
|
|
731
|
+
</div>
|
|
732
|
+
{dtVirtualized && (
|
|
733
|
+
<div className="p-2 bg-slate-100 dark:bg-slate-800 rounded text-xs text-slate-500">
|
|
734
|
+
size, density, layout y auto-cards aplican al demo principal; virtualized y sticky header solo al bloque "Server-side virtualization" de la galería. Ese bloque no recibe autoCardBreakpoint: las cards nunca se virtualizan y el fallback de auto-cards desactivaría la virtualización en silencio.
|
|
735
|
+
</div>
|
|
736
|
+
)}
|
|
737
|
+
</div>
|
|
514
738
|
</div>
|
|
515
739
|
}
|
|
740
|
+
doc={{
|
|
741
|
+
summary: "Tabla server-side con fetch, filtros, ordenamiento, paginación y activación de fila/card.",
|
|
742
|
+
examples: [
|
|
743
|
+
'<ITDataTable columns={columns} fetchData={api.fetchUsers} />',
|
|
744
|
+
'<ITDataTable columns={columns} fetchData={api.fetchUsers} onRowClick={(row) => open(row)} />',
|
|
745
|
+
'<ITDataTable columns={fixedColumns} fetchData={api.fetchUsers} layout="fixed" density="compact" />',
|
|
746
|
+
'<ITDataTable columns={cols} fetchData={api.page} virtualized stickyHeader defaultItemsPerPage={200} itemsPerPageOptions={[50, 100, 200]} debounceMs={0} />',
|
|
747
|
+
'<ITDataTable columns={cols} fetchData={api.page} layout="fixed" density="compact" autoCardBreakpoint={640} />',
|
|
748
|
+
],
|
|
749
|
+
props: [
|
|
750
|
+
{ name: "columns", type: "Column<T>[]", description: "Definiciones de columna." },
|
|
751
|
+
{ name: "fetchData", type: "(params: ITDataTableFetchParams) => Promise<ITDataTableResponse<T>>", required: true, description: "Callback asíncrono disparado al cambiar página, filtros u orden. Retorna { data, total }." },
|
|
752
|
+
{ name: "debounceMs", type: "number", default: "500", description: "Espera antes de refetch tras cambiar filtros." },
|
|
753
|
+
{ name: "externalFilters", type: "Record<string, string | number | boolean | Date>", default: "{}", description: "Filtros externos fusionados con los internos." },
|
|
754
|
+
{ name: "loadingIndicator", type: "ReactNode", description: "Elemento custom mostrado mientras carga." },
|
|
755
|
+
{ name: "fetchOnMount", type: "boolean", default: "true", description: "Dispara fetch al montar." },
|
|
756
|
+
{ name: "reloadTrigger", type: "number | string | boolean", description: "Cambiar su valor fuerza un refetch." },
|
|
757
|
+
{ name: "containerClassName", type: "string", description: "Clases del contenedor externo." },
|
|
758
|
+
{ name: "className", type: "string", description: "Clases aplicadas al elemento <table>." },
|
|
759
|
+
{ name: "variant", type: '"default" | "striped" | "bordered" | "minimal"', default: '"default"', description: "Variante visual. minimal no tiene estilos (igual que default)." },
|
|
760
|
+
{ name: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "Tamaño de fuente; no cambia padding (usar density)." },
|
|
761
|
+
{ name: "itemsPerPageOptions", type: "number[]", default: "[5, 10, 20]", description: "Opciones del selector de filas por página." },
|
|
762
|
+
{ name: "defaultItemsPerPage", type: "number", default: "10", description: "Filas por página iniciales." },
|
|
763
|
+
{ name: "title", type: "string | ReactNode", description: "Título sobre la tabla." },
|
|
764
|
+
{ name: "renderCard", type: "(row: T) => ReactNode", description: "Renderer custom de card." },
|
|
765
|
+
{ name: "defaultView", type: '"table" | "cards"', default: '"table"', description: "Vista inicial." },
|
|
766
|
+
{ name: "showVerticalBorder", type: "boolean", default: "true", description: "Bordes verticales entre columnas." },
|
|
767
|
+
{ name: "verticalBorderClassname", type: "string", description: "Clases custom de los bordes verticales." },
|
|
768
|
+
{ name: "onRowClick", type: "(row: T, event: MouseEvent<HTMLElement> | KeyboardEvent<HTMLElement>) => void", description: "Activa fila/card. MouseEvent en puntero, KeyboardEvent en Enter/Space. No se dispara mientras carga y se ignora en hijos interactivos." },
|
|
769
|
+
{ name: "layout", type: '"auto" | "fixed"', default: '"auto"', description: "fixed respeta width de columna y elimina min-w, evitando scroll horizontal." },
|
|
770
|
+
{ name: "density", type: '"compact" | "normal" | "comfortable"', default: '"normal"', description: "Padding / alto de fila (independiente de size, que solo cambia font-size)." },
|
|
771
|
+
{ name: "autoCardBreakpoint", type: "number", default: "0", description: "Ancho de contenedor (px) bajo el cual cae a la vista cards. 0 desactiva." },
|
|
772
|
+
{ name: "virtualized", type: "boolean", default: "false", description: "Vista tabla: renderiza solo la ventana visible con spacer rows. Cards nunca virtualiza." },
|
|
773
|
+
{ name: "virtualizedMaxHeight", type: "number", default: "400", description: "Alto máx (px) del contenedor con scroll virtual." },
|
|
774
|
+
{ name: "rowHeight", type: "number", default: "getRowHeight(size, density)", description: "Alto de fila asumido (px). Size-aware: baseline size=\"md\" = 33/45/53 según densidad; size lo desplaza por el line box (sm 16 / md 20 / lg 28 px)." },
|
|
775
|
+
{ name: "overscan", type: "number", default: "5", description: "Filas extra renderizadas arriba/abajo del viewport." },
|
|
776
|
+
{ name: "stickyHeader", type: "boolean", default: "false", description: "Header sticky mientras scrollea el cuerpo virtualizado." },
|
|
777
|
+
],
|
|
778
|
+
notes: [
|
|
779
|
+
"onRowClick funciona en ambas vistas (tabla y cards).",
|
|
780
|
+
"Interacciones que inician en botones, links o inputs no disparan onRowClick.",
|
|
781
|
+
"Column acepta width (number=px | string CSS), minWidth (px), align (left/center/right) y truncate (ellipsis + title nativo).",
|
|
782
|
+
"Anti scroll horizontal: layout=\"fixed\" + width por columna + truncate + density.",
|
|
783
|
+
"Virtualización: solo vista tabla; rinde con page size grande; filas de alto uniforme; el scroll no se reinicia al cambiar filtros.",
|
|
784
|
+
"La virtualización server-side hace ventana sobre la PÁGINA ACTUAL (data.length), no sobre total: usar un defaultItemsPerPage grande (p. ej. 200) con itemsPerPageOptions que lo incluya, o el efecto es invisible.",
|
|
785
|
+
"Virtualización exige alto de fila uniforme. El default es size-aware (getRowHeight(size, density)): con size=\"md\" el baseline es compact 33 / normal 45 / comfortable 53 (padding de celda + line box text-sm de 20px + 1px del divide-y); size lo desplaza por el line box (sm 16 / md 20 / lg 28 px, es decir −4 / 0 / +8 px). Una celda de dos líneas (avatar + texto) mide ~50px real y desalinea los spacers: usar celdas de una sola línea o pasar un rowHeight explícito.",
|
|
786
|
+
"autoCardBreakpoint fuerza la vista cards bajo el ancho dado, y la virtualización se desactiva en silencio al pasar a cards: no combinar virtualized + autoCardBreakpoint.",
|
|
787
|
+
"El toggle interno Table/Cards también desactiva la virtualización; las cards nunca se virtualizan.",
|
|
788
|
+
"debounceMs={0} evita el retardo de refetch en demos.",
|
|
789
|
+
"variant=\"minimal\" se acepta por tipo pero no tiene estilos (igual que default). ITSearchTable no soporta layout/density/autoCardBreakpoint/virtualized/width.",
|
|
790
|
+
],
|
|
791
|
+
}}
|
|
516
792
|
gallery={
|
|
517
793
|
<div className="space-y-6">
|
|
518
794
|
<div>
|
|
@@ -568,6 +844,27 @@ export const DataTableShowcase = () => {
|
|
|
568
844
|
/>
|
|
569
845
|
</div>
|
|
570
846
|
</div>
|
|
847
|
+
|
|
848
|
+
<div>
|
|
849
|
+
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Server-side virtualization — 240 rows {dtVirtualized ? "(page 200)" : "(page 10)"}</h4>
|
|
850
|
+
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
851
|
+
<ITDataTable
|
|
852
|
+
key={dtVirtualized ? "dt-virt-on" : "dt-virt-off"}
|
|
853
|
+
columns={virtualUserColumns}
|
|
854
|
+
fetchData={virtualFetchData}
|
|
855
|
+
title="Virtualized users"
|
|
856
|
+
defaultItemsPerPage={dtVirtualized ? 200 : 10}
|
|
857
|
+
itemsPerPageOptions={dtVirtualized ? [50, 100, 200] : [5, 10, 20]}
|
|
858
|
+
size={dtSize}
|
|
859
|
+
virtualized={dtVirtualized}
|
|
860
|
+
virtualizedMaxHeight={360}
|
|
861
|
+
stickyHeader={dtStickyHeader}
|
|
862
|
+
layout={dtLayout}
|
|
863
|
+
density={dtDensity}
|
|
864
|
+
debounceMs={0}
|
|
865
|
+
/>
|
|
866
|
+
</div>
|
|
867
|
+
</div>
|
|
571
868
|
</div>
|
|
572
869
|
}
|
|
573
870
|
/>
|
|
@@ -397,6 +397,15 @@ export const PageShowcase = () => {
|
|
|
397
397
|
const [screen, setScreen] = useState<"list" | "detail">("list");
|
|
398
398
|
const [selectedUser, setSelectedUser] = useState<Record<string, unknown> | null>(null);
|
|
399
399
|
const [showIcon, setShowIcon] = useState(true);
|
|
400
|
+
const [hPadding, setHPadding] = useState<"default" | "none" | "tight" | "wide">("default");
|
|
401
|
+
|
|
402
|
+
// `undefined` lets ITPage fall back to its own default ("px-4 sm:px-6 lg:px-8").
|
|
403
|
+
const hPaddingValue: string | undefined = {
|
|
404
|
+
default: undefined,
|
|
405
|
+
none: "px-0",
|
|
406
|
+
tight: "px-2 sm:px-3",
|
|
407
|
+
wide: "px-8 sm:px-12 lg:px-16",
|
|
408
|
+
}[hPadding];
|
|
400
409
|
|
|
401
410
|
const isNormal = state === "normal";
|
|
402
411
|
const isDetail = screen === "detail";
|
|
@@ -551,6 +560,7 @@ export const PageShowcase = () => {
|
|
|
551
560
|
error={state === "error" ? "Could not load user details. Please try again." : null}
|
|
552
561
|
errorTitle="Details error"
|
|
553
562
|
onRetry={() => setState("normal")}
|
|
563
|
+
horizontalPadding={hPaddingValue}
|
|
554
564
|
empty={state === "empty"}
|
|
555
565
|
emptyTitle="No user data"
|
|
556
566
|
emptyDescription="No information available for this user."
|
|
@@ -601,6 +611,7 @@ export const PageShowcase = () => {
|
|
|
601
611
|
error={state === "error" ? "Could not load team members. Check your connection and try again." : null}
|
|
602
612
|
errorTitle="Connection error"
|
|
603
613
|
onRetry={() => setState("normal")}
|
|
614
|
+
horizontalPadding={hPaddingValue}
|
|
604
615
|
empty={state === "empty"}
|
|
605
616
|
emptyTitle="No team members yet"
|
|
606
617
|
emptyDescription="Invite your first team member to get started."
|
|
@@ -640,6 +651,21 @@ export const PageShowcase = () => {
|
|
|
640
651
|
<ITSlideToggle isOn={showIcon} onToggle={setShowIcon} size="sm" />
|
|
641
652
|
</ITFlex>
|
|
642
653
|
|
|
654
|
+
<div>
|
|
655
|
+
<p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">Horizontal padding</p>
|
|
656
|
+
<ITFlex gap={2} wrap="wrap">
|
|
657
|
+
{(["default", "none", "tight", "wide"] as const).map((p) => (
|
|
658
|
+
<ITButton
|
|
659
|
+
key={p}
|
|
660
|
+
label={p === "default" ? "Default" : p === "none" ? "px-0" : p === "tight" ? "px-2" : "px-8"}
|
|
661
|
+
variant={hPadding === p ? "filled" : "outlined"}
|
|
662
|
+
size="sm"
|
|
663
|
+
onClick={() => setHPadding(p)}
|
|
664
|
+
/>
|
|
665
|
+
))}
|
|
666
|
+
</ITFlex>
|
|
667
|
+
</div>
|
|
668
|
+
|
|
643
669
|
{isNormal && (
|
|
644
670
|
<div>
|
|
645
671
|
<p className="text-xs font-bold uppercase tracking-wider text-slate-400 dark:text-slate-500 mb-2">Screen</p>
|
|
@@ -674,6 +700,7 @@ export const PageShowcase = () => {
|
|
|
674
700
|
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Users — table with actions</h4>
|
|
675
701
|
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
676
702
|
<ITPage
|
|
703
|
+
horizontalPadding={hPaddingValue}
|
|
677
704
|
title="Users"
|
|
678
705
|
description="1,245 registered · 38 active now"
|
|
679
706
|
icon={<FaUsers size={20} />}
|
|
@@ -701,6 +728,7 @@ export const PageShowcase = () => {
|
|
|
701
728
|
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Product detail</h4>
|
|
702
729
|
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
703
730
|
<ITPage
|
|
731
|
+
horizontalPadding={hPaddingValue}
|
|
704
732
|
title="MacBook Pro 16'' M3 Max"
|
|
705
733
|
description="SKU: MBP-16-M3MAX-2024 · Stock: 24"
|
|
706
734
|
icon={<FaBoxOpen size={20} />}
|
|
@@ -748,6 +776,7 @@ export const PageShowcase = () => {
|
|
|
748
776
|
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Edit profile</h4>
|
|
749
777
|
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
750
778
|
<ITPage
|
|
779
|
+
horizontalPadding={hPaddingValue}
|
|
751
780
|
title="Edit profile"
|
|
752
781
|
description="Update your personal information and contact details"
|
|
753
782
|
icon={<FaEdit size={20} />}
|
|
@@ -798,6 +827,7 @@ export const PageShowcase = () => {
|
|
|
798
827
|
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Orders — status table</h4>
|
|
799
828
|
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
800
829
|
<ITPage
|
|
830
|
+
horizontalPadding={hPaddingValue}
|
|
801
831
|
title="Orders"
|
|
802
832
|
description="89 pending · 1,247 completed this month"
|
|
803
833
|
icon={<FaShoppingCart size={20} />}
|
|
@@ -850,6 +880,7 @@ export const PageShowcase = () => {
|
|
|
850
880
|
<h4 className="text-sm font-bold text-slate-700 dark:text-slate-200 mb-3">Settings</h4>
|
|
851
881
|
<div className="border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
|
|
852
882
|
<ITPage
|
|
883
|
+
horizontalPadding={hPaddingValue}
|
|
853
884
|
title="Settings"
|
|
854
885
|
description="Manage your account and workspace preferences"
|
|
855
886
|
icon={<FaCog size={20} />}
|
|
@@ -901,13 +901,20 @@ export const TextShowcase = () => {
|
|
|
901
901
|
|
|
902
902
|
// 3. ITLayout & ITNavbar Showcase
|
|
903
903
|
export const LayoutShowcase = () => {
|
|
904
|
-
const [collapsed, setCollapsed] = useState(
|
|
904
|
+
const [collapsed, setCollapsed] = useState(true);
|
|
905
905
|
const [activeId, setActiveId] = useState("dashboard");
|
|
906
906
|
const [navActiveId, setNavActiveId] = useState<string | null>("1");
|
|
907
907
|
|
|
908
908
|
const topBar = {
|
|
909
909
|
logo: <div className="w-9 h-9 rounded-lg bg-primary-500 flex items-center justify-center text-white font-bold">A</div>,
|
|
910
910
|
logoText: "AXZY Console",
|
|
911
|
+
centerContent: (
|
|
912
|
+
<input
|
|
913
|
+
type="search"
|
|
914
|
+
placeholder="Buscar componente…"
|
|
915
|
+
className="w-full rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 px-3 py-1.5 text-xs text-slate-700 dark:text-slate-200 outline-none focus:ring-2 focus:ring-primary-500/40"
|
|
916
|
+
/>
|
|
917
|
+
),
|
|
911
918
|
navItems: [
|
|
912
919
|
{ id: "home", label: "Inicio", icon: <FaHome />, action: () => setActiveId("home") },
|
|
913
920
|
{ id: "docs", label: "Documentos", icon: <FaFileAlt />, action: () => setActiveId("docs") },
|
|
@@ -942,19 +949,29 @@ export const LayoutShowcase = () => {
|
|
|
942
949
|
{ id: "settings", label: "Configuración", icon: <FaCog />, isActive: activeId === "settings", action: () => setActiveId("settings") },
|
|
943
950
|
],
|
|
944
951
|
isCollapsed: collapsed,
|
|
945
|
-
|
|
952
|
+
header: (
|
|
953
|
+
<ITText
|
|
954
|
+
className="text-xs font-bold tracking-wide"
|
|
955
|
+
style={{ color: "var(--it-sidebar-label-color, var(--color-secondary-700))" }}
|
|
956
|
+
>
|
|
957
|
+
AXZY v1.3
|
|
958
|
+
</ITText>
|
|
959
|
+
),
|
|
946
960
|
};
|
|
947
961
|
|
|
948
962
|
const code = `<ITLayout
|
|
949
963
|
topBar={{
|
|
950
964
|
logo: <Logo />,
|
|
951
965
|
logoText: "AXZY Console",
|
|
966
|
+
// Slot central opcional (visible desde lg) — ideal para un buscador global
|
|
967
|
+
centerContent: <SearchBox value={term} onChange={setTerm} />,
|
|
952
968
|
userMenu: { userName, userEmail, menuItems }
|
|
953
969
|
}}
|
|
954
970
|
sidebar={{
|
|
955
971
|
navigationItems: [...],
|
|
956
972
|
isCollapsed: ${collapsed},
|
|
957
|
-
|
|
973
|
+
// Slot opcional sobre la navegación (oculto al colapsar)
|
|
974
|
+
header: <span>AXZY v1.3</span>
|
|
958
975
|
}}
|
|
959
976
|
>
|
|
960
977
|
{/* Tu contenido */}
|
|
@@ -1025,7 +1042,7 @@ export const LayoutShowcase = () => {
|
|
|
1025
1042
|
<ITSlideToggle isOn={collapsed} onToggle={setCollapsed} size="sm" />
|
|
1026
1043
|
</div>
|
|
1027
1044
|
<div className="text-xs text-slate-500">
|
|
1028
|
-
En móvil (<lg) el sidebar se abre como drawer con un fondo oscuro. Usa el botón ☰ del topbar.
|
|
1045
|
+
En móvil (<lg) el sidebar se abre como drawer con un fondo oscuro. Usa el botón ☰ del topbar. El botón ⟩ del propio sidebar también colapsa/expande.
|
|
1029
1046
|
</div>
|
|
1030
1047
|
</ITStack>
|
|
1031
1048
|
}
|
|
@@ -1088,6 +1105,27 @@ export const LayoutShowcase = () => {
|
|
|
1088
1105
|
</div>
|
|
1089
1106
|
</ITStack>
|
|
1090
1107
|
}
|
|
1108
|
+
doc={{
|
|
1109
|
+
summary: "Chasis estructural del portal: topbar + sidebar colapsable + área de contenido.",
|
|
1110
|
+
examples: [
|
|
1111
|
+
'<ITLayout topBar={{ centerContent: <SearchBox /> }} sidebar={{ navigationItems }}>',
|
|
1112
|
+
" <Dashboard />",
|
|
1113
|
+
"</ITLayout>",
|
|
1114
|
+
],
|
|
1115
|
+
props: [
|
|
1116
|
+
{ name: "topBar", type: "ITTopBarProps", required: true, description: "Configuración de la barra superior." },
|
|
1117
|
+
{ name: "sidebar", type: "ITSidebarProps", required: true, description: "Configuración del sidebar (navigationItems, isCollapsed, header…)." },
|
|
1118
|
+
{ name: "children", type: "React.ReactNode", required: true, description: "Contenido principal." },
|
|
1119
|
+
{ name: "className", type: "string", description: "Clases del wrapper externo." },
|
|
1120
|
+
{ name: "contentClassName", type: "string", description: "Clases del contenedor de contenido." },
|
|
1121
|
+
{ name: "sidebar.header", type: "React.ReactNode", description: "Slot opcional sobre la navegación (ej. wordmark o filtro). Oculto al colapsar." },
|
|
1122
|
+
],
|
|
1123
|
+
notes: [
|
|
1124
|
+
"El sidebar hereda aria-current/aria-expanded y acepta un slot header opcional.",
|
|
1125
|
+
"Con `sidebar.isCollapsed` omitido el contenido reserva 88px (hover expande sobre el contenido). Si lo controlas a `false`, reserva 280px y no tapa el contenido.",
|
|
1126
|
+
"El riel colapsado se expande automáticamente al pasar el cursor por encima; no hay botón de colapso.",
|
|
1127
|
+
],
|
|
1128
|
+
}}
|
|
1091
1129
|
/>
|
|
1092
1130
|
);
|
|
1093
1131
|
};
|
package/src/theme/theme.types.ts
CHANGED
|
@@ -50,6 +50,8 @@ export interface ITThemePalette {
|
|
|
50
50
|
rowBg: string;
|
|
51
51
|
/** Text color of table body rows. @default "#1e293b" */
|
|
52
52
|
rowText: string;
|
|
53
|
+
/** Background color applied to a table body row while the pointer is over it (hex). @default "#f1f5f9" */
|
|
54
|
+
rowHover: string;
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
57
|
|