@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
|
@@ -2,7 +2,7 @@ import { useTableState } from "@/hooks/useTableState";
|
|
|
2
2
|
import { sizeStyles, variantStyles } from "@/types/table.types";
|
|
3
3
|
import clsx from "clsx";
|
|
4
4
|
import React, { useState, useEffect, useCallback, useRef } from "react";
|
|
5
|
-
import { FaCheck, FaSpinner, FaTimes } from "react-icons/fa";
|
|
5
|
+
import { FaCheck, FaSpinner, FaTimes, FaTable, FaThLarge } from "react-icons/fa";
|
|
6
6
|
import { MdOutlineSwapVert } from "react-icons/md";
|
|
7
7
|
import ITInput from "../input/input";
|
|
8
8
|
import ITPagination from "../pagination/pagination";
|
|
@@ -18,6 +18,27 @@ const getNestedValue = (obj: unknown, path: string) => {
|
|
|
18
18
|
|
|
19
19
|
const EMPTY_OBJECT = {};
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Async server-side data table with sorting, filtering, and pagination.
|
|
23
|
+
*
|
|
24
|
+
* Fetches data via the `fetchData` callback whenever pagination, sorting, or
|
|
25
|
+
* filters change. Supports column-level text/catalog/boolean filters, sortable
|
|
26
|
+
* columns, and a loading overlay. Built on-top of internal table hooks for
|
|
27
|
+
* automatic state coordination.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <ITDataTable
|
|
32
|
+
* columns={[
|
|
33
|
+
* { key: "name", label: "Name", sortable: true, filter: true },
|
|
34
|
+
* { key: "status", label: "Status", type: "boolean", filter: true },
|
|
35
|
+
* ]}
|
|
36
|
+
* fetchData={async (params) => api.fetchItems(params)}
|
|
37
|
+
* title="Users"
|
|
38
|
+
* variant="bordered"
|
|
39
|
+
* />
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
21
42
|
export default function ITDataTable<T extends Record<string, unknown>>({
|
|
22
43
|
columns,
|
|
23
44
|
fetchData,
|
|
@@ -33,7 +54,12 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
33
54
|
itemsPerPageOptions = [5, 10, 20],
|
|
34
55
|
defaultItemsPerPage = 10,
|
|
35
56
|
title,
|
|
57
|
+
renderCard,
|
|
58
|
+
defaultView,
|
|
59
|
+
showVerticalBorder = true,
|
|
60
|
+
verticalBorderClassname,
|
|
36
61
|
}: ITDataTableProps<T>) {
|
|
62
|
+
const [viewMode, setViewMode] = useState<"table" | "cards">(defaultView || "table");
|
|
37
63
|
const [data, setData] = useState<T[]>([]);
|
|
38
64
|
const [totalItems, setTotalItems] = useState(0);
|
|
39
65
|
const [isLoading, setIsLoading] = useState(fetchOnMount);
|
|
@@ -194,17 +220,75 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
194
220
|
}
|
|
195
221
|
};
|
|
196
222
|
|
|
223
|
+
const renderDefaultCard = (row: T) => {
|
|
224
|
+
const dataCols = columns.filter((c) => c.type !== "actions");
|
|
225
|
+
const actionCol = columns.find((c) => c.type === "actions");
|
|
226
|
+
return (
|
|
227
|
+
<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">
|
|
228
|
+
{dataCols.map((col) => (
|
|
229
|
+
<div key={col.key} className="flex items-center justify-between gap-2">
|
|
230
|
+
<span className="text-[10px] font-bold uppercase tracking-wider text-slate-400 whitespace-nowrap flex-shrink-0 min-w-[70px]">
|
|
231
|
+
{col.label}
|
|
232
|
+
</span>
|
|
233
|
+
<span className="text-sm font-medium text-slate-800 dark:text-white text-right truncate">
|
|
234
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
235
|
+
</span>
|
|
236
|
+
</div>
|
|
237
|
+
))}
|
|
238
|
+
{actionCol?.actions && (
|
|
239
|
+
<div className="flex items-center justify-end gap-2 pt-2 border-t border-slate-100 dark:border-slate-700">
|
|
240
|
+
{actionCol.actions(row)}
|
|
241
|
+
</div>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const segCtrlClass = (mode: "table" | "cards") =>
|
|
248
|
+
`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-semibold transition-all ${
|
|
249
|
+
viewMode === mode
|
|
250
|
+
? "bg-white dark:bg-slate-900 text-slate-900 dark:text-white shadow-sm border border-slate-200/50 dark:border-slate-700"
|
|
251
|
+
: "text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200"
|
|
252
|
+
}`;
|
|
253
|
+
|
|
197
254
|
return (
|
|
198
255
|
<div className={clsx("space-y-4 w-full relative", containerClassName)}>
|
|
199
256
|
<div className="rounded-xl shadow-sm overflow-hidden" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
200
257
|
{title && (
|
|
201
|
-
<div className="px-6 py-5
|
|
258
|
+
<div className="px-6 py-5 flex items-center justify-between" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
202
259
|
<ITText as="h2" className="text-xl font-bold text-secondary-900 leading-tight">{title}</ITText>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
260
|
+
<div className="flex items-center gap-3">
|
|
261
|
+
{isLoading && (
|
|
262
|
+
<div className="text-secondary-400">
|
|
263
|
+
{loadingIndicator || <FaSpinner className="animate-spin text-primary-500 text-xl" />}
|
|
264
|
+
</div>
|
|
265
|
+
)}
|
|
266
|
+
<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">
|
|
267
|
+
<button onClick={() => setViewMode("table")} className={segCtrlClass("table")}>
|
|
268
|
+
<FaTable size={11} />
|
|
269
|
+
Table
|
|
270
|
+
</button>
|
|
271
|
+
<button onClick={() => setViewMode("cards")} className={segCtrlClass("cards")}>
|
|
272
|
+
<FaThLarge size={11} />
|
|
273
|
+
Cards
|
|
274
|
+
</button>
|
|
206
275
|
</div>
|
|
207
|
-
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
)}
|
|
279
|
+
|
|
280
|
+
{!title && (
|
|
281
|
+
<div className="flex justify-end px-4 pt-3">
|
|
282
|
+
<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">
|
|
283
|
+
<button onClick={() => setViewMode("table")} className={segCtrlClass("table")}>
|
|
284
|
+
<FaTable size={11} />
|
|
285
|
+
Table
|
|
286
|
+
</button>
|
|
287
|
+
<button onClick={() => setViewMode("cards")} className={segCtrlClass("cards")}>
|
|
288
|
+
<FaThLarge size={11} />
|
|
289
|
+
Cards
|
|
290
|
+
</button>
|
|
291
|
+
</div>
|
|
208
292
|
</div>
|
|
209
293
|
)}
|
|
210
294
|
|
|
@@ -222,81 +306,102 @@ export default function ITDataTable<T extends Record<string, unknown>>({
|
|
|
222
306
|
</div>
|
|
223
307
|
)}
|
|
224
308
|
|
|
225
|
-
|
|
226
|
-
className=
|
|
227
|
-
"min-w-max w-full text-sm text-left text-secondary-600 transition-opacity duration-300",
|
|
228
|
-
isLoading ? "opacity-50" : "opacity-100",
|
|
229
|
-
variantStyles[variant],
|
|
230
|
-
sizeStyles[size],
|
|
231
|
-
className
|
|
232
|
-
)}
|
|
233
|
-
>
|
|
234
|
-
<thead>
|
|
235
|
-
<tr className="bg-white border-b border-secondary-200 text-xs uppercase tracking-wider font-semibold text-secondary-500">
|
|
236
|
-
{columns.map((col) => (
|
|
237
|
-
<th key={col.key} scope="col" className={clsx("px-4 py-4 align-top", col.className)}>
|
|
238
|
-
<div className="flex flex-col gap-3 min-w-[150px]">
|
|
239
|
-
<div className="flex items-center justify-between gap-2">
|
|
240
|
-
<ITText as="span" className="text-secondary-700 font-bold">{col.label}</ITText>
|
|
241
|
-
{col.sortable && col.type !== "actions" && (
|
|
242
|
-
<button
|
|
243
|
-
onClick={() => handleSort(col.key)}
|
|
244
|
-
disabled={isLoading}
|
|
245
|
-
className={`p-1 rounded-md transition-colors ${
|
|
246
|
-
sortConfig?.key === col.key
|
|
247
|
-
? "bg-secondary-200 text-secondary-900"
|
|
248
|
-
: "hover:bg-secondary-200 text-secondary-400 hover:text-secondary-700"
|
|
249
|
-
} disabled:opacity-50`}
|
|
250
|
-
title={`Ordenar por ${col.label}`}
|
|
251
|
-
>
|
|
252
|
-
<MdOutlineSwapVert className="w-4 h-4" aria-hidden="true" />
|
|
253
|
-
</button>
|
|
254
|
-
)}
|
|
255
|
-
</div>
|
|
256
|
-
|
|
257
|
-
<div className="w-full">{col.filter ? renderFilterInput(col) : null}</div>
|
|
258
|
-
</div>
|
|
259
|
-
</th>
|
|
260
|
-
))}
|
|
261
|
-
</tr>
|
|
262
|
-
</thead>
|
|
263
|
-
<tbody className="divide-y divide-secondary-100">
|
|
309
|
+
{viewMode === "cards" ? (
|
|
310
|
+
<div className="p-4 space-y-3 max-h-[400px] overflow-y-auto">
|
|
264
311
|
{data.length > 0 ? (
|
|
265
|
-
data.map((row,
|
|
266
|
-
<
|
|
267
|
-
{
|
|
268
|
-
|
|
269
|
-
{col.type === "actions" ? (
|
|
270
|
-
<div className="flex items-center justify-center gap-2">
|
|
271
|
-
{renderCellContent(col, row) as React.ReactNode}
|
|
272
|
-
</div>
|
|
273
|
-
) : (
|
|
274
|
-
<div className="text-secondary-700 font-medium">
|
|
275
|
-
{renderCellContent(col, row) as React.ReactNode}
|
|
276
|
-
</div>
|
|
277
|
-
)}
|
|
278
|
-
</td>
|
|
279
|
-
))}
|
|
280
|
-
</tr>
|
|
312
|
+
data.map((row, i) => (
|
|
313
|
+
<div key={i}>
|
|
314
|
+
{renderCard ? renderCard(row) : renderDefaultCard(row)}
|
|
315
|
+
</div>
|
|
281
316
|
))
|
|
282
317
|
) : (
|
|
283
|
-
|
|
284
|
-
<
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
318
|
+
!isLoading && (
|
|
319
|
+
<div className="flex flex-col items-center justify-center py-12 text-secondary-400">
|
|
320
|
+
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
321
|
+
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
322
|
+
</div>
|
|
323
|
+
)
|
|
324
|
+
)}
|
|
325
|
+
</div>
|
|
326
|
+
) : (
|
|
327
|
+
<table
|
|
328
|
+
className={clsx(
|
|
329
|
+
"min-w-max w-full text-sm text-left text-secondary-600 transition-opacity duration-300",
|
|
330
|
+
showVerticalBorder && "[&_th]:border-r [&_th:last-child]:border-r-0 [&_td]:border-r [&_td:last-child]:border-r-0",
|
|
331
|
+
showVerticalBorder && (verticalBorderClassname || "[&_th]:border-slate-100 dark:[&_th]:border-slate-700/30 [&_td]:border-slate-100 dark:[&_td]:border-slate-700/30"),
|
|
332
|
+
isLoading ? "opacity-50" : "opacity-100",
|
|
333
|
+
variantStyles[variant],
|
|
334
|
+
sizeStyles[size],
|
|
335
|
+
className
|
|
336
|
+
)}
|
|
337
|
+
>
|
|
338
|
+
<thead>
|
|
339
|
+
<tr className="bg-secondary-50 text-xs uppercase tracking-wider font-semibold text-slate-700 dark:text-slate-200">
|
|
340
|
+
{columns.map((col) => (
|
|
341
|
+
<th key={col.key} scope="col" className={clsx("px-4 py-4 align-top", col.className)}>
|
|
342
|
+
<div className="flex flex-col gap-3 min-w-[150px]">
|
|
343
|
+
<div className="flex items-center justify-between gap-2">
|
|
344
|
+
<ITText as="span" className="text-slate-900 dark:text-white font-bold">{col.label}</ITText>
|
|
345
|
+
{col.sortable && col.type !== "actions" && (
|
|
346
|
+
<button
|
|
347
|
+
onClick={() => handleSort(col.key)}
|
|
348
|
+
disabled={isLoading}
|
|
349
|
+
className={`p-1 rounded-md transition-colors ${
|
|
350
|
+
sortConfig?.key === col.key
|
|
351
|
+
? "bg-secondary-200 text-secondary-900"
|
|
352
|
+
: "hover:bg-secondary-200 text-secondary-400 hover:text-secondary-700"
|
|
353
|
+
} disabled:opacity-50`}
|
|
354
|
+
title={`Ordenar por ${col.label}`}
|
|
355
|
+
>
|
|
356
|
+
<MdOutlineSwapVert className="w-4 h-4" aria-hidden="true" />
|
|
357
|
+
</button>
|
|
358
|
+
)}
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<div className="w-full">{col.filter ? renderFilterInput(col) : null}</div>
|
|
289
362
|
</div>
|
|
290
|
-
|
|
291
|
-
|
|
363
|
+
</th>
|
|
364
|
+
))}
|
|
292
365
|
</tr>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
366
|
+
</thead>
|
|
367
|
+
<tbody className="divide-y divide-slate-100 dark:divide-slate-700/30">
|
|
368
|
+
{data.length > 0 ? (
|
|
369
|
+
data.map((row, rowIndex) => (
|
|
370
|
+
<tr key={rowIndex} 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")}>
|
|
371
|
+
{columns.map((col) => (
|
|
372
|
+
<td key={`${rowIndex}-${col.key}`} className={clsx("px-4 py-3 align-middle", col.className)}>
|
|
373
|
+
{col.type === "actions" ? (
|
|
374
|
+
<div className="flex items-center justify-center gap-2">
|
|
375
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
376
|
+
</div>
|
|
377
|
+
) : (
|
|
378
|
+
<div className="text-secondary-700 font-medium">
|
|
379
|
+
{renderCellContent(col, row) as React.ReactNode}
|
|
380
|
+
</div>
|
|
381
|
+
)}
|
|
382
|
+
</td>
|
|
383
|
+
))}
|
|
384
|
+
</tr>
|
|
385
|
+
))
|
|
386
|
+
) : (
|
|
387
|
+
<tr>
|
|
388
|
+
<td colSpan={columns.length} className="px-6 py-12 text-center">
|
|
389
|
+
{!isLoading && (
|
|
390
|
+
<div className="flex flex-col items-center justify-center text-secondary-400">
|
|
391
|
+
<ITText as="span" className="text-lg">No se encontraron resultados</ITText>
|
|
392
|
+
<ITText as="span" className="text-sm mt-1">Intenta ajustar los filtros</ITText>
|
|
393
|
+
</div>
|
|
394
|
+
)}
|
|
395
|
+
</td>
|
|
396
|
+
</tr>
|
|
397
|
+
)}
|
|
398
|
+
</tbody>
|
|
399
|
+
</table>
|
|
400
|
+
)}
|
|
296
401
|
</div>
|
|
297
402
|
</div>
|
|
298
403
|
|
|
299
|
-
<div className="rounded-b-xl
|
|
404
|
+
<div className="rounded-b-xl px-6 py-4" style={{ backgroundColor: 'var(--color-table-rowBg, #ffffff)' }}>
|
|
300
405
|
<ITPagination
|
|
301
406
|
currentPage={currentPage}
|
|
302
407
|
totalPages={computedTotalPages}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './date-picker.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# ITDatePicker
|
|
7
|
+
|
|
8
|
+
A date picker supporting single date and date range selection. Includes built-in validation error display, min/max date constraints, and placeholder formatting.
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
|------|------|---------|-------------|
|
|
14
|
+
| `name` | `string` | — | Unique name attribute for the input. |
|
|
15
|
+
| `value` | `Date \| [Date \| null, Date \| null]` | — | Selected date or start–end tuple when `range` is true. |
|
|
16
|
+
| `onChange` | `(event) => void` | — | Callback with `{ target: { name, value } }` on date selection. |
|
|
17
|
+
| `onBlur` | `(event) => void` | — | Callback when the input loses focus. |
|
|
18
|
+
| `range` | `boolean` | `false` | Enable date-range selection mode. |
|
|
19
|
+
| `variant` | `ColorsTypes` | `"primary"` | Color variant from design system. |
|
|
20
|
+
| `size` | `SizesTypes` | `"medium"` | Size variant from design system. |
|
|
21
|
+
| `className` | `string` | — | CSS classes for the wrapper. |
|
|
22
|
+
| `calendarClassName` | `string` | — | CSS classes for the calendar popover. |
|
|
23
|
+
| `disabled` | `boolean` | `false` | Disables the date picker. |
|
|
24
|
+
| `placeholder` | `string` | — | Placeholder text when no date is selected. |
|
|
25
|
+
| `label` | `string` | — | Accessible label rendered above the input. |
|
|
26
|
+
| `required` | `boolean` | — | Marks the field as required. |
|
|
27
|
+
| `touched` | `boolean` | — | Visually indicates the field has been interacted with. |
|
|
28
|
+
| `error` | `string \| boolean` | — | Error message string or `true` for default error styling. |
|
|
29
|
+
| `minDate` | `Date` | — | Earliest selectable date. |
|
|
30
|
+
| `maxDate` | `Date` | — | Latest selectable date. |
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { ITDatePicker } from '@axzydev/axzy_ui_system';
|
|
36
|
+
|
|
37
|
+
<ITDatePicker
|
|
38
|
+
name="birthdate"
|
|
39
|
+
label="Birth Date"
|
|
40
|
+
placeholder="DD/MM/YYYY"
|
|
41
|
+
value={date}
|
|
42
|
+
onChange={(e) => setDate(e.target.value)}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<ITDatePicker
|
|
46
|
+
range
|
|
47
|
+
name="audit_range"
|
|
48
|
+
label="Audit Range"
|
|
49
|
+
value={range}
|
|
50
|
+
onChange={(e) => setRange(e.target.value)}
|
|
51
|
+
/>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- When `range` is true, `value` must be a tuple `[startDate, endDate]` and the onChange target value will be the same shape.
|
|
57
|
+
- The component emits a synthetic event with `target.name` and `target.value`, compatible with Formik integration.
|
|
58
|
+
- Error state is shown when both `error` and `touched` are truthy.
|
|
59
|
+
- Date format follows the locale configuration; placeholder text like `DD/MM/YYYY` guides the user.
|
|
60
|
+
|
|
61
|
+
## Stories
|
|
62
|
+
|
|
63
|
+
<Primary />
|
|
64
|
+
<Controls />
|
|
65
|
+
<Stories />
|
|
@@ -2,29 +2,46 @@ import { ColorsTypes } from "@/types/colors.types";
|
|
|
2
2
|
import { SizesTypes } from "@/types/sizes.types";
|
|
3
3
|
|
|
4
4
|
export interface ITDatePickerProps {
|
|
5
|
+
/** Unique name attribute for the underlying input element. */
|
|
5
6
|
name: string;
|
|
7
|
+
/** Selected date. Accepts a single Date or a tuple `[startDate, endDate]` when `range` is true. */
|
|
6
8
|
value?: Date | [Date | null, Date | null];
|
|
9
|
+
/** Called when the selected date changes. The event object carries `name` and `value` in its target. */
|
|
7
10
|
onChange: (
|
|
8
11
|
event:
|
|
9
12
|
| React.ChangeEvent<HTMLInputElement>
|
|
10
13
|
| { target: { name: string; value: Date | [Date | null, Date | null] } }
|
|
11
14
|
) => void;
|
|
15
|
+
/** Called when the input loses focus. */
|
|
12
16
|
onBlur?: (
|
|
13
17
|
event:
|
|
14
18
|
| React.FocusEvent<HTMLInputElement>
|
|
15
19
|
| { target: { name: string; value: Date | [Date | null, Date | null] } }
|
|
16
20
|
) => void;
|
|
21
|
+
/** Enables date-range selection mode. When true, `value` is treated as a start-end tuple. @default false */
|
|
17
22
|
range?: boolean;
|
|
23
|
+
/** Color variant matching the design system (ColorsTypes). @default "primary" */
|
|
18
24
|
variant?: ColorsTypes;
|
|
25
|
+
/** Size variant matching the design system (SizesTypes). @default "medium" */
|
|
19
26
|
size?: SizesTypes;
|
|
27
|
+
/** Additional CSS classes for the wrapper element. */
|
|
20
28
|
className?: string;
|
|
29
|
+
/** Additional CSS classes for the calendar popover. */
|
|
21
30
|
calendarClassName?: string;
|
|
31
|
+
/** Disables the date picker. @default false */
|
|
22
32
|
disabled?: boolean;
|
|
33
|
+
/** Placeholder text shown when no date is selected. */
|
|
23
34
|
placeholder?: string;
|
|
35
|
+
/** Accessible label rendered above the input. */
|
|
24
36
|
label?: string;
|
|
37
|
+
/** Marks the field as required. */
|
|
25
38
|
required?: boolean;
|
|
39
|
+
/** Visually indicates the field has been interacted with. */
|
|
26
40
|
touched?: boolean;
|
|
41
|
+
/** Error state — pass a string message or `true` for default styling. */
|
|
27
42
|
error?: string | boolean;
|
|
43
|
+
/** Earliest selectable date. */
|
|
28
44
|
minDate?: Date;
|
|
45
|
+
/** Latest selectable date. */
|
|
29
46
|
maxDate?: Date;
|
|
30
47
|
}
|
|
@@ -7,6 +7,36 @@ import ITInput from "../input/input";
|
|
|
7
7
|
import { ITDatePickerProps } from "./date-picker.props";
|
|
8
8
|
import { theme } from "@/theme/theme";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Date picker component with single-date and date-range modes.
|
|
12
|
+
*
|
|
13
|
+
* Supports manual text input (DD/MM/YYYY format) and a calendar popover.
|
|
14
|
+
* In range mode the user selects a start date then an end date; the calendar
|
|
15
|
+
* highlights the interval. Position-aware popover flips above the input when
|
|
16
|
+
* near the bottom of the viewport.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <ITDatePicker
|
|
21
|
+
* name="birthDate"
|
|
22
|
+
* label="Date of birth"
|
|
23
|
+
* value={date}
|
|
24
|
+
* onChange={(e) => setDate(e.target.value)}
|
|
25
|
+
* maxDate={new Date()}
|
|
26
|
+
* />
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <ITDatePicker
|
|
32
|
+
* name="travelRange"
|
|
33
|
+
* label="Travel dates"
|
|
34
|
+
* range
|
|
35
|
+
* value={[startDate, endDate]}
|
|
36
|
+
* onChange={(e) => setRange(e.target.value)}
|
|
37
|
+
* />
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
10
40
|
export default function ITDatePicker({
|
|
11
41
|
name,
|
|
12
42
|
value,
|
|
@@ -257,7 +287,7 @@ export default function ITDatePicker({
|
|
|
257
287
|
<span>
|
|
258
288
|
<FaCalendarAlt
|
|
259
289
|
onClick={handleIconClick}
|
|
260
|
-
className="text-slate-
|
|
290
|
+
className="text-slate-500 dark:text-slate-400 cursor-pointer"
|
|
261
291
|
/>
|
|
262
292
|
</span>
|
|
263
293
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './dialog.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# ITDialog
|
|
7
|
+
|
|
8
|
+
Modal dialog rendered via a React portal with a semi-transparent overlay backdrop. Supports optional title bar (plain or ITFormHeader), Escape-key dismissal, and a full-screen variant.
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
|------|------|---------|-------------|
|
|
14
|
+
| `isOpen` | `boolean` | — | Controls dialog visibility. |
|
|
15
|
+
| `onClose` | `() => void` | — | Called on overlay click, close button, or Escape key. |
|
|
16
|
+
| `children` | `ReactNode` | — | Content rendered in the dialog body. |
|
|
17
|
+
| `className` | `string` | — | CSS classes for the dialog panel (e.g., `"w-96"` for width). |
|
|
18
|
+
| `title` | `string` | — | Optional heading at the top of the dialog. |
|
|
19
|
+
| `useFormHeader` | `boolean` | `false` | When true and `title` is set, renders an ITFormHeader with colored styling. |
|
|
20
|
+
| `fullScreen` | `boolean` | `false` | Makes the dialog fill the entire viewport. |
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { ITDialog } from '@axzydev/axzy_ui_system';
|
|
26
|
+
|
|
27
|
+
<ITDialog
|
|
28
|
+
isOpen={open}
|
|
29
|
+
onClose={() => setOpen(false)}
|
|
30
|
+
title="User Details"
|
|
31
|
+
useFormHeader
|
|
32
|
+
className="w-[500px]"
|
|
33
|
+
>
|
|
34
|
+
<UserForm />
|
|
35
|
+
</ITDialog>
|
|
36
|
+
|
|
37
|
+
<ITDialog isOpen={open} onClose={close} fullScreen>
|
|
38
|
+
<FullPageEditor />
|
|
39
|
+
</ITDialog>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Notes
|
|
43
|
+
|
|
44
|
+
- Content is portaled to `document.body` via `createPortal`, ensuring it renders above all page layers.
|
|
45
|
+
- Click outside the dialog panel or pressing Escape dismisses it.
|
|
46
|
+
- When `useFormHeader` is true, the header uses the ITFormHeader design pattern with brand coloring.
|
|
47
|
+
- Control the dialog width via the `className` prop (e.g., `"w-96"`, `"w-[600px]"`).
|
|
48
|
+
- The overlay uses `bg-black/50` for a darkened backdrop effect.
|
|
49
|
+
|
|
50
|
+
## Stories
|
|
51
|
+
|
|
52
|
+
<Primary />
|
|
53
|
+
<Controls />
|
|
54
|
+
<Stories />
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
export interface ITDialogProps {
|
|
2
|
+
/** Controls whether the dialog is visible. */
|
|
2
3
|
isOpen: boolean;
|
|
4
|
+
/** Callback fired when the overlay or close button is clicked, or Escape is pressed. */
|
|
3
5
|
onClose: () => void;
|
|
6
|
+
/** Content rendered inside the dialog body. */
|
|
4
7
|
children: React.ReactNode;
|
|
8
|
+
/** Additional CSS classes for the dialog panel. */
|
|
5
9
|
className?: string;
|
|
10
|
+
/** Optional heading displayed at the top of the dialog. */
|
|
6
11
|
title?: string;
|
|
12
|
+
/** When true and `title` is provided, renders an ITFormHeader instead of the default title bar. @default false */
|
|
7
13
|
useFormHeader?: boolean;
|
|
14
|
+
/** Makes the dialog fill the entire viewport. @default false */
|
|
8
15
|
fullScreen?: boolean;
|
|
9
16
|
}
|
|
@@ -7,6 +7,32 @@ import useClickOutside from "@/hooks/useClickOutside";
|
|
|
7
7
|
import ITFormHeader from "../form-header/form-header";
|
|
8
8
|
import ITText from "@/components/text/text";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Modal dialog rendered via React portal with overlay backdrop.
|
|
12
|
+
*
|
|
13
|
+
* Supports Escape-key dismissal, click-outside-to-close, optional title bar
|
|
14
|
+
* (plain or ITFormHeader), and a full-screen variant. Content is portaled to
|
|
15
|
+
* `document.body` so it sits above all other page layers.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <ITDialog
|
|
20
|
+
* isOpen={open}
|
|
21
|
+
* onClose={() => setOpen(false)}
|
|
22
|
+
* title="User details"
|
|
23
|
+
* useFormHeader
|
|
24
|
+
* >
|
|
25
|
+
* <UserForm />
|
|
26
|
+
* </ITDialog>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <ITDialog isOpen={open} onClose={close} fullScreen>
|
|
32
|
+
* <FullPageEditor />
|
|
33
|
+
* </ITDialog>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
10
36
|
export default function ITDialog({
|
|
11
37
|
isOpen,
|
|
12
38
|
onClose,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
+
import * as Stories from './divider.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={Stories} />
|
|
5
|
+
|
|
6
|
+
# ITDivider
|
|
7
|
+
|
|
8
|
+
A thin horizontal or vertical separator line for visually dividing content sections. Supports custom color and thickness via Tailwind utility classes.
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
| Prop | Type | Default | Description |
|
|
13
|
+
|------|------|---------|-------------|
|
|
14
|
+
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Direction the divider flows. |
|
|
15
|
+
| `className` | `string` | — | Additional CSS classes. |
|
|
16
|
+
| `color` | `string` | `"bg-slate-200 dark:bg-slate-700"` | Tailwind background color class(es). |
|
|
17
|
+
| `thickness` | `string` | `"h-px"` (horizontal) / `"w-px"` (vertical) | Tailwind width/height utility. |
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { ITDivider } from '@axzydev/axzy_ui_system';
|
|
23
|
+
|
|
24
|
+
<ITDivider />
|
|
25
|
+
|
|
26
|
+
<ITDivider orientation="vertical" color="bg-red-500" thickness="w-1" />
|
|
27
|
+
|
|
28
|
+
<ITDivider color="bg-primary-300" thickness="h-0.5" />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Notes
|
|
32
|
+
|
|
33
|
+
- Horizontal divider is full-width by default; vertical divider is full-height of its parent.
|
|
34
|
+
- Use in combination with ITStack or ITFlex for consistent layout spacing between sections.
|
|
35
|
+
- Auto-detects orientation for default thickness: `h-px` for horizontal, `w-px` for vertical.
|
|
36
|
+
- The `color` prop accepts any valid Tailwind background color class, including dark mode variants.
|
|
37
|
+
|
|
38
|
+
## Stories
|
|
39
|
+
|
|
40
|
+
<Primary />
|
|
41
|
+
<Controls />
|
|
42
|
+
<Stories />
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
/** Direction the divider flows: "horizontal" | "vertical". */
|
|
1
2
|
export type DividerOrientation = "horizontal" | "vertical";
|
|
2
3
|
|
|
3
4
|
export interface ITDividerProps {
|
|
5
|
+
/** Layout direction. @default "horizontal" */
|
|
4
6
|
orientation?: DividerOrientation;
|
|
7
|
+
/** Additional CSS classes for the divider element. */
|
|
5
8
|
className?: string;
|
|
9
|
+
/** Tailwind background color class(es). @default "bg-slate-200 dark:bg-slate-700" */
|
|
6
10
|
color?: string;
|
|
11
|
+
/** Tailwind width/height utility (e.g. "h-px", "h-1", "w-px"). Auto-set based on orientation if omitted. */
|
|
7
12
|
thickness?: string;
|
|
8
13
|
}
|
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import clsx from "clsx";
|
|
2
2
|
import { ITDividerProps } from "./divider.props";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Horizontal or vertical separator line.
|
|
6
|
+
*
|
|
7
|
+
* Renders a thin full-width (horizontal) or full-height (vertical) bar using
|
|
8
|
+
* Tailwind utility classes. Supports custom color and thickness overrides.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <ITDivider />
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <ITDivider orientation="vertical" color="bg-red-500" thickness="w-1" />
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
4
20
|
export default function ITDivider({
|
|
5
21
|
orientation = "horizontal",
|
|
6
22
|
className,
|