@axzydev/axzy_ui_system 1.2.7 → 1.2.8
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/dist/index.cjs +576 -398
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +1463 -86
- package/dist/index.d.ts +1463 -86
- package/dist/index.js +597 -417
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/App.tsx +19 -54
- package/src/components/alert/alert.doc.mdx +48 -0
- package/src/components/alert/alert.props.ts +7 -0
- package/src/components/alert/alert.tsx +8 -0
- package/src/components/avatar/avatar.doc.mdx +48 -0
- package/src/components/avatar/avatar.props.ts +8 -0
- package/src/components/avatar/avatar.tsx +25 -5
- package/src/components/badget/badget.doc.mdx +46 -0
- package/src/components/badget/badget.props.ts +6 -0
- package/src/components/badget/badget.tsx +11 -0
- package/src/components/breadcrumbs/breadcrumbs.doc.mdx +57 -0
- package/src/components/breadcrumbs/breadcrumbs.props.ts +6 -0
- package/src/components/breadcrumbs/breadcrumbs.tsx +12 -0
- package/src/components/button/button.doc.mdx +59 -0
- package/src/components/button/button.props.ts +12 -0
- package/src/components/button/button.tsx +11 -0
- package/src/components/calendar/calendar.doc.mdx +75 -0
- package/src/components/calendar/calendar.props.ts +27 -7
- package/src/components/calendar/calendar.tsx +13 -0
- package/src/components/card/card.doc.mdx +54 -0
- package/src/components/card/card.props.ts +11 -0
- package/src/components/card/card.tsx +13 -0
- package/src/components/checkbox/checkbox.doc.mdx +54 -0
- package/src/components/checkbox/checkbox.props.ts +7 -0
- package/src/components/checkbox/checkbox.tsx +9 -0
- package/src/components/confirm-dialog/confirm-dialog.doc.mdx +60 -0
- package/src/components/confirm-dialog/confirm-dialog.props.ts +9 -0
- package/src/components/confirm-dialog/confirm-dialog.tsx +19 -0
- package/src/components/data-table/ITDataTable.doc.mdx +59 -0
- package/src/components/data-table/dataTable.props.ts +15 -0
- package/src/components/data-table/dataTable.tsx +178 -73
- package/src/components/date-picker/date-picker.doc.mdx +65 -0
- package/src/components/date-picker/date-picker.props.ts +17 -0
- package/src/components/date-picker/datePicker.tsx +31 -1
- package/src/components/dialog/dialog.doc.mdx +54 -0
- package/src/components/dialog/dialog.props.ts +7 -0
- package/src/components/dialog/dialog.tsx +26 -0
- package/src/components/divider/divider.doc.mdx +42 -0
- package/src/components/divider/divider.props.ts +5 -0
- package/src/components/divider/divider.tsx +16 -0
- package/src/components/drawer/drawer.doc.mdx +59 -0
- package/src/components/drawer/drawer.props.ts +9 -0
- package/src/components/drawer/drawer.tsx +20 -0
- package/src/components/dropfile/dropfile.doc.mdx +59 -0
- package/src/components/dropfile/dropfile.props.ts +41 -0
- package/src/components/dropfile/dropfile.tsx +21 -33
- package/src/components/empty-state/empty-state.doc.mdx +50 -0
- package/src/components/empty-state/empty-state.props.ts +5 -0
- package/src/components/empty-state/empty-state.tsx +16 -0
- package/src/components/flex/flex.doc.mdx +59 -0
- package/src/components/flex/flex.props.ts +13 -0
- package/src/components/flex/flex.tsx +15 -0
- package/src/components/form-builder/fieldRenderer.tsx +52 -2
- package/src/components/form-builder/formBuilder.doc.mdx +124 -0
- package/src/components/form-builder/formBuilder.props.ts +12 -2
- package/src/components/form-builder/formBuilder.tsx +31 -0
- package/src/components/form-header/form-header.doc.mdx +38 -0
- package/src/components/form-header/form-header.props.ts +3 -0
- package/src/components/form-header/form-header.stories.tsx +53 -0
- package/src/components/form-header/form-header.tsx +11 -0
- package/src/components/grid/grid.doc.mdx +54 -0
- package/src/components/grid/grid.props.ts +13 -0
- package/src/components/grid/grid.tsx +19 -0
- package/src/components/image/image.doc.mdx +44 -0
- package/src/components/image/image.props.ts +5 -0
- package/src/components/image/image.stories.tsx +49 -0
- package/src/components/image/image.tsx +19 -0
- package/src/components/input/input.doc.mdx +70 -0
- package/src/components/input/input.props.ts +34 -2
- package/src/components/input/input.tsx +29 -0
- package/src/components/layout/layout.doc.mdx +51 -0
- package/src/components/layout/layout.props.ts +5 -0
- package/src/components/layout/layout.tsx +29 -2
- package/src/components/loader/loader.doc.mdx +43 -0
- package/src/components/loader/loader.props.ts +4 -0
- package/src/components/loader/loader.tsx +17 -0
- package/src/components/navbar/navbar.doc.mdx +77 -0
- package/src/components/navbar/navbar.props.ts +37 -1
- package/src/components/navbar/navbar.stories.tsx +84 -0
- package/src/components/navbar/navbar.tsx +27 -0
- package/src/components/page/page.doc.mdx +80 -0
- package/src/components/page/page.props.ts +21 -0
- package/src/components/page/page.tsx +18 -0
- package/src/components/page-header/page-header.doc.mdx +54 -0
- package/src/components/page-header/page-header.props.ts +9 -0
- package/src/components/page-header/page-header.tsx +15 -0
- package/src/components/pagination/pagination.doc.mdx +62 -0
- package/src/components/pagination/pagination.props.ts +11 -32
- package/src/components/pagination/pagination.tsx +17 -0
- package/src/components/popover/popover.doc.mdx +53 -0
- package/src/components/popover/popover.props.ts +8 -0
- package/src/components/popover/popover.tsx +13 -0
- package/src/components/progress/progress.doc.mdx +48 -0
- package/src/components/progress/progress.props.ts +8 -0
- package/src/components/progress/progress.tsx +9 -0
- package/src/components/radio/radio.doc.mdx +62 -0
- package/src/components/radio/radio.props.ts +11 -0
- package/src/components/radio/radio.tsx +18 -0
- package/src/components/search-select/search-select.doc.mdx +72 -0
- package/src/components/search-select/search-select.props.ts +23 -18
- package/src/components/search-select/search-select.tsx +27 -2
- package/src/components/searchTable/searchTable.doc.mdx +79 -0
- package/src/components/searchTable/searchTable.props.ts +42 -3
- package/src/components/searchTable/searchTable.stories.tsx +74 -0
- package/src/components/searchTable/searchTable.tsx +23 -0
- package/src/components/segmented-control/segmented-control.doc.mdx +60 -0
- package/src/components/segmented-control/segmented-control.props.ts +12 -0
- package/src/components/segmented-control/segmented-control.tsx +15 -0
- package/src/components/select/select.doc.mdx +75 -0
- package/src/components/select/select.props.ts +22 -0
- package/src/components/select/select.tsx +2 -5
- package/src/components/sidebar/sidebar.doc.mdx +86 -0
- package/src/components/sidebar/sidebar.props.ts +22 -0
- package/src/components/sidebar/sidebar.tsx +22 -0
- package/src/components/skeleton/skeleton.doc.mdx +43 -0
- package/src/components/skeleton/skeleton.props.ts +8 -0
- package/src/components/skeleton/skeleton.tsx +10 -0
- package/src/components/slide/slide.doc.mdx +46 -0
- package/src/components/slide/slide.props.ts +11 -24
- package/src/components/slide/slide.tsx +10 -1
- package/src/components/slider/slider.doc.mdx +49 -0
- package/src/components/slider/slider.props.ts +9 -0
- package/src/components/slider/slider.tsx +15 -0
- package/src/components/stack/stack.doc.mdx +126 -0
- package/src/components/stack/stack.props.ts +18 -0
- package/src/components/stack/stack.stories.tsx +221 -16
- package/src/components/stack/stack.tsx +21 -0
- package/src/components/stat-card/stat-card.doc.mdx +48 -0
- package/src/components/stat-card/stat-card.props.ts +9 -0
- package/src/components/stat-card/stat-card.tsx +10 -0
- package/src/components/stepper/stepper.doc.mdx +54 -0
- package/src/components/stepper/stepper.props.ts +11 -1
- package/src/components/stepper/stepper.tsx +15 -0
- package/src/components/table/table.doc.mdx +58 -0
- package/src/components/table/table.props.ts +17 -0
- package/src/components/table/table.tsx +199 -84
- package/src/components/tabs/tabs.doc.mdx +52 -0
- package/src/components/tabs/tabs.props.ts +11 -0
- package/src/components/tabs/tabs.tsx +14 -0
- package/src/components/text/text.doc.mdx +41 -0
- package/src/components/text/text.props.ts +5 -0
- package/src/components/text/text.stories.tsx +67 -0
- package/src/components/text/text.tsx +10 -0
- package/src/components/textarea/textarea.doc.mdx +64 -0
- package/src/components/textarea/textarea.props.ts +11 -0
- package/src/components/textarea/textarea.tsx +16 -0
- package/src/components/theme-provider/themeProvider.doc.mdx +75 -0
- package/src/components/theme-provider/themeProvider.props.ts +3 -0
- package/src/components/theme-provider/themeProvider.stories.tsx +59 -0
- package/src/components/theme-provider/themeProvider.tsx +14 -0
- package/src/components/time-picker/timePicker.doc.mdx +68 -0
- package/src/components/time-picker/timePicker.props.ts +14 -0
- package/src/components/time-picker/timePicker.tsx +22 -1
- package/src/components/toast/toast.doc.mdx +50 -0
- package/src/components/toast/toast.props.ts +5 -0
- package/src/components/toast/toast.tsx +21 -0
- package/src/components/tooltip/tooltip.doc.mdx +47 -0
- package/src/components/tooltip/tooltip.props.ts +6 -0
- package/src/components/tooltip/tooltip.tsx +19 -0
- package/src/components/topbar/topbar.doc.mdx +69 -0
- package/src/components/topbar/topbar.props.ts +15 -0
- package/src/components/topbar/topbar.tsx +38 -0
- package/src/components/triple-filter/tripleFilter.doc.mdx +62 -0
- package/src/components/triple-filter/tripleFilter.props.ts +7 -0
- package/src/components/triple-filter/tripleFilter.tsx +32 -0
- package/src/index.css +2 -0
- package/src/showcases/DataShowcases.tsx +529 -45
- package/src/showcases/FormShowcases.tsx +399 -29
- package/src/showcases/HomeShowcase.tsx +252 -272
- package/src/showcases/LayoutPrimitivesShowcases.tsx +1224 -248
- package/src/showcases/PageShowcases.tsx +688 -391
- package/src/showcases/ShowcaseLayout.tsx +237 -73
- package/src/showcases/StructureShowcases.tsx +846 -73
- package/src/types/table.types.ts +1 -1
- package/src/utils/styles.ts +2 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { useTableState } from "@/hooks/useTableState";
|
|
2
2
|
import { sizeStyles, variantStyles } from "@/types/table.types";
|
|
3
3
|
import clsx from "clsx";
|
|
4
|
-
import React from "react";
|
|
4
|
+
import React, { useState } from "react";
|
|
5
5
|
import {
|
|
6
6
|
FaCheck,
|
|
7
7
|
FaSpinner,
|
|
8
|
-
FaTimes
|
|
8
|
+
FaTimes,
|
|
9
|
+
FaTable,
|
|
10
|
+
FaThLarge
|
|
9
11
|
} from "react-icons/fa";
|
|
10
12
|
import { MdOutlineSwapVert } from "react-icons/md";
|
|
11
13
|
import ITInput from "../input/input";
|
|
@@ -23,6 +25,23 @@ export const formatCurrencyMX = (value: number) => {
|
|
|
23
25
|
currency: "MXN",
|
|
24
26
|
});
|
|
25
27
|
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A feature-rich data table with per-column filtering, sortable columns,
|
|
31
|
+
* pagination, boolean/catalog type support, and currency formatting.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* <ITTable
|
|
35
|
+
* columns={[
|
|
36
|
+
* { key: "name", label: "Name", type: "string", sortable: true },
|
|
37
|
+
* { key: "active", label: "Active", type: "boolean", filter: true },
|
|
38
|
+
* { key: "actions", label: "", type: "actions", actions: (row) => <ITButton>Edit</ITButton> },
|
|
39
|
+
* ]}
|
|
40
|
+
* data={users}
|
|
41
|
+
* title="User List"
|
|
42
|
+
* size="sm"
|
|
43
|
+
* />
|
|
44
|
+
*/
|
|
26
45
|
export default function ITTable<T extends Record<string, unknown>>({
|
|
27
46
|
columns,
|
|
28
47
|
data = [],
|
|
@@ -33,7 +52,13 @@ export default function ITTable<T extends Record<string, unknown>>({
|
|
|
33
52
|
itemsPerPageOptions = [5, 10, 20],
|
|
34
53
|
defaultItemsPerPage = 10,
|
|
35
54
|
title,
|
|
55
|
+
renderCard,
|
|
56
|
+
defaultView = "table",
|
|
57
|
+
showVerticalBorder = true,
|
|
58
|
+
verticalBorderClassname,
|
|
36
59
|
}: ITTableProps<T>) {
|
|
60
|
+
const [viewMode, setViewMode] = useState<"table" | "cards">(defaultView);
|
|
61
|
+
|
|
37
62
|
const {
|
|
38
63
|
currentPage,
|
|
39
64
|
itemsPerPage,
|
|
@@ -261,103 +286,193 @@ export default function ITTable<T extends Record<string, unknown>>({
|
|
|
261
286
|
}
|
|
262
287
|
};
|
|
263
288
|
|
|
289
|
+
const renderDefaultCard = (row: T) => {
|
|
290
|
+
const dataCols = columns.filter((c) => c.type !== "actions");
|
|
291
|
+
const actionCol = columns.find((c) => c.type === "actions");
|
|
292
|
+
return (
|
|
293
|
+
<div className="bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 p-3 space-y-2 shadow-sm">
|
|
294
|
+
{dataCols.map((col) => (
|
|
295
|
+
<div key={col.key} className="flex items-center justify-between gap-2">
|
|
296
|
+
<span className="text-[10px] font-bold uppercase tracking-wider text-slate-400 whitespace-nowrap flex-shrink-0 min-w-[70px]">
|
|
297
|
+
{col.label}
|
|
298
|
+
</span>
|
|
299
|
+
<span className="text-sm font-medium text-slate-800 dark:text-white text-right truncate">
|
|
300
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
301
|
+
</span>
|
|
302
|
+
</div>
|
|
303
|
+
))}
|
|
304
|
+
{actionCol?.actions && (
|
|
305
|
+
<div className="flex items-center justify-end gap-2 pt-2 border-t border-slate-100 dark:border-slate-700">
|
|
306
|
+
{actionCol.actions(row)}
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
);
|
|
311
|
+
};
|
|
312
|
+
|
|
264
313
|
return (
|
|
265
314
|
<div className={clsx("space-y-4 w-full", containerClassName)}>
|
|
266
315
|
<div className="rounded-xl shadow-sm overflow-hidden" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
267
316
|
{/* Header outside overflow */}
|
|
268
317
|
{title && (
|
|
269
|
-
<div className="px-6 py-5
|
|
318
|
+
<div className="px-6 py-5 flex items-center justify-between" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
270
319
|
<ITText as="h2" className="text-xl font-bold text-secondary-900 leading-tight">{title}</ITText>
|
|
320
|
+
<div className="flex items-center bg-slate-100 dark:bg-slate-800/80 rounded-lg p-0.5 border border-slate-200 dark:border-slate-700/50">
|
|
321
|
+
<button
|
|
322
|
+
onClick={() => setViewMode("table")}
|
|
323
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
|
|
324
|
+
viewMode === "table"
|
|
325
|
+
? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
|
|
326
|
+
: "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
|
|
327
|
+
}`}
|
|
328
|
+
>
|
|
329
|
+
<FaTable size={11} />
|
|
330
|
+
Table
|
|
331
|
+
</button>
|
|
332
|
+
<button
|
|
333
|
+
onClick={() => setViewMode("cards")}
|
|
334
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
|
|
335
|
+
viewMode === "cards"
|
|
336
|
+
? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
|
|
337
|
+
: "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
|
|
338
|
+
}`}
|
|
339
|
+
>
|
|
340
|
+
<FaThLarge size={11} />
|
|
341
|
+
Cards
|
|
342
|
+
</button>
|
|
343
|
+
</div>
|
|
271
344
|
</div>
|
|
272
345
|
)}
|
|
273
346
|
|
|
274
|
-
{
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
347
|
+
{!title && (
|
|
348
|
+
<div className="flex justify-end px-4 pt-3">
|
|
349
|
+
<div className="flex items-center bg-slate-100 dark:bg-slate-800/80 rounded-lg p-0.5 border border-slate-200 dark:border-slate-700/50">
|
|
350
|
+
<button
|
|
351
|
+
onClick={() => setViewMode("table")}
|
|
352
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
|
|
353
|
+
viewMode === "table"
|
|
354
|
+
? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
|
|
355
|
+
: "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
|
|
356
|
+
}`}
|
|
357
|
+
>
|
|
358
|
+
<FaTable size={11} />
|
|
359
|
+
Table
|
|
360
|
+
</button>
|
|
361
|
+
<button
|
|
362
|
+
onClick={() => setViewMode("cards")}
|
|
363
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
|
|
364
|
+
viewMode === "cards"
|
|
365
|
+
? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
|
|
366
|
+
: "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
|
|
367
|
+
}`}
|
|
368
|
+
>
|
|
369
|
+
<FaThLarge size={11} />
|
|
370
|
+
Cards
|
|
371
|
+
</button>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
)}
|
|
283
375
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
376
|
+
{viewMode === "cards" ? (
|
|
377
|
+
<div className="p-4 space-y-3 max-h-[400px] overflow-y-auto">
|
|
378
|
+
{currentData.length > 0 ? (
|
|
379
|
+
currentData.map((row, i) => (
|
|
380
|
+
<div key={i}>
|
|
381
|
+
{renderCard ? renderCard(row) : renderDefaultCard(row)}
|
|
382
|
+
</div>
|
|
383
|
+
))
|
|
384
|
+
) : (
|
|
385
|
+
<div className="flex flex-col items-center justify-center py-12 text-secondary-400">
|
|
386
|
+
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
387
|
+
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
388
|
+
</div>
|
|
389
|
+
)}
|
|
390
|
+
</div>
|
|
391
|
+
) : (
|
|
392
|
+
<div className="overflow-x-auto">
|
|
393
|
+
<table
|
|
394
|
+
className={clsx(
|
|
395
|
+
"min-w-max w-full text-sm text-left text-secondary-600",
|
|
396
|
+
showVerticalBorder && "[&_th]:border-r [&_th:last-child]:border-r-0 [&_td]:border-r [&_td:last-child]:border-r-0",
|
|
397
|
+
showVerticalBorder && (verticalBorderClassname || "[&_th]:border-slate-100 dark:[&_th]:border-slate-700/30 [&_td]:border-slate-100 dark:[&_td]:border-slate-700/30"),
|
|
398
|
+
variantStyles[variant],
|
|
399
|
+
sizeStyles[size]
|
|
400
|
+
)}
|
|
401
|
+
>
|
|
402
|
+
<thead>
|
|
403
|
+
<tr className="bg-secondary-50 text-xs uppercase tracking-wider font-semibold text-slate-700 dark:text-slate-200">
|
|
404
|
+
{columns.map((col) => (
|
|
405
|
+
<th
|
|
406
|
+
key={col.key}
|
|
407
|
+
scope="col"
|
|
408
|
+
className={clsx("px-4 py-4 align-top", col.className)}
|
|
409
|
+
>
|
|
410
|
+
<div className="flex flex-col gap-3 min-w-[150px]">
|
|
411
|
+
<div className="flex items-center justify-between gap-2">
|
|
412
|
+
<ITText as="span" className="text-slate-900 dark:text-white font-bold">{col.label}</ITText>
|
|
413
|
+
{col.sortable && col.type !== "actions" && (
|
|
414
|
+
<button
|
|
415
|
+
onClick={() => handleSort(col.key)}
|
|
416
|
+
className={`p-1 rounded-md transition-colors ${sortConfig?.key === col.key
|
|
417
|
+
? "bg-secondary-200 text-secondary-900"
|
|
418
|
+
: "hover:bg-secondary-200 text-secondary-400 hover:text-secondary-700"
|
|
419
|
+
}`}
|
|
420
|
+
title={`Ordenar por ${col.label}`}
|
|
421
|
+
>
|
|
422
|
+
<MdOutlineSwapVert className="w-4 h-4" aria-hidden="true" />
|
|
423
|
+
</button>
|
|
424
|
+
)}
|
|
425
|
+
</div>
|
|
426
|
+
<div className="w-full">
|
|
427
|
+
{col.filter ? renderFilterInput(col) : null}
|
|
428
|
+
</div>
|
|
308
429
|
</div>
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
430
|
+
</th>
|
|
431
|
+
))}
|
|
432
|
+
</tr>
|
|
433
|
+
</thead>
|
|
434
|
+
<tbody className="divide-y divide-slate-100 dark:divide-slate-700/30">
|
|
435
|
+
{currentData.length > 0 ? (
|
|
436
|
+
currentData.map((row, rowIndex) => (
|
|
437
|
+
<tr
|
|
438
|
+
key={rowIndex}
|
|
439
|
+
className={clsx("hover:bg-secondary-50/50 transition-colors duration-150 group", variant === "striped" && "odd:bg-secondary-50/40 dark:odd:bg-slate-800/20")}
|
|
440
|
+
>
|
|
441
|
+
{columns.map((col) => (
|
|
442
|
+
<td
|
|
443
|
+
key={`${rowIndex}-${col.key}`}
|
|
444
|
+
className={clsx("px-4 py-3 align-middle", col.className)}
|
|
445
|
+
>
|
|
446
|
+
{col.type === "actions" ? (
|
|
447
|
+
<div className="flex items-center justify-center gap-2">
|
|
448
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
449
|
+
</div>
|
|
450
|
+
) : (
|
|
451
|
+
<div className="text-secondary-700 font-medium">
|
|
452
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
</td>
|
|
456
|
+
))}
|
|
457
|
+
</tr>
|
|
458
|
+
))
|
|
459
|
+
) : (
|
|
460
|
+
<tr>
|
|
461
|
+
<td colSpan={columns.length} className="px-6 py-12 text-center">
|
|
462
|
+
<div className="flex flex-col items-center justify-center text-secondary-400">
|
|
463
|
+
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
464
|
+
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
313
465
|
</div>
|
|
314
|
-
</
|
|
315
|
-
</th>
|
|
316
|
-
))}
|
|
317
|
-
</tr>
|
|
318
|
-
</thead>
|
|
319
|
-
<tbody className="divide-y divide-secondary-100">
|
|
320
|
-
{currentData.length > 0 ? (
|
|
321
|
-
currentData.map((row, rowIndex) => (
|
|
322
|
-
<tr
|
|
323
|
-
key={rowIndex}
|
|
324
|
-
className="hover:bg-secondary-50/50 transition-colors duration-150 group"
|
|
325
|
-
>
|
|
326
|
-
{columns.map((col) => (
|
|
327
|
-
<td
|
|
328
|
-
key={`${rowIndex}-${col.key}`}
|
|
329
|
-
className={clsx("px-4 py-3 align-middle", col.className)}
|
|
330
|
-
>
|
|
331
|
-
{col.type === "actions" ? (
|
|
332
|
-
<div className="flex items-center justify-center gap-2">
|
|
333
|
-
{renderCellContent(col, row) as React.ReactNode}
|
|
334
|
-
</div>
|
|
335
|
-
) : (
|
|
336
|
-
<div className="text-secondary-700 font-medium">
|
|
337
|
-
{renderCellContent(col, row) as React.ReactNode}
|
|
338
|
-
</div>
|
|
339
|
-
)}
|
|
340
|
-
</td>
|
|
341
|
-
))}
|
|
466
|
+
</td>
|
|
342
467
|
</tr>
|
|
343
|
-
)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
349
|
-
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
350
|
-
</div>
|
|
351
|
-
</td>
|
|
352
|
-
</tr>
|
|
353
|
-
)}
|
|
354
|
-
</tbody>
|
|
355
|
-
</table>
|
|
356
|
-
</div>
|
|
357
|
-
|
|
468
|
+
)}
|
|
469
|
+
</tbody>
|
|
470
|
+
</table>
|
|
471
|
+
</div>
|
|
472
|
+
)}
|
|
358
473
|
|
|
359
474
|
{/* Pagination */}
|
|
360
|
-
<div className="rounded-b-xl
|
|
475
|
+
<div className="rounded-b-xl px-6 py-4" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
361
476
|
<ITPagination
|
|
362
477
|
currentPage={currentPage}
|
|
363
478
|
totalPages={computedTotalPages}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './tabs.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# ITTabs
|
|
7
|
+
|
|
8
|
+
Tab navigation component with multiple visual variants, orientation options, and color themes.
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
|------|------|---------|-------------|
|
|
14
|
+
| `tabs` | `{ label: string; icon?: ReactNode; disabled?: boolean }[]` | — | Tab definitions |
|
|
15
|
+
| `activeTab` | `number` | `0` | Zero-indexed active tab |
|
|
16
|
+
| `onChange` | `(index: number) => void` | — | Tab change handler |
|
|
17
|
+
| `variant` | `'default' \| 'pills' \| 'underline' \| 'enclosed'` | `'default'` | Visual style variant |
|
|
18
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Tab layout orientation |
|
|
19
|
+
| `fullWidth` | `boolean` | `false` | Stretch tabs to fill container |
|
|
20
|
+
| `color` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'danger' \| 'info' \| 'neutral'` | `'primary'` | Color theme |
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { ITTabs } from '@axzydev/axzy_ui_system';
|
|
26
|
+
|
|
27
|
+
const tabs = [
|
|
28
|
+
{ label: 'General' },
|
|
29
|
+
{ label: 'Security', icon: <LockIcon /> },
|
|
30
|
+
{ label: 'Billing', disabled: true },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
<ITTabs tabs={tabs} activeTab={0} onChange={(i) => console.log(i)} />
|
|
34
|
+
<ITTabs tabs={tabs} variant="pills" fullWidth />
|
|
35
|
+
<ITTabs tabs={tabs} variant="underline" orientation="vertical" color="success" />
|
|
36
|
+
<ITTabs tabs={tabs} variant="enclosed" />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Notes
|
|
40
|
+
|
|
41
|
+
- Four visual variants: default, pills, underline, enclosed
|
|
42
|
+
- Horizontal or vertical tab layout
|
|
43
|
+
- `fullWidth` makes tabs span the full container width
|
|
44
|
+
- Icons rendered alongside tab labels via the `icon` prop
|
|
45
|
+
- Individual tabs can be disabled
|
|
46
|
+
- Seven color themes available
|
|
47
|
+
|
|
48
|
+
## Stories
|
|
49
|
+
|
|
50
|
+
<Primary />
|
|
51
|
+
<Controls />
|
|
52
|
+
<Stories />
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
2
|
|
|
3
3
|
export interface ITTabItem {
|
|
4
|
+
/** Unique identifier for the tab. */
|
|
4
5
|
id: string;
|
|
6
|
+
/** Display label shown on the tab button. */
|
|
5
7
|
label: string;
|
|
8
|
+
/** Content rendered when this tab is active. */
|
|
6
9
|
content: ReactNode;
|
|
10
|
+
/** Optional icon rendered beside the label. */
|
|
7
11
|
icon?: ReactNode;
|
|
12
|
+
/** Whether the tab is disabled and non-interactive. */
|
|
8
13
|
disabled?: boolean;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
export interface ITTabsProps {
|
|
17
|
+
/** Array of tab definitions: id, label, content, icon, and disabled state. */
|
|
12
18
|
items: ITTabItem[];
|
|
19
|
+
/** The id of the tab active by default (first item if omitted). */
|
|
13
20
|
defaultActiveId?: string;
|
|
21
|
+
/** Callback fired when the active tab changes, receiving the new tab id. */
|
|
14
22
|
onChange?: (id: string) => void;
|
|
23
|
+
/** Visual variant: "line" (underline indicator) or "pill" (rounded background). */
|
|
15
24
|
variant?: 'line' | 'pill';
|
|
25
|
+
/** Additional CSS classes for the tab header row. */
|
|
16
26
|
className?: string;
|
|
27
|
+
/** Additional CSS classes for the outermost wrapper. */
|
|
17
28
|
containerClassName?: string;
|
|
18
29
|
}
|
|
@@ -3,6 +3,20 @@ import { ITTabsProps } from './tabs.props';
|
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import ITText from "@/components/text/text";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Tabbed navigation component with "line" and "pill" visual variants.
|
|
8
|
+
* Manages its own active state internally via a defaultActiveId.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <ITTabs
|
|
12
|
+
* items={[
|
|
13
|
+
* { id: "tab1", label: "General", content: <GeneralPanel /> },
|
|
14
|
+
* { id: "tab2", label: "Settings", content: <SettingsPanel />, disabled: true },
|
|
15
|
+
* ]}
|
|
16
|
+
* defaultActiveId="tab1"
|
|
17
|
+
* onChange={(id) => console.log("Active tab:", id)}
|
|
18
|
+
* />
|
|
19
|
+
*/
|
|
6
20
|
const ITTabs: React.FC<ITTabsProps> = ({
|
|
7
21
|
items,
|
|
8
22
|
defaultActiveId,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './text.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# ITText
|
|
7
|
+
|
|
8
|
+
Semantic typography wrapper rendering any HTML text element. Foundation for all text in the system.
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
|------|------|---------|-------------|
|
|
14
|
+
| `as` | `'p' \| 'span' \| 'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'small' \| 'label' \| 'strong' \| 'em'` | `'p'` | HTML element to render |
|
|
15
|
+
| `muted` | `boolean` | `false` | Apply muted style via CSS variable |
|
|
16
|
+
| `children` | `ReactNode` | — | Text content |
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { ITText } from '@axzydev/axzy_ui_system';
|
|
22
|
+
|
|
23
|
+
<ITText>Default paragraph text</ITText>
|
|
24
|
+
<ITText as="h1">Heading level 1</ITText>
|
|
25
|
+
<ITText as="span" muted>Muted secondary text</ITText>
|
|
26
|
+
<ITText as="small">Small print</ITText>
|
|
27
|
+
<ITText as="strong">Bold emphasis</ITText>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Notes
|
|
31
|
+
|
|
32
|
+
- Polymorphic `as` prop renders any HTML text-level element
|
|
33
|
+
- `muted` toggles a CSS variable for reduced-importance text
|
|
34
|
+
- Used as the foundational text component throughout the system
|
|
35
|
+
- Renders default paragraph (`<p>`) when no `as` specified
|
|
36
|
+
|
|
37
|
+
## Stories
|
|
38
|
+
|
|
39
|
+
<Primary />
|
|
40
|
+
<Controls />
|
|
41
|
+
<Stories />
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { ReactNode, ElementType, HTMLAttributes } from "react";
|
|
2
2
|
|
|
3
3
|
export interface ITTextProps extends HTMLAttributes<HTMLElement> {
|
|
4
|
+
/** The content rendered inside the element. */
|
|
4
5
|
children?: ReactNode;
|
|
6
|
+
/** Additional CSS classes applied to the element. */
|
|
5
7
|
className?: string;
|
|
8
|
+
/** HTML element type to render (e.g. "p", "span", "h1", "label"). */
|
|
6
9
|
as?: ElementType;
|
|
10
|
+
/** Applies muted text color (--color-text-muted) when true. */
|
|
7
11
|
muted?: boolean;
|
|
12
|
+
/** Associates a label with a form element (rendered as `htmlFor` when `as` is "label"). */
|
|
8
13
|
htmlFor?: string;
|
|
9
14
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import ITText from './text';
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Layout & Navigation/ITText',
|
|
6
|
+
component: ITText,
|
|
7
|
+
parameters: { layout: 'centered' },
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
as: {
|
|
11
|
+
control: 'select',
|
|
12
|
+
options: ['p', 'span', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'small', 'strong', 'em', 'label'],
|
|
13
|
+
description: 'HTML element to render',
|
|
14
|
+
},
|
|
15
|
+
children: { control: 'text', description: 'Text content' },
|
|
16
|
+
muted: { control: 'boolean', description: 'Apply muted text color' },
|
|
17
|
+
className: { control: 'text', description: 'Additional CSS classes' },
|
|
18
|
+
},
|
|
19
|
+
} satisfies Meta<typeof ITText>;
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
|
|
24
|
+
export const Paragraph: Story = {
|
|
25
|
+
args: {
|
|
26
|
+
as: 'p',
|
|
27
|
+
children: 'This is a paragraph of text rendered via ITText.',
|
|
28
|
+
className: 'text-base',
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const Headings: Story = {
|
|
33
|
+
render: () => (
|
|
34
|
+
<div className="space-y-3">
|
|
35
|
+
<ITText as="h1" className="text-4xl font-bold">Heading 1</ITText>
|
|
36
|
+
<ITText as="h2" className="text-3xl font-bold">Heading 2</ITText>
|
|
37
|
+
<ITText as="h3" className="text-2xl font-semibold">Heading 3</ITText>
|
|
38
|
+
<ITText as="h4" className="text-xl font-semibold">Heading 4</ITText>
|
|
39
|
+
<ITText as="h5" className="text-lg font-medium">Heading 5</ITText>
|
|
40
|
+
<ITText as="h6" className="text-base font-medium">Heading 6</ITText>
|
|
41
|
+
</div>
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Muted: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
as: 'p',
|
|
48
|
+
children: 'This text appears muted / secondary.',
|
|
49
|
+
muted: true,
|
|
50
|
+
className: 'text-sm',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const InlineElements: Story = {
|
|
55
|
+
render: () => (
|
|
56
|
+
<div className="space-y-2">
|
|
57
|
+
<ITText as="p">
|
|
58
|
+
This is <ITText as="strong" className="font-bold">bold</ITText>,{' '}
|
|
59
|
+
<ITText as="em" className="italic">italic</ITText>, and{' '}
|
|
60
|
+
<ITText as="small" className="text-xs">small</ITText> text.
|
|
61
|
+
</ITText>
|
|
62
|
+
<ITText as="p">
|
|
63
|
+
A <ITText as="span" className="text-primary-600 font-mono">span</ITText> can be used for inline styling.
|
|
64
|
+
</ITText>
|
|
65
|
+
</div>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { ITTextProps } from "./text.props";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Semantic typography wrapper that renders any HTML element.
|
|
5
|
+
* Supports muted styling via the `muted` prop and passes through all standard HTML attributes.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <ITText as="h1" className="text-2xl font-bold">Welcome</ITText>
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <ITText as="label" htmlFor="email" muted>Email Address</ITText>
|
|
12
|
+
*/
|
|
3
13
|
export default function ITText({
|
|
4
14
|
children,
|
|
5
15
|
as: Tag = "p",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './textarea.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# ITTextarea
|
|
7
|
+
|
|
8
|
+
Multi-line text input with label, validation error display, character limit, and configurable resize behavior. Built on native `<textarea>` with consistent styling and focus ring effects.
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
|------|------|---------|-------------|
|
|
14
|
+
| `value` | `string` | — | Controlled value of the textarea. |
|
|
15
|
+
| `onChange` | `(value: string) => void` | — | Callback fired when the value changes, receiving the new string. |
|
|
16
|
+
| `label` | `string` | — | Label text rendered above the textarea. |
|
|
17
|
+
| `placeholder` | `string` | — | Placeholder text shown when the textarea is empty. |
|
|
18
|
+
| `rows` | `number` | `4` | Number of visible text rows. |
|
|
19
|
+
| `disabled` | `boolean` | `false` | Disables the textarea when true. |
|
|
20
|
+
| `error` | `string` | — | Validation error message displayed below the textarea. |
|
|
21
|
+
| `className` | `string` | — | Additional CSS classes for the wrapper. |
|
|
22
|
+
| `name` | `string` | — | Name attribute for form submission and label association via `htmlFor`. |
|
|
23
|
+
| `maxLength` | `number` | — | Maximum number of characters allowed. |
|
|
24
|
+
| `resize` | `"none" \| "vertical" \| "horizontal" \| "both"` | `"vertical"` | Resize behavior of the textarea. |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { ITTextarea } from '@axzydev/axzy_ui_system';
|
|
30
|
+
|
|
31
|
+
<ITTextarea
|
|
32
|
+
name="bio"
|
|
33
|
+
label="Biography"
|
|
34
|
+
value={bio}
|
|
35
|
+
onChange={setBio}
|
|
36
|
+
maxLength={500}
|
|
37
|
+
rows={6}
|
|
38
|
+
resize="vertical"
|
|
39
|
+
error={bio.length > 500 ? "Max 500 characters" : undefined}
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<ITTextarea
|
|
43
|
+
name="comments"
|
|
44
|
+
label="Comments"
|
|
45
|
+
placeholder="Write your feedback..."
|
|
46
|
+
rows={3}
|
|
47
|
+
resize="none"
|
|
48
|
+
disabled
|
|
49
|
+
/>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Notes
|
|
53
|
+
|
|
54
|
+
- **resize** controls the native CSS resize property: `none` (no resize handle), `vertical` (height only), `horizontal` (width only), or `both`.
|
|
55
|
+
- **error** renders a red error message below the textarea and applies red border/ring styling.
|
|
56
|
+
- **maxLength** enforces a hard character limit via the native `maxLength` attribute — no visual counter included.
|
|
57
|
+
- **rows** only sets the initial visible height; combined with `resize`, the user can adjust as needed.
|
|
58
|
+
- Disabled state applies `opacity-50` and `cursor-not-allowed` with a gray background.
|
|
59
|
+
|
|
60
|
+
## Stories
|
|
61
|
+
|
|
62
|
+
<Primary />
|
|
63
|
+
<Controls />
|
|
64
|
+
<Stories />
|