@axzydev/axzy_ui_system 1.4.7 → 1.5.0
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 +2 -11
- package/dist/index.cjs +2174 -1570
- 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 +232 -19
- package/dist/index.d.ts +232 -19
- package/dist/index.js +1838 -1238
- package/dist/index.js.map +1 -1
- package/dist/layered.css +1 -1
- package/package.json +3 -16
- package/src/App.tsx +90 -73
- package/src/components/atoms/badget/badget.tsx +23 -5
- package/src/components/atoms/chip/chip.tsx +3 -1
- package/src/components/atoms/image/image.tsx +3 -1
- package/src/components/atoms/input/input.tsx +8 -5
- package/src/components/atoms/loader/loader.tsx +3 -1
- package/src/components/atoms/slide/slide.props.ts +12 -0
- package/src/components/atoms/slide/slide.tsx +3 -0
- package/src/components/molecules/alert/alert.tsx +3 -1
- package/src/components/molecules/breadcrumbs/breadcrumbs.tsx +3 -1
- package/src/components/molecules/calendar/calendar.tsx +9 -6
- package/src/components/molecules/card/card.tsx +5 -2
- package/src/components/molecules/chip-input/chip-input.tsx +9 -7
- package/src/components/molecules/date-picker/datePicker.tsx +9 -9
- package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +1 -1
- package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +6 -3
- package/src/components/molecules/field/field.tsx +3 -1
- package/src/components/molecules/form-header/form-header.tsx +3 -1
- package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +2 -2
- package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +20 -15
- package/src/components/molecules/masked-input/masked-input.tsx +3 -1
- package/src/components/molecules/multi-select/multi-select.tsx +12 -9
- package/src/components/molecules/pagination/pagination.tsx +11 -5
- package/src/components/molecules/search-select/search-select.tsx +11 -9
- package/src/components/molecules/select/select.tsx +6 -4
- package/src/components/molecules/stepper/stepper.tsx +6 -4
- package/src/components/molecules/table/table.props.ts +10 -2
- package/src/components/molecules/table/table.tsx +163 -114
- package/src/components/molecules/time-picker/timePicker.tsx +8 -6
- package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
- package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
- package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
- package/src/components/organisms/data-table/dataTable.props.ts +3 -2
- package/src/components/organisms/data-table/dataTable.tsx +149 -81
- package/src/components/organisms/dialog/dialog.tsx +3 -0
- package/src/components/organisms/drawer/drawer.tsx +3 -0
- package/src/components/organisms/dropfile/dropfile.tsx +19 -17
- package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
- package/src/components/organisms/navbar/navbar.tsx +4 -2
- package/src/components/organisms/page-header/page-header.tsx +3 -1
- package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
- package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
- package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
- package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
- package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
- package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
- package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
- package/src/components/organisms/searchTable/searchTable.tsx +4 -2
- package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
- package/src/components/organisms/sidebar/sidebar.tsx +9 -4
- package/src/components/organisms/toast/toast.tsx +3 -1
- package/src/components/organisms/topbar/topbar.tsx +4 -2
- package/src/components/templates/page/page.props.ts +4 -4
- package/src/components/templates/page/page.tsx +6 -4
- package/src/components/theme-provider/themeProvider.tsx +28 -17
- package/src/dev.css +77 -2
- package/src/global.d.ts +10 -0
- package/src/i18n/context.ts +9 -0
- package/src/i18n/index.ts +22 -0
- package/src/i18n/locales/en.ts +163 -0
- package/src/i18n/locales/es.ts +165 -0
- package/src/i18n/provider.tsx +44 -0
- package/src/i18n/translator.ts +82 -0
- package/src/i18n/types.ts +54 -0
- package/src/i18n/useITTranslation.ts +20 -0
- package/src/index.css +0 -1
- package/src/index.ts +9 -3
- package/src/sandbox/LandingShowcase.tsx +224 -580
- package/src/sandbox/NotFoundShowcase.tsx +51 -36
- package/src/sandbox/TopbarSearch.tsx +6 -4
- package/src/sandbox/controls.tsx +180 -0
- package/src/sandbox/i18n.ts +1929 -0
- package/src/sandbox/icons.tsx +25 -0
- package/src/sandbox/locale.tsx +72 -0
- package/src/sandbox/ui.tsx +377 -0
- package/src/sandbox/useHashRoute.ts +46 -10
- package/src/showcases/DataShowcases.tsx +201 -80
- package/src/showcases/FeedbackShowcases.tsx +37 -30
- package/src/showcases/FormShowcases.tsx +302 -214
- package/src/showcases/GettingStartedShowcase.tsx +151 -145
- package/src/showcases/HelpShowcase.tsx +126 -128
- package/src/showcases/HomeShowcase.tsx +678 -482
- package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
- package/src/showcases/NavigationShowcases.tsx +45 -37
- package/src/showcases/PageShowcases.tsx +171 -152
- package/src/showcases/ProductoDetalle.tsx +272 -129
- package/src/showcases/ShowcaseLayout.tsx +270 -216
- package/src/showcases/SizesShowcase.tsx +123 -64
- package/src/showcases/StructureShowcases.tsx +157 -131
- package/src/showcases/portfolioUi.tsx +153 -0
- package/src/showcases/techIcons.ts +3 -0
- package/src/types/table.types.ts +110 -0
- package/src/utils/table.utils.ts +10 -2
- package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
- package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
- package/src/components/atoms/badget/badget.doc.mdx +0 -46
- package/src/components/atoms/badget/badget.stories.tsx +0 -75
- package/src/components/atoms/button/button.doc.mdx +0 -59
- package/src/components/atoms/button/button.stories.tsx +0 -173
- package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
- package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
- package/src/components/atoms/chip/chip.doc.mdx +0 -47
- package/src/components/atoms/chip/chip.stories.tsx +0 -64
- package/src/components/atoms/divider/divider.doc.mdx +0 -42
- package/src/components/atoms/divider/divider.stories.tsx +0 -33
- package/src/components/atoms/flex/flex.doc.mdx +0 -59
- package/src/components/atoms/flex/flex.stories.tsx +0 -70
- package/src/components/atoms/grid/grid.doc.mdx +0 -54
- package/src/components/atoms/grid/grid.stories.tsx +0 -71
- package/src/components/atoms/image/image.doc.mdx +0 -44
- package/src/components/atoms/image/image.stories.tsx +0 -48
- package/src/components/atoms/input/input.doc.mdx +0 -70
- package/src/components/atoms/input/input.stories.tsx +0 -113
- package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
- package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
- package/src/components/atoms/loader/loader.doc.mdx +0 -43
- package/src/components/atoms/loader/loader.stories.tsx +0 -104
- package/src/components/atoms/popover/popover.doc.mdx +0 -53
- package/src/components/atoms/popover/popover.stories.tsx +0 -24
- package/src/components/atoms/progress/progress.doc.mdx +0 -48
- package/src/components/atoms/progress/progress.stories.tsx +0 -39
- package/src/components/atoms/radio/radio.doc.mdx +0 -62
- package/src/components/atoms/radio/radio.stories.tsx +0 -48
- package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
- package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
- package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
- package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
- package/src/components/atoms/slide/slide.doc.mdx +0 -46
- package/src/components/atoms/slide/slide.stories.tsx +0 -120
- package/src/components/atoms/slider/slider.doc.mdx +0 -49
- package/src/components/atoms/slider/slider.stories.tsx +0 -29
- package/src/components/atoms/stack/stack.doc.mdx +0 -126
- package/src/components/atoms/stack/stack.stories.tsx +0 -283
- package/src/components/atoms/text/text.doc.mdx +0 -41
- package/src/components/atoms/text/text.stories.tsx +0 -66
- package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
- package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
- package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
- package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
- package/src/components/isolation/isolation.stories.tsx +0 -202
- package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
- package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
- package/src/components/molecules/alert/alert.doc.mdx +0 -48
- package/src/components/molecules/alert/alert.stories.tsx +0 -40
- package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
- package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
- package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
- package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
- package/src/components/molecules/card/card.doc.mdx +0 -54
- package/src/components/molecules/card/card.stories.tsx +0 -57
- package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
- package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
- package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
- package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
- package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
- package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
- package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
- package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
- package/src/components/molecules/field/field.doc.mdx +0 -43
- package/src/components/molecules/field/field.stories.tsx +0 -42
- package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
- package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
- package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
- package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
- package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
- package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
- package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
- package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
- package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
- package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
- package/src/components/molecules/select/select.doc.mdx +0 -75
- package/src/components/molecules/select/select.stories.tsx +0 -85
- package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
- package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
- package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
- package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
- package/src/components/molecules/table/table.doc.mdx +0 -123
- package/src/components/molecules/table/table.stories.tsx +0 -402
- package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
- package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
- package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
- package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
- package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
- package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
- package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
- package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -115
- package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
- package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
- package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
- package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
- package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
- package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
- package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
- package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
- package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
- package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
- package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
- package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
- package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
- package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
- package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
- package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
- package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
- package/src/components/organisms/toast/toast.doc.mdx +0 -50
- package/src/components/organisms/toast/toast.stories.tsx +0 -137
- package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
- package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
- package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
- package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
- package/src/components/templates/layout/layout.doc.mdx +0 -53
- package/src/components/templates/layout/layout.stories.tsx +0 -113
- package/src/components/templates/page/page.doc.mdx +0 -86
- package/src/components/templates/page/page.stories.tsx +0 -97
- package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
- package/src/components/theme-provider/themeProvider.stories.tsx +0 -58
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
import React, { useState, useCallback, useMemo } from 'react';
|
|
2
|
-
import { Meta, StoryObj } from '@storybook/react';
|
|
3
|
-
import { startOfDay, endOfDay } from 'date-fns';
|
|
4
|
-
import ITDataTable from './dataTable';
|
|
5
|
-
import { ITDataTableFetchParams, ITDataTableResponse } from './dataTable.props';
|
|
6
|
-
import ITCard from '@/components/molecules/card/card';
|
|
7
|
-
import ITBadget from '@/components/atoms/badget/badget';
|
|
8
|
-
import ITDatePicker from '@/components/molecules/date-picker/datePicker';
|
|
9
|
-
|
|
10
|
-
const meta: Meta<typeof ITDataTable> = {
|
|
11
|
-
title: 'Data Display/ITDataTable',
|
|
12
|
-
component: ITDataTable,
|
|
13
|
-
decorators: [
|
|
14
|
-
(Story) => (
|
|
15
|
-
<div className="p-8 bg-gray-50 min-h-screen">
|
|
16
|
-
<Story />
|
|
17
|
-
</div>
|
|
18
|
-
),
|
|
19
|
-
],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export default meta;
|
|
23
|
-
type Story = StoryObj<typeof ITDataTable>;
|
|
24
|
-
|
|
25
|
-
// --- MOCK CONSTANTS ---
|
|
26
|
-
const ROLES = ['ADMIN', 'CAJERO', 'TECNICO', 'SUPERVISOR'];
|
|
27
|
-
const MOCK_DATA = Array.from({ length: 100 }).map((_, i) => ({
|
|
28
|
-
id: i + 1,
|
|
29
|
-
username: `user_${i + 1}`,
|
|
30
|
-
name: `Nombre ${i + 1}`,
|
|
31
|
-
role: ROLES[i % ROLES.length],
|
|
32
|
-
active: i % 3 !== 0,
|
|
33
|
-
createdAt: new Date(Date.now() - i * 86400000).toISOString(),
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
// --- MOCK FETCH FUNCTION ---
|
|
37
|
-
const mockFetchItems = async (
|
|
38
|
-
params: ITDataTableFetchParams
|
|
39
|
-
): Promise<ITDataTableResponse<any>> => {
|
|
40
|
-
console.log('Fetching with params:', params);
|
|
41
|
-
|
|
42
|
-
// Simulate network delay
|
|
43
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
44
|
-
|
|
45
|
-
let filtered = [...MOCK_DATA];
|
|
46
|
-
|
|
47
|
-
// Apply filters
|
|
48
|
-
Object.entries(params.filters).forEach(([key, value]) => {
|
|
49
|
-
if (value === undefined || value === '') return;
|
|
50
|
-
|
|
51
|
-
if (key === 'username') {
|
|
52
|
-
filtered = filtered.filter(item =>
|
|
53
|
-
item.username.toLowerCase().includes(String(value).toLowerCase())
|
|
54
|
-
);
|
|
55
|
-
} else if (key === 'role') {
|
|
56
|
-
filtered = filtered.filter(item => item.role === value);
|
|
57
|
-
} else if (key === 'active') {
|
|
58
|
-
filtered = filtered.filter(item => item.active === value);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Apply sorting
|
|
63
|
-
if (params.sort) {
|
|
64
|
-
const { key, direction } = params.sort;
|
|
65
|
-
filtered.sort((a: any, b: any) => {
|
|
66
|
-
if (a[key] < b[key]) return direction === 'asc' ? -1 : 1;
|
|
67
|
-
if (a[key] > b[key]) return direction === 'asc' ? 1 : -1;
|
|
68
|
-
return 0;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Apply pagination
|
|
73
|
-
const start = (params.page - 1) * params.limit;
|
|
74
|
-
const end = start + params.limit;
|
|
75
|
-
const data = filtered.slice(start, end);
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
data,
|
|
79
|
-
total: filtered.length,
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const columns = [
|
|
84
|
-
{
|
|
85
|
-
key: 'id',
|
|
86
|
-
label: 'ID',
|
|
87
|
-
type: 'number' as const,
|
|
88
|
-
sortable: true,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
key: 'username',
|
|
92
|
-
label: 'Usuario',
|
|
93
|
-
type: 'string' as const,
|
|
94
|
-
filter: true,
|
|
95
|
-
sortable: true,
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
key: 'name',
|
|
99
|
-
label: 'Nombre Completo',
|
|
100
|
-
type: 'string' as const,
|
|
101
|
-
filter: true,
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
key: 'role',
|
|
105
|
-
label: 'Rol',
|
|
106
|
-
type: 'string' as const,
|
|
107
|
-
filter: 'catalog' as const,
|
|
108
|
-
catalogOptions: {
|
|
109
|
-
loading: false,
|
|
110
|
-
data: [
|
|
111
|
-
{ id: 'ADMIN', name: 'Administrador' },
|
|
112
|
-
{ id: 'CAJERO', name: 'Cajero' },
|
|
113
|
-
{ id: 'TECNICO', name: 'Técnico' },
|
|
114
|
-
{ id: 'SUPERVISOR', name: 'Supervisor' },
|
|
115
|
-
],
|
|
116
|
-
},
|
|
117
|
-
render: (row: any) => (
|
|
118
|
-
<ITBadget
|
|
119
|
-
label={row.role}
|
|
120
|
-
color={
|
|
121
|
-
row.role === 'ADMIN' ? 'primary' :
|
|
122
|
-
row.role === 'CAJERO' ? 'success' :
|
|
123
|
-
row.role === 'TECNICO' ? 'warning' : 'secondary'
|
|
124
|
-
}
|
|
125
|
-
/>
|
|
126
|
-
)
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
key: 'active',
|
|
130
|
-
label: 'Estado',
|
|
131
|
-
type: 'boolean' as const,
|
|
132
|
-
filter: true,
|
|
133
|
-
sortable: true,
|
|
134
|
-
},
|
|
135
|
-
];
|
|
136
|
-
|
|
137
|
-
export const Default: Story = {
|
|
138
|
-
args: {
|
|
139
|
-
title: 'Gestión de Usuarios',
|
|
140
|
-
columns,
|
|
141
|
-
fetchData: mockFetchItems,
|
|
142
|
-
defaultItemsPerPage: 10,
|
|
143
|
-
},
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
// Column widths keep the fixed layout from forcing horizontal scroll.
|
|
147
|
-
const fixedColumns = columns.map((col, i) => ({
|
|
148
|
-
...col,
|
|
149
|
-
width: [64, '28%', '34%', '22%', '16%'][i],
|
|
150
|
-
truncate: col.type === 'string' || col.type === 'catalog',
|
|
151
|
-
})) as any;
|
|
152
|
-
|
|
153
|
-
export const FixedLayoutCompact: Story = {
|
|
154
|
-
args: {
|
|
155
|
-
title: 'Compact fixed layout',
|
|
156
|
-
columns: fixedColumns,
|
|
157
|
-
fetchData: mockFetchItems,
|
|
158
|
-
defaultItemsPerPage: 10,
|
|
159
|
-
layout: 'fixed',
|
|
160
|
-
density: 'compact',
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// Pixel widths push the total past the 520px wrapper: the first column stays
|
|
165
|
-
// frozen left and the last one frozen right while the middle scrolls sideways.
|
|
166
|
-
const pinnedColumns = columns.map((col, i) => ({
|
|
167
|
-
...col,
|
|
168
|
-
width: [72, 240, 280, 160, 120][i],
|
|
169
|
-
pinned: i === 0 ? 'left' : i === columns.length - 1 ? 'right' : undefined,
|
|
170
|
-
truncate: col.type === 'string' || col.type === 'catalog',
|
|
171
|
-
})) as any;
|
|
172
|
-
|
|
173
|
-
export const PinnedColumns: Story = {
|
|
174
|
-
render: (args) => (
|
|
175
|
-
<div style={{ width: 520 }}>
|
|
176
|
-
<ITDataTable {...args} />
|
|
177
|
-
</div>
|
|
178
|
-
),
|
|
179
|
-
args: {
|
|
180
|
-
title: 'Pinned left & right columns',
|
|
181
|
-
columns: pinnedColumns,
|
|
182
|
-
fetchData: mockFetchItems,
|
|
183
|
-
defaultItemsPerPage: 10,
|
|
184
|
-
layout: 'fixed',
|
|
185
|
-
density: 'compact',
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
export const VirtualizedLargeDataset: Story = {
|
|
190
|
-
args: {
|
|
191
|
-
title: 'Virtualized · 100 rows',
|
|
192
|
-
columns: fixedColumns,
|
|
193
|
-
fetchData: mockFetchItems,
|
|
194
|
-
defaultItemsPerPage: 100,
|
|
195
|
-
itemsPerPageOptions: [10, 50, 100],
|
|
196
|
-
virtualized: true,
|
|
197
|
-
stickyHeader: true,
|
|
198
|
-
layout: 'fixed',
|
|
199
|
-
density: 'compact',
|
|
200
|
-
virtualizedMaxHeight: 360,
|
|
201
|
-
},
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
export const RemoteWithExternalFilters: Story = {
|
|
205
|
-
render: () => {
|
|
206
|
-
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
|
|
207
|
-
const [dateRange, setDateRange] = useState<[Date | null, Date | null]>([null, null]);
|
|
208
|
-
|
|
209
|
-
// Stable references for ITDataTable
|
|
210
|
-
const memoizedFetch = useCallback(mockFetchItems, []);
|
|
211
|
-
const memoizedColumns = useMemo(() => columns, []);
|
|
212
|
-
const externalFilters = useMemo(() => ({
|
|
213
|
-
date: selectedDate,
|
|
214
|
-
startDate: dateRange[0],
|
|
215
|
-
endDate: dateRange[1]
|
|
216
|
-
}), [selectedDate, dateRange]);
|
|
217
|
-
|
|
218
|
-
return (
|
|
219
|
-
<div className="space-y-6">
|
|
220
|
-
<ITCard title="Filtros Globales de Negocio" className="bg-white">
|
|
221
|
-
<div className="flex flex-wrap gap-4 items-end">
|
|
222
|
-
<div className="w-64">
|
|
223
|
-
<ITDatePicker
|
|
224
|
-
name="single"
|
|
225
|
-
label="Fecha Específica"
|
|
226
|
-
value={selectedDate}
|
|
227
|
-
onChange={(e: any) => setSelectedDate(e.target.value)}
|
|
228
|
-
placeholder="Seleccionar..."
|
|
229
|
-
/>
|
|
230
|
-
</div>
|
|
231
|
-
<div className="w-80">
|
|
232
|
-
<ITDatePicker
|
|
233
|
-
range
|
|
234
|
-
name="range"
|
|
235
|
-
label="Rango de Auditoría"
|
|
236
|
-
value={dateRange}
|
|
237
|
-
onChange={(e: any) => setDateRange(e.target.value)}
|
|
238
|
-
placeholder="Seleccionar rango..."
|
|
239
|
-
/>
|
|
240
|
-
</div>
|
|
241
|
-
<button
|
|
242
|
-
onClick={() => { setSelectedDate(undefined); setDateRange([null, null]); }}
|
|
243
|
-
className="px-4 py-2 text-sm text-primary-600 hover:text-primary-700 font-medium"
|
|
244
|
-
>
|
|
245
|
-
Resetear Filtros
|
|
246
|
-
</button>
|
|
247
|
-
</div>
|
|
248
|
-
</ITCard>
|
|
249
|
-
|
|
250
|
-
<ITDataTable
|
|
251
|
-
title="Resultados Filtrados"
|
|
252
|
-
columns={memoizedColumns}
|
|
253
|
-
fetchData={memoizedFetch}
|
|
254
|
-
externalFilters={externalFilters}
|
|
255
|
-
defaultItemsPerPage={5}
|
|
256
|
-
/>
|
|
257
|
-
</div>
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
export const WithRowClick: Story = {
|
|
263
|
-
args: {
|
|
264
|
-
title: 'Filas clickeables',
|
|
265
|
-
columns,
|
|
266
|
-
fetchData: mockFetchItems,
|
|
267
|
-
defaultItemsPerPage: 10,
|
|
268
|
-
onRowClick: (row: any) => console.log('Row clicked:', row),
|
|
269
|
-
},
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
export const WithSearchAndDateFilters: Story = {
|
|
273
|
-
render: () => {
|
|
274
|
-
const searchDateFetch = useCallback(
|
|
275
|
-
async (params: ITDataTableFetchParams): Promise<ITDataTableResponse<any>> => {
|
|
276
|
-
await new Promise((r) => setTimeout(r, 400));
|
|
277
|
-
let filtered = [...MOCK_DATA];
|
|
278
|
-
|
|
279
|
-
Object.entries(params.filters).forEach(([key, value]) => {
|
|
280
|
-
if (value === undefined || value === '') return;
|
|
281
|
-
|
|
282
|
-
if (key === 'role') {
|
|
283
|
-
filtered = filtered.filter((item) => item.role === value);
|
|
284
|
-
} else if (key === 'createdAt') {
|
|
285
|
-
if (Array.isArray(value)) {
|
|
286
|
-
const [rangeStart, rangeEnd] = value as [Date | null, Date | null];
|
|
287
|
-
if (!rangeStart && !rangeEnd) return;
|
|
288
|
-
filtered = filtered.filter((item) => {
|
|
289
|
-
const time = new Date(item.createdAt).getTime();
|
|
290
|
-
if (rangeStart && time < startOfDay(rangeStart).getTime()) return false;
|
|
291
|
-
if (rangeEnd && time > endOfDay(rangeEnd).getTime()) return false;
|
|
292
|
-
return true;
|
|
293
|
-
});
|
|
294
|
-
} else if (value instanceof Date) {
|
|
295
|
-
filtered = filtered.filter(
|
|
296
|
-
(item) => new Date(item.createdAt).toDateString() === value.toDateString()
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
const start = (params.page - 1) * params.limit;
|
|
303
|
-
return { data: filtered.slice(start, start + params.limit), total: filtered.length };
|
|
304
|
-
},
|
|
305
|
-
[]
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
const filterColumns = useMemo(
|
|
309
|
-
() => [
|
|
310
|
-
{ key: 'username', label: 'Usuario', type: 'string' as const, filter: true, sortable: true },
|
|
311
|
-
{
|
|
312
|
-
key: 'role',
|
|
313
|
-
label: 'Rol (ITSearchSelect)',
|
|
314
|
-
type: 'string' as const,
|
|
315
|
-
filter: 'search' as const,
|
|
316
|
-
sortable: true,
|
|
317
|
-
catalogOptions: { data: ROLES.map((role) => ({ id: role, name: role })) },
|
|
318
|
-
render: (row: any) => <ITBadget label={row.role} />,
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
key: 'createdAt',
|
|
322
|
-
label: 'Alta (ITDatePicker range)',
|
|
323
|
-
type: 'date' as const,
|
|
324
|
-
filter: 'date-range' as const,
|
|
325
|
-
sortable: true,
|
|
326
|
-
dateFilterOptions: { maxDate: new Date() },
|
|
327
|
-
render: (row: any) => new Date(row.createdAt).toLocaleDateString('es-MX'),
|
|
328
|
-
},
|
|
329
|
-
],
|
|
330
|
-
[]
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
return (
|
|
334
|
-
<ITDataTable
|
|
335
|
-
title="Filtros: ITSearchSelect + ITDatePicker (rango)"
|
|
336
|
-
columns={filterColumns as any}
|
|
337
|
-
fetchData={searchDateFetch}
|
|
338
|
-
defaultItemsPerPage={8}
|
|
339
|
-
/>
|
|
340
|
-
);
|
|
341
|
-
},
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
export const SkeletonLoading: Story = {
|
|
345
|
-
args: {
|
|
346
|
-
title: 'Cargando Datos...',
|
|
347
|
-
columns,
|
|
348
|
-
fetchData: async () => {
|
|
349
|
-
await new Promise(r => setTimeout(r, 10000));
|
|
350
|
-
return { data: [], total: 0 };
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
-
import * as StoriesData from './dialog.stories';
|
|
3
|
-
|
|
4
|
-
<Meta of={StoriesData} />
|
|
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,126 +0,0 @@
|
|
|
1
|
-
import type { Meta } from '@storybook/react';
|
|
2
|
-
import ITDialog from '@/components/organisms/dialog/dialog';
|
|
3
|
-
import ITSearchSelect from '@/components/molecules/search-select/search-select';
|
|
4
|
-
import { useState } from 'react';
|
|
5
|
-
import { expect, userEvent, within } from 'storybook/test';
|
|
6
|
-
import ITButton from '@/components/atoms/button/button';
|
|
7
|
-
|
|
8
|
-
const meta = {
|
|
9
|
-
title: 'Components/Feedback/ITDialog',
|
|
10
|
-
component: ITDialog,
|
|
11
|
-
parameters: {
|
|
12
|
-
layout: 'centered',
|
|
13
|
-
},
|
|
14
|
-
argTypes: {
|
|
15
|
-
isOpen: { control: 'boolean' },
|
|
16
|
-
onClose: { action: 'closed' },
|
|
17
|
-
title: { control: 'text' },
|
|
18
|
-
useFormHeader: { control: 'boolean' },
|
|
19
|
-
},
|
|
20
|
-
} satisfies Meta<typeof ITDialog>;
|
|
21
|
-
|
|
22
|
-
export default meta;
|
|
23
|
-
|
|
24
|
-
// Wrapper to handle open/close state
|
|
25
|
-
const DialogWrapper = (args: any) => {
|
|
26
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<div>
|
|
30
|
-
<ITButton onClick={() => setIsOpen(true)}>Open Dialog</ITButton>
|
|
31
|
-
<ITDialog {...args} isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
32
|
-
{args.children}
|
|
33
|
-
</ITDialog>
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const Default: any = {
|
|
39
|
-
render: (args) => <DialogWrapper {...args} />,
|
|
40
|
-
args: {
|
|
41
|
-
title: 'Basic Dialog',
|
|
42
|
-
children: <p className="text-gray-600">This is a simple dialog usage standard headers.</p>,
|
|
43
|
-
useFormHeader: false,
|
|
44
|
-
className: 'w-96',
|
|
45
|
-
} as any,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const WithFormHeader: any = {
|
|
49
|
-
render: (args) => <DialogWrapper {...args} />,
|
|
50
|
-
args: {
|
|
51
|
-
title: 'Form Header Dialog',
|
|
52
|
-
children: (
|
|
53
|
-
<div className="space-y-4">
|
|
54
|
-
<p className="text-gray-600">This dialog uses the standard form header styling.</p>
|
|
55
|
-
<div className="flex justify-end">
|
|
56
|
-
<ITButton variant="primary">Confirm</ITButton>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
),
|
|
60
|
-
useFormHeader: true,
|
|
61
|
-
className: 'w-[500px]',
|
|
62
|
-
} as any,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const LongContent: any = {
|
|
66
|
-
render: (args) => <DialogWrapper {...args} />,
|
|
67
|
-
args: {
|
|
68
|
-
title: 'Terms of Service',
|
|
69
|
-
children: (
|
|
70
|
-
<div className="h-64 overflow-y-auto text-sm text-gray-600">
|
|
71
|
-
<p className="mb-4">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
|
|
72
|
-
<p className="mb-4">Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
|
73
|
-
<p className="mb-4">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.</p>
|
|
74
|
-
<p className="mb-4">Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</p>
|
|
75
|
-
</div>
|
|
76
|
-
),
|
|
77
|
-
useFormHeader: true,
|
|
78
|
-
className: 'w-[600px]',
|
|
79
|
-
} as any,
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Dialog opened on mount but controlled, so an unexpected close is observable.
|
|
83
|
-
const DialogWithSearchSelect = () => {
|
|
84
|
-
const [fruit, setFruit] = useState('');
|
|
85
|
-
const [isOpen, setIsOpen] = useState(true);
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<ITDialog isOpen={isOpen} onClose={() => setIsOpen(false)} title="Fruta favorita">
|
|
89
|
-
<ITSearchSelect
|
|
90
|
-
label="Fruta"
|
|
91
|
-
placeholder="Busca una fruta..."
|
|
92
|
-
options={[
|
|
93
|
-
{ value: 'apple', label: 'Apple' },
|
|
94
|
-
{ value: 'banana', label: 'Banana' },
|
|
95
|
-
]}
|
|
96
|
-
value={fruit}
|
|
97
|
-
onChange={(next) => setFruit(String(next))}
|
|
98
|
-
/>
|
|
99
|
-
</ITDialog>
|
|
100
|
-
);
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Regression test: floating panels (selects, pickers, menus) are portaled to
|
|
105
|
-
* `<body>`, so they live outside the dialog DOM. Picking an option there must
|
|
106
|
-
* not count as a click outside the dialog.
|
|
107
|
-
*
|
|
108
|
-
* Note the whole dialog and its dropdown are portaled, so they are queried
|
|
109
|
-
* from `document.body` instead of the story canvas.
|
|
110
|
-
*/
|
|
111
|
-
export const FloatingPanelInsideDialog: any = {
|
|
112
|
-
render: () => <DialogWithSearchSelect />,
|
|
113
|
-
play: async () => {
|
|
114
|
-
const view = within(document.body);
|
|
115
|
-
|
|
116
|
-
const input = await view.findByPlaceholderText('Busca una fruta...');
|
|
117
|
-
await userEvent.click(input);
|
|
118
|
-
|
|
119
|
-
await userEvent.click(await view.findByText('Banana'));
|
|
120
|
-
|
|
121
|
-
// The dialog must still be mounted after picking an option.
|
|
122
|
-
await expect(view.getByText('Fruta favorita')).toBeVisible();
|
|
123
|
-
await expect((input as HTMLInputElement).value).toBe('Banana');
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Meta, Title, Description, Primary, Controls, Stories } from '@storybook/blocks';
|
|
2
|
-
import * as StoriesData from './drawer.stories';
|
|
3
|
-
|
|
4
|
-
<Meta of={StoriesData} />
|
|
5
|
-
|
|
6
|
-
# ITDrawer
|
|
7
|
-
|
|
8
|
-
A sliding panel that emerges from the left or right edge with a backdrop overlay. Includes an optional title bar with close button and a scrollable body. Ideal for side panels, navigation menus, filters, and detail views.
|
|
9
|
-
|
|
10
|
-
## Props
|
|
11
|
-
|
|
12
|
-
| Prop | Type | Default | Description |
|
|
13
|
-
|------|------|---------|-------------|
|
|
14
|
-
| `isOpen` | `boolean` | — | Controls drawer visibility. |
|
|
15
|
-
| `onClose` | `() => void` | — | Called on overlay click or close button press. |
|
|
16
|
-
| `position` | `"left" \| "right"` | `"right"` | Screen edge the drawer attaches to. |
|
|
17
|
-
| `size` | `string` | `"w-80"` | Tailwind width class (e.g., `"w-72"`, `"w-96"`). |
|
|
18
|
-
| `title` | `ReactNode` | — | Optional heading in the drawer header. |
|
|
19
|
-
| `children` | `ReactNode` | — | Content displayed in the drawer body. |
|
|
20
|
-
| `className` | `string` | — | Additional CSS classes for the drawer panel. |
|
|
21
|
-
| `style` | `CSSProperties` | — | Inline styles for the drawer panel. |
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
import { ITDrawer } from '@axzydev/axzy_ui_system';
|
|
27
|
-
|
|
28
|
-
<ITDrawer
|
|
29
|
-
isOpen={menuOpen}
|
|
30
|
-
onClose={() => setMenuOpen(false)}
|
|
31
|
-
title="Navigation"
|
|
32
|
-
position="left"
|
|
33
|
-
size="w-72"
|
|
34
|
-
>
|
|
35
|
-
<NavMenu />
|
|
36
|
-
</ITDrawer>
|
|
37
|
-
|
|
38
|
-
<ITDrawer
|
|
39
|
-
isOpen={filterOpen}
|
|
40
|
-
onClose={() => setFilterOpen(false)}
|
|
41
|
-
title="Filters"
|
|
42
|
-
>
|
|
43
|
-
<FilterPanel />
|
|
44
|
-
</ITDrawer>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Notes
|
|
48
|
-
|
|
49
|
-
- The overlay uses `bg-black/40 backdrop-blur-sm` for a blurred background effect.
|
|
50
|
-
- Overlay click outside the panel dismisses the drawer via `useClickOutside`.
|
|
51
|
-
- The body area scrolls independently (`overflow-y-auto`), keeping the header fixed.
|
|
52
|
-
- Default size `w-80` (320px); adjust with any Tailwind width class like `w-96` (384px) or `w-[400px]`.
|
|
53
|
-
- No built-in footer slot — add buttons or actions directly in the children content.
|
|
54
|
-
|
|
55
|
-
## Stories
|
|
56
|
-
|
|
57
|
-
<Primary />
|
|
58
|
-
<Controls />
|
|
59
|
-
<Stories />
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import ITDrawer from "@/components/organisms/drawer/drawer";
|
|
4
|
-
import ITButton from "@/components/atoms/button/button";
|
|
5
|
-
|
|
6
|
-
const meta: Meta<typeof ITDrawer> = {
|
|
7
|
-
title: "Components/Overlay/ITDrawer",
|
|
8
|
-
component: ITDrawer,
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export default meta;
|
|
12
|
-
type Story = StoryObj<typeof ITDrawer>;
|
|
13
|
-
|
|
14
|
-
export const Default: Story = {
|
|
15
|
-
render: () => {
|
|
16
|
-
const [open, setOpen] = useState(false);
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
<ITButton label="Abrir Drawer" onClick={() => setOpen(true)} />
|
|
20
|
-
<ITDrawer isOpen={open} onClose={() => setOpen(false)} title="Panel Lateral">
|
|
21
|
-
<p className="text-sm text-slate-600 dark:text-slate-300">Contenido del drawer.</p>
|
|
22
|
-
</ITDrawer>
|
|
23
|
-
</>
|
|
24
|
-
);
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const PositionLeft: Story = {
|
|
29
|
-
render: () => {
|
|
30
|
-
const [open, setOpen] = useState(false);
|
|
31
|
-
return (
|
|
32
|
-
<>
|
|
33
|
-
<ITButton label="Abrir Izquierda" onClick={() => setOpen(true)} />
|
|
34
|
-
<ITDrawer isOpen={open} onClose={() => setOpen(false)} position="left" title="Menú">
|
|
35
|
-
<p className="text-sm text-slate-600">Drawer desde la izquierda.</p>
|
|
36
|
-
</ITDrawer>
|
|
37
|
-
</>
|
|
38
|
-
);
|
|
39
|
-
},
|
|
40
|
-
};
|