@soyfri/shared-library 2.0.0-beta.2 → 2.0.0-beta.4

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.
Files changed (187) hide show
  1. package/.dockerignore +8 -0
  2. package/.github/workflows/publish.yml +107 -0
  3. package/.prettierrc +3 -0
  4. package/.storybook/main.ts +19 -0
  5. package/.storybook/preview.ts +14 -0
  6. package/.storybook/vitest.setup.ts +9 -0
  7. package/Dockerfile +37 -0
  8. package/build.js +102 -0
  9. package/chromatic.config.json +5 -0
  10. package/cleanDirectories.js +40 -0
  11. package/dist/README.md +243 -0
  12. package/dist/components/Icon/Icon.js +1 -1
  13. package/dist/components/Table/Table.js +1 -1
  14. package/dist/index.cjs +24 -0
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +7 -1
  17. package/dist/mui.d.ts +1 -0
  18. package/dist/package.json +197 -0
  19. package/package.json +4 -32
  20. package/rollup.config.cjs +87 -0
  21. package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
  22. package/src/components/ActionMenu/ActionMenu.tsx +174 -0
  23. package/src/components/ActionMenu/index.ts +2 -0
  24. package/src/components/AppBar/AppBar.stories.tsx +272 -0
  25. package/src/components/AppBar/AppBar.sx.ts +32 -0
  26. package/src/components/AppBar/AppBar.tsx +123 -0
  27. package/src/components/AppBar/AppBarBrand.tsx +120 -0
  28. package/src/components/AppBar/AppBarContext.ts +25 -0
  29. package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
  30. package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
  31. package/src/components/AppBar/index.ts +25 -0
  32. package/src/components/Autocomplete/Autocomplete.definitions.ts +477 -0
  33. package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
  34. package/src/components/Autocomplete/Autocomplete.stories.tsx +748 -0
  35. package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
  36. package/src/components/Autocomplete/Autocomplete.tsx +361 -0
  37. package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
  38. package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
  39. package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
  40. package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
  41. package/src/components/Autocomplete/index.ts +12 -0
  42. package/src/components/Avatar/Avatar.definitions.ts +162 -0
  43. package/src/components/Avatar/Avatar.stories.tsx +258 -0
  44. package/src/components/Avatar/Avatar.tsx +206 -0
  45. package/src/components/Avatar/index.ts +1 -0
  46. package/src/components/Button/Button.definition.ts +97 -0
  47. package/src/components/Button/Button.stories.tsx +285 -0
  48. package/src/components/Button/Button.tsx +67 -0
  49. package/src/components/Button/index.ts +1 -0
  50. package/src/components/Card/Card.definition.ts +5 -0
  51. package/src/components/Card/Card.stories.tsx +221 -0
  52. package/src/components/Card/Card.sx.ts +104 -0
  53. package/src/components/Card/Card.tsx +200 -0
  54. package/src/components/Card/index.ts +9 -0
  55. package/src/components/Chip/Chip.definitions.ts +167 -0
  56. package/src/components/Chip/Chip.stories.tsx +265 -0
  57. package/src/components/Chip/Chip.tsx +61 -0
  58. package/src/components/Chip/index.ts +1 -0
  59. package/src/components/Column/Column.tsx +29 -0
  60. package/src/components/Column/index.ts +1 -0
  61. package/src/components/DatePicker/DatePicker.definitions.ts +228 -0
  62. package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
  63. package/src/components/DatePicker/DatePicker.stories.tsx +309 -0
  64. package/src/components/DatePicker/DatePicker.sx.ts +33 -0
  65. package/src/components/DatePicker/DatePicker.tsx +189 -0
  66. package/src/components/DatePicker/DatePicker.types.ts +10 -0
  67. package/src/components/DatePicker/index.ts +9 -0
  68. package/src/components/DateRangePicker/DateRangePicker.definitions.ts +191 -0
  69. package/src/components/DateRangePicker/DateRangePicker.stories.tsx +252 -0
  70. package/src/components/DateRangePicker/DateRangePicker.tsx +56 -0
  71. package/src/components/DateRangePicker/index.ts +1 -0
  72. package/src/components/DateTimePicker/DateTimePicker.definitions.ts +256 -0
  73. package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
  74. package/src/components/DateTimePicker/DateTimePicker.stories.tsx +418 -0
  75. package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
  76. package/src/components/DateTimePicker/DateTimePicker.tsx +225 -0
  77. package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
  78. package/src/components/DateTimePicker/index.ts +9 -0
  79. package/src/components/Drawer/Drawer.stories.tsx +270 -0
  80. package/src/components/Drawer/Drawer.sx.ts +106 -0
  81. package/src/components/Drawer/Drawer.tsx +214 -0
  82. package/src/components/Drawer/DrawerContext.ts +26 -0
  83. package/src/components/Drawer/DrawerItem.tsx +110 -0
  84. package/src/components/Drawer/index.ts +10 -0
  85. package/src/components/Flyout/Flyout.stories.tsx +282 -0
  86. package/src/components/Flyout/Flyout.tsx +122 -0
  87. package/src/components/Flyout/index.ts +1 -0
  88. package/src/components/Gallery/Gallery.definition.tsx +37 -0
  89. package/src/components/Gallery/Gallery.stories.tsx +82 -0
  90. package/src/components/Gallery/Gallery.tsx +118 -0
  91. package/src/components/Gallery/GalleryLightbox.tsx +170 -0
  92. package/src/components/Gallery/GalleryMain.tsx +84 -0
  93. package/src/components/Gallery/GalleryThumbnails.tsx +106 -0
  94. package/src/components/Gallery/index.ts +1 -0
  95. package/src/components/Icon/Icon.stories.tsx +121 -0
  96. package/src/components/Icon/Icon.tsx +175 -0
  97. package/src/components/Icon/index.ts +2 -0
  98. package/src/components/Input/Input.definitions.ts +324 -0
  99. package/src/components/Input/Input.helpers.ts +49 -0
  100. package/src/components/Input/Input.stories.tsx +499 -0
  101. package/src/components/Input/Input.sx.ts +42 -0
  102. package/src/components/Input/Input.tsx +141 -0
  103. package/src/components/Input/Input.types.ts +10 -0
  104. package/src/components/Input/index.ts +9 -0
  105. package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
  106. package/src/components/InputGroup/InputGroup.stories.tsx +267 -0
  107. package/src/components/InputGroup/InputGroup.tsx +179 -0
  108. package/src/components/InputGroup/index.ts +1 -0
  109. package/src/components/MenuButton/MenuButton.stories.tsx +197 -0
  110. package/src/components/MenuButton/MenuButton.tsx +100 -0
  111. package/src/components/MenuButton/index.ts +1 -0
  112. package/src/components/Modal/Modal.stories.tsx +721 -0
  113. package/src/components/Modal/Modal.tsx +355 -0
  114. package/src/components/Modal/ModalBody.tsx +16 -0
  115. package/src/components/Modal/ModalFooter.tsx +71 -0
  116. package/src/components/Modal/ModalHeader.tsx +18 -0
  117. package/src/components/Modal/index.ts +6 -0
  118. package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
  119. package/src/components/PageLoader/PageLoader.tsx +96 -0
  120. package/src/components/PageLoader/index.ts +2 -0
  121. package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
  122. package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
  123. package/src/components/ScrollTopButton/index.ts +8 -0
  124. package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
  125. package/src/components/Select/Select.definitions.ts +602 -0
  126. package/src/components/Select/Select.helpers.ts +71 -0
  127. package/src/components/Select/Select.stories.tsx +687 -0
  128. package/src/components/Select/Select.sx.ts +14 -0
  129. package/src/components/Select/Select.tsx +429 -0
  130. package/src/components/Select/Select.types.ts +15 -0
  131. package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
  132. package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
  133. package/src/components/Select/_parts/SelectValue.tsx +96 -0
  134. package/src/components/Select/index.ts +14 -0
  135. package/src/components/Stat/Stat.stories.tsx +85 -0
  136. package/src/components/Stat/Stat.tsx +117 -0
  137. package/src/components/Stat/index.ts +2 -0
  138. package/src/components/StatusMessage/StatusMessage.stories.tsx +130 -0
  139. package/src/components/StatusMessage/StatusMessage.tsx +162 -0
  140. package/src/components/StatusMessage/index.ts +2 -0
  141. package/src/components/Stepper/Step.tsx +21 -0
  142. package/src/components/Stepper/Stepper.definition.ts +75 -0
  143. package/src/components/Stepper/Stepper.stories.tsx +122 -0
  144. package/src/components/Stepper/Stepper.tsx +75 -0
  145. package/src/components/Stepper/index.ts +2 -0
  146. package/src/components/Table/EmptyTable.png +0 -0
  147. package/src/components/Table/Table.definition.ts +580 -0
  148. package/src/components/Table/Table.stories.tsx +853 -0
  149. package/src/components/Table/Table.tsx +495 -0
  150. package/src/components/Table/data.ts +134 -0
  151. package/src/components/Table/exportsUtils.ts +195 -0
  152. package/src/components/Table/index.ts +3 -0
  153. package/src/components/Table/types.ts +34 -0
  154. package/src/components/Tabs/Tab.definition.ts +53 -0
  155. package/src/components/Tabs/Tab.tsx +19 -0
  156. package/src/components/Tabs/Tabs.stories.tsx +118 -0
  157. package/src/components/Tabs/Tabs.tsx +99 -0
  158. package/src/components/Tabs/_tabUtils.tsx +4 -0
  159. package/src/components/Tabs/index.ts +2 -0
  160. package/src/components/Timeline/Timeline.definition.ts +43 -0
  161. package/src/components/Timeline/Timeline.stories.tsx +108 -0
  162. package/src/components/Timeline/Timeline.tsx +49 -0
  163. package/src/components/Timeline/TimelineItem.tsx +31 -0
  164. package/src/components/Timeline/index.ts +2 -0
  165. package/src/components/Tooltip/Tooltip.stories.tsx +129 -0
  166. package/src/components/Tooltip/Tooltip.tsx +58 -0
  167. package/src/components/Tooltip/index.ts +1 -0
  168. package/src/components/_shared/formField.sx.ts +118 -0
  169. package/src/components/_shared/resolvePreset.ts +35 -0
  170. package/src/hooks/ClipBoard/ClipBoard.stories.tsx +168 -0
  171. package/src/hooks/ClipBoard/ClipBoard.tsx +131 -0
  172. package/src/hooks/ClipBoard/ClipboardUnifiedDemo.tsx +111 -0
  173. package/src/hooks/ClipBoard/index.ts +1 -0
  174. package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
  175. package/src/hooks/Wizard/WizardContext.tsx +166 -0
  176. package/src/hooks/Wizard/index.ts +6 -0
  177. package/src/hooks/Wizard/useWizard.ts +13 -0
  178. package/src/index.ts +17 -0
  179. package/src/mui.ts +54 -0
  180. package/src/styles.css +3 -0
  181. package/src/theme/componentStyles.ts +47 -0
  182. package/src/theme/tokens.ts +43 -0
  183. package/tailwind.config.js +10 -0
  184. package/tsconfig.json +48 -0
  185. package/tsup.config.js +41 -0
  186. package/vite.config.js +132 -0
  187. package/vitest.config.ts +35 -0
@@ -0,0 +1,495 @@
1
+ import React, { Children, isValidElement, useState, useEffect, ReactElement } from 'react';
2
+ import {
3
+ TableContainer,
4
+ Table as MuiTable,
5
+ TableHead,
6
+ TableBody,
7
+ TableRow,
8
+ TableCell,
9
+ Paper,
10
+ Typography,
11
+ Select,
12
+ MenuItem,
13
+ Box,
14
+ Button,
15
+ Snackbar,
16
+ IconButton,
17
+ Checkbox,
18
+ } from '@mui/material';
19
+ import MuiAlert, { AlertProps } from '@mui/material/Alert';
20
+ import { ColumnProps } from '../Column/Column'; // Asegúrate de que esta ruta sea correcta
21
+ import DownloadOutlinedIcon from '@mui/icons-material/DownloadOutlined';
22
+ import FirstPageIcon from '@mui/icons-material/FirstPage';
23
+ import LastPageIcon from '@mui/icons-material/LastPage';
24
+ import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
25
+ import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
26
+
27
+ import { exportToCSV, exportToExcel } from './exportsUtils';
28
+
29
+ // @ts-ignore
30
+ import EmptyImage from './EmptyTable.png';
31
+
32
+ export type FieldName<T> = keyof T | Array<keyof T>;
33
+
34
+ interface TableProps<T> {
35
+ data: T[];
36
+ children: React.ReactNode;
37
+
38
+ currentPage?: number;
39
+ pageSize?: number;
40
+ totalPages?: number;
41
+ previousPage?: number;
42
+ nextPage?: number;
43
+ pageSizeSelectorValue?: number;
44
+ onPageChange?: (newPage: number) => void;
45
+ onPageSizeChange?: (newSize: number) => void;
46
+ visiblePageNumbers?: number;
47
+
48
+ enableCSVExport?: boolean;
49
+ csvExportFileName?: string;
50
+ csvExportButtonText?: string;
51
+ csvExportColumns?: string[];
52
+
53
+ enableExcelExport?: boolean;
54
+ excelExportFileName?: string;
55
+ excelExportButtonText?: string;
56
+ excelExportColumns?: string[];
57
+
58
+ enableRowSelection?: boolean;
59
+ rowIdentifier?: keyof T;
60
+ onSelectionChange?: (selectedItems: T[]) => void;
61
+ showPageSizeSelector?: boolean;
62
+ emptyTitle?: string;
63
+ emptyMessage?: string;
64
+ emptyImageSrc?: React.ReactNode;
65
+ }
66
+
67
+ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(function Alert(
68
+ props,
69
+ ref,
70
+ ) {
71
+ return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />;
72
+ });
73
+
74
+ export function Table<T>({
75
+ data,
76
+ children,
77
+ currentPage,
78
+ pageSize,
79
+ totalPages,
80
+ previousPage,
81
+ nextPage,
82
+ pageSizeSelectorValue = 10,
83
+ onPageChange,
84
+ onPageSizeChange,
85
+ visiblePageNumbers = 5,
86
+ enableCSVExport = false,
87
+ csvExportFileName = 'data.csv',
88
+ csvExportButtonText = 'Exportar CSV',
89
+ csvExportColumns,
90
+ enableExcelExport = false,
91
+ excelExportFileName = 'data.xlsx',
92
+ excelExportButtonText = 'Exportar Excel',
93
+ excelExportColumns,
94
+ enableRowSelection = false,
95
+ rowIdentifier,
96
+ onSelectionChange,
97
+ showPageSizeSelector = true,
98
+ emptyTitle = 'No hay datos disponibles',
99
+ emptyMessage,
100
+ emptyImageSrc = <Box mb={2}>
101
+ <img src={EmptyImage} alt="No data" style={{ maxWidth: '150px', opacity: 0.6 }} />
102
+ </Box>,
103
+ }: TableProps<T>) {
104
+ const columns = Children.toArray(children).filter(
105
+ (child): child is React.ReactElement<ColumnProps<T>> =>
106
+ isValidElement(child) && (child.type as any).displayName === 'Column'
107
+ );
108
+
109
+ const [snackbarOpen, setSnackbarOpen] = useState(false);
110
+ const [snackbarMessage, setSnackbarMessage] = useState("");
111
+ const [snackbarSeverity, setSnackbarSeverity] = useState<AlertProps['severity']>('info');
112
+
113
+ const [selectedRows, setSelectedRows] = useState<T[]>([]);
114
+
115
+ useEffect(() => {
116
+ setSelectedRows([]);
117
+ }, [data, currentPage, pageSize]);
118
+
119
+ useEffect(() => {
120
+ onSelectionChange?.(selectedRows);
121
+ }, [selectedRows, onSelectionChange]);
122
+
123
+ const handleSnackbarClose = (event?: React.SyntheticEvent | Event, reason?: string) => {
124
+ if (reason === 'clickaway') {
125
+ return;
126
+ }
127
+ setSnackbarOpen(false);
128
+ };
129
+
130
+ const isControlled = typeof onPageChange === 'function' && typeof currentPage === 'number';
131
+ const [internalPage, setInternalPage] = useState(1);
132
+ const [internalPageSize, setInternalPageSize] = useState(pageSizeSelectorValue);
133
+
134
+ const activePage = isControlled ? currentPage! : internalPage;
135
+ const activePageSize = isControlled ? pageSize! : internalPageSize;
136
+
137
+ const paginatedData = isControlled ? data : data.slice((activePage - 1) * activePageSize, activePage * activePageSize);
138
+
139
+ const effectiveTotalPages = isControlled
140
+ ? totalPages!
141
+ : Math.ceil(data.length / activePageSize);
142
+
143
+ const handlePageChange = (newPage: number) => {
144
+ if (newPage < 1 || newPage > effectiveTotalPages) return;
145
+ if (isControlled) onPageChange?.(newPage);
146
+ else setInternalPage(newPage);
147
+ };
148
+
149
+ const handlePageSizeChange = (newSize: number) => {
150
+ if (isControlled) {
151
+ onPageSizeChange?.(newSize);
152
+ onPageChange?.(1);
153
+ } else {
154
+ setInternalPageSize(newSize);
155
+ setInternalPage(1);
156
+ }
157
+ };
158
+
159
+ const getPageNumbers = () => {
160
+ const pages = [];
161
+ const maxPages = effectiveTotalPages;
162
+ const current = activePage;
163
+ const half = Math.floor(visiblePageNumbers / 2);
164
+
165
+ let startPage = Math.max(1, current - half);
166
+ let endPage = Math.min(maxPages, current + half);
167
+
168
+ if (endPage - startPage + 1 < visiblePageNumbers) {
169
+ if (startPage === 1) {
170
+ endPage = Math.min(maxPages, startPage + visiblePageNumbers - 1);
171
+ } else if (endPage === maxPages) {
172
+ startPage = Math.max(1, maxPages - visiblePageNumbers + 1);
173
+ }
174
+ }
175
+
176
+ for (let i = startPage; i <= endPage; i++) {
177
+ pages.push(i);
178
+ }
179
+ return pages;
180
+ };
181
+
182
+ const pageNumbers = getPageNumbers();
183
+
184
+ const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => {
185
+ if (!rowIdentifier) {
186
+ console.warn("`rowIdentifier` prop is required for row selection.");
187
+ return;
188
+ }
189
+
190
+ if (event.target.checked) {
191
+ const newSelecteds = [...selectedRows];
192
+ paginatedData.forEach(row => {
193
+ const rowId = row[rowIdentifier] as any;
194
+ if (!newSelecteds.some(selectedRow => (selectedRow as any)[rowIdentifier] === rowId)) {
195
+ newSelecteds.push(row);
196
+ }
197
+ });
198
+ setSelectedRows(newSelecteds);
199
+ } else {
200
+ const newSelecteds = selectedRows.filter(selectedRow =>
201
+ !paginatedData.some(row => (row as any)[rowIdentifier] === (selectedRow as any)[rowIdentifier])
202
+ );
203
+ setSelectedRows(newSelecteds);
204
+ }
205
+ };
206
+
207
+ const handleRowClick = (event: React.MouseEvent<unknown>, row: T) => {
208
+ if (!rowIdentifier) {
209
+ console.warn("`rowIdentifier` prop is required for row selection.");
210
+ return;
211
+ }
212
+
213
+ const rowId = row[rowIdentifier] as any;
214
+ const selectedIndex = selectedRows.findIndex(selectedRow => (selectedRow as any)[rowIdentifier] === rowId);
215
+ let newSelected: T[] = [];
216
+
217
+ if (selectedIndex === -1) {
218
+ newSelected = newSelected.concat(selectedRows, row);
219
+ } else if (selectedIndex === 0) {
220
+ newSelected = newSelected.concat(selectedRows.slice(1));
221
+ } else if (selectedIndex === selectedRows.length - 1) {
222
+ newSelected = newSelected.concat(selectedRows.slice(0, -1));
223
+ } else if (selectedIndex > 0) {
224
+ newSelected = newSelected.concat(
225
+ selectedRows.slice(0, selectedIndex),
226
+ selectedRows.slice(selectedIndex + 1),
227
+ );
228
+ }
229
+ setSelectedRows(newSelected);
230
+ };
231
+
232
+ const isSelected = (row: T) => {
233
+ if (!rowIdentifier) return false;
234
+ return selectedRows.some(selectedRow => (selectedRow as any)[rowIdentifier] === (row as any)[rowIdentifier]);
235
+ };
236
+
237
+ const numSelectedOnPage = paginatedData.filter(isSelected).length;
238
+ const isAllSelectedOnPage = paginatedData.length > 0 && numSelectedOnPage === paginatedData.length;
239
+ const isIndeterminateOnPage = numSelectedOnPage > 0 && numSelectedOnPage < paginatedData.length;
240
+
241
+ return (
242
+ <TableContainer >
243
+
244
+ <MuiTable sx={{ minWidth: 650 }} aria-label="custom table" style={ paginatedData.length === 0 ? { display: 'none' } : {} }>
245
+ {/* Table Header */}
246
+ <TableHead sx={{ backgroundColor: '#fff' }}>
247
+ <TableRow>
248
+ {enableRowSelection && (
249
+ <TableCell padding="checkbox" sx={{ width: '50px' }}> {/* Puedes ajustar este ancho fijo si lo deseas */}
250
+ <Checkbox
251
+ color="primary"
252
+ indeterminate={isIndeterminateOnPage}
253
+ checked={isAllSelectedOnPage}
254
+ onChange={handleSelectAllClick}
255
+ inputProps={{ 'aria-label': 'select all desserts' }}
256
+ />
257
+ </TableCell>
258
+ )}
259
+ {columns.map((column, index) => (
260
+ <TableCell
261
+ key={index}
262
+ sx={{
263
+
264
+ fontSize: '12px',
265
+ color: (theme) => theme.palette.text.secondary,
266
+ width: column.props.width || 'auto', // APLICA EL ANCHO AQUÍ
267
+ whiteSpace: 'nowrap', // Evita que el texto se ajuste si la columna es pequeña
268
+
269
+ }}
270
+ >
271
+ <Typography sx={{ fontSize: '12px' }}>
272
+ {column.props.name}
273
+ </Typography>
274
+ </TableCell>
275
+ ))}
276
+ </TableRow>
277
+ </TableHead>
278
+ {/* Table Body */}
279
+ <TableBody sx={{ borderRadius: '10px', overflow: 'hidden' }}>
280
+ {paginatedData.map((row, rowIndex) => {
281
+ const isRowSelected = enableRowSelection && isSelected(row);
282
+ return (
283
+ <TableRow
284
+ key={rowIndex}
285
+ sx={{ backgroundColor: '#F8F8F8', '&:last-child td, &:last-child th': { border: 0 }, '&:hover': { backgroundColor: (theme) => '#FFF' } }}
286
+ selected={isRowSelected}
287
+ >
288
+ {enableRowSelection && (
289
+ <TableCell padding="checkbox" sx={{ width: '50px' }}> {/* Mismo ancho para la celda de la fila */}
290
+ <Checkbox
291
+ color="primary"
292
+ checked={isRowSelected}
293
+ onClick={(event) => handleRowClick(event, row)}
294
+ inputProps={{ 'aria-labelledby': `table-checkbox-${rowIndex}` }}
295
+ />
296
+ </TableCell>
297
+ )}
298
+ {columns.map((column, colIndex) => {
299
+ const { field, children: cellRenderer } = column.props;
300
+
301
+ let fieldData: Partial<T>;
302
+
303
+ if (Array.isArray(field)) {
304
+ if(field.length === 0){
305
+ fieldData = {...row};
306
+ }else{
307
+ fieldData = (field as Array<keyof T>).reduce((acc, currentField) => {
308
+ (acc as any)[currentField] = (row as any)[currentField];
309
+ return acc;
310
+ }, {} as Partial<T>);
311
+ }
312
+
313
+ } else {
314
+ fieldData = { [field]: (row as any)[field] } as Partial<T>;
315
+ }
316
+
317
+ return (
318
+ <TableCell
319
+ key={colIndex}
320
+ sx={{
321
+ fontSize: '14px',
322
+ width: column.props.width || 'auto', // APLICA EL ANCHO AQUÍ TAMBIÉN
323
+ whiteSpace: 'nowrap', // Asegura que el contenido no se rompa prematuramente
324
+ padding: '12px 16px', // Asegura un padding consistente
325
+ borderBottom: 'none'
326
+ }}
327
+ >
328
+ {cellRenderer(fieldData)}
329
+ </TableCell>
330
+ );
331
+ })}
332
+ </TableRow>
333
+ );
334
+ })}
335
+ {/* Empty table message */}
336
+
337
+ </TableBody>
338
+ </MuiTable>
339
+
340
+ { paginatedData.length === 0 && (
341
+ <Box display="flex" flexDirection="column" alignItems="center" justifyContent="center" p={4} >
342
+ { emptyImageSrc }
343
+
344
+ <Typography variant="body2" color="#878E9A" fontSize="18px" fontWeight="600">
345
+ {emptyTitle || 'No hay datos disponibles para mostrar.'}
346
+ </Typography>
347
+ <Typography variant="body2" color="#878E9A" fontSize="14px" mt={1}>
348
+ {emptyMessage || 'Intente ajustar sus filtros o agregar nuevos datos.'}
349
+ </Typography>
350
+
351
+ </Box>
352
+ )
353
+ }
354
+
355
+ {/* Pagination and Export Controls */}
356
+ {(effectiveTotalPages > 0 || enableCSVExport || enableExcelExport || (enableRowSelection && selectedRows.length > 0)) && (
357
+ <Box display="flex" justifyContent="space-between" alignItems="center" p={2}>
358
+ {/* Page Size Selector (Left) */}
359
+ {(effectiveTotalPages > 0 && showPageSizeSelector) && (
360
+ <Select
361
+ size="small"
362
+ value={activePageSize}
363
+ onChange={(e) => handlePageSizeChange(Number(e.target.value))}
364
+ >
365
+ {[1, 2, 3, 5, 10, 20, 50, 100].map((size) => (
366
+ <MenuItem key={size} value={size}>
367
+ Mostrar {size}
368
+ </MenuItem>
369
+ ))}
370
+ </Select>
371
+ )}
372
+
373
+ {/* Pagination Controls (Center) */}
374
+ {effectiveTotalPages > 0 && (
375
+ <Box
376
+ display="flex"
377
+ alignItems="center"
378
+ gap={0.5}
379
+ sx={{ flexGrow: 1, justifyContent: 'center' }}
380
+ >
381
+ <IconButton
382
+ onClick={() => handlePageChange(1)}
383
+ disabled={activePage <= 1}
384
+ size="small"
385
+ color="primary"
386
+ >
387
+ <FirstPageIcon fontSize="small" />
388
+ </IconButton>
389
+
390
+ <IconButton
391
+ onClick={() => handlePageChange(activePage - 1)}
392
+ disabled={activePage <= 1}
393
+ size="small"
394
+ color="primary"
395
+ >
396
+ <KeyboardArrowLeftIcon fontSize="small" />
397
+ </IconButton>
398
+
399
+ {pageNumbers.map((pageNumber) => (
400
+ <Button
401
+ key={pageNumber}
402
+ onClick={() => handlePageChange(pageNumber)}
403
+ variant={activePage === pageNumber ? 'contained' : 'text'}
404
+ size="small"
405
+ sx={{ minWidth: '32px', height: '32px' }}
406
+ >
407
+ {pageNumber}
408
+ </Button>
409
+ ))}
410
+
411
+ <IconButton
412
+ onClick={() => handlePageChange(activePage + 1)}
413
+ disabled={activePage >= effectiveTotalPages}
414
+ size="small"
415
+ color="primary"
416
+ >
417
+ <KeyboardArrowRightIcon fontSize="small" />
418
+ </IconButton>
419
+
420
+ <IconButton
421
+ onClick={() => handlePageChange(effectiveTotalPages)}
422
+ disabled={activePage >= effectiveTotalPages}
423
+ size="small"
424
+ color="primary"
425
+ >
426
+ <LastPageIcon fontSize="small" />
427
+ </IconButton>
428
+ </Box>
429
+ )}
430
+
431
+ {enableRowSelection && selectedRows.length > 0 && (
432
+ <Typography variant="subtitle2" sx={{ mr: 2 }}>
433
+ {selectedRows.length} seleccionados
434
+ </Typography>
435
+ )}
436
+
437
+ {(enableCSVExport || enableExcelExport) && (
438
+ <Box display="flex" alignItems="center" gap={1}>
439
+ {enableCSVExport && (
440
+ <Button
441
+ variant="text"
442
+ color="primary"
443
+ onClick={() =>
444
+ exportToCSV({
445
+ data,
446
+ columns,
447
+ fileName: csvExportFileName,
448
+ exportColumns: csvExportColumns,
449
+ setSnackbarOpen,
450
+ setSnackbarMessage,
451
+ setSnackbarSeverity,
452
+ })
453
+ }
454
+ size="small"
455
+ startIcon={<DownloadOutlinedIcon />}
456
+ >
457
+ {csvExportButtonText}
458
+ </Button>
459
+ )}
460
+ {enableExcelExport && (
461
+ <Button
462
+ variant="text"
463
+ color="primary"
464
+ onClick={() =>
465
+ exportToExcel({
466
+ data,
467
+ columns,
468
+ fileName: excelExportFileName,
469
+ exportColumns: excelExportColumns,
470
+ setSnackbarOpen,
471
+ setSnackbarMessage,
472
+ setSnackbarSeverity,
473
+ })
474
+ }
475
+ size="small"
476
+ startIcon={<DownloadOutlinedIcon />}
477
+ >
478
+ {excelExportButtonText}
479
+ </Button>
480
+ )}
481
+ </Box>
482
+ )}
483
+ </Box>
484
+ )}
485
+
486
+ <Snackbar open={snackbarOpen} autoHideDuration={3000} onClose={handleSnackbarClose} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}>
487
+ <Alert onClose={handleSnackbarClose} severity={snackbarSeverity} sx={{ width: '100%' }}>
488
+ {snackbarMessage}
489
+ </Alert>
490
+ </Snackbar>
491
+ </TableContainer>
492
+ );
493
+ }
494
+
495
+ export default Table;
@@ -0,0 +1,134 @@
1
+ // Datos de ejemplo
2
+ export const usuarios: Usuario[] = [
3
+ {
4
+ id: 1,
5
+ nombre: 'Juan Pérez',
6
+ email: 'juan@example.com',
7
+ avatar: 'https://placehold.co/50x50/F0F8FF/333333?text=JP', // Placeholder para avatar
8
+ edad: 25,
9
+ activo: true,
10
+ rol: 'admin',
11
+ fechaRegistro: '2023-01-15',
12
+ puntuacion: 4.5
13
+ },
14
+ {
15
+ id: 2,
16
+ nombre: 'Ana García',
17
+ email: 'ana@example.com',
18
+ avatar: 'https://placehold.co/50x50/FFF0F5/333333?text=AG', // Placeholder para avatar
19
+ edad: 30,
20
+ activo: false,
21
+ rol: 'usuario',
22
+ fechaRegistro: '2023-02-20',
23
+ puntuacion: 3.8
24
+ },
25
+ {
26
+ id: 3,
27
+ nombre: 'Carlos López',
28
+ email: 'carlos@example.com',
29
+ avatar: 'https://placehold.co/50x50/F5FFFA/333333?text=CL', // Placeholder para avatar
30
+ edad: 28,
31
+ activo: true,
32
+ rol: 'moderador',
33
+ fechaRegistro: '2023-03-10',
34
+ puntuacion: 4.2
35
+ },
36
+ {
37
+ id: 4,
38
+ nombre: 'María Rodríguez',
39
+ email: 'maria@example.com',
40
+ avatar: 'https://placehold.co/50x50/F8F8FF/333333?text=MR', // Placeholder para avatar
41
+ edad: 35,
42
+ activo: true,
43
+ rol: 'usuario',
44
+ fechaRegistro: '2023-04-05',
45
+ puntuacion: 4.9
46
+ },
47
+ {
48
+ id: 5,
49
+ nombre: 'Luis Martínez',
50
+ email: 'luis@example.com',
51
+ avatar: 'https://placehold.co/50x50/E6E6FA/333333?text=LM', // Placeholder para avatar
52
+ edad: 22,
53
+ activo: false,
54
+ rol: 'usuario',
55
+ fechaRegistro: '2023-05-12',
56
+ puntuacion: 3.2
57
+ }
58
+ ]
59
+
60
+ export const productos:Producto[] = [
61
+ {
62
+ id: 1,
63
+ nombre: 'iPhone 15',
64
+ precio: 999,
65
+ categoria: 'Electrónicos',
66
+ stock: 15,
67
+ imagen: 'https://placehold.co/60x60/ADD8E6/000000?text=I15', // Placeholder para imagen
68
+ enOferta: true,
69
+ rating: 4.8
70
+ },
71
+ {
72
+ id: 2,
73
+ nombre: 'Samsung Galaxy S24',
74
+ precio: 899,
75
+ categoria: 'Electrónicos',
76
+ stock: 8,
77
+ imagen: 'https://placehold.co/60x60/90EE90/000000?text=S24', // Placeholder para imagen
78
+ enOferta: false,
79
+ rating: 4.6
80
+ },
81
+ {
82
+ id: 3,
83
+ nombre: 'MacBook Pro',
84
+ precio: 1999,
85
+ categoria: 'Computadoras',
86
+ stock: 3,
87
+ imagen: 'https://placehold.co/60x60/DDA0DD/000000?text=MBP', // Placeholder para imagen
88
+ enOferta: true,
89
+ rating: 4.9
90
+ },
91
+ {
92
+ id: 4,
93
+ nombre: 'Dell XPS 13',
94
+ precio: 1299,
95
+ categoria: 'Computadoras',
96
+ stock: 0,
97
+ imagen: 'https://placehold.co/60x60/FFB6C1/000000?text=DX13', // Placeholder para imagen
98
+ enOferta: false,
99
+ rating: 4.4
100
+ }
101
+ ]
102
+
103
+ export const empleados:Empleado[] = [
104
+ {
105
+ id: 1,
106
+ nombre: 'Roberto',
107
+ apellido: 'Silva',
108
+ departamento: 'Desarrollo',
109
+ salario: 75000,
110
+ fechaIngreso: '2022-01-15',
111
+ activo: true,
112
+ avatar: 'https://placehold.co/50x50/B0E0E6/000000?text=RS' // Placeholder para avatar
113
+ },
114
+ {
115
+ id: 2,
116
+ nombre: 'Sofía',
117
+ apellido: 'Herrera',
118
+ departamento: 'Diseño',
119
+ salario: 65000,
120
+ fechaIngreso: '2022-03-20',
121
+ activo: true,
122
+ avatar: 'https://placehold.co/50x50/F5DEB3/000000?text=SH' // Placeholder para avatar
123
+ },
124
+ {
125
+ id: 3,
126
+ nombre: 'Diego',
127
+ apellido: 'Morales',
128
+ departamento: 'Marketing',
129
+ salario: 55000,
130
+ fechaIngreso: '2021-11-10',
131
+ activo: false,
132
+ avatar: 'https://placehold.co/50x50/D8BFD8/000000?text=DM' // Placeholder para avatar
133
+ }
134
+ ]