ywana-core8 0.1.79 → 0.1.81
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 +3493 -2320
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +2096 -1125
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +3493 -2320
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +3493 -2320
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/html/ExampleLayout.css +401 -0
- package/src/html/ExampleLayout.js +192 -0
- package/src/html/README-sidebar-navigation.md +195 -0
- package/src/html/accordion.example.js +123 -4
- package/src/html/accordion.example.js.backup +390 -0
- package/src/html/button.example.js +50 -3
- package/src/html/button.example.js.backup +374 -0
- package/src/html/button.example.new.js +416 -0
- package/src/html/button.js +22 -4
- package/src/html/checkbox.example.js +93 -4
- package/src/html/checkbox.example.js.backup +316 -0
- package/src/html/chip.example.js +108 -4
- package/src/html/chip.example.js.backup +355 -0
- package/src/html/color.example.js +108 -4
- package/src/html/color.example.js.backup +527 -0
- package/src/html/components.example.js +123 -4
- package/src/html/components.example.js.backup +492 -0
- package/src/html/convert-examples.js +183 -0
- package/src/html/demo-sidebar.html +410 -0
- package/src/html/form.example.js +93 -4
- package/src/html/form.example.js.backup +385 -0
- package/src/html/header.js +20 -3
- package/src/html/header2.example.js +108 -4
- package/src/html/header2.example.js.backup +411 -0
- package/src/html/icon.example.js +77 -3
- package/src/html/icon.example.js.backup +268 -0
- package/src/html/list.example.js +93 -4
- package/src/html/list.example.js.backup +404 -0
- package/src/html/progress.example.js +74 -4
- package/src/html/progress.example.js.backup +424 -0
- package/src/html/property.example.js +123 -4
- package/src/html/property.example.js.backup +553 -0
- package/src/html/radio.example.js +108 -4
- package/src/html/radio.example.js.backup +389 -0
- package/src/html/section.example.js +42 -3
- package/src/html/section.example.js.backup +99 -0
- package/src/html/switch.example.js +108 -4
- package/src/html/switch.example.js.backup +461 -0
- package/src/html/tab.example.js +93 -4
- package/src/html/tab.example.js.backup +446 -0
- package/src/html/table-export-utils.js +483 -0
- package/src/html/table-summary-functions.js +363 -0
- package/src/html/table2.css +1449 -479
- package/src/html/table2.example.js +2937 -512
- package/src/html/table2.example.js.broken +1226 -0
- package/src/html/table2.js +1426 -1000
- package/src/html/test-resize.html +279 -0
- package/src/html/test-selection.html +387 -0
- package/src/html/textfield.js +73 -7
- package/src/html/textfield2.example.js +108 -4
- package/src/html/textfield2.example.js.backup +1370 -0
- package/src/html/textfield2.js +19 -4
- package/src/html/tokenfield.example.js +108 -4
- package/src/html/tokenfield.example.js.backup +503 -0
- package/src/html/tooltip.js +21 -3
- package/src/html/tree.css +2 -4
- package/src/html/tree.example.js +93 -4
- package/src/html/tree.example.js.backup +475 -0
- package/src/html/tree.js +19 -3
- package/src/widgets/login/LoginBox.css +1 -0
- package/src/widgets/login/LoginBox.js +29 -6
@@ -1,25 +1,67 @@
|
|
1
|
-
import React, { useState } from 'react'
|
1
|
+
import React, { useState, useCallback } from 'react'
|
2
2
|
import { DataTable2 } from './table2'
|
3
3
|
import { Button } from './button'
|
4
|
-
import {
|
4
|
+
import { ExampleLayout, ExampleSection, ExampleSubsection, CodeSnippet } from './ExampleLayout'
|
5
|
+
|
6
|
+
// Ejemplo de componente personalizado para el panel
|
7
|
+
const CustomAnalyticsPanel = ({ columns = [], visibleColumns = [] }) => {
|
8
|
+
// Validación robusta de props para evitar errores
|
9
|
+
const safeColumns = Array.isArray(columns) ? columns : []
|
10
|
+
const safeVisibleColumns = Array.isArray(visibleColumns) ? visibleColumns : []
|
11
|
+
|
12
|
+
const totalColumns = safeColumns.length
|
13
|
+
const visibleCount = safeVisibleColumns.length
|
14
|
+
const hiddenCount = Math.max(0, totalColumns - visibleCount)
|
15
|
+
|
16
|
+
console.log('CustomAnalyticsPanel props:', {
|
17
|
+
columns: safeColumns.length,
|
18
|
+
visibleColumns: safeVisibleColumns.length
|
19
|
+
})
|
20
|
+
|
21
|
+
return (
|
22
|
+
<div className="datatable2__panel-section">
|
23
|
+
<h4 className="datatable2__panel-section-title">Análisis Personalizado</h4>
|
24
|
+
<div style={{ padding: '1rem 0' }}>
|
25
|
+
<p style={{ margin: '0 0 1rem 0', fontSize: '0.875rem', color: '#6b7280' }}>
|
26
|
+
Este es un componente React personalizado que puede acceder a los datos de la tabla.
|
27
|
+
</p>
|
28
|
+
<div style={{ background: '#f8fafc', padding: '1rem', borderRadius: '6px', border: '1px solid #e2e8f0' }}>
|
29
|
+
<h5 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem', fontWeight: 'bold' }}>Estadísticas:</h5>
|
30
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
31
|
+
<li>Total de columnas: {totalColumns}</li>
|
32
|
+
<li>Columnas visibles: {visibleCount}</li>
|
33
|
+
<li>Columnas ocultas: {hiddenCount}</li>
|
34
|
+
</ul>
|
35
|
+
{totalColumns > 0 ? (
|
36
|
+
<div style={{ marginTop: '1rem', padding: '0.5rem', background: '#e0f2fe', borderRadius: '4px' }}>
|
37
|
+
<strong style={{ fontSize: '0.8rem', color: '#0277bd' }}>
|
38
|
+
Visibilidad: {Math.round((visibleCount / totalColumns) * 100)}%
|
39
|
+
</strong>
|
40
|
+
</div>
|
41
|
+
) : (
|
42
|
+
<div style={{ marginTop: '1rem', padding: '0.5rem', background: '#fef3c7', borderRadius: '4px' }}>
|
43
|
+
<strong style={{ fontSize: '0.8rem', color: '#92400e' }}>
|
44
|
+
No hay datos de columnas disponibles
|
45
|
+
</strong>
|
46
|
+
</div>
|
47
|
+
)}
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
)
|
52
|
+
}
|
5
53
|
|
6
54
|
/**
|
7
55
|
* Ejemplos del componente DataTable2 mejorado manteniendo 100% compatibilidad
|
8
56
|
*/
|
9
57
|
export const DataTable2Examples = () => {
|
10
58
|
const [isLoading, setIsLoading] = useState(false)
|
11
|
-
const [showAdvanced, setShowAdvanced] = useState(false)
|
12
59
|
const [selectedRows, setSelectedRows] = useState([])
|
60
|
+
const [allChecked, setAllChecked] = useState(false)
|
61
|
+
const [checkedRows, setCheckedRows] = useState({})
|
13
62
|
|
14
63
|
// Datos de ejemplo
|
15
64
|
const columns = [
|
16
|
-
{
|
17
|
-
id: 'checked',
|
18
|
-
label: '',
|
19
|
-
type: 'CHECK',
|
20
|
-
sortable: false,
|
21
|
-
width: 50
|
22
|
-
},
|
23
65
|
{
|
24
66
|
id: 'id',
|
25
67
|
label: 'ID',
|
@@ -35,8 +77,7 @@ export const DataTable2Examples = () => {
|
|
35
77
|
sortable: true,
|
36
78
|
filterable: true,
|
37
79
|
editable: true,
|
38
|
-
resizable: true
|
39
|
-
onChange: (rowId, fieldId, value) => console.log('Edit:', rowId, fieldId, value)
|
80
|
+
resizable: true
|
40
81
|
},
|
41
82
|
{
|
42
83
|
id: 'email',
|
@@ -45,123 +86,235 @@ export const DataTable2Examples = () => {
|
|
45
86
|
sortable: true,
|
46
87
|
filterable: true,
|
47
88
|
editable: true,
|
48
|
-
resizable: true
|
49
|
-
onChange: (rowId, fieldId, value) => console.log('Edit:', rowId, fieldId, value)
|
89
|
+
resizable: true
|
50
90
|
},
|
51
91
|
{
|
52
92
|
id: 'age',
|
53
93
|
label: 'Age',
|
54
94
|
type: 'Number',
|
55
95
|
sortable: true,
|
96
|
+
filterable: true,
|
56
97
|
editable: true,
|
57
|
-
resizable: true
|
58
|
-
min: 0,
|
59
|
-
max: 120,
|
60
|
-
onChange: (rowId, fieldId, value) => console.log('Edit:', rowId, fieldId, value)
|
98
|
+
resizable: true
|
61
99
|
},
|
62
100
|
{
|
63
101
|
id: 'status',
|
64
102
|
label: 'Status',
|
65
|
-
type: '
|
103
|
+
type: 'String',
|
66
104
|
sortable: true,
|
67
105
|
filterable: true,
|
68
106
|
editable: true,
|
69
107
|
resizable: true,
|
70
108
|
options: [
|
71
|
-
{
|
72
|
-
{
|
73
|
-
{
|
74
|
-
]
|
75
|
-
onChange: (rowId, fieldId, value) => console.log('Edit:', rowId, fieldId, value)
|
76
|
-
},
|
77
|
-
{
|
78
|
-
id: 'verified',
|
79
|
-
label: 'Verified',
|
80
|
-
type: 'Boolean',
|
81
|
-
sortable: true,
|
82
|
-
editable: true,
|
83
|
-
resizable: true,
|
84
|
-
onChange: (rowId, fieldId, value) => console.log('Edit:', rowId, fieldId, value)
|
109
|
+
{ id: 'active', label: 'Active' },
|
110
|
+
{ id: 'inactive', label: 'Inactive' },
|
111
|
+
{ id: 'pending', label: 'Pending' }
|
112
|
+
]
|
85
113
|
}
|
86
114
|
]
|
87
115
|
|
88
116
|
const rows = [
|
89
|
-
{
|
90
|
-
id: 1,
|
91
|
-
name: 'John Doe',
|
92
|
-
email: 'john@example.com',
|
93
|
-
age: 30,
|
94
|
-
status: 'active',
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
{
|
108
|
-
id: 3,
|
109
|
-
name: 'Bob Johnson',
|
110
|
-
email: 'bob@example.com',
|
111
|
-
age: 35,
|
112
|
-
status: 'active',
|
113
|
-
verified: true
|
117
|
+
{
|
118
|
+
id: 1,
|
119
|
+
name: 'John Doe',
|
120
|
+
email: 'john@example.com',
|
121
|
+
age: 30,
|
122
|
+
status: 'active',
|
123
|
+
info: {
|
124
|
+
action: () => console.log('Expandir usuario 1'),
|
125
|
+
content: (
|
126
|
+
<div style={{ padding: '1rem', background: '#f8f9fa', borderRadius: '4px' }}>
|
127
|
+
<h5 style={{ margin: '0 0 0.5rem 0' }}>Información del Usuario</h5>
|
128
|
+
<p style={{ margin: '0', fontSize: '0.875rem' }}>
|
129
|
+
Usuario administrador con acceso completo al sistema.
|
130
|
+
Última conexión: hace 2 horas.
|
131
|
+
</p>
|
132
|
+
</div>
|
133
|
+
)
|
134
|
+
}
|
114
135
|
},
|
115
|
-
{
|
116
|
-
id:
|
117
|
-
name: '
|
118
|
-
email: '
|
119
|
-
age:
|
120
|
-
status: '
|
121
|
-
|
122
|
-
disabled: true
|
136
|
+
{
|
137
|
+
id: 2,
|
138
|
+
name: 'Jane Smith',
|
139
|
+
email: 'jane@example.com',
|
140
|
+
age: 25,
|
141
|
+
status: 'pending',
|
142
|
+
info: 'Usuario nuevo pendiente de verificación. Se requiere aprobación manual.'
|
123
143
|
},
|
124
|
-
{
|
125
|
-
id:
|
126
|
-
name: '
|
127
|
-
email: '
|
128
|
-
age:
|
129
|
-
status: '
|
130
|
-
|
144
|
+
{
|
145
|
+
id: 3,
|
146
|
+
name: 'Bob Johnson',
|
147
|
+
email: 'bob@example.com',
|
148
|
+
age: 35,
|
149
|
+
status: 'inactive',
|
150
|
+
info: {
|
151
|
+
action: () => console.log('Expandir usuario 3'),
|
152
|
+
content: (
|
153
|
+
<div style={{ padding: '1rem', background: '#fff3cd', border: '1px solid #ffeaa7', borderRadius: '4px' }}>
|
154
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#856404' }}>⚠️ Cuenta Inactiva</h5>
|
155
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#856404' }}>
|
156
|
+
Cuenta suspendida por inactividad. Último acceso: hace 6 meses.
|
157
|
+
</p>
|
158
|
+
</div>
|
159
|
+
)
|
160
|
+
}
|
131
161
|
}
|
132
162
|
]
|
133
163
|
|
134
|
-
//
|
135
|
-
const
|
136
|
-
id: i + 1,
|
137
|
-
name: `User ${i + 1}`,
|
138
|
-
email: `user${i + 1}@example.com`,
|
139
|
-
age: 20 + (i % 50),
|
140
|
-
status: ['active', 'inactive', 'pending'][i % 3],
|
141
|
-
verified: i % 2 === 0
|
142
|
-
}))
|
143
|
-
|
144
|
-
const handleRowSelection = (row, event) => {
|
164
|
+
// Funciones para manejar selección múltiple
|
165
|
+
const handleRowSelection = (rowId, row) => {
|
145
166
|
console.log('Row selected:', row)
|
146
|
-
setSelectedRows(prev =>
|
167
|
+
setSelectedRows(prev => {
|
168
|
+
if (prev.includes(rowId)) {
|
169
|
+
return prev.filter(id => id !== rowId)
|
170
|
+
} else {
|
171
|
+
return [...prev, rowId]
|
172
|
+
}
|
173
|
+
})
|
147
174
|
}
|
148
175
|
|
149
|
-
const
|
150
|
-
console.log('
|
176
|
+
const handleCheckboxSelection = (rowId, row) => {
|
177
|
+
console.log('Checkbox selection:', rowId, row)
|
178
|
+
setCheckedRows(prev => ({
|
179
|
+
...prev,
|
180
|
+
[rowId]: !prev[rowId]
|
181
|
+
}))
|
151
182
|
}
|
152
183
|
|
153
|
-
const
|
154
|
-
console.log('
|
155
|
-
|
184
|
+
const handleSelectAll = (checked) => {
|
185
|
+
console.log('Select all:', checked)
|
186
|
+
setAllChecked(checked)
|
187
|
+
if (checked) {
|
188
|
+
const allIds = {}
|
189
|
+
rows.forEach(row => {
|
190
|
+
allIds[row.id] = true
|
191
|
+
})
|
192
|
+
setCheckedRows(allIds)
|
193
|
+
} else {
|
194
|
+
setCheckedRows({})
|
195
|
+
}
|
156
196
|
}
|
157
197
|
|
158
|
-
const
|
159
|
-
|
160
|
-
|
198
|
+
const handleBulkAction = (action) => {
|
199
|
+
const selectedIds = Object.keys(checkedRows).filter(id => checkedRows[id])
|
200
|
+
console.log(`Bulk action "${action}" on rows:`, selectedIds)
|
201
|
+
|
202
|
+
switch (action) {
|
203
|
+
case 'delete':
|
204
|
+
alert(`Eliminar ${selectedIds.length} elementos seleccionados`)
|
205
|
+
break
|
206
|
+
case 'activate':
|
207
|
+
alert(`Activar ${selectedIds.length} elementos seleccionados`)
|
208
|
+
break
|
209
|
+
case 'export':
|
210
|
+
alert(`Exportar ${selectedIds.length} elementos seleccionados`)
|
211
|
+
break
|
212
|
+
default:
|
213
|
+
console.log('Acción no reconocida:', action)
|
214
|
+
}
|
161
215
|
}
|
162
216
|
|
163
|
-
|
164
|
-
|
217
|
+
// Datos con estado de checkbox actualizado
|
218
|
+
const rowsWithCheckboxes = rows.map(row => ({
|
219
|
+
...row,
|
220
|
+
checked: checkedRows[row.id] || false
|
221
|
+
}))
|
222
|
+
|
223
|
+
// Definir secciones para el menú lateral
|
224
|
+
const sections = [
|
225
|
+
{
|
226
|
+
id: 'overview',
|
227
|
+
title: 'Introducción',
|
228
|
+
icon: 'info'
|
229
|
+
},
|
230
|
+
{
|
231
|
+
id: 'basic-examples',
|
232
|
+
title: 'Ejemplos Básicos',
|
233
|
+
icon: 'table_chart',
|
234
|
+
subsections: [
|
235
|
+
{ id: 'simple-table', title: 'Tabla Simple' },
|
236
|
+
{ id: 'sortable-table', title: 'Tabla Ordenable' },
|
237
|
+
{ id: 'editable-table', title: 'Tabla Editable' }
|
238
|
+
]
|
239
|
+
},
|
240
|
+
{
|
241
|
+
id: 'selection-examples',
|
242
|
+
title: 'Selección Múltiple',
|
243
|
+
icon: 'check_box',
|
244
|
+
subsections: [
|
245
|
+
{ id: 'table-js-compatibility', title: '🔄 Compatibilidad con table.js' },
|
246
|
+
{ id: 'checkbox-selection', title: 'Selección con Checkboxes' },
|
247
|
+
{ id: 'row-selection', title: 'Selección por Click' },
|
248
|
+
{ id: 'bulk-actions', title: 'Acciones en Lote' }
|
249
|
+
]
|
250
|
+
},
|
251
|
+
{
|
252
|
+
id: 'expandable-rows',
|
253
|
+
title: 'Filas Expandibles',
|
254
|
+
icon: 'expand_more',
|
255
|
+
subsections: [
|
256
|
+
{ id: 'info-react-component', title: 'Info como Componente React' },
|
257
|
+
{ id: 'info-string-simple', title: 'Info como String Simple' },
|
258
|
+
{ id: 'info-custom-actions', title: 'Info con Acciones Personalizadas' }
|
259
|
+
]
|
260
|
+
},
|
261
|
+
{
|
262
|
+
id: 'themes-variants',
|
263
|
+
title: 'Temas y Variantes',
|
264
|
+
icon: 'palette',
|
265
|
+
subsections: [
|
266
|
+
{ id: 'density-variants', title: 'Variantes de Densidad' },
|
267
|
+
{ id: 'theme-variants', title: 'Variantes de Tema' }
|
268
|
+
]
|
269
|
+
},
|
270
|
+
{
|
271
|
+
id: 'readability-variants',
|
272
|
+
title: 'Variantes de Legibilidad',
|
273
|
+
icon: 'visibility',
|
274
|
+
subsections: [
|
275
|
+
{ id: 'readable-standard', title: 'Legibilidad Estándar' },
|
276
|
+
{ id: 'readable-large', title: 'Texto Grande' },
|
277
|
+
{ id: 'readable-contrast', title: 'Alto Contraste' },
|
278
|
+
{ id: 'readable-dyslexia', title: 'Optimizado para Dislexia' },
|
279
|
+
{ id: 'readable-focus', title: 'Modo Enfoque' },
|
280
|
+
{ id: 'readable-dark', title: 'Modo Oscuro Legible' }
|
281
|
+
]
|
282
|
+
},
|
283
|
+
{
|
284
|
+
id: 'advanced-features',
|
285
|
+
title: 'Funcionalidades Avanzadas',
|
286
|
+
icon: 'tune',
|
287
|
+
subsections: [
|
288
|
+
{ id: 'sidebar-column-visibility', title: 'Toolbar Vertical y Panel de Columnas' },
|
289
|
+
{ id: 'row-numbers-only', title: 'Solo Números de Fila' }
|
290
|
+
]
|
291
|
+
},
|
292
|
+
{
|
293
|
+
id: 'performance',
|
294
|
+
title: 'Rendimiento y Escalabilidad',
|
295
|
+
icon: 'speed',
|
296
|
+
subsections: [
|
297
|
+
{ id: 'massive-data', title: 'Datos Masivos (5,000 filas)' },
|
298
|
+
{ id: 'height-configurations', title: 'Configuraciones de Altura' },
|
299
|
+
{ id: 'advanced-filters', title: 'Sistema de Filtros Avanzado' },
|
300
|
+
{ id: 'summary-system', title: 'Sistema de Sumarios y Totales' },
|
301
|
+
{ id: 'export-system', title: 'Sistema de Exportación Avanzado' }
|
302
|
+
]
|
303
|
+
},
|
304
|
+
{
|
305
|
+
id: 'loading-states',
|
306
|
+
title: 'Estados de Carga',
|
307
|
+
icon: 'hourglass_empty'
|
308
|
+
},
|
309
|
+
{
|
310
|
+
id: 'api-reference',
|
311
|
+
title: 'Referencia API',
|
312
|
+
icon: 'code'
|
313
|
+
}
|
314
|
+
]
|
315
|
+
|
316
|
+
const handleCellEdit = (columnId, rowId, value) => {
|
317
|
+
console.log('Cell edited:', columnId, rowId, value)
|
165
318
|
}
|
166
319
|
|
167
320
|
const simulateLoading = () => {
|
@@ -170,36 +323,26 @@ export const DataTable2Examples = () => {
|
|
170
323
|
}
|
171
324
|
|
172
325
|
return (
|
173
|
-
<
|
174
|
-
<
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
<li>🔄 <strong>Ordenamiento avanzado</strong> - single, multiple, none</li>
|
193
|
-
<li>📏 <strong>Columnas redimensionables</strong> - Resize interactivo</li>
|
194
|
-
<li>🎭 <strong>Estados avanzados</strong> - loading, skeleton, striped</li>
|
195
|
-
<li>♿ <strong>Accesibilidad total</strong> - WCAG 2.1 AA compliant</li>
|
196
|
-
<li>📱 <strong>Responsive completo</strong> - Mobile-first design</li>
|
197
|
-
</ul>
|
198
|
-
</div>
|
326
|
+
<ExampleLayout title="DataTable2 Examples" sections={sections}>
|
327
|
+
<ExampleSection id="overview" title="DataTable2 - Versión Mejorada (100% Compatible)" icon="table_chart">
|
328
|
+
<div style={{
|
329
|
+
background: '#f8f9fa',
|
330
|
+
padding: '1rem',
|
331
|
+
borderRadius: '8px',
|
332
|
+
marginBottom: '2rem',
|
333
|
+
border: '1px solid #e9ecef'
|
334
|
+
}}>
|
335
|
+
<h3>✅ Mejoras Implementadas (100% Compatibilidad):</h3>
|
336
|
+
<ul style={{ columns: 2, columnGap: '2rem' }}>
|
337
|
+
<li>🔄 <strong>Filas expandibles</strong> - Con campo info</li>
|
338
|
+
<li>📏 <strong>Columnas redimensionables</strong> - Con persistencia</li>
|
339
|
+
<li>✏️ <strong>Edición inline</strong> - Campos integrados sin bordes</li>
|
340
|
+
<li>🎨 <strong>Estilos minimalistas</strong> - Diseño elegante</li>
|
341
|
+
<li>🔍 <strong>Filtros por columna</strong> - Búsqueda avanzada</li>
|
342
|
+
<li>📱 <strong>Responsive completo</strong> - Mobile-first design</li>
|
343
|
+
</ul>
|
344
|
+
</div>
|
199
345
|
|
200
|
-
{/* Controles de demostración */}
|
201
|
-
<section style={{ marginBottom: '2rem' }}>
|
202
|
-
<h3>Controles de Demostración</h3>
|
203
346
|
<div style={{
|
204
347
|
background: '#fff',
|
205
348
|
padding: '1rem',
|
@@ -208,445 +351,1839 @@ export const DataTable2Examples = () => {
|
|
208
351
|
display: 'flex',
|
209
352
|
gap: '1rem',
|
210
353
|
alignItems: 'center',
|
211
|
-
flexWrap: 'wrap'
|
354
|
+
flexWrap: 'wrap',
|
355
|
+
marginBottom: '2rem'
|
212
356
|
}}>
|
213
357
|
<Button
|
214
358
|
label="Simulate Loading"
|
215
359
|
icon="refresh"
|
216
360
|
action={simulateLoading}
|
217
|
-
|
218
|
-
/>
|
219
|
-
<Button
|
220
|
-
label={showAdvanced ? "Hide Advanced" : "Show Advanced"}
|
221
|
-
icon={showAdvanced ? "visibility_off" : "visibility"}
|
222
|
-
action={() => setShowAdvanced(!showAdvanced)}
|
361
|
+
loading={isLoading}
|
223
362
|
/>
|
224
363
|
<span style={{ marginLeft: 'auto', color: '#666' }}>
|
225
364
|
Selected: {selectedRows.length} rows
|
226
365
|
</span>
|
227
366
|
</div>
|
228
|
-
</
|
229
|
-
|
230
|
-
{/* DataTable original (Compatible) */}
|
231
|
-
<section style={{ marginBottom: '2rem' }}>
|
232
|
-
<h3>📋 DataTable Original (100% Compatible)</h3>
|
233
|
-
<div style={{
|
234
|
-
background: '#fff',
|
235
|
-
padding: '1rem',
|
236
|
-
border: '1px solid #ddd',
|
237
|
-
borderRadius: '8px'
|
238
|
-
}}>
|
239
|
-
<DataTable2
|
240
|
-
columns={columns}
|
241
|
-
rows={rows}
|
242
|
-
onRowSelection={handleRowSelection}
|
243
|
-
onSort={handleSort}
|
244
|
-
onCheckAll={handleCheckAll}
|
245
|
-
editable={true}
|
246
|
-
outlined={true}
|
247
|
-
expanded={false}
|
248
|
-
multisort={true}
|
249
|
-
filterable={true}
|
250
|
-
emptyMessage="No users found"
|
251
|
-
emptyIcon="people_outline"
|
252
|
-
/>
|
253
|
-
<Text size="sm" color="muted" style={{ marginTop: '0.5rem' }}>
|
254
|
-
API original sin cambios - todas las props funcionan idéntico
|
255
|
-
</Text>
|
256
|
-
</div>
|
257
|
-
</section>
|
258
|
-
|
259
|
-
{/* DataTable con nuevas características */}
|
260
|
-
<section style={{ marginBottom: '2rem' }}>
|
261
|
-
<h3>🚀 DataTable2 con Nuevas Características</h3>
|
262
|
-
<div style={{
|
263
|
-
background: '#fff',
|
264
|
-
padding: '1rem',
|
265
|
-
border: '1px solid #ddd',
|
266
|
-
borderRadius: '8px'
|
267
|
-
}}>
|
268
|
-
<DataTable2
|
269
|
-
columns={columns}
|
270
|
-
rows={rows}
|
271
|
-
onRowSelection={handleRowSelection}
|
272
|
-
onSort={handleSort}
|
273
|
-
onCheckAll={handleCheckAll}
|
274
|
-
editable={true}
|
275
|
-
outlined={true}
|
276
|
-
// Nuevas características
|
277
|
-
loading={isLoading}
|
278
|
-
skeleton={isLoading}
|
279
|
-
striped={true}
|
280
|
-
hover={true}
|
281
|
-
bordered={true}
|
282
|
-
responsive={true}
|
283
|
-
stickyHeader={true}
|
284
|
-
selectionMode="multiple"
|
285
|
-
sortMode="multiple"
|
286
|
-
searchable={true}
|
287
|
-
searchPlaceholder="Search users..."
|
288
|
-
onSearch={handleSearch}
|
289
|
-
exportable={true}
|
290
|
-
onExport={handleExport}
|
291
|
-
showRowNumbers={true}
|
292
|
-
rowHeight="medium"
|
293
|
-
density="normal"
|
294
|
-
theme="default"
|
295
|
-
accessibility={true}
|
296
|
-
onRowClick={(row) => console.log('Row clicked:', row)}
|
297
|
-
onRowDoubleClick={(row) => console.log('Row double-clicked:', row)}
|
298
|
-
onCellClick={(row, column, cell) => console.log('Cell clicked:', row, column, cell)}
|
299
|
-
footerContent={
|
300
|
-
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
301
|
-
<span>Total: {rows.length} users</span>
|
302
|
-
<span>Selected: {selectedRows.length}</span>
|
303
|
-
</div>
|
304
|
-
}
|
305
|
-
/>
|
306
|
-
</div>
|
307
|
-
</section>
|
367
|
+
</ExampleSection>
|
308
368
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
<DataTable2
|
324
|
-
columns={columns.slice(0, 4)}
|
325
|
-
rows={rows.slice(0, 3)}
|
326
|
-
theme="dark"
|
369
|
+
<ExampleSection id="basic-examples" title="Ejemplos Básicos" icon="table_chart">
|
370
|
+
<ExampleSubsection id="simple-table" title="Tabla Simple">
|
371
|
+
<div style={{
|
372
|
+
background: '#fff',
|
373
|
+
padding: '1rem',
|
374
|
+
border: '1px solid #ddd',
|
375
|
+
borderRadius: '8px',
|
376
|
+
marginBottom: '1rem'
|
377
|
+
}}>
|
378
|
+
<DataTable2
|
379
|
+
id="simple-table-demo"
|
380
|
+
columns={columns}
|
381
|
+
rows={rows}
|
382
|
+
density="normal"
|
327
383
|
striped={true}
|
328
|
-
|
329
|
-
compact={true}
|
330
|
-
/>
|
331
|
-
</div>
|
332
|
-
<div>
|
333
|
-
<h4>Tema Minimal</h4>
|
334
|
-
<DataTable2
|
335
|
-
columns={columns.slice(0, 4)}
|
336
|
-
rows={rows.slice(0, 3)}
|
337
|
-
theme="minimal"
|
338
|
-
hover={false}
|
339
|
-
density="comfortable"
|
384
|
+
hover={true}
|
340
385
|
/>
|
341
386
|
</div>
|
342
|
-
</div>
|
343
|
-
</section>
|
344
387
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
388
|
+
<CodeSnippet
|
389
|
+
title="Código de Tabla Simple"
|
390
|
+
code={`const columns = [
|
391
|
+
{
|
392
|
+
id: 'id',
|
393
|
+
label: 'ID',
|
394
|
+
type: 'Number',
|
395
|
+
sortable: true,
|
396
|
+
resizable: true,
|
397
|
+
width: 80
|
398
|
+
},
|
399
|
+
{
|
400
|
+
id: 'name',
|
401
|
+
label: 'Name',
|
402
|
+
type: 'String',
|
403
|
+
sortable: true,
|
404
|
+
filterable: true,
|
405
|
+
editable: true,
|
406
|
+
resizable: true
|
407
|
+
},
|
408
|
+
{
|
409
|
+
id: 'email',
|
410
|
+
label: 'Email',
|
411
|
+
type: 'String',
|
412
|
+
sortable: true,
|
413
|
+
filterable: true,
|
414
|
+
editable: true,
|
415
|
+
resizable: true
|
416
|
+
},
|
417
|
+
{
|
418
|
+
id: 'age',
|
419
|
+
label: 'Age',
|
420
|
+
type: 'Number',
|
421
|
+
sortable: true,
|
422
|
+
filterable: true,
|
423
|
+
editable: true,
|
424
|
+
resizable: true
|
425
|
+
},
|
426
|
+
{
|
427
|
+
id: 'status',
|
428
|
+
label: 'Status',
|
429
|
+
type: 'String',
|
430
|
+
sortable: true,
|
431
|
+
filterable: true,
|
432
|
+
editable: true,
|
433
|
+
resizable: true,
|
434
|
+
options: [
|
435
|
+
{ label: 'Active', value: 'active' },
|
436
|
+
{ label: 'Inactive', value: 'inactive' },
|
437
|
+
{ label: 'Pending', value: 'pending' }
|
438
|
+
]
|
439
|
+
}
|
440
|
+
]
|
441
|
+
|
442
|
+
const rows = [
|
443
|
+
{
|
444
|
+
id: 1,
|
445
|
+
name: 'John Doe',
|
446
|
+
email: 'john@example.com',
|
447
|
+
age: 30,
|
448
|
+
status: 'active'
|
449
|
+
},
|
450
|
+
{
|
451
|
+
id: 2,
|
452
|
+
name: 'Jane Smith',
|
453
|
+
email: 'jane@example.com',
|
454
|
+
age: 25,
|
455
|
+
status: 'pending'
|
456
|
+
},
|
457
|
+
// ... más filas
|
458
|
+
]
|
459
|
+
|
460
|
+
<DataTable2
|
461
|
+
id="simple-table-demo"
|
462
|
+
columns={columns}
|
463
|
+
rows={rows}
|
464
|
+
density="normal"
|
465
|
+
striped={true}
|
466
|
+
hover={true}
|
467
|
+
/>`}
|
468
|
+
/>
|
469
|
+
</ExampleSubsection>
|
470
|
+
|
471
|
+
<ExampleSubsection id="sortable-table" title="Tabla Ordenable">
|
472
|
+
<div style={{
|
473
|
+
background: '#fff',
|
474
|
+
padding: '1rem',
|
475
|
+
border: '1px solid #ddd',
|
476
|
+
borderRadius: '8px',
|
477
|
+
marginBottom: '1rem'
|
478
|
+
}}>
|
356
479
|
<DataTable2
|
357
|
-
|
358
|
-
|
480
|
+
id="sortable-table-demo"
|
481
|
+
columns={columns}
|
482
|
+
rows={rows}
|
483
|
+
onSort={(columnId, direction) => console.log('Sort:', columnId, direction)}
|
359
484
|
density="compact"
|
360
|
-
rowHeight="small"
|
361
|
-
compact={true}
|
362
|
-
resizable={true}
|
363
485
|
bordered={true}
|
364
|
-
onColumnResize={(columnId, width) => console.log(`Column ${columnId} resized to ${width}px`)}
|
365
486
|
/>
|
366
487
|
</div>
|
367
|
-
|
368
|
-
|
488
|
+
|
489
|
+
<CodeSnippet
|
490
|
+
title="Código de Tabla Ordenable"
|
491
|
+
code={`const columns = [
|
492
|
+
{
|
493
|
+
id: 'id',
|
494
|
+
label: 'ID',
|
495
|
+
type: 'Number',
|
496
|
+
sortable: true, // ← Habilita ordenamiento
|
497
|
+
resizable: true,
|
498
|
+
width: 80
|
499
|
+
},
|
500
|
+
{
|
501
|
+
id: 'name',
|
502
|
+
label: 'Name',
|
503
|
+
type: 'String',
|
504
|
+
sortable: true, // ← Habilita ordenamiento
|
505
|
+
filterable: true,
|
506
|
+
editable: true,
|
507
|
+
resizable: true
|
508
|
+
},
|
509
|
+
{
|
510
|
+
id: 'email',
|
511
|
+
label: 'Email',
|
512
|
+
type: 'String',
|
513
|
+
sortable: true, // ← Habilita ordenamiento
|
514
|
+
filterable: true,
|
515
|
+
editable: true,
|
516
|
+
resizable: true
|
517
|
+
},
|
518
|
+
{
|
519
|
+
id: 'age',
|
520
|
+
label: 'Age',
|
521
|
+
type: 'Number',
|
522
|
+
sortable: true, // ← Habilita ordenamiento
|
523
|
+
filterable: true,
|
524
|
+
editable: true,
|
525
|
+
resizable: true
|
526
|
+
}
|
527
|
+
]
|
528
|
+
|
529
|
+
const handleSort = (columnId, direction) => {
|
530
|
+
console.log('Sorting column:', columnId, 'direction:', direction)
|
531
|
+
// DataTable2 maneja el ordenamiento automáticamente
|
532
|
+
// Esta función es opcional para lógica adicional
|
533
|
+
}
|
534
|
+
|
535
|
+
<DataTable2
|
536
|
+
id="sortable-table-demo"
|
537
|
+
columns={columns}
|
538
|
+
rows={rows}
|
539
|
+
onSort={handleSort}
|
540
|
+
density="compact"
|
541
|
+
bordered={true}
|
542
|
+
/>`}
|
543
|
+
/>
|
544
|
+
</ExampleSubsection>
|
545
|
+
|
546
|
+
<ExampleSubsection id="editable-table" title="Tabla Editable">
|
547
|
+
<div style={{
|
548
|
+
background: '#fff',
|
549
|
+
padding: '1rem',
|
550
|
+
border: '1px solid #ddd',
|
551
|
+
borderRadius: '8px',
|
552
|
+
marginBottom: '1rem'
|
553
|
+
}}>
|
369
554
|
<DataTable2
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
555
|
+
id="editable-table-demo"
|
556
|
+
columns={columns}
|
557
|
+
rows={rows}
|
558
|
+
editable={true}
|
559
|
+
onCellEdit={handleCellEdit}
|
560
|
+
onSelect={handleRowSelection}
|
561
|
+
selectedRows={selectedRows}
|
374
562
|
resizable={true}
|
375
|
-
|
376
|
-
onColumnResize={(columnId, width) => console.log(`Column ${columnId} resized to ${width}px`)}
|
377
|
-
/>
|
378
|
-
</div>
|
379
|
-
<div>
|
380
|
-
<h4>🛋️ Comfortable (Altura: 52px) - Máximo espacio</h4>
|
381
|
-
<DataTable2
|
382
|
-
columns={columns.slice(1, 5)}
|
383
|
-
rows={rows.slice(0, 3)}
|
563
|
+
filterable={true}
|
384
564
|
density="comfortable"
|
385
|
-
rowHeight="large"
|
386
|
-
resizable={true}
|
387
|
-
bordered={true}
|
388
|
-
onColumnResize={(columnId, width) => console.log(`Column ${columnId} resized to ${width}px`)}
|
389
565
|
/>
|
390
566
|
</div>
|
567
|
+
|
568
|
+
<CodeSnippet
|
569
|
+
title="Código de Tabla Editable"
|
570
|
+
code={`const columns = [
|
571
|
+
{
|
572
|
+
id: 'id',
|
573
|
+
label: 'ID',
|
574
|
+
type: 'Number',
|
575
|
+
sortable: true,
|
576
|
+
resizable: true,
|
577
|
+
width: 80
|
578
|
+
// No editable - ID no se puede editar
|
579
|
+
},
|
580
|
+
{
|
581
|
+
id: 'name',
|
582
|
+
label: 'Name',
|
583
|
+
type: 'String',
|
584
|
+
sortable: true,
|
585
|
+
filterable: true,
|
586
|
+
editable: true, // ← Habilita edición
|
587
|
+
resizable: true
|
588
|
+
},
|
589
|
+
{
|
590
|
+
id: 'email',
|
591
|
+
label: 'Email',
|
592
|
+
type: 'String',
|
593
|
+
sortable: true,
|
594
|
+
filterable: true,
|
595
|
+
editable: true, // ← Habilita edición
|
596
|
+
resizable: true
|
597
|
+
},
|
598
|
+
{
|
599
|
+
id: 'age',
|
600
|
+
label: 'Age',
|
601
|
+
type: 'Number',
|
602
|
+
sortable: true,
|
603
|
+
filterable: true,
|
604
|
+
editable: true, // ← Habilita edición
|
605
|
+
resizable: true
|
606
|
+
},
|
607
|
+
{
|
608
|
+
id: 'status',
|
609
|
+
label: 'Status',
|
610
|
+
type: 'String',
|
611
|
+
sortable: true,
|
612
|
+
filterable: true,
|
613
|
+
editable: true, // ← Habilita edición
|
614
|
+
resizable: true,
|
615
|
+
options: [ // ← Opciones para dropdown
|
616
|
+
{ label: 'Active', value: 'active' },
|
617
|
+
{ label: 'Inactive', value: 'inactive' },
|
618
|
+
{ label: 'Pending', value: 'pending' }
|
619
|
+
]
|
620
|
+
}
|
621
|
+
]
|
622
|
+
|
623
|
+
const handleCellEdit = (rowId, columnId, newValue) => {
|
624
|
+
console.log('Cell edited:', rowId, columnId, newValue)
|
625
|
+
// Aquí actualizarías los datos
|
626
|
+
}
|
627
|
+
|
628
|
+
<DataTable2
|
629
|
+
id="editable-table-demo"
|
630
|
+
columns={columns}
|
631
|
+
rows={rows}
|
632
|
+
editable={true}
|
633
|
+
onCellEdit={handleCellEdit}
|
634
|
+
onSelect={handleRowSelection}
|
635
|
+
selectedRows={selectedRows}
|
636
|
+
resizable={true}
|
637
|
+
filterable={true}
|
638
|
+
density="comfortable"
|
639
|
+
/>`}
|
640
|
+
/>
|
641
|
+
</ExampleSubsection>
|
642
|
+
</ExampleSection>
|
643
|
+
|
644
|
+
<ExampleSection id="selection-examples" title="Selección Múltiple" icon="check_box">
|
645
|
+
<ExampleSubsection id="table-js-compatibility" title="🔄 Compatibilidad 100% con table.js">
|
391
646
|
<div style={{
|
392
|
-
|
393
|
-
padding: '
|
394
|
-
background: '#e3f2fd',
|
647
|
+
background: '#e8f5e8',
|
648
|
+
padding: '1rem',
|
395
649
|
borderRadius: '4px',
|
396
|
-
border: '1px solid #
|
650
|
+
border: '1px solid #4caf50',
|
651
|
+
marginBottom: '1rem'
|
397
652
|
}}>
|
398
|
-
<
|
399
|
-
|
400
|
-
|
401
|
-
|
653
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#2e7d32' }}>✅ Migración Sin Cambios</h5>
|
654
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#2e7d32' }}>
|
655
|
+
DataTable2 es 100% compatible con table.js. Puedes reemplazar <code><DataTable></code> por
|
656
|
+
<code><DataTable2></code> sin cambiar tu código existente. La columna especial <code>id="checked"</code>
|
657
|
+
y todos los props funcionan exactamente igual.
|
658
|
+
</p>
|
402
659
|
</div>
|
403
|
-
</div>
|
404
|
-
</section>
|
405
660
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
<h3>📱 Virtual Scrolling (1000 filas)</h3>
|
410
|
-
<div style={{
|
411
|
-
background: '#fff',
|
412
|
-
padding: '1rem',
|
661
|
+
<div style={{
|
662
|
+
background: '#fff',
|
663
|
+
padding: '1rem',
|
413
664
|
border: '1px solid #ddd',
|
414
|
-
borderRadius: '8px'
|
665
|
+
borderRadius: '8px',
|
666
|
+
marginBottom: '1rem'
|
415
667
|
}}>
|
416
|
-
<DataTable2
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
668
|
+
<DataTable2
|
669
|
+
id="table-js-compatibility-example"
|
670
|
+
columns={[
|
671
|
+
{
|
672
|
+
id: 'checked',
|
673
|
+
label: '',
|
674
|
+
type: 'CHECK',
|
675
|
+
sortable: false,
|
676
|
+
width: 50,
|
677
|
+
onChange: (rowId, fieldId, value) => {
|
678
|
+
console.log('table.js style onChange:', rowId, fieldId, value)
|
679
|
+
setCheckedRows(prev => ({
|
680
|
+
...prev,
|
681
|
+
[rowId]: value
|
682
|
+
}))
|
683
|
+
}
|
684
|
+
},
|
685
|
+
...columns.slice(0, 4)
|
686
|
+
]}
|
687
|
+
rows={rows.map(row => ({
|
688
|
+
...row,
|
689
|
+
checked: checkedRows[row.id] || false
|
690
|
+
}))}
|
691
|
+
onRowSelection={(row, event) => {
|
692
|
+
console.log('table.js style onRowSelection:', row, event)
|
693
|
+
handleRowSelection(row.id, row)
|
694
|
+
}}
|
695
|
+
onCheckAll={(ids, checked) => {
|
696
|
+
console.log('table.js style onCheckAll:', ids, checked)
|
697
|
+
if (checked) {
|
698
|
+
const allIds = {}
|
699
|
+
ids.forEach(id => {
|
700
|
+
allIds[id] = true
|
701
|
+
})
|
702
|
+
setCheckedRows(allIds)
|
703
|
+
} else {
|
704
|
+
setCheckedRows({})
|
705
|
+
}
|
706
|
+
setAllChecked(checked)
|
707
|
+
}}
|
708
|
+
allChecked={allChecked}
|
709
|
+
density="normal"
|
425
710
|
striped={true}
|
426
|
-
onSearch={handleSearch}
|
427
|
-
onExport={handleExport}
|
428
711
|
/>
|
429
|
-
<Text size="sm" color="muted" style={{ marginTop: '0.5rem' }}>
|
430
|
-
Rendimiento optimizado para grandes datasets con paginación virtual
|
431
|
-
</Text>
|
432
712
|
</div>
|
433
|
-
</section>
|
434
|
-
)}
|
435
713
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
714
|
+
<CodeSnippet
|
715
|
+
language="javascript"
|
716
|
+
code={`// ✅ ANTES (table.js) - Funciona exactamente igual
|
717
|
+
import { DataTable } from './table'
|
718
|
+
|
719
|
+
const columns = [
|
720
|
+
{
|
721
|
+
id: 'checked', // ← Columna especial para checkboxes
|
722
|
+
label: '',
|
723
|
+
type: 'CHECK',
|
724
|
+
sortable: false,
|
725
|
+
width: 50,
|
726
|
+
onChange: (rowId, fieldId, value) => {
|
727
|
+
console.log('Checkbox changed:', rowId, fieldId, value)
|
728
|
+
// Actualizar estado del checkbox
|
729
|
+
}
|
730
|
+
},
|
731
|
+
{
|
732
|
+
id: 'name',
|
733
|
+
label: 'Name',
|
734
|
+
type: 'String',
|
735
|
+
sortable: true,
|
736
|
+
filterable: true,
|
737
|
+
editable: true
|
738
|
+
},
|
739
|
+
{
|
740
|
+
id: 'email',
|
741
|
+
label: 'Email',
|
742
|
+
type: 'String',
|
743
|
+
sortable: true,
|
744
|
+
filterable: true,
|
745
|
+
editable: true
|
746
|
+
}
|
747
|
+
]
|
748
|
+
|
749
|
+
const rows = [
|
750
|
+
{
|
751
|
+
id: 1,
|
752
|
+
name: 'John Doe',
|
753
|
+
email: 'john@example.com',
|
754
|
+
checked: false, // ← Estado del checkbox
|
755
|
+
checkDisabled: false // ← Opcional: deshabilitar checkbox
|
756
|
+
},
|
757
|
+
// ... más filas
|
758
|
+
]
|
759
|
+
|
760
|
+
<DataTable
|
761
|
+
columns={columns}
|
762
|
+
rows={rows}
|
763
|
+
onRowSelection={(row, event) => {
|
764
|
+
console.log('Row clicked:', row)
|
765
|
+
}}
|
766
|
+
onCheckAll={(ids, checked) => {
|
767
|
+
console.log('Select all:', ids, checked)
|
768
|
+
}}
|
769
|
+
/>
|
770
|
+
|
771
|
+
// ✅ DESPUÉS (table2.js) - ¡Sin cambios!
|
772
|
+
import { DataTable2 } from './table2'
|
773
|
+
|
774
|
+
<DataTable2
|
775
|
+
columns={columns} // ← Misma configuración
|
776
|
+
rows={rows} // ← Mismos datos
|
777
|
+
onRowSelection={(row, event) => { // ← Misma función
|
778
|
+
console.log('Row clicked:', row)
|
779
|
+
}}
|
780
|
+
onCheckAll={(ids, checked) => { // ← Misma función
|
781
|
+
console.log('Select all:', ids, checked)
|
782
|
+
}}
|
783
|
+
/>`}
|
784
|
+
/>
|
785
|
+
</ExampleSubsection>
|
786
|
+
|
787
|
+
<ExampleSubsection id="checkbox-selection" title="Selección con Checkboxes">
|
788
|
+
<div style={{
|
789
|
+
background: '#fff',
|
790
|
+
padding: '1rem',
|
443
791
|
border: '1px solid #ddd',
|
444
792
|
borderRadius: '8px',
|
445
|
-
|
446
|
-
gridTemplateColumns: '1fr 1fr',
|
447
|
-
gap: '1rem'
|
793
|
+
marginBottom: '1rem'
|
448
794
|
}}>
|
449
|
-
<
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
columns={columns.slice(0, 4)}
|
462
|
-
rows={[]}
|
463
|
-
loading={false}
|
464
|
-
skeleton={true}
|
465
|
-
/>
|
466
|
-
</div>
|
795
|
+
<DataTable2
|
796
|
+
id="checkbox-selection-table"
|
797
|
+
columns={columns}
|
798
|
+
rows={rowsWithCheckboxes}
|
799
|
+
onSelect={handleCheckboxSelection}
|
800
|
+
showSelectAll={true}
|
801
|
+
allChecked={allChecked}
|
802
|
+
onCheckAll={handleSelectAll}
|
803
|
+
density="normal"
|
804
|
+
striped={true}
|
805
|
+
resizable={true}
|
806
|
+
/>
|
467
807
|
</div>
|
468
|
-
</section>
|
469
|
-
)}
|
470
808
|
|
471
|
-
{/* Comparación antes/después */}
|
472
|
-
<section style={{ marginBottom: '2rem' }}>
|
473
|
-
<h3>Comparación: DataTable Original vs DataTable2</h3>
|
474
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
475
809
|
<div style={{
|
476
|
-
background: '#
|
810
|
+
background: '#f8f9fa',
|
477
811
|
padding: '1rem',
|
478
812
|
borderRadius: '4px',
|
479
|
-
border: '1px solid #
|
813
|
+
border: '1px solid #e9ecef',
|
814
|
+
marginBottom: '1rem'
|
480
815
|
}}>
|
481
|
-
<
|
482
|
-
<
|
483
|
-
|
484
|
-
<li>Sorting y filtering básico</li>
|
485
|
-
<li>Edición inline limitada</li>
|
486
|
-
<li>Selección de filas básica</li>
|
487
|
-
<li>Sin PropTypes ni validación</li>
|
488
|
-
<li>Sin búsqueda integrada</li>
|
489
|
-
<li>Sin exportación</li>
|
490
|
-
<li>Sin virtual scrolling</li>
|
491
|
-
<li>Sin temas ni variantes</li>
|
492
|
-
<li>Sin accesibilidad</li>
|
493
|
-
<li>CSS básico sin responsive</li>
|
494
|
-
<li>Rendimiento limitado</li>
|
495
|
-
</ul>
|
816
|
+
<strong>Estado actual:</strong> {Object.keys(checkedRows).filter(id => checkedRows[id]).length} elementos seleccionados
|
817
|
+
<br />
|
818
|
+
<strong>IDs seleccionados:</strong> {Object.keys(checkedRows).filter(id => checkedRows[id]).join(', ') || 'Ninguno'}
|
496
819
|
</div>
|
820
|
+
|
821
|
+
<CodeSnippet
|
822
|
+
language="javascript"
|
823
|
+
code={`const columns = [
|
824
|
+
{
|
825
|
+
id: 'id',
|
826
|
+
label: 'ID',
|
827
|
+
type: 'Number',
|
828
|
+
sortable: true,
|
829
|
+
resizable: true,
|
830
|
+
width: 80
|
831
|
+
},
|
832
|
+
{
|
833
|
+
id: 'name',
|
834
|
+
label: 'Name',
|
835
|
+
type: 'String',
|
836
|
+
sortable: true,
|
837
|
+
filterable: true,
|
838
|
+
editable: true,
|
839
|
+
resizable: true
|
840
|
+
},
|
841
|
+
{
|
842
|
+
id: 'email',
|
843
|
+
label: 'Email',
|
844
|
+
type: 'String',
|
845
|
+
sortable: true,
|
846
|
+
filterable: true,
|
847
|
+
editable: true,
|
848
|
+
resizable: true
|
849
|
+
},
|
850
|
+
{
|
851
|
+
id: 'age',
|
852
|
+
label: 'Age',
|
853
|
+
type: 'Number',
|
854
|
+
sortable: true,
|
855
|
+
filterable: true,
|
856
|
+
editable: true,
|
857
|
+
resizable: true
|
858
|
+
},
|
859
|
+
{
|
860
|
+
id: 'status',
|
861
|
+
label: 'Status',
|
862
|
+
type: 'String',
|
863
|
+
sortable: true,
|
864
|
+
filterable: true,
|
865
|
+
editable: true,
|
866
|
+
resizable: true,
|
867
|
+
options: [
|
868
|
+
{ label: 'Active', value: 'active' },
|
869
|
+
{ label: 'Inactive', value: 'inactive' },
|
870
|
+
{ label: 'Pending', value: 'pending' }
|
871
|
+
]
|
872
|
+
}
|
873
|
+
]
|
874
|
+
|
875
|
+
const rows = [
|
876
|
+
{
|
877
|
+
id: 1,
|
878
|
+
name: 'John Doe',
|
879
|
+
email: 'john@example.com',
|
880
|
+
age: 30,
|
881
|
+
status: 'active',
|
882
|
+
checked: false // Campo requerido para checkboxes
|
883
|
+
},
|
884
|
+
// ... más filas
|
885
|
+
]
|
886
|
+
|
887
|
+
const handleCheckboxSelection = (rowId, row) => {
|
888
|
+
setCheckedRows(prev => ({
|
889
|
+
...prev,
|
890
|
+
[rowId]: !prev[rowId]
|
891
|
+
}))
|
892
|
+
}
|
893
|
+
|
894
|
+
const handleSelectAll = (checked) => {
|
895
|
+
setAllChecked(checked)
|
896
|
+
if (checked) {
|
897
|
+
const allIds = {}
|
898
|
+
rows.forEach(row => {
|
899
|
+
allIds[row.id] = true
|
900
|
+
})
|
901
|
+
setCheckedRows(allIds)
|
902
|
+
} else {
|
903
|
+
setCheckedRows({})
|
904
|
+
}
|
905
|
+
}
|
906
|
+
|
907
|
+
<DataTable2
|
908
|
+
columns={columns}
|
909
|
+
rows={rowsWithCheckboxes}
|
910
|
+
onSelect={handleCheckboxSelection}
|
911
|
+
showSelectAll={true}
|
912
|
+
allChecked={allChecked}
|
913
|
+
onCheckAll={handleSelectAll}
|
914
|
+
density="normal"
|
915
|
+
striped={true}
|
916
|
+
resizable={true}
|
917
|
+
/>`}
|
918
|
+
/>
|
919
|
+
</ExampleSubsection>
|
920
|
+
|
921
|
+
<ExampleSubsection id="row-selection" title="Selección por Click">
|
497
922
|
<div style={{
|
498
|
-
background: '#
|
923
|
+
background: '#fff',
|
924
|
+
padding: '1rem',
|
925
|
+
border: '1px solid #ddd',
|
926
|
+
borderRadius: '8px',
|
927
|
+
marginBottom: '1rem'
|
928
|
+
}}>
|
929
|
+
<DataTable2
|
930
|
+
id="row-selection-table"
|
931
|
+
columns={columns.slice(0, 4)}
|
932
|
+
rows={rows.slice(0, 3)}
|
933
|
+
onSelect={handleRowSelection}
|
934
|
+
selectedRows={selectedRows}
|
935
|
+
density="comfortable"
|
936
|
+
hover={true}
|
937
|
+
bordered={true}
|
938
|
+
/>
|
939
|
+
</div>
|
940
|
+
|
941
|
+
<div style={{
|
942
|
+
background: '#f8f9fa',
|
499
943
|
padding: '1rem',
|
500
944
|
borderRadius: '4px',
|
501
|
-
border: '1px solid #
|
945
|
+
border: '1px solid #e9ecef',
|
946
|
+
marginBottom: '1rem'
|
502
947
|
}}>
|
503
|
-
<
|
504
|
-
<
|
505
|
-
|
506
|
-
<li>PropTypes completos y validación</li>
|
507
|
-
<li>Búsqueda integrada en tiempo real</li>
|
508
|
-
<li>Exportación CSV automática/custom</li>
|
509
|
-
<li>Virtual scrolling para performance</li>
|
510
|
-
<li>Múltiples temas y variantes</li>
|
511
|
-
<li>Estados loading y skeleton</li>
|
512
|
-
<li>Accesibilidad total (WCAG 2.1 AA)</li>
|
513
|
-
<li>Selección avanzada (single/multiple)</li>
|
514
|
-
<li>Columnas redimensionables</li>
|
515
|
-
<li>CSS responsive con dark mode</li>
|
516
|
-
<li>Optimizado para miles de filas</li>
|
517
|
-
</ul>
|
948
|
+
<strong>Filas seleccionadas:</strong> {selectedRows.length}
|
949
|
+
<br />
|
950
|
+
<strong>IDs:</strong> {selectedRows.join(', ') || 'Ninguna'}
|
518
951
|
</div>
|
519
|
-
</div>
|
520
|
-
</section>
|
521
952
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
953
|
+
<CodeSnippet
|
954
|
+
language="javascript"
|
955
|
+
code={`const handleRowSelection = (rowId, row) => {
|
956
|
+
setSelectedRows(prev => {
|
957
|
+
if (prev.includes(rowId)) {
|
958
|
+
return prev.filter(id => id !== rowId)
|
959
|
+
} else {
|
960
|
+
return [...prev, rowId]
|
961
|
+
}
|
962
|
+
})
|
963
|
+
}
|
964
|
+
|
965
|
+
<DataTable2
|
966
|
+
columns={columns}
|
967
|
+
rows={rows}
|
968
|
+
onSelect={handleRowSelection}
|
969
|
+
selectedRows={selectedRows}
|
970
|
+
density="comfortable"
|
971
|
+
hover={true}
|
972
|
+
bordered={true}
|
973
|
+
/>`}
|
974
|
+
/>
|
975
|
+
</ExampleSubsection>
|
541
976
|
|
977
|
+
<ExampleSubsection id="table-js-compatibility" title="Compatibilidad con table.js">
|
542
978
|
<div style={{
|
543
|
-
background: '#
|
979
|
+
background: '#fff3cd',
|
544
980
|
padding: '1rem',
|
545
981
|
borderRadius: '4px',
|
546
|
-
|
547
|
-
|
982
|
+
border: '1px solid #ffeaa7',
|
983
|
+
marginBottom: '1rem'
|
548
984
|
}}>
|
549
|
-
<
|
550
|
-
<
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
<li><strong>Opcional:</strong> Agrega nuevas props cuando las necesites</li>
|
555
|
-
</ol>
|
985
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#856404' }}>🔄 100% Compatible con table.js</h5>
|
986
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#856404' }}>
|
987
|
+
DataTable2 es completamente compatible con la API de table.js. Puedes usar la columna especial <code>id="checked"</code>
|
988
|
+
y el prop <code>onRowSelection</code> exactamente como en la versión original.
|
989
|
+
</p>
|
556
990
|
</div>
|
557
|
-
</div>
|
558
|
-
</section>
|
559
991
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
992
|
+
<div style={{
|
993
|
+
background: '#fff',
|
994
|
+
padding: '1rem',
|
995
|
+
border: '1px solid #ddd',
|
996
|
+
borderRadius: '8px',
|
997
|
+
marginBottom: '1rem'
|
998
|
+
}}>
|
999
|
+
<DataTable2
|
1000
|
+
id="table-js-compatibility-example"
|
1001
|
+
columns={[
|
1002
|
+
{
|
1003
|
+
id: 'checked',
|
1004
|
+
label: '',
|
1005
|
+
type: 'CHECK',
|
1006
|
+
sortable: false,
|
1007
|
+
width: 50,
|
1008
|
+
onChange: (rowId, fieldId, value) => {
|
1009
|
+
console.log('table.js style onChange:', rowId, fieldId, value)
|
1010
|
+
setCheckedRows(prev => ({
|
1011
|
+
...prev,
|
1012
|
+
[rowId]: value
|
1013
|
+
}))
|
1014
|
+
}
|
1015
|
+
},
|
1016
|
+
...columns.slice(0, 4)
|
1017
|
+
]}
|
1018
|
+
rows={rows.map(row => ({
|
1019
|
+
...row,
|
1020
|
+
checked: checkedRows[row.id] || false
|
1021
|
+
}))}
|
1022
|
+
onRowSelection={(row, event) => {
|
1023
|
+
console.log('table.js style onRowSelection:', row, event)
|
1024
|
+
handleRowSelection(row.id, row)
|
1025
|
+
}}
|
1026
|
+
onCheckAll={(ids, checked) => {
|
1027
|
+
console.log('table.js style onCheckAll:', ids, checked)
|
1028
|
+
if (checked) {
|
1029
|
+
const allIds = {}
|
1030
|
+
ids.forEach(id => {
|
1031
|
+
allIds[id] = true
|
1032
|
+
})
|
1033
|
+
setCheckedRows(allIds)
|
1034
|
+
} else {
|
1035
|
+
setCheckedRows({})
|
1036
|
+
}
|
1037
|
+
setAllChecked(checked)
|
1038
|
+
}}
|
1039
|
+
density="normal"
|
1040
|
+
striped={true}
|
1041
|
+
/>
|
1042
|
+
</div>
|
1043
|
+
|
1044
|
+
<CodeSnippet
|
1045
|
+
language="javascript"
|
1046
|
+
code={`// Configuración 100% compatible con table.js
|
1047
|
+
const columns = [
|
1048
|
+
{
|
1049
|
+
id: 'checked', // Columna especial detectada automáticamente
|
1050
|
+
label: '',
|
1051
|
+
type: 'CHECK',
|
1052
|
+
sortable: false,
|
1053
|
+
width: 50,
|
1054
|
+
onChange: (rowId, fieldId, value) => {
|
1055
|
+
// Manejo de cambios en checkboxes individuales
|
1056
|
+
console.log('Checkbox changed:', rowId, fieldId, value)
|
1057
|
+
}
|
1058
|
+
},
|
1059
|
+
{
|
1060
|
+
id: 'name',
|
1061
|
+
label: 'Name',
|
1062
|
+
type: 'String',
|
1063
|
+
sortable: true,
|
1064
|
+
// ... resto de configuración
|
1065
|
+
}
|
1066
|
+
]
|
1067
|
+
|
1068
|
+
const rows = [
|
1069
|
+
{
|
1070
|
+
id: 1,
|
1071
|
+
name: 'John Doe',
|
1072
|
+
checked: false, // Estado del checkbox
|
1073
|
+
checkDisabled: false // Opcional: deshabilitar checkbox
|
1074
|
+
},
|
1075
|
+
// ... más filas
|
1076
|
+
]
|
1077
|
+
|
1078
|
+
<DataTable2
|
1079
|
+
columns={columns}
|
1080
|
+
rows={rows}
|
1081
|
+
onRowSelection={(row, event) => {
|
1082
|
+
// Compatible con table.js - se llama al hacer click en la fila
|
1083
|
+
console.log('Row selected:', row)
|
1084
|
+
}}
|
1085
|
+
onCheckAll={(ids, checked) => {
|
1086
|
+
// Compatible con table.js - se llama al usar "Seleccionar todo"
|
1087
|
+
console.log('Check all:', ids, checked)
|
1088
|
+
}}
|
1089
|
+
/>`}
|
1090
|
+
/>
|
1091
|
+
</ExampleSubsection>
|
1092
|
+
|
1093
|
+
<ExampleSubsection id="bulk-actions" title="Acciones en Lote">
|
1094
|
+
<div style={{
|
1095
|
+
display: 'flex',
|
1096
|
+
gap: '1rem',
|
1097
|
+
marginBottom: '1rem',
|
1098
|
+
padding: '1rem',
|
1099
|
+
background: '#f8f9fa',
|
1100
|
+
borderRadius: '4px',
|
1101
|
+
alignItems: 'center'
|
1102
|
+
}}>
|
1103
|
+
<span style={{ fontWeight: 'bold' }}>
|
1104
|
+
Seleccionados: {Object.keys(checkedRows).filter(id => checkedRows[id]).length}
|
1105
|
+
</span>
|
1106
|
+
<Button
|
1107
|
+
label="🗑️ Eliminar"
|
1108
|
+
variant="danger"
|
1109
|
+
size="small"
|
1110
|
+
disabled={Object.keys(checkedRows).filter(id => checkedRows[id]).length === 0}
|
1111
|
+
action={() => handleBulkAction('delete')}
|
1112
|
+
/>
|
1113
|
+
<Button
|
1114
|
+
label="✅ Activar"
|
1115
|
+
variant="primary"
|
1116
|
+
size="small"
|
1117
|
+
disabled={Object.keys(checkedRows).filter(id => checkedRows[id]).length === 0}
|
1118
|
+
action={() => handleBulkAction('activate')}
|
1119
|
+
/>
|
1120
|
+
<Button
|
1121
|
+
label="📤 Exportar"
|
1122
|
+
variant="secondary"
|
1123
|
+
size="small"
|
1124
|
+
disabled={Object.keys(checkedRows).filter(id => checkedRows[id]).length === 0}
|
1125
|
+
action={() => handleBulkAction('export')}
|
1126
|
+
/>
|
1127
|
+
</div>
|
1128
|
+
|
1129
|
+
<div style={{
|
1130
|
+
background: '#fff',
|
1131
|
+
padding: '1rem',
|
1132
|
+
border: '1px solid #ddd',
|
1133
|
+
borderRadius: '8px',
|
1134
|
+
marginBottom: '1rem'
|
1135
|
+
}}>
|
1136
|
+
<DataTable2
|
1137
|
+
id="bulk-actions-table"
|
1138
|
+
columns={columns}
|
1139
|
+
rows={rowsWithCheckboxes}
|
1140
|
+
onSelect={handleCheckboxSelection}
|
1141
|
+
showSelectAll={true}
|
1142
|
+
allChecked={allChecked}
|
1143
|
+
onCheckAll={handleSelectAll}
|
1144
|
+
density="compact"
|
1145
|
+
striped={true}
|
1146
|
+
filterable={true}
|
1147
|
+
/>
|
1148
|
+
</div>
|
1149
|
+
|
1150
|
+
<CodeSnippet
|
1151
|
+
language="javascript"
|
1152
|
+
code={`const handleBulkAction = (action) => {
|
1153
|
+
const selectedIds = Object.keys(checkedRows).filter(id => checkedRows[id])
|
1154
|
+
|
1155
|
+
switch (action) {
|
1156
|
+
case 'delete':
|
1157
|
+
alert(\`Eliminar \${selectedIds.length} elementos seleccionados\`)
|
1158
|
+
break
|
1159
|
+
case 'activate':
|
1160
|
+
alert(\`Activar \${selectedIds.length} elementos seleccionados\`)
|
1161
|
+
break
|
1162
|
+
case 'export':
|
1163
|
+
alert(\`Exportar \${selectedIds.length} elementos seleccionados\`)
|
1164
|
+
break
|
1165
|
+
}
|
1166
|
+
}
|
1167
|
+
|
1168
|
+
// Barra de acciones
|
1169
|
+
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
|
1170
|
+
<span>Seleccionados: {Object.keys(checkedRows).filter(id => checkedRows[id]).length}</span>
|
1171
|
+
<Button
|
1172
|
+
label="Eliminar"
|
1173
|
+
variant="danger"
|
1174
|
+
disabled={Object.keys(checkedRows).filter(id => checkedRows[id]).length === 0}
|
1175
|
+
action={() => handleBulkAction('delete')}
|
1176
|
+
/>
|
1177
|
+
<Button
|
1178
|
+
label="Activar"
|
1179
|
+
variant="primary"
|
1180
|
+
disabled={Object.keys(checkedRows).filter(id => checkedRows[id]).length === 0}
|
1181
|
+
action={() => handleBulkAction('activate')}
|
1182
|
+
/>
|
1183
|
+
</div>
|
1184
|
+
|
1185
|
+
<DataTable2
|
1186
|
+
columns={columns}
|
1187
|
+
rows={rowsWithCheckboxes}
|
1188
|
+
onSelect={handleCheckboxSelection}
|
1189
|
+
showSelectAll={true}
|
1190
|
+
allChecked={allChecked}
|
1191
|
+
onCheckAll={handleSelectAll}
|
1192
|
+
filterable={true}
|
1193
|
+
/>`}
|
1194
|
+
/>
|
1195
|
+
</ExampleSubsection>
|
1196
|
+
</ExampleSection>
|
1197
|
+
|
1198
|
+
<ExampleSection id="expandable-rows" title="Filas Expandibles" icon="expand_more">
|
1199
|
+
<ExampleSubsection id="info-react-component" title="Info como Componente React">
|
1200
|
+
<div style={{
|
1201
|
+
background: '#fff',
|
1202
|
+
padding: '1rem',
|
1203
|
+
border: '1px solid #ddd',
|
1204
|
+
borderRadius: '8px',
|
1205
|
+
marginBottom: '1rem'
|
1206
|
+
}}>
|
1207
|
+
<DataTable2
|
1208
|
+
id="expandable-react-demo"
|
1209
|
+
columns={[
|
1210
|
+
{ id: 'id', label: 'ID', width: 60 },
|
1211
|
+
{ id: 'name', label: 'Usuario', sortable: true },
|
1212
|
+
{ id: 'email', label: 'Email', sortable: true },
|
1213
|
+
{ id: 'status', label: 'Estado', filterable: true }
|
1214
|
+
]}
|
1215
|
+
rows={rows.filter(row => typeof row.info === 'object' && row.info.content)}
|
1216
|
+
expanded={true}
|
1217
|
+
density="normal"
|
1218
|
+
striped={true}
|
1219
|
+
resizable={true}
|
1220
|
+
/>
|
1221
|
+
</div>
|
1222
|
+
|
1223
|
+
<CodeSnippet
|
1224
|
+
title="Código de Info como Componente React"
|
1225
|
+
code={`const columns = [
|
1226
|
+
{
|
1227
|
+
id: 'id',
|
1228
|
+
label: 'ID',
|
1229
|
+
type: 'Number',
|
1230
|
+
sortable: true,
|
1231
|
+
resizable: true,
|
1232
|
+
width: 80
|
1233
|
+
},
|
1234
|
+
{
|
1235
|
+
id: 'name',
|
1236
|
+
label: 'Name',
|
1237
|
+
type: 'String',
|
1238
|
+
sortable: true,
|
1239
|
+
filterable: true,
|
1240
|
+
resizable: true
|
1241
|
+
},
|
1242
|
+
{
|
1243
|
+
id: 'email',
|
1244
|
+
label: 'Email',
|
1245
|
+
type: 'String',
|
1246
|
+
sortable: true,
|
1247
|
+
filterable: true,
|
1248
|
+
resizable: true
|
1249
|
+
},
|
1250
|
+
{
|
1251
|
+
id: 'status',
|
1252
|
+
label: 'Status',
|
1253
|
+
type: 'String',
|
1254
|
+
sortable: true,
|
1255
|
+
filterable: true,
|
1256
|
+
resizable: true
|
1257
|
+
}
|
1258
|
+
]
|
1259
|
+
|
1260
|
+
const rows = [
|
1261
|
+
{
|
1262
|
+
id: 1,
|
1263
|
+
name: 'John Doe',
|
1264
|
+
email: 'john@example.com',
|
1265
|
+
status: 'active',
|
1266
|
+
info: {
|
1267
|
+
action: () => console.log('Expandir usuario 1'),
|
1268
|
+
content: (
|
1269
|
+
<div style={{ padding: '1rem', background: '#f8f9fa', borderRadius: '4px' }}>
|
1270
|
+
<h5 style={{ margin: '0 0 0.5rem 0' }}>Información del Usuario</h5>
|
1271
|
+
<p style={{ margin: '0', fontSize: '0.875rem' }}>
|
1272
|
+
Usuario administrador con acceso completo al sistema.
|
1273
|
+
Última conexión: hace 2 horas.
|
1274
|
+
</p>
|
1275
|
+
</div>
|
1276
|
+
)
|
1277
|
+
}
|
1278
|
+
},
|
1279
|
+
{
|
1280
|
+
id: 2,
|
1281
|
+
name: 'Jane Smith',
|
1282
|
+
email: 'jane@example.com',
|
1283
|
+
status: 'pending',
|
1284
|
+
info: 'Información simple como string'
|
1285
|
+
}
|
1286
|
+
// ... más filas
|
1287
|
+
]
|
1288
|
+
|
1289
|
+
<DataTable2
|
1290
|
+
columns={columns}
|
1291
|
+
rows={rows}
|
1292
|
+
expanded={true} // ← Filas expandidas por defecto
|
1293
|
+
density="normal"
|
1294
|
+
striped={true}
|
1295
|
+
resizable={true}
|
1296
|
+
/>`}
|
1297
|
+
/>
|
1298
|
+
</ExampleSubsection>
|
1299
|
+
|
1300
|
+
<ExampleSubsection id="info-string-simple" title="Info como String Simple">
|
1301
|
+
<div style={{
|
1302
|
+
background: '#fff',
|
1303
|
+
padding: '1rem',
|
1304
|
+
border: '1px solid #ddd',
|
1305
|
+
borderRadius: '8px',
|
1306
|
+
marginBottom: '1rem'
|
1307
|
+
}}>
|
1308
|
+
<DataTable2
|
1309
|
+
id="expandable-string-demo"
|
1310
|
+
columns={[
|
1311
|
+
{ id: 'id', label: 'ID', width: 60 },
|
1312
|
+
{ id: 'name', label: 'Usuario', sortable: true },
|
1313
|
+
{ id: 'email', label: 'Email', sortable: true },
|
1314
|
+
{ id: 'status', label: 'Estado', filterable: true }
|
1315
|
+
]}
|
1316
|
+
rows={rows.filter(row => typeof row.info === 'string')}
|
1317
|
+
expanded={true}
|
1318
|
+
density="comfortable"
|
1319
|
+
bordered={true}
|
1320
|
+
resizable={true}
|
1321
|
+
/>
|
1322
|
+
</div>
|
1323
|
+
|
1324
|
+
<CodeSnippet
|
1325
|
+
title="Código de Info como String Simple"
|
1326
|
+
code={`const rows = [
|
1327
|
+
{
|
1328
|
+
id: 2,
|
1329
|
+
name: 'Jane Smith',
|
1330
|
+
email: 'jane@example.com',
|
1331
|
+
status: 'pending',
|
1332
|
+
info: 'Usuario nuevo pendiente de verificación. Se requiere aprobación manual.'
|
1333
|
+
}
|
1334
|
+
]
|
1335
|
+
|
1336
|
+
<DataTable2
|
1337
|
+
columns={columns}
|
1338
|
+
rows={rows}
|
1339
|
+
expanded={true}
|
1340
|
+
density="comfortable"
|
1341
|
+
bordered={true}
|
1342
|
+
resizable={true}
|
1343
|
+
/>`}
|
1344
|
+
/>
|
1345
|
+
</ExampleSubsection>
|
1346
|
+
|
1347
|
+
<ExampleSubsection id="info-custom-actions" title="Info con Acciones Personalizadas">
|
1348
|
+
<div style={{
|
1349
|
+
background: '#fff',
|
1350
|
+
padding: '1rem',
|
1351
|
+
border: '1px solid #ddd',
|
1352
|
+
borderRadius: '8px',
|
1353
|
+
marginBottom: '1rem'
|
1354
|
+
}}>
|
1355
|
+
<DataTable2
|
1356
|
+
id="expandable-actions-demo"
|
1357
|
+
columns={[
|
1358
|
+
{ id: 'id', label: 'ID', width: 60 },
|
1359
|
+
{ id: 'name', label: 'Usuario', sortable: true },
|
1360
|
+
{ id: 'email', label: 'Email', sortable: true },
|
1361
|
+
{ id: 'status', label: 'Estado', filterable: true }
|
1362
|
+
]}
|
1363
|
+
rows={[rows[2]]} // Bob Johnson con cuenta inactiva
|
1364
|
+
expanded={true}
|
1365
|
+
density="normal"
|
1366
|
+
theme="default"
|
1367
|
+
resizable={true}
|
1368
|
+
/>
|
1369
|
+
</div>
|
1370
|
+
|
1371
|
+
<CodeSnippet
|
1372
|
+
title="Código de Info con Acciones Personalizadas"
|
1373
|
+
code={`const rows = [
|
1374
|
+
{
|
1375
|
+
id: 3,
|
1376
|
+
name: 'Bob Johnson',
|
1377
|
+
email: 'bob@example.com',
|
1378
|
+
status: 'inactive',
|
1379
|
+
info: {
|
1380
|
+
action: () => console.log('Expandir usuario 3'),
|
1381
|
+
content: (
|
1382
|
+
<div style={{ padding: '1rem', background: '#fff3cd', border: '1px solid #ffeaa7' }}>
|
1383
|
+
<h5 style={{ color: '#856404' }}>⚠️ Cuenta Inactiva</h5>
|
1384
|
+
<p style={{ color: '#856404' }}>
|
1385
|
+
Cuenta suspendida por inactividad. Último acceso: hace 6 meses.
|
1386
|
+
</p>
|
608
1387
|
</div>
|
609
|
-
|
1388
|
+
)
|
1389
|
+
}
|
1390
|
+
}
|
1391
|
+
]
|
1392
|
+
|
1393
|
+
<DataTable2
|
1394
|
+
columns={columns}
|
1395
|
+
rows={rows}
|
1396
|
+
expanded={true}
|
1397
|
+
density="normal"
|
1398
|
+
resizable={true}
|
1399
|
+
/>`}
|
1400
|
+
/>
|
1401
|
+
</ExampleSubsection>
|
1402
|
+
</ExampleSection>
|
1403
|
+
|
1404
|
+
<ExampleSection id="themes-variants" title="Temas y Variantes" icon="palette">
|
1405
|
+
<ExampleSubsection id="density-variants" title="Variantes de Densidad">
|
1406
|
+
<div style={{ display: 'grid', gap: '1rem', marginBottom: '1rem' }}>
|
1407
|
+
<div style={{ background: '#fff', padding: '1rem', border: '1px solid #ddd', borderRadius: '8px' }}>
|
1408
|
+
<h4>Compact</h4>
|
1409
|
+
<DataTable2
|
1410
|
+
id="density-compact-demo"
|
1411
|
+
columns={columns.slice(0, 4)}
|
1412
|
+
rows={rows.slice(0, 2)}
|
1413
|
+
density="compact"
|
1414
|
+
striped={true}
|
1415
|
+
/>
|
1416
|
+
</div>
|
1417
|
+
|
1418
|
+
<div style={{ background: '#fff', padding: '1rem', border: '1px solid #ddd', borderRadius: '8px' }}>
|
1419
|
+
<h4>Normal</h4>
|
1420
|
+
<DataTable2
|
1421
|
+
id="density-normal-demo"
|
1422
|
+
columns={columns.slice(0, 4)}
|
1423
|
+
rows={rows.slice(0, 2)}
|
1424
|
+
density="normal"
|
1425
|
+
striped={true}
|
1426
|
+
/>
|
1427
|
+
</div>
|
1428
|
+
|
1429
|
+
<div style={{ background: '#fff', padding: '1rem', border: '1px solid #ddd', borderRadius: '8px' }}>
|
1430
|
+
<h4>Comfortable</h4>
|
1431
|
+
<DataTable2
|
1432
|
+
id="density-comfortable-demo"
|
1433
|
+
columns={columns.slice(0, 4)}
|
1434
|
+
rows={rows.slice(0, 2)}
|
1435
|
+
density="comfortable"
|
1436
|
+
striped={true}
|
1437
|
+
/>
|
1438
|
+
</div>
|
1439
|
+
</div>
|
1440
|
+
|
1441
|
+
<CodeSnippet
|
1442
|
+
title="Código de Variantes de Densidad"
|
1443
|
+
code={`const columns = [
|
1444
|
+
{
|
1445
|
+
id: 'id',
|
1446
|
+
label: 'ID',
|
1447
|
+
type: 'Number',
|
1448
|
+
sortable: true,
|
1449
|
+
resizable: true,
|
1450
|
+
width: 80
|
1451
|
+
},
|
1452
|
+
{
|
1453
|
+
id: 'name',
|
1454
|
+
label: 'Name',
|
1455
|
+
type: 'String',
|
1456
|
+
sortable: true,
|
1457
|
+
filterable: true,
|
1458
|
+
resizable: true
|
1459
|
+
},
|
1460
|
+
{
|
1461
|
+
id: 'email',
|
1462
|
+
label: 'Email',
|
1463
|
+
type: 'String',
|
1464
|
+
sortable: true,
|
1465
|
+
filterable: true,
|
1466
|
+
resizable: true
|
1467
|
+
},
|
1468
|
+
{
|
1469
|
+
id: 'status',
|
1470
|
+
label: 'Status',
|
1471
|
+
type: 'String',
|
1472
|
+
sortable: true,
|
1473
|
+
filterable: true,
|
1474
|
+
resizable: true
|
1475
|
+
}
|
1476
|
+
]
|
1477
|
+
|
1478
|
+
// Compact - Para máxima densidad de información
|
1479
|
+
<DataTable2
|
1480
|
+
columns={columns}
|
1481
|
+
rows={rows}
|
1482
|
+
density="compact"
|
1483
|
+
striped={true}
|
1484
|
+
/>
|
1485
|
+
|
1486
|
+
// Normal - Balance perfecto
|
1487
|
+
<DataTable2
|
1488
|
+
columns={columns}
|
1489
|
+
rows={rows}
|
1490
|
+
density="normal"
|
1491
|
+
striped={true}
|
1492
|
+
/>
|
1493
|
+
|
1494
|
+
// Comfortable - Para mejor legibilidad
|
1495
|
+
<DataTable2
|
1496
|
+
columns={columns}
|
1497
|
+
rows={rows}
|
1498
|
+
density="comfortable"
|
1499
|
+
striped={true}
|
1500
|
+
/>`}
|
1501
|
+
/>
|
1502
|
+
</ExampleSubsection>
|
1503
|
+
|
1504
|
+
<ExampleSubsection id="theme-variants" title="Variantes de Tema">
|
1505
|
+
<div style={{ display: 'grid', gap: '1rem', marginBottom: '1rem' }}>
|
1506
|
+
<div style={{ background: '#fff', padding: '1rem', border: '1px solid #ddd', borderRadius: '8px' }}>
|
1507
|
+
<h4>Default Theme</h4>
|
1508
|
+
<DataTable2
|
1509
|
+
id="theme-default-demo"
|
1510
|
+
columns={columns.slice(0, 4)}
|
1511
|
+
rows={rows.slice(0, 2)}
|
1512
|
+
theme="default"
|
1513
|
+
striped={true}
|
1514
|
+
/>
|
1515
|
+
</div>
|
1516
|
+
|
1517
|
+
<div style={{ background: '#2c1810', padding: '1rem', border: '1px solid #5d4037', borderRadius: '8px' }}>
|
1518
|
+
<h4 style={{ color: '#f4e6d7' }}>Dark Theme</h4>
|
1519
|
+
<DataTable2
|
1520
|
+
id="theme-dark-demo"
|
1521
|
+
columns={columns.slice(0, 4)}
|
1522
|
+
rows={rows.slice(0, 2)}
|
1523
|
+
theme="dark"
|
1524
|
+
striped={true}
|
1525
|
+
/>
|
1526
|
+
</div>
|
610
1527
|
|
611
|
-
|
612
|
-
|
613
|
-
|
1528
|
+
<div style={{ background: '#fff', padding: '1rem', border: '1px solid #f0f0f0', borderRadius: '8px' }}>
|
1529
|
+
<h4>Minimal Theme</h4>
|
1530
|
+
<DataTable2
|
1531
|
+
id="theme-minimal-demo"
|
1532
|
+
columns={columns.slice(0, 4)}
|
1533
|
+
rows={rows.slice(0, 2)}
|
1534
|
+
theme="minimal"
|
1535
|
+
hover={false}
|
1536
|
+
/>
|
1537
|
+
</div>
|
1538
|
+
</div>
|
1539
|
+
|
1540
|
+
<CodeSnippet
|
1541
|
+
title="Código de Variantes de Tema"
|
1542
|
+
code={`const columns = [
|
1543
|
+
{
|
1544
|
+
id: 'id',
|
1545
|
+
label: 'ID',
|
1546
|
+
type: 'Number',
|
1547
|
+
sortable: true,
|
1548
|
+
resizable: true,
|
1549
|
+
width: 80
|
1550
|
+
},
|
1551
|
+
{
|
1552
|
+
id: 'name',
|
1553
|
+
label: 'Name',
|
1554
|
+
type: 'String',
|
1555
|
+
sortable: true,
|
1556
|
+
filterable: true,
|
1557
|
+
resizable: true
|
1558
|
+
},
|
1559
|
+
{
|
1560
|
+
id: 'email',
|
1561
|
+
label: 'Email',
|
1562
|
+
type: 'String',
|
1563
|
+
sortable: true,
|
1564
|
+
filterable: true,
|
1565
|
+
resizable: true
|
1566
|
+
},
|
1567
|
+
{
|
1568
|
+
id: 'status',
|
1569
|
+
label: 'Status',
|
1570
|
+
type: 'String',
|
1571
|
+
sortable: true,
|
1572
|
+
filterable: true,
|
1573
|
+
resizable: true
|
1574
|
+
}
|
1575
|
+
]
|
1576
|
+
|
1577
|
+
// Default - Colores claros y profesionales
|
1578
|
+
<DataTable2
|
1579
|
+
columns={columns}
|
1580
|
+
rows={rows}
|
1581
|
+
theme="default"
|
1582
|
+
striped={true}
|
1583
|
+
/>
|
1584
|
+
|
1585
|
+
// Dark - Paleta tostada elegante
|
1586
|
+
<DataTable2
|
1587
|
+
columns={columns}
|
1588
|
+
rows={rows}
|
1589
|
+
theme="dark"
|
1590
|
+
striped={true}
|
1591
|
+
/>
|
1592
|
+
|
1593
|
+
// Minimal - Sin sombras, bordes sutiles
|
1594
|
+
<DataTable2
|
1595
|
+
columns={columns}
|
1596
|
+
rows={rows}
|
1597
|
+
theme="minimal"
|
1598
|
+
hover={false}
|
1599
|
+
striped={true}
|
1600
|
+
/>`}
|
1601
|
+
/>
|
1602
|
+
</ExampleSubsection>
|
1603
|
+
</ExampleSection>
|
1604
|
+
|
1605
|
+
<ExampleSection id="readability-variants" title="Variantes de Legibilidad" icon="visibility">
|
614
1606
|
<div style={{
|
615
|
-
background: '#
|
1607
|
+
background: '#e8f5e8',
|
616
1608
|
padding: '1rem',
|
617
|
-
|
618
|
-
|
1609
|
+
borderRadius: '4px',
|
1610
|
+
border: '1px solid #4caf50',
|
1611
|
+
marginBottom: '2rem'
|
1612
|
+
}}>
|
1613
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#2e7d32' }}>🎯 Optimizado para Legibilidad</h5>
|
1614
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#2e7d32' }}>
|
1615
|
+
Estas variantes están diseñadas con principios de tipografía, accesibilidad y usabilidad para
|
1616
|
+
maximizar la legibilidad en diferentes contextos y necesidades específicas.
|
1617
|
+
</p>
|
1618
|
+
</div>
|
1619
|
+
|
1620
|
+
<ExampleSubsection id="readable-standard" title="Legibilidad Estándar">
|
1621
|
+
<div style={{
|
1622
|
+
background: '#fff',
|
1623
|
+
padding: '1rem',
|
1624
|
+
border: '1px solid #ddd',
|
1625
|
+
borderRadius: '8px',
|
1626
|
+
marginBottom: '1rem'
|
1627
|
+
}}>
|
1628
|
+
<DataTable2
|
1629
|
+
id="readable-standard-demo"
|
1630
|
+
columns={columns.slice(0, 4)}
|
1631
|
+
rows={rows.slice(0, 3)}
|
1632
|
+
className="datatable2--readable"
|
1633
|
+
density="normal"
|
1634
|
+
striped={true}
|
1635
|
+
/>
|
1636
|
+
</div>
|
1637
|
+
|
1638
|
+
<CodeSnippet
|
1639
|
+
title="Legibilidad Estándar"
|
1640
|
+
code={`<DataTable2
|
1641
|
+
columns={columns}
|
1642
|
+
rows={rows}
|
1643
|
+
className="datatable2--readable" // ← Tipografía optimizada
|
1644
|
+
density="normal"
|
1645
|
+
striped={true}
|
1646
|
+
/>`}
|
1647
|
+
/>
|
1648
|
+
</ExampleSubsection>
|
1649
|
+
|
1650
|
+
<ExampleSubsection id="readable-large" title="Texto Grande">
|
1651
|
+
<div style={{
|
1652
|
+
background: '#fff',
|
1653
|
+
padding: '1rem',
|
1654
|
+
border: '1px solid #ddd',
|
1655
|
+
borderRadius: '8px',
|
1656
|
+
marginBottom: '1rem'
|
1657
|
+
}}>
|
1658
|
+
<DataTable2
|
1659
|
+
id="readable-large-demo"
|
1660
|
+
columns={columns.slice(0, 4)}
|
1661
|
+
rows={rows.slice(0, 3)}
|
1662
|
+
readability="large"
|
1663
|
+
density="comfortable"
|
1664
|
+
striped={true}
|
1665
|
+
/>
|
1666
|
+
</div>
|
1667
|
+
|
1668
|
+
<CodeSnippet
|
1669
|
+
title="Texto Grande para Accesibilidad"
|
1670
|
+
code={`<DataTable2
|
1671
|
+
columns={columns}
|
1672
|
+
rows={rows}
|
1673
|
+
readability="large" // ← Texto más grande (16px)
|
1674
|
+
density="comfortable" // ← Más espacio
|
1675
|
+
striped={true}
|
1676
|
+
/>`}
|
1677
|
+
/>
|
1678
|
+
</ExampleSubsection>
|
1679
|
+
|
1680
|
+
<ExampleSubsection id="readable-contrast" title="Alto Contraste">
|
1681
|
+
<div style={{
|
1682
|
+
background: '#fff',
|
1683
|
+
padding: '1rem',
|
1684
|
+
border: '2px solid #000',
|
1685
|
+
borderRadius: '8px',
|
1686
|
+
marginBottom: '1rem'
|
1687
|
+
}}>
|
1688
|
+
<DataTable2
|
1689
|
+
id="readable-contrast-demo"
|
1690
|
+
columns={columns.slice(0, 4)}
|
1691
|
+
rows={rows.slice(0, 3)}
|
1692
|
+
readability="contrast"
|
1693
|
+
density="normal"
|
1694
|
+
striped={true}
|
1695
|
+
/>
|
1696
|
+
</div>
|
1697
|
+
|
1698
|
+
<CodeSnippet
|
1699
|
+
title="Alto Contraste para Visibilidad"
|
1700
|
+
code={`<DataTable2
|
1701
|
+
columns={columns}
|
1702
|
+
rows={rows}
|
1703
|
+
readability="contrast" // ← Colores de alto contraste
|
1704
|
+
density="normal"
|
1705
|
+
striped={true}
|
1706
|
+
/>`}
|
1707
|
+
/>
|
1708
|
+
</ExampleSubsection>
|
1709
|
+
|
1710
|
+
<ExampleSubsection id="readable-dyslexia" title="Optimizado para Dislexia">
|
1711
|
+
<div style={{
|
1712
|
+
background: '#fff',
|
1713
|
+
padding: '1rem',
|
1714
|
+
border: '1px solid #ddd',
|
1715
|
+
borderRadius: '8px',
|
1716
|
+
marginBottom: '1rem'
|
1717
|
+
}}>
|
1718
|
+
<DataTable2
|
1719
|
+
id="readable-dyslexia-demo"
|
1720
|
+
columns={columns.slice(0, 4)}
|
1721
|
+
rows={rows.slice(0, 3)}
|
1722
|
+
readability="dyslexia"
|
1723
|
+
density="comfortable"
|
1724
|
+
striped={true}
|
1725
|
+
/>
|
1726
|
+
</div>
|
1727
|
+
|
1728
|
+
<CodeSnippet
|
1729
|
+
title="Optimizado para Dislexia"
|
1730
|
+
code={`<DataTable2
|
1731
|
+
columns={columns}
|
1732
|
+
rows={rows}
|
1733
|
+
readability="dyslexia" // ← Fuente y espaciado especial
|
1734
|
+
density="comfortable" // ← Más espacio entre líneas
|
1735
|
+
striped={true}
|
1736
|
+
/>`}
|
1737
|
+
/>
|
1738
|
+
</ExampleSubsection>
|
1739
|
+
|
1740
|
+
<ExampleSubsection id="readable-focus" title="Modo Enfoque">
|
1741
|
+
<div style={{
|
1742
|
+
background: '#fff',
|
1743
|
+
padding: '1rem',
|
1744
|
+
border: '1px solid #ddd',
|
1745
|
+
borderRadius: '8px',
|
1746
|
+
marginBottom: '1rem'
|
1747
|
+
}}>
|
1748
|
+
<DataTable2
|
1749
|
+
id="readable-focus-demo"
|
1750
|
+
columns={columns.slice(0, 4)}
|
1751
|
+
rows={rows.slice(0, 3)}
|
1752
|
+
readability="focus"
|
1753
|
+
density="normal"
|
1754
|
+
hover={true}
|
1755
|
+
/>
|
1756
|
+
</div>
|
1757
|
+
|
1758
|
+
<CodeSnippet
|
1759
|
+
title="Modo Enfoque - Reduce Distracciones"
|
1760
|
+
code={`<DataTable2
|
1761
|
+
columns={columns}
|
1762
|
+
rows={rows}
|
1763
|
+
readability="focus" // ← Reduce elementos visuales
|
1764
|
+
density="normal"
|
1765
|
+
hover={true} // ← Hover sutil
|
1766
|
+
/>`}
|
1767
|
+
/>
|
1768
|
+
</ExampleSubsection>
|
1769
|
+
|
1770
|
+
<ExampleSubsection id="readable-dark" title="Modo Oscuro Legible">
|
1771
|
+
<div style={{
|
1772
|
+
background: '#1a202c',
|
1773
|
+
padding: '1rem',
|
1774
|
+
border: '1px solid #2d3748',
|
1775
|
+
borderRadius: '8px',
|
1776
|
+
marginBottom: '1rem'
|
1777
|
+
}}>
|
1778
|
+
<DataTable2
|
1779
|
+
id="readable-dark-demo"
|
1780
|
+
columns={columns.slice(0, 4)}
|
1781
|
+
rows={rows.slice(0, 3)}
|
1782
|
+
readability="dark"
|
1783
|
+
density="normal"
|
1784
|
+
striped={true}
|
1785
|
+
/>
|
1786
|
+
</div>
|
1787
|
+
|
1788
|
+
<CodeSnippet
|
1789
|
+
title="Modo Oscuro con Legibilidad Optimizada"
|
1790
|
+
code={`<DataTable2
|
1791
|
+
columns={columns}
|
1792
|
+
rows={rows}
|
1793
|
+
readability="dark" // ← Modo oscuro legible
|
1794
|
+
density="normal"
|
1795
|
+
striped={true}
|
1796
|
+
/>`}
|
1797
|
+
/>
|
1798
|
+
</ExampleSubsection>
|
1799
|
+
|
1800
|
+
<div style={{
|
1801
|
+
background: '#f8f9fa',
|
1802
|
+
padding: '1.5rem',
|
1803
|
+
borderRadius: '8px',
|
1804
|
+
border: '1px solid #e9ecef',
|
1805
|
+
marginTop: '2rem'
|
1806
|
+
}}>
|
1807
|
+
<h4 style={{ margin: '0 0 1rem 0', color: '#495057' }}>🎨 Características de las Variantes de Legibilidad</h4>
|
1808
|
+
|
1809
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '1rem' }}>
|
1810
|
+
<div>
|
1811
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>📖 Standard</h5>
|
1812
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
1813
|
+
<li>Tipografía optimizada (Inter, SF Pro)</li>
|
1814
|
+
<li>Espaciado mejorado (line-height 1.6)</li>
|
1815
|
+
<li>Colores de alto contraste</li>
|
1816
|
+
<li>Bordes redondeados suaves</li>
|
1817
|
+
</ul>
|
1818
|
+
</div>
|
1819
|
+
|
1820
|
+
<div>
|
1821
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🔍 Large</h5>
|
1822
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
1823
|
+
<li>Texto 16px (vs 15px estándar)</li>
|
1824
|
+
<li>Padding aumentado 20%</li>
|
1825
|
+
<li>Ideal para accesibilidad</li>
|
1826
|
+
<li>Mejor para pantallas grandes</li>
|
1827
|
+
</ul>
|
1828
|
+
</div>
|
1829
|
+
|
1830
|
+
<div>
|
1831
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>⚫ Contrast</h5>
|
1832
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
1833
|
+
<li>Negro puro sobre blanco</li>
|
1834
|
+
<li>Bordes más gruesos</li>
|
1835
|
+
<li>Cumple WCAG AAA</li>
|
1836
|
+
<li>Ideal para baja visión</li>
|
1837
|
+
</ul>
|
1838
|
+
</div>
|
1839
|
+
|
1840
|
+
<div>
|
1841
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🧠 Dyslexia</h5>
|
1842
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
1843
|
+
<li>Fuente OpenDyslexic</li>
|
1844
|
+
<li>Espaciado de letras aumentado</li>
|
1845
|
+
<li>Line-height 1.8</li>
|
1846
|
+
<li>Filas alternadas marcadas</li>
|
1847
|
+
</ul>
|
1848
|
+
</div>
|
1849
|
+
|
1850
|
+
<div>
|
1851
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🎯 Focus</h5>
|
1852
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
1853
|
+
<li>Bordes mínimos</li>
|
1854
|
+
<li>Hover con elevación</li>
|
1855
|
+
<li>Reduce distracciones visuales</li>
|
1856
|
+
<li>Enfoque en contenido</li>
|
1857
|
+
</ul>
|
1858
|
+
</div>
|
1859
|
+
|
1860
|
+
<div>
|
1861
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🌙 Dark</h5>
|
1862
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
1863
|
+
<li>Fondo oscuro suave</li>
|
1864
|
+
<li>Texto claro optimizado</li>
|
1865
|
+
<li>Reduce fatiga ocular</li>
|
1866
|
+
<li>Ideal para uso nocturno</li>
|
1867
|
+
</ul>
|
1868
|
+
</div>
|
1869
|
+
</div>
|
1870
|
+
</div>
|
1871
|
+
</ExampleSection>
|
1872
|
+
|
1873
|
+
<ExampleSection id="advanced-features" title="Funcionalidades Avanzadas" icon="tune">
|
1874
|
+
<ExampleSubsection id="sidebar-column-visibility" title="Toolbar Vertical y Panel de Columnas">
|
1875
|
+
<div style={{
|
1876
|
+
background: '#e3f2fd',
|
1877
|
+
padding: '1rem',
|
1878
|
+
borderRadius: '4px',
|
1879
|
+
border: '1px solid #2196f3',
|
1880
|
+
marginBottom: '1rem'
|
1881
|
+
}}>
|
1882
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#1565c0' }}>🎛️ Toolbar Multi-Herramientas</h5>
|
1883
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#1565c0' }}>
|
1884
|
+
Observa la toolbar vertical en el lado derecho con múltiples herramientas. Haz clic en cualquier botón
|
1885
|
+
(📋 Columnas, 📥 Exportar, 🔍 Filtros, 📊 Custom) para abrir su panel correspondiente. Solo una herramienta puede estar activa a la vez.
|
1886
|
+
</p>
|
1887
|
+
</div>
|
1888
|
+
|
1889
|
+
<div style={{
|
1890
|
+
background: '#fff',
|
1891
|
+
padding: '1rem',
|
1892
|
+
border: '1px solid #ddd',
|
1893
|
+
borderRadius: '8px',
|
1894
|
+
marginBottom: '1rem'
|
1895
|
+
}}>
|
1896
|
+
<DataTable2
|
1897
|
+
id="sidebar-demo"
|
1898
|
+
sidebarId="example-sidebar" // ID personalizable para localStorage
|
1899
|
+
columns={columns}
|
1900
|
+
rows={rows}
|
1901
|
+
showSidebar={true}
|
1902
|
+
showRowNumbers={true}
|
1903
|
+
sidebarTools={[
|
1904
|
+
|
1905
|
+
{
|
1906
|
+
id: "custom-analytics",
|
1907
|
+
icon: "person",
|
1908
|
+
title: "Análisis Personalizado",
|
1909
|
+
component: CustomAnalyticsPanel
|
1910
|
+
}
|
1911
|
+
]}
|
1912
|
+
density="normal"
|
1913
|
+
striped={true}
|
1914
|
+
resizable={true}
|
1915
|
+
/>
|
1916
|
+
</div>
|
1917
|
+
|
1918
|
+
<CodeSnippet
|
1919
|
+
title="Toolbar Vertical con Panel de Columnas"
|
1920
|
+
code={`const columns = [
|
1921
|
+
{
|
1922
|
+
id: 'id',
|
1923
|
+
label: 'ID',
|
1924
|
+
type: 'Number',
|
1925
|
+
sortable: true,
|
1926
|
+
resizable: true,
|
1927
|
+
width: 80
|
1928
|
+
},
|
1929
|
+
{
|
1930
|
+
id: 'name',
|
1931
|
+
label: 'Name',
|
1932
|
+
type: 'String',
|
1933
|
+
sortable: true,
|
1934
|
+
filterable: true,
|
1935
|
+
resizable: true
|
1936
|
+
},
|
1937
|
+
{
|
1938
|
+
id: 'email',
|
1939
|
+
label: 'Email',
|
1940
|
+
type: 'String',
|
1941
|
+
sortable: true,
|
1942
|
+
filterable: true,
|
1943
|
+
resizable: true
|
1944
|
+
},
|
1945
|
+
{
|
1946
|
+
id: 'age',
|
1947
|
+
label: 'Age',
|
1948
|
+
type: 'Number',
|
1949
|
+
sortable: true,
|
1950
|
+
filterable: true,
|
1951
|
+
resizable: true
|
1952
|
+
},
|
1953
|
+
{
|
1954
|
+
id: 'status',
|
1955
|
+
label: 'Status',
|
1956
|
+
type: 'String',
|
1957
|
+
sortable: true,
|
1958
|
+
filterable: true,
|
1959
|
+
resizable: true,
|
1960
|
+
options: [
|
1961
|
+
{ label: 'Active', value: 'active' },
|
1962
|
+
{ label: 'Inactive', value: 'inactive' },
|
1963
|
+
{ label: 'Pending', value: 'pending' }
|
1964
|
+
]
|
1965
|
+
}
|
1966
|
+
]
|
1967
|
+
|
1968
|
+
<DataTable2
|
1969
|
+
id="sidebar-demo"
|
1970
|
+
sidebarId="example-sidebar" // ← ID personalizable para localStorage
|
1971
|
+
columns={columns}
|
1972
|
+
rows={rows}
|
1973
|
+
showSidebar={true} // ← Habilita toolbar multi-herramientas
|
1974
|
+
showRowNumbers={true} // ← Muestra números de fila
|
1975
|
+
sidebarTools={[ // ← Herramientas personalizadas (opcional)
|
1976
|
+
{
|
1977
|
+
id: 'custom-analytics',
|
1978
|
+
icon: 'person',
|
1979
|
+
title: 'Análisis personalizado',
|
1980
|
+
component: 'analytics'
|
1981
|
+
},
|
1982
|
+
{
|
1983
|
+
id: 'custom-settings',
|
1984
|
+
icon: 'person',
|
1985
|
+
title: 'Configuración avanzada',
|
1986
|
+
component: 'settings'
|
1987
|
+
}
|
1988
|
+
]}
|
1989
|
+
density="normal"
|
1990
|
+
striped={true}
|
1991
|
+
resizable={true}
|
1992
|
+
/>`}
|
1993
|
+
/>
|
1994
|
+
</ExampleSubsection>
|
1995
|
+
|
1996
|
+
<ExampleSubsection id="row-numbers-only" title="Solo Números de Fila">
|
1997
|
+
<div style={{
|
1998
|
+
background: '#fff',
|
1999
|
+
padding: '1rem',
|
2000
|
+
border: '1px solid #ddd',
|
2001
|
+
borderRadius: '8px',
|
2002
|
+
marginBottom: '1rem'
|
2003
|
+
}}>
|
2004
|
+
<DataTable2
|
2005
|
+
id="row-numbers-demo"
|
2006
|
+
columns={columns.slice(0, 4)}
|
2007
|
+
rows={rows.slice(0, 5)}
|
2008
|
+
showRowNumbers={true}
|
2009
|
+
density="compact"
|
2010
|
+
striped={true}
|
2011
|
+
/>
|
2012
|
+
</div>
|
2013
|
+
|
2014
|
+
<CodeSnippet
|
2015
|
+
title="Tabla con Números de Fila"
|
2016
|
+
code={`<DataTable2
|
2017
|
+
columns={columns}
|
2018
|
+
rows={rows}
|
2019
|
+
showRowNumbers={true} // ← Solo números de fila
|
2020
|
+
density="compact"
|
2021
|
+
striped={true}
|
2022
|
+
/>`}
|
2023
|
+
/>
|
2024
|
+
</ExampleSubsection>
|
2025
|
+
|
2026
|
+
<div style={{
|
2027
|
+
background: '#f8f9fa',
|
2028
|
+
padding: '1.5rem',
|
2029
|
+
borderRadius: '8px',
|
2030
|
+
border: '1px solid #e9ecef',
|
2031
|
+
marginTop: '2rem'
|
619
2032
|
}}>
|
620
|
-
<
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
<
|
2033
|
+
<h4 style={{ margin: '0 0 1rem 0', color: '#495057' }}>🎛️ Características del Sistema Toolbar + Panel</h4>
|
2034
|
+
|
2035
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: '1rem' }}>
|
2036
|
+
<div>
|
2037
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🔧 Toolbar Vertical</h5>
|
2038
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
2039
|
+
<li>Integrada en el lado derecho</li>
|
2040
|
+
<li>Botones compactos y elegantes</li>
|
2041
|
+
<li>Estados activo/inactivo visuales</li>
|
2042
|
+
<li>Extensible para más herramientas</li>
|
2043
|
+
</ul>
|
2044
|
+
</div>
|
2045
|
+
|
2046
|
+
<div>
|
2047
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>📋 Panel Deslizable</h5>
|
2048
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
2049
|
+
<li>Se desliza entre toolbar y tabla</li>
|
2050
|
+
<li>No overlay - ajusta el layout</li>
|
2051
|
+
<li>Animación suave hacia la izquierda</li>
|
2052
|
+
<li>Header sticky con título y cerrar</li>
|
2053
|
+
</ul>
|
625
2054
|
</div>
|
626
|
-
|
627
|
-
|
628
|
-
<
|
629
|
-
<
|
2055
|
+
|
2056
|
+
<div>
|
2057
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>👁️ Control de Columnas</h5>
|
2058
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
2059
|
+
<li>Mostrar/ocultar individualmente</li>
|
2060
|
+
<li>Contador dinámico (visible/total)</li>
|
2061
|
+
<li>Etiquetas de tipo de columna</li>
|
2062
|
+
<li>Persistencia en localStorage</li>
|
2063
|
+
</ul>
|
2064
|
+
</div>
|
2065
|
+
|
2066
|
+
<div>
|
2067
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🔢 Números de Fila</h5>
|
2068
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
2069
|
+
<li>Columna fija en el lado izquierdo</li>
|
2070
|
+
<li>Numeración automática (1, 2, 3...)</li>
|
2071
|
+
<li>Fuente tabular para alineación</li>
|
2072
|
+
<li>Responsive en dispositivos móviles</li>
|
2073
|
+
</ul>
|
2074
|
+
</div>
|
2075
|
+
|
2076
|
+
<div>
|
2077
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>📱 Responsive</h5>
|
2078
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
2079
|
+
<li>Toolbar se oculta en móviles</li>
|
2080
|
+
<li>Panel se convierte en overlay</li>
|
2081
|
+
<li>Adaptación automática del layout</li>
|
2082
|
+
<li>Touch-friendly en dispositivos táctiles</li>
|
2083
|
+
</ul>
|
630
2084
|
</div>
|
631
|
-
|
632
|
-
|
633
|
-
<
|
634
|
-
<
|
2085
|
+
|
2086
|
+
<div>
|
2087
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#6c757d' }}>🎨 Integración</h5>
|
2088
|
+
<ul style={{ margin: '0', paddingLeft: '1.2rem', fontSize: '0.875rem' }}>
|
2089
|
+
<li>Compatible con todos los temas</li>
|
2090
|
+
<li>Animaciones fluidas (cubic-bezier)</li>
|
2091
|
+
<li>Estados visuales consistentes</li>
|
2092
|
+
<li>Accesibilidad completa (ARIA)</li>
|
2093
|
+
</ul>
|
635
2094
|
</div>
|
636
2095
|
</div>
|
637
2096
|
</div>
|
638
|
-
</
|
2097
|
+
</ExampleSection>
|
639
2098
|
|
640
|
-
|
641
|
-
<section style={{ marginBottom: '2rem' }}>
|
642
|
-
<h3>📚 API Reference - Nuevas Props</h3>
|
2099
|
+
<ExampleSection id="loading-states" title="Estados de Carga" icon="hourglass_empty">
|
643
2100
|
<div style={{
|
644
2101
|
background: '#fff',
|
645
2102
|
padding: '1rem',
|
646
2103
|
border: '1px solid #ddd',
|
647
|
-
borderRadius: '8px'
|
2104
|
+
borderRadius: '8px',
|
2105
|
+
marginBottom: '1rem'
|
648
2106
|
}}>
|
649
|
-
<
|
2107
|
+
<DataTable2
|
2108
|
+
id="loading-demo"
|
2109
|
+
columns={columns}
|
2110
|
+
rows={rows}
|
2111
|
+
loading={isLoading}
|
2112
|
+
skeleton={isLoading}
|
2113
|
+
density="normal"
|
2114
|
+
/>
|
2115
|
+
</div>
|
2116
|
+
|
2117
|
+
<CodeSnippet
|
2118
|
+
title="Código de Estados de Carga"
|
2119
|
+
code={`const columns = [
|
2120
|
+
{
|
2121
|
+
id: 'id',
|
2122
|
+
label: 'ID',
|
2123
|
+
type: 'Number',
|
2124
|
+
sortable: true,
|
2125
|
+
resizable: true,
|
2126
|
+
width: 80
|
2127
|
+
},
|
2128
|
+
{
|
2129
|
+
id: 'name',
|
2130
|
+
label: 'Name',
|
2131
|
+
type: 'String',
|
2132
|
+
sortable: true,
|
2133
|
+
filterable: true,
|
2134
|
+
resizable: true
|
2135
|
+
},
|
2136
|
+
{
|
2137
|
+
id: 'email',
|
2138
|
+
label: 'Email',
|
2139
|
+
type: 'String',
|
2140
|
+
sortable: true,
|
2141
|
+
filterable: true,
|
2142
|
+
resizable: true
|
2143
|
+
},
|
2144
|
+
{
|
2145
|
+
id: 'status',
|
2146
|
+
label: 'Status',
|
2147
|
+
type: 'String',
|
2148
|
+
sortable: true,
|
2149
|
+
filterable: true,
|
2150
|
+
resizable: true
|
2151
|
+
}
|
2152
|
+
]
|
2153
|
+
|
2154
|
+
const [isLoading, setIsLoading] = useState(false)
|
2155
|
+
|
2156
|
+
const simulateLoading = () => {
|
2157
|
+
setIsLoading(true)
|
2158
|
+
setTimeout(() => setIsLoading(false), 3000)
|
2159
|
+
}
|
2160
|
+
|
2161
|
+
<DataTable2
|
2162
|
+
columns={columns}
|
2163
|
+
rows={rows}
|
2164
|
+
loading={isLoading} // ← Muestra spinner de carga
|
2165
|
+
skeleton={isLoading} // ← Muestra skeleton loading
|
2166
|
+
density="normal"
|
2167
|
+
striped={true}
|
2168
|
+
/>
|
2169
|
+
|
2170
|
+
<Button
|
2171
|
+
label="Simular Carga"
|
2172
|
+
action={simulateLoading}
|
2173
|
+
variant="primary"
|
2174
|
+
/>`}
|
2175
|
+
/>
|
2176
|
+
</ExampleSection>
|
2177
|
+
|
2178
|
+
<ExampleSection id="api-reference" title="Referencia API" icon="code">
|
2179
|
+
<div style={{
|
2180
|
+
background: '#fff',
|
2181
|
+
padding: '1.5rem',
|
2182
|
+
borderRadius: '8px',
|
2183
|
+
border: '1px solid #ddd'
|
2184
|
+
}}>
|
2185
|
+
<h4>Props principales del componente DataTable2:</h4>
|
2186
|
+
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.875rem' }}>
|
650
2187
|
<thead>
|
651
2188
|
<tr style={{ background: '#f8f9fa' }}>
|
652
2189
|
<th style={{ padding: '0.5rem', border: '1px solid #ddd', textAlign: 'left' }}>Prop</th>
|
@@ -657,46 +2194,934 @@ export const DataTable2Examples = () => {
|
|
657
2194
|
</thead>
|
658
2195
|
<tbody>
|
659
2196
|
<tr>
|
660
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>
|
661
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
662
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
663
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
2197
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>columns</code></td>
|
2198
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>array</td>
|
2199
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>[]</td>
|
2200
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Definición de columnas</td>
|
664
2201
|
</tr>
|
665
2202
|
<tr>
|
666
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>
|
667
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
668
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
669
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
670
|
-
</tr>
|
671
|
-
<tr>
|
672
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>virtualScrolling</code></td>
|
673
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
|
674
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
|
675
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Habilita virtual scrolling para performance</td>
|
2203
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>rows</code></td>
|
2204
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>array</td>
|
2205
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>[]</td>
|
2206
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Datos de las filas</td>
|
676
2207
|
</tr>
|
677
2208
|
<tr>
|
678
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>
|
2209
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>density</code></td>
|
679
2210
|
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>string</td>
|
680
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
681
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
2211
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>normal</td>
|
2212
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Densidad: compact, normal, comfortable</td>
|
682
2213
|
</tr>
|
683
2214
|
<tr>
|
684
2215
|
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>theme</code></td>
|
685
2216
|
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>string</td>
|
686
2217
|
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>default</td>
|
687
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Tema
|
2218
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Tema: default, dark, minimal</td>
|
688
2219
|
</tr>
|
689
2220
|
<tr>
|
690
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>
|
691
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
692
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
693
|
-
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>
|
2221
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>expanded</code></td>
|
2222
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
|
2223
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
|
2224
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Filas expandidas por defecto</td>
|
2225
|
+
</tr>
|
2226
|
+
<tr>
|
2227
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>resizable</code></td>
|
2228
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
|
2229
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
|
2230
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Columnas redimensionables</td>
|
2231
|
+
</tr>
|
2232
|
+
<tr>
|
2233
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>editable</code></td>
|
2234
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
|
2235
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
|
2236
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Celdas editables</td>
|
2237
|
+
</tr>
|
2238
|
+
<tr>
|
2239
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}><code>filterable</code></td>
|
2240
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
|
2241
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
|
2242
|
+
<td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Filtros por columna</td>
|
694
2243
|
</tr>
|
695
2244
|
</tbody>
|
696
2245
|
</table>
|
697
2246
|
</div>
|
698
|
-
</
|
699
|
-
|
2247
|
+
</ExampleSection>
|
2248
|
+
|
2249
|
+
{/* Ejemplo con datos masivos */}
|
2250
|
+
<ExampleSection title="📊 Rendimiento con Datos Masivos" id="massive-data">
|
2251
|
+
<div style={{
|
2252
|
+
background: '#fff3cd',
|
2253
|
+
padding: '1rem',
|
2254
|
+
borderRadius: '4px',
|
2255
|
+
border: '1px solid #ffeaa7',
|
2256
|
+
marginBottom: '1rem'
|
2257
|
+
}}>
|
2258
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#856404' }}>🚀 Tabla con 5,000 Filas</h5>
|
2259
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#856404' }}>
|
2260
|
+
Esta tabla demuestra el rendimiento con grandes volúmenes de datos. Incluye scroll virtual,
|
2261
|
+
búsqueda eficiente y ordenamiento optimizado. La altura se adapta automáticamente al contenedor.
|
2262
|
+
</p>
|
2263
|
+
</div>
|
2264
|
+
|
2265
|
+
{(() => {
|
2266
|
+
// Función para generar datos masivos
|
2267
|
+
const generateMassiveData = (count) => {
|
2268
|
+
const departments = ['IT', 'Marketing', 'Ventas', 'HR', 'Finanzas', 'Operaciones', 'Legal', 'Diseño', 'Producto', 'Soporte']
|
2269
|
+
const statuses = ['Activo', 'Inactivo', 'Pendiente', 'Suspendido']
|
2270
|
+
const firstNames = ['Juan', 'María', 'Carlos', 'Ana', 'Luis', 'Carmen', 'José', 'Laura', 'Miguel', 'Elena', 'David', 'Sara', 'Pedro', 'Lucía', 'Antonio', 'Isabel', 'Francisco', 'Patricia', 'Manuel', 'Rosa']
|
2271
|
+
const lastNames = ['García', 'Rodríguez', 'González', 'Fernández', 'López', 'Martínez', 'Sánchez', 'Pérez', 'Gómez', 'Martín', 'Jiménez', 'Ruiz', 'Hernández', 'Díaz', 'Moreno', 'Muñoz', 'Álvarez', 'Romero', 'Alonso', 'Gutiérrez']
|
2272
|
+
|
2273
|
+
const data = []
|
2274
|
+
for (let i = 1; i <= count; i++) {
|
2275
|
+
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]
|
2276
|
+
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]
|
2277
|
+
const department = departments[Math.floor(Math.random() * departments.length)]
|
2278
|
+
const status = statuses[Math.floor(Math.random() * statuses.length)]
|
2279
|
+
const age = Math.floor(Math.random() * 40) + 22 // 22-62 años
|
2280
|
+
const salary = Math.floor(Math.random() * 80000) + 30000 // 30k-110k
|
2281
|
+
const joinYear = Math.floor(Math.random() * 5) + 2019 // 2019-2023
|
2282
|
+
const joinMonth = Math.floor(Math.random() * 12) + 1
|
2283
|
+
const joinDay = Math.floor(Math.random() * 28) + 1
|
2284
|
+
|
2285
|
+
data.push({
|
2286
|
+
id: i,
|
2287
|
+
name: `${firstName} ${lastName}`,
|
2288
|
+
email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}${i}@company.com`,
|
2289
|
+
age,
|
2290
|
+
status,
|
2291
|
+
department,
|
2292
|
+
salary,
|
2293
|
+
joinDate: `${joinYear}-${joinMonth.toString().padStart(2, '0')}-${joinDay.toString().padStart(2, '0')}`
|
2294
|
+
})
|
2295
|
+
}
|
2296
|
+
return data
|
2297
|
+
}
|
2298
|
+
|
2299
|
+
const massiveColumns = [
|
2300
|
+
{ id: 'id', label: 'ID', type: 'number', sortable: true, width: 80 },
|
2301
|
+
{ id: 'name', label: 'Nombre', type: 'text', sortable: true, filterable: true },
|
2302
|
+
{ id: 'email', label: 'Email', type: 'email', sortable: true, filterable: true },
|
2303
|
+
{ id: 'age', label: 'Edad', type: 'number', sortable: true, filterable: true },
|
2304
|
+
{ id: 'status', label: 'Estado', type: 'text', sortable: true, filterable: true },
|
2305
|
+
{ id: 'department', label: 'Departamento', type: 'text', sortable: true, filterable: true },
|
2306
|
+
{ id: 'salary', label: 'Salario', type: 'currency', sortable: true, filterable: true },
|
2307
|
+
{ id: 'joinDate', label: 'Fecha Ingreso', type: 'date', sortable: true, filterable: true }
|
2308
|
+
]
|
2309
|
+
|
2310
|
+
const massiveRows = generateMassiveData(5000)
|
2311
|
+
|
2312
|
+
return (
|
2313
|
+
<div style={{ height: '500px' }}>
|
2314
|
+
<DataTable2
|
2315
|
+
id="massive-data-table"
|
2316
|
+
columns={massiveColumns}
|
2317
|
+
rows={massiveRows}
|
2318
|
+
showSidebar={true}
|
2319
|
+
showRowNumbers={true}
|
2320
|
+
searchable={true}
|
2321
|
+
filterable={true}
|
2322
|
+
striped={true}
|
2323
|
+
resizable={true}
|
2324
|
+
density="compact"
|
2325
|
+
height="100%"
|
2326
|
+
sidebarTools={[
|
2327
|
+
{
|
2328
|
+
id: "analytics",
|
2329
|
+
icon: "person",
|
2330
|
+
title: "Análisis de Datos",
|
2331
|
+
component: CustomAnalyticsPanel
|
2332
|
+
}
|
2333
|
+
]}
|
2334
|
+
/>
|
2335
|
+
</div>
|
2336
|
+
)
|
2337
|
+
})()}
|
2338
|
+
|
2339
|
+
<CodeSnippet
|
2340
|
+
title="Tabla con Datos Masivos y Altura Adaptable"
|
2341
|
+
code={`// Generar datos masivos
|
2342
|
+
const generateMassiveData = (count) => {
|
2343
|
+
const departments = ['IT', 'Marketing', 'Ventas', 'HR', 'Finanzas']
|
2344
|
+
const statuses = ['Activo', 'Inactivo', 'Pendiente', 'Suspendido']
|
2345
|
+
const firstNames = ['Juan', 'María', 'Carlos', 'Ana', 'Luis']
|
2346
|
+
const lastNames = ['García', 'Rodríguez', 'González', 'Fernández', 'López']
|
2347
|
+
|
2348
|
+
const data = []
|
2349
|
+
for (let i = 1; i <= count; i++) {
|
2350
|
+
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]
|
2351
|
+
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]
|
2352
|
+
|
2353
|
+
data.push({
|
2354
|
+
id: i,
|
2355
|
+
name: \`\${firstName} \${lastName}\`,
|
2356
|
+
email: \`\${firstName.toLowerCase()}.\${lastName.toLowerCase()}\${i}@company.com\`,
|
2357
|
+
age: Math.floor(Math.random() * 40) + 22,
|
2358
|
+
status: statuses[Math.floor(Math.random() * statuses.length)],
|
2359
|
+
department: departments[Math.floor(Math.random() * departments.length)],
|
2360
|
+
salary: Math.floor(Math.random() * 80000) + 30000,
|
2361
|
+
joinDate: \`2023-\${Math.floor(Math.random() * 12) + 1}-\${Math.floor(Math.random() * 28) + 1}\`
|
2362
|
+
})
|
2363
|
+
}
|
2364
|
+
return data
|
2365
|
+
}
|
2366
|
+
|
2367
|
+
const massiveRows = generateMassiveData(5000)
|
2368
|
+
|
2369
|
+
// Contenedor con altura fija
|
2370
|
+
<div style={{ height: '500px' }}>
|
2371
|
+
<DataTable2
|
2372
|
+
columns={columns}
|
2373
|
+
rows={massiveRows}
|
2374
|
+
height="100%" // ← Se adapta al contenedor
|
2375
|
+
showSidebar={true}
|
2376
|
+
searchable={true}
|
2377
|
+
filterable={true}
|
2378
|
+
density="compact" // ← Más filas visibles
|
2379
|
+
/>
|
2380
|
+
</div>
|
2381
|
+
|
2382
|
+
// Sin contenedor - altura automática
|
2383
|
+
<DataTable2
|
2384
|
+
columns={columns}
|
2385
|
+
rows={massiveRows}
|
2386
|
+
// height no especificado = altura automática
|
2387
|
+
maxHeight="400px" // ← Altura máxima opcional
|
2388
|
+
showSidebar={true}
|
2389
|
+
/>`}
|
2390
|
+
/>
|
2391
|
+
</ExampleSection>
|
2392
|
+
|
2393
|
+
{/* Ejemplo de configuraciones de altura */}
|
2394
|
+
<ExampleSection title="📏 Configuraciones de Altura" id="height-configurations">
|
2395
|
+
<div style={{
|
2396
|
+
background: '#e8f5e8',
|
2397
|
+
padding: '1rem',
|
2398
|
+
borderRadius: '4px',
|
2399
|
+
border: '1px solid #4caf50',
|
2400
|
+
marginBottom: '1rem'
|
2401
|
+
}}>
|
2402
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#2e7d32' }}>🎯 Control de Altura Flexible</h5>
|
2403
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#2e7d32' }}>
|
2404
|
+
DataTable2 se adapta automáticamente: sin altura = se ajusta al contenido,
|
2405
|
+
con altura = scroll vertical automático. Perfecto para cualquier layout.
|
2406
|
+
</p>
|
2407
|
+
</div>
|
2408
|
+
|
2409
|
+
<ExampleSubsection title="Altura Automática (Pocas Filas)">
|
2410
|
+
<p style={{ fontSize: '0.875rem', color: '#6b7280', marginBottom: '1rem' }}>
|
2411
|
+
Sin especificar altura, la tabla se adapta al contenido. Ideal para pocas filas.
|
2412
|
+
</p>
|
2413
|
+
|
2414
|
+
<DataTable2
|
2415
|
+
id="auto-height-small"
|
2416
|
+
columns={columns.slice(0, 4)}
|
2417
|
+
rows={rows.slice(0, 3)}
|
2418
|
+
striped={true}
|
2419
|
+
showRowNumbers={true}
|
2420
|
+
/>
|
2421
|
+
</ExampleSubsection>
|
2422
|
+
|
2423
|
+
<ExampleSubsection title="Altura Fija con Scroll">
|
2424
|
+
<p style={{ fontSize: '0.875rem', color: '#6b7280', marginBottom: '1rem' }}>
|
2425
|
+
Con altura fija, la tabla tiene scroll vertical automático.
|
2426
|
+
</p>
|
2427
|
+
|
2428
|
+
<DataTable2
|
2429
|
+
id="fixed-height"
|
2430
|
+
columns={columns.slice(0, 4)}
|
2431
|
+
rows={rows}
|
2432
|
+
height="300px"
|
2433
|
+
striped={true}
|
2434
|
+
showRowNumbers={true}
|
2435
|
+
/>
|
2436
|
+
</ExampleSubsection>
|
2437
|
+
|
2438
|
+
<ExampleSubsection title="Altura Máxima">
|
2439
|
+
<p style={{ fontSize: '0.875rem', color: '#6b7280', marginBottom: '1rem' }}>
|
2440
|
+
Con altura máxima, la tabla crece hasta el límite y luego hace scroll.
|
2441
|
+
</p>
|
2442
|
+
|
2443
|
+
<DataTable2
|
2444
|
+
id="max-height"
|
2445
|
+
columns={columns.slice(0, 4)}
|
2446
|
+
rows={rows}
|
2447
|
+
maxHeight="250px"
|
2448
|
+
striped={true}
|
2449
|
+
showRowNumbers={true}
|
2450
|
+
/>
|
2451
|
+
</ExampleSubsection>
|
2452
|
+
|
2453
|
+
<CodeSnippet
|
2454
|
+
title="Configuraciones de Altura"
|
2455
|
+
code={`// Altura automática - se adapta al contenido
|
2456
|
+
<DataTable2
|
2457
|
+
columns={columns}
|
2458
|
+
rows={fewRows}
|
2459
|
+
// Sin height = altura automática
|
2460
|
+
/>
|
2461
|
+
|
2462
|
+
// Altura fija - scroll vertical automático
|
2463
|
+
<DataTable2
|
2464
|
+
columns={columns}
|
2465
|
+
rows={manyRows}
|
2466
|
+
height="400px" // ← Altura fija
|
2467
|
+
/>
|
2468
|
+
|
2469
|
+
// Altura máxima - crece hasta el límite
|
2470
|
+
<DataTable2
|
2471
|
+
columns={columns}
|
2472
|
+
rows={variableRows}
|
2473
|
+
maxHeight="300px" // ← Altura máxima
|
2474
|
+
/>
|
2475
|
+
|
2476
|
+
// Altura mínima - nunca más pequeña
|
2477
|
+
<DataTable2
|
2478
|
+
columns={columns}
|
2479
|
+
rows={fewRows}
|
2480
|
+
minHeight="200px" // ← Altura mínima
|
2481
|
+
/>
|
2482
|
+
|
2483
|
+
// Combinación de restricciones
|
2484
|
+
<DataTable2
|
2485
|
+
columns={columns}
|
2486
|
+
rows={rows}
|
2487
|
+
minHeight="200px"
|
2488
|
+
maxHeight="500px"
|
2489
|
+
// Crece entre 200px y 500px
|
2490
|
+
/>
|
2491
|
+
|
2492
|
+
// Adaptarse al contenedor padre
|
2493
|
+
<div style={{ height: '400px' }}>
|
2494
|
+
<DataTable2
|
2495
|
+
columns={columns}
|
2496
|
+
rows={rows}
|
2497
|
+
height="100%" // ← Se adapta al contenedor
|
2498
|
+
/>
|
2499
|
+
</div>`}
|
2500
|
+
/>
|
2501
|
+
</ExampleSection>
|
2502
|
+
|
2503
|
+
{/* Ejemplo de filtros avanzados */}
|
2504
|
+
<ExampleSection title="🔍 Sistema de Filtros Avanzado" id="advanced-filters">
|
2505
|
+
<div style={{
|
2506
|
+
background: '#f0f9ff',
|
2507
|
+
padding: '1rem',
|
2508
|
+
borderRadius: '4px',
|
2509
|
+
border: '1px solid #0ea5e9',
|
2510
|
+
marginBottom: '1rem'
|
2511
|
+
}}>
|
2512
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#0c4a6e' }}>🎯 Filtros Inteligentes</h5>
|
2513
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#0c4a6e' }}>
|
2514
|
+
Fila de filtros debajo de la cabecera con campos de texto y dropdowns.
|
2515
|
+
Filtrado en tiempo real con botón de limpiar. Compatible con todos los tipos de columna.
|
2516
|
+
</p>
|
2517
|
+
</div>
|
2518
|
+
|
2519
|
+
{(() => {
|
2520
|
+
// Estado para filtros
|
2521
|
+
const [filteredData, setFilteredData] = useState(rows)
|
2522
|
+
const [activeFilters, setActiveFilters] = useState({})
|
2523
|
+
|
2524
|
+
// Función de filtrado
|
2525
|
+
const handleFilter = useCallback((columnId, value) => {
|
2526
|
+
const newFilters = { ...activeFilters, [columnId]: value }
|
2527
|
+
|
2528
|
+
// Remover filtros vacíos
|
2529
|
+
Object.keys(newFilters).forEach(key => {
|
2530
|
+
if (!newFilters[key]) {
|
2531
|
+
delete newFilters[key]
|
2532
|
+
}
|
2533
|
+
})
|
2534
|
+
|
2535
|
+
setActiveFilters(newFilters)
|
2536
|
+
|
2537
|
+
// Aplicar filtros
|
2538
|
+
const filtered = rows.filter(row => {
|
2539
|
+
return Object.keys(newFilters).every(key => {
|
2540
|
+
const filterValue = newFilters[key].toLowerCase()
|
2541
|
+
const cellValue = row[key] ? row[key].toString().toLowerCase() : ''
|
2542
|
+
return cellValue.includes(filterValue)
|
2543
|
+
})
|
2544
|
+
})
|
2545
|
+
|
2546
|
+
setFilteredData(filtered)
|
2547
|
+
}, [activeFilters])
|
2548
|
+
|
2549
|
+
// Limpiar filtros
|
2550
|
+
const handleClearFilters = useCallback(() => {
|
2551
|
+
setActiveFilters({})
|
2552
|
+
setFilteredData(rows)
|
2553
|
+
}, [])
|
2554
|
+
|
2555
|
+
// Columnas con filtros configurados
|
2556
|
+
const filterableColumns = [
|
2557
|
+
{
|
2558
|
+
id: 'id',
|
2559
|
+
label: 'ID',
|
2560
|
+
type: 'number',
|
2561
|
+
sortable: true,
|
2562
|
+
filterable: true,
|
2563
|
+
onFilter: handleFilter
|
2564
|
+
},
|
2565
|
+
{
|
2566
|
+
id: 'name',
|
2567
|
+
label: 'Nombre',
|
2568
|
+
type: 'text',
|
2569
|
+
sortable: true,
|
2570
|
+
filterable: true,
|
2571
|
+
onFilter: handleFilter
|
2572
|
+
},
|
2573
|
+
{
|
2574
|
+
id: 'email',
|
2575
|
+
label: 'Email',
|
2576
|
+
type: 'email',
|
2577
|
+
sortable: true,
|
2578
|
+
filterable: true,
|
2579
|
+
onFilter: handleFilter
|
2580
|
+
},
|
2581
|
+
{
|
2582
|
+
id: 'status',
|
2583
|
+
label: 'Estado',
|
2584
|
+
type: 'text',
|
2585
|
+
sortable: true,
|
2586
|
+
filterable: true,
|
2587
|
+
onFilter: handleFilter,
|
2588
|
+
options: [
|
2589
|
+
{ value: '', label: 'Todos los estados' },
|
2590
|
+
{ value: 'Activo', label: 'Activo' },
|
2591
|
+
{ value: 'Inactivo', label: 'Inactivo' },
|
2592
|
+
{ value: 'Pendiente', label: 'Pendiente' }
|
2593
|
+
]
|
2594
|
+
},
|
2595
|
+
{
|
2596
|
+
id: 'department',
|
2597
|
+
label: 'Departamento',
|
2598
|
+
type: 'text',
|
2599
|
+
sortable: true,
|
2600
|
+
filterable: true,
|
2601
|
+
onFilter: handleFilter,
|
2602
|
+
options: [
|
2603
|
+
{ value: '', label: 'Todos los departamentos' },
|
2604
|
+
{ value: 'IT', label: 'IT' },
|
2605
|
+
{ value: 'Marketing', label: 'Marketing' },
|
2606
|
+
{ value: 'Ventas', label: 'Ventas' },
|
2607
|
+
{ value: 'HR', label: 'HR' }
|
2608
|
+
]
|
2609
|
+
}
|
2610
|
+
]
|
2611
|
+
|
2612
|
+
return (
|
2613
|
+
<div>
|
2614
|
+
<div style={{ marginBottom: '1rem', padding: '1rem', background: '#f8fafc', borderRadius: '6px', border: '1px solid #e2e8f0' }}>
|
2615
|
+
<h6 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem', fontWeight: 'bold' }}>Estado de Filtros:</h6>
|
2616
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#6b7280' }}>
|
2617
|
+
Filtros activos: {Object.keys(activeFilters).length} |
|
2618
|
+
Filas mostradas: {filteredData.length} de {rows.length}
|
2619
|
+
</p>
|
2620
|
+
{Object.keys(activeFilters).length > 0 && (
|
2621
|
+
<div style={{ marginTop: '0.5rem' }}>
|
2622
|
+
<strong style={{ fontSize: '0.8rem' }}>Filtros aplicados:</strong>
|
2623
|
+
{Object.entries(activeFilters).map(([key, value]) => (
|
2624
|
+
<span key={key} style={{
|
2625
|
+
display: 'inline-block',
|
2626
|
+
margin: '0.25rem 0.5rem 0.25rem 0',
|
2627
|
+
padding: '0.25rem 0.5rem',
|
2628
|
+
background: '#dbeafe',
|
2629
|
+
color: '#1e40af',
|
2630
|
+
borderRadius: '12px',
|
2631
|
+
fontSize: '0.75rem'
|
2632
|
+
}}>
|
2633
|
+
{key}: "{value}"
|
2634
|
+
</span>
|
2635
|
+
))}
|
2636
|
+
</div>
|
2637
|
+
)}
|
2638
|
+
</div>
|
2639
|
+
|
2640
|
+
<DataTable2
|
2641
|
+
id="filters-demo"
|
2642
|
+
columns={filterableColumns}
|
2643
|
+
rows={filteredData}
|
2644
|
+
filterable={true}
|
2645
|
+
onClearFilters={handleClearFilters}
|
2646
|
+
showRowNumbers={true}
|
2647
|
+
striped={true}
|
2648
|
+
resizable={true}
|
2649
|
+
maxHeight="400px"
|
2650
|
+
/>
|
2651
|
+
</div>
|
2652
|
+
)
|
2653
|
+
})()}
|
2654
|
+
|
2655
|
+
<CodeSnippet
|
2656
|
+
title="Configuración de Filtros Avanzados"
|
2657
|
+
code={`// Estado para manejar filtros
|
2658
|
+
const [filteredData, setFilteredData] = useState(rows)
|
2659
|
+
const [activeFilters, setActiveFilters] = useState({})
|
2660
|
+
|
2661
|
+
// Función de filtrado
|
2662
|
+
const handleFilter = useCallback((columnId, value) => {
|
2663
|
+
const newFilters = { ...activeFilters, [columnId]: value }
|
2664
|
+
|
2665
|
+
// Remover filtros vacíos
|
2666
|
+
Object.keys(newFilters).forEach(key => {
|
2667
|
+
if (!newFilters[key]) delete newFilters[key]
|
2668
|
+
})
|
2669
|
+
|
2670
|
+
setActiveFilters(newFilters)
|
2671
|
+
|
2672
|
+
// Aplicar filtros
|
2673
|
+
const filtered = rows.filter(row => {
|
2674
|
+
return Object.keys(newFilters).every(key => {
|
2675
|
+
const filterValue = newFilters[key].toLowerCase()
|
2676
|
+
const cellValue = row[key] ? row[key].toString().toLowerCase() : ''
|
2677
|
+
return cellValue.includes(filterValue)
|
2678
|
+
})
|
2679
|
+
})
|
2680
|
+
|
2681
|
+
setFilteredData(filtered)
|
2682
|
+
}, [activeFilters])
|
2683
|
+
|
2684
|
+
// Configuración de columnas con filtros
|
2685
|
+
const filterableColumns = [
|
2686
|
+
{
|
2687
|
+
id: 'name',
|
2688
|
+
label: 'Nombre',
|
2689
|
+
filterable: true,
|
2690
|
+
onFilter: handleFilter // ← Callback de filtro
|
2691
|
+
},
|
2692
|
+
{
|
2693
|
+
id: 'status',
|
2694
|
+
label: 'Estado',
|
2695
|
+
filterable: true,
|
2696
|
+
onFilter: handleFilter,
|
2697
|
+
options: [ // ← Dropdown con opciones
|
2698
|
+
{ value: '', label: 'Todos los estados' },
|
2699
|
+
{ value: 'Activo', label: 'Activo' },
|
2700
|
+
{ value: 'Inactivo', label: 'Inactivo' }
|
2701
|
+
]
|
2702
|
+
}
|
2703
|
+
]
|
2704
|
+
|
2705
|
+
// Uso en DataTable2
|
2706
|
+
<DataTable2
|
2707
|
+
columns={filterableColumns}
|
2708
|
+
rows={filteredData} // ← Datos filtrados
|
2709
|
+
filterable={true} // ← Habilita fila de filtros
|
2710
|
+
onClearFilters={handleClearFilters}
|
2711
|
+
showRowNumbers={true}
|
2712
|
+
/>`}
|
2713
|
+
/>
|
2714
|
+
</ExampleSection>
|
2715
|
+
|
2716
|
+
{/* Ejemplo de sumarios/totales */}
|
2717
|
+
<ExampleSection title="📊 Sistema de Sumarios y Totales" id="summary-system">
|
2718
|
+
<div style={{
|
2719
|
+
background: '#f0f9ff',
|
2720
|
+
padding: '1rem',
|
2721
|
+
borderRadius: '4px',
|
2722
|
+
border: '1px solid #0ea5e9',
|
2723
|
+
marginBottom: '1rem'
|
2724
|
+
}}>
|
2725
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#0c4a6e' }}>📊 Sumarios Inteligentes</h5>
|
2726
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#0c4a6e' }}>
|
2727
|
+
Fila de totales automática en el pie de tabla. Funciones de sumario específicas por tipo de datos:
|
2728
|
+
suma, promedio, conteo, fechas, porcentajes y más. Cálculo dinámico en tiempo real.
|
2729
|
+
</p>
|
2730
|
+
</div>
|
2731
|
+
|
2732
|
+
{(() => {
|
2733
|
+
// Datos de ejemplo con diferentes tipos para sumarios
|
2734
|
+
const summaryData = [
|
2735
|
+
{
|
2736
|
+
id: 1,
|
2737
|
+
producto: 'Laptop Dell',
|
2738
|
+
precio: 899.99,
|
2739
|
+
cantidad: 5,
|
2740
|
+
vendido: true,
|
2741
|
+
fecha: '2024-01-15',
|
2742
|
+
categoria: 'Electrónicos'
|
2743
|
+
},
|
2744
|
+
{
|
2745
|
+
id: 2,
|
2746
|
+
producto: 'Mouse Logitech',
|
2747
|
+
precio: 29.99,
|
2748
|
+
cantidad: 12,
|
2749
|
+
vendido: true,
|
2750
|
+
fecha: '2024-01-16',
|
2751
|
+
categoria: 'Accesorios'
|
2752
|
+
},
|
2753
|
+
{
|
2754
|
+
id: 3,
|
2755
|
+
producto: 'Teclado Mecánico',
|
2756
|
+
precio: 149.99,
|
2757
|
+
cantidad: 8,
|
2758
|
+
vendido: false,
|
2759
|
+
fecha: '2024-01-17',
|
2760
|
+
categoria: 'Accesorios'
|
2761
|
+
},
|
2762
|
+
{
|
2763
|
+
id: 4,
|
2764
|
+
producto: 'Monitor 4K',
|
2765
|
+
precio: 399.99,
|
2766
|
+
cantidad: 3,
|
2767
|
+
vendido: true,
|
2768
|
+
fecha: '2024-01-18',
|
2769
|
+
categoria: 'Electrónicos'
|
2770
|
+
},
|
2771
|
+
{
|
2772
|
+
id: 5,
|
2773
|
+
producto: 'Webcam HD',
|
2774
|
+
precio: 79.99,
|
2775
|
+
cantidad: 15,
|
2776
|
+
vendido: false,
|
2777
|
+
fecha: '2024-01-19',
|
2778
|
+
categoria: 'Accesorios'
|
2779
|
+
}
|
2780
|
+
]
|
2781
|
+
|
2782
|
+
// Columnas con diferentes tipos de sumarios
|
2783
|
+
const summaryColumns = [
|
2784
|
+
{
|
2785
|
+
id: 'id',
|
2786
|
+
label: 'ID',
|
2787
|
+
type: 'number',
|
2788
|
+
summary: { function: 'count' } // Contar elementos
|
2789
|
+
},
|
2790
|
+
{
|
2791
|
+
id: 'producto',
|
2792
|
+
label: 'Producto',
|
2793
|
+
type: 'text',
|
2794
|
+
summary: { function: 'count' } // Contar productos
|
2795
|
+
},
|
2796
|
+
{
|
2797
|
+
id: 'precio',
|
2798
|
+
label: 'Precio',
|
2799
|
+
type: 'currency',
|
2800
|
+
summary: { function: 'sum' } // Suma total de precios
|
2801
|
+
},
|
2802
|
+
{
|
2803
|
+
id: 'cantidad',
|
2804
|
+
label: 'Cantidad',
|
2805
|
+
type: 'number',
|
2806
|
+
summary: { function: 'sum' } // Suma total de cantidades
|
2807
|
+
},
|
2808
|
+
{
|
2809
|
+
id: 'vendido',
|
2810
|
+
label: 'Vendido',
|
2811
|
+
type: 'boolean',
|
2812
|
+
summary: { function: 'percentage' } // Porcentaje vendido
|
2813
|
+
},
|
2814
|
+
{
|
2815
|
+
id: 'fecha',
|
2816
|
+
label: 'Fecha',
|
2817
|
+
type: 'date',
|
2818
|
+
summary: { function: 'latest' } // Fecha más reciente
|
2819
|
+
},
|
2820
|
+
{
|
2821
|
+
id: 'categoria',
|
2822
|
+
label: 'Categoría',
|
2823
|
+
type: 'text',
|
2824
|
+
summary: { function: 'countUnique' } // Categorías únicas
|
2825
|
+
}
|
2826
|
+
]
|
2827
|
+
|
2828
|
+
return (
|
2829
|
+
<div>
|
2830
|
+
<div style={{ marginBottom: '1rem', padding: '1rem', background: '#f8fafc', borderRadius: '6px', border: '1px solid #e2e8f0' }}>
|
2831
|
+
<h6 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem', fontWeight: 'bold' }}>Funciones de Sumario Aplicadas:</h6>
|
2832
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '0.5rem', fontSize: '0.8rem' }}>
|
2833
|
+
<div><strong>ID:</strong> Contar elementos</div>
|
2834
|
+
<div><strong>Producto:</strong> Contar productos</div>
|
2835
|
+
<div><strong>Precio:</strong> Suma total (€)</div>
|
2836
|
+
<div><strong>Cantidad:</strong> Suma total</div>
|
2837
|
+
<div><strong>Vendido:</strong> Porcentaje vendido</div>
|
2838
|
+
<div><strong>Fecha:</strong> Más reciente</div>
|
2839
|
+
<div><strong>Categoría:</strong> Únicas</div>
|
2840
|
+
</div>
|
2841
|
+
</div>
|
2842
|
+
|
2843
|
+
<DataTable2
|
2844
|
+
id="summary-demo"
|
2845
|
+
columns={summaryColumns}
|
2846
|
+
rows={summaryData}
|
2847
|
+
showRowNumbers={true}
|
2848
|
+
striped={true}
|
2849
|
+
bordered={true}
|
2850
|
+
resizable={true}
|
2851
|
+
/>
|
2852
|
+
</div>
|
2853
|
+
)
|
2854
|
+
})()}
|
2855
|
+
|
2856
|
+
<CodeSnippet
|
2857
|
+
title="Configuración de Sumarios por Tipo de Datos"
|
2858
|
+
code={`// Importar funciones de sumario
|
2859
|
+
import {
|
2860
|
+
numericSummaryFunctions,
|
2861
|
+
textSummaryFunctions,
|
2862
|
+
dateSummaryFunctions,
|
2863
|
+
booleanSummaryFunctions
|
2864
|
+
} from './table-summary-functions'
|
2865
|
+
|
2866
|
+
// Configuración de columnas con sumarios
|
2867
|
+
const summaryColumns = [
|
2868
|
+
{
|
2869
|
+
id: 'precio',
|
2870
|
+
label: 'Precio',
|
2871
|
+
type: 'currency',
|
2872
|
+
summary: { function: 'sum' } // ← Suma total de precios
|
2873
|
+
},
|
2874
|
+
{
|
2875
|
+
id: 'cantidad',
|
2876
|
+
label: 'Cantidad',
|
2877
|
+
type: 'number',
|
2878
|
+
summary: { function: 'avg' } // ← Promedio de cantidades
|
2879
|
+
},
|
2880
|
+
{
|
2881
|
+
id: 'vendido',
|
2882
|
+
label: 'Vendido',
|
2883
|
+
type: 'boolean',
|
2884
|
+
summary: { function: 'percentage' } // ← Porcentaje vendido
|
2885
|
+
},
|
2886
|
+
{
|
2887
|
+
id: 'fecha',
|
2888
|
+
label: 'Fecha',
|
2889
|
+
type: 'date',
|
2890
|
+
summary: { function: 'latest' } // ← Fecha más reciente
|
2891
|
+
},
|
2892
|
+
{
|
2893
|
+
id: 'categoria',
|
2894
|
+
label: 'Categoría',
|
2895
|
+
type: 'text',
|
2896
|
+
summary: { function: 'countUnique' } // ← Categorías únicas
|
2897
|
+
}
|
2898
|
+
]
|
2899
|
+
|
2900
|
+
// Funciones disponibles por tipo:
|
2901
|
+
// NUMÉRICAS: sum, avg, min, max
|
2902
|
+
// TEXTO: count, countUnique, mostCommon
|
2903
|
+
// FECHAS: count, earliest, latest
|
2904
|
+
// BOOLEANAS: countTrue, countFalse, percentage
|
2905
|
+
|
2906
|
+
// Uso en DataTable2
|
2907
|
+
<DataTable2
|
2908
|
+
columns={summaryColumns} // ← Columnas con sumarios
|
2909
|
+
rows={data}
|
2910
|
+
showRowNumbers={true}
|
2911
|
+
/>`}
|
2912
|
+
/>
|
2913
|
+
</ExampleSection>
|
2914
|
+
|
2915
|
+
{/* Ejemplo de exportación funcional */}
|
2916
|
+
<ExampleSection title="📤 Sistema de Exportación Avanzado" id="export-system">
|
2917
|
+
<div style={{
|
2918
|
+
background: '#f0f9ff',
|
2919
|
+
padding: '1rem',
|
2920
|
+
borderRadius: '4px',
|
2921
|
+
border: '1px solid #0ea5e9',
|
2922
|
+
marginBottom: '1rem'
|
2923
|
+
}}>
|
2924
|
+
<h5 style={{ margin: '0 0 0.5rem 0', color: '#0c4a6e' }}>📤 Exportación Profesional</h5>
|
2925
|
+
<p style={{ margin: '0', fontSize: '0.875rem', color: '#0c4a6e' }}>
|
2926
|
+
Sistema completo de exportación con múltiples formatos: CSV, Excel, JSON, XML.
|
2927
|
+
Exportación de todos los datos o solo filas seleccionadas. Estadísticas en tiempo real.
|
2928
|
+
</p>
|
2929
|
+
<p style={{ margin: '0.5rem 0 0 0', fontSize: '0.8rem', color: '#0369a1', fontWeight: 'bold' }}>
|
2930
|
+
💡 En preview.js: Los archivos se abren en nueva ventana. Usa Ctrl+S para guardar.
|
2931
|
+
</p>
|
2932
|
+
</div>
|
2933
|
+
|
2934
|
+
{(() => {
|
2935
|
+
// Datos de ejemplo para exportación
|
2936
|
+
const exportData = [
|
2937
|
+
{
|
2938
|
+
id: 1,
|
2939
|
+
nombre: 'Ana García',
|
2940
|
+
email: 'ana@empresa.com',
|
2941
|
+
departamento: 'Marketing',
|
2942
|
+
salario: 45000,
|
2943
|
+
fechaIngreso: '2023-01-15',
|
2944
|
+
activo: true
|
2945
|
+
},
|
2946
|
+
{
|
2947
|
+
id: 2,
|
2948
|
+
nombre: 'Carlos López',
|
2949
|
+
email: 'carlos@empresa.com',
|
2950
|
+
departamento: 'IT',
|
2951
|
+
salario: 55000,
|
2952
|
+
fechaIngreso: '2022-08-20',
|
2953
|
+
activo: true
|
2954
|
+
},
|
2955
|
+
{
|
2956
|
+
id: 3,
|
2957
|
+
nombre: 'María Rodríguez',
|
2958
|
+
email: 'maria@empresa.com',
|
2959
|
+
departamento: 'Ventas',
|
2960
|
+
salario: 48000,
|
2961
|
+
fechaIngreso: '2023-03-10',
|
2962
|
+
activo: false
|
2963
|
+
},
|
2964
|
+
{
|
2965
|
+
id: 4,
|
2966
|
+
nombre: 'Juan Martínez',
|
2967
|
+
email: 'juan@empresa.com',
|
2968
|
+
departamento: 'HR',
|
2969
|
+
salario: 42000,
|
2970
|
+
fechaIngreso: '2023-06-01',
|
2971
|
+
activo: true
|
2972
|
+
},
|
2973
|
+
{
|
2974
|
+
id: 5,
|
2975
|
+
nombre: 'Laura Sánchez',
|
2976
|
+
email: 'laura@empresa.com',
|
2977
|
+
departamento: 'IT',
|
2978
|
+
salario: 52000,
|
2979
|
+
fechaIngreso: '2022-11-15',
|
2980
|
+
activo: true
|
2981
|
+
}
|
2982
|
+
]
|
2983
|
+
|
2984
|
+
// Columnas con sumarios para exportación
|
2985
|
+
const exportColumns = [
|
2986
|
+
{
|
2987
|
+
id: 'id',
|
2988
|
+
label: 'ID',
|
2989
|
+
type: 'number',
|
2990
|
+
summary: { function: 'count' }
|
2991
|
+
},
|
2992
|
+
{
|
2993
|
+
id: 'nombre',
|
2994
|
+
label: 'Nombre Completo',
|
2995
|
+
type: 'text',
|
2996
|
+
summary: { function: 'count' }
|
2997
|
+
},
|
2998
|
+
{
|
2999
|
+
id: 'email',
|
3000
|
+
label: 'Email',
|
3001
|
+
type: 'email'
|
3002
|
+
},
|
3003
|
+
{
|
3004
|
+
id: 'departamento',
|
3005
|
+
label: 'Departamento',
|
3006
|
+
type: 'text',
|
3007
|
+
summary: { function: 'countUnique' }
|
3008
|
+
},
|
3009
|
+
{
|
3010
|
+
id: 'salario',
|
3011
|
+
label: 'Salario',
|
3012
|
+
type: 'currency',
|
3013
|
+
summary: { function: 'avg' }
|
3014
|
+
},
|
3015
|
+
{
|
3016
|
+
id: 'fechaIngreso',
|
3017
|
+
label: 'Fecha de Ingreso',
|
3018
|
+
type: 'date',
|
3019
|
+
summary: { function: 'latest' }
|
3020
|
+
},
|
3021
|
+
{
|
3022
|
+
id: 'activo',
|
3023
|
+
label: 'Estado',
|
3024
|
+
type: 'boolean',
|
3025
|
+
summary: { function: 'percentage' }
|
3026
|
+
}
|
3027
|
+
]
|
3028
|
+
|
3029
|
+
return (
|
3030
|
+
<div>
|
3031
|
+
<div style={{ marginBottom: '1rem', padding: '1rem', background: '#f8fafc', borderRadius: '6px', border: '1px solid #e2e8f0' }}>
|
3032
|
+
<h6 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem', fontWeight: 'bold' }}>Funcionalidades de Exportación:</h6>
|
3033
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '0.5rem', fontSize: '0.8rem' }}>
|
3034
|
+
<div><strong>📄 CSV:</strong> Valores separados por comas</div>
|
3035
|
+
<div><strong>📊 Excel:</strong> Hoja de cálculo compatible</div>
|
3036
|
+
<div><strong>🔧 JSON:</strong> Formato de datos estructurado</div>
|
3037
|
+
<div><strong>📋 XML:</strong> Markup extensible</div>
|
3038
|
+
<div><strong>✅ Selección:</strong> Solo filas marcadas</div>
|
3039
|
+
<div><strong>📈 Estadísticas:</strong> Información en tiempo real</div>
|
3040
|
+
</div>
|
3041
|
+
</div>
|
3042
|
+
|
3043
|
+
<DataTable2
|
3044
|
+
id="export-demo"
|
3045
|
+
columns={exportColumns}
|
3046
|
+
rows={exportData}
|
3047
|
+
showSidebar={true}
|
3048
|
+
showSelectAll={true}
|
3049
|
+
showRowNumbers={true}
|
3050
|
+
striped={true}
|
3051
|
+
resizable={true}
|
3052
|
+
maxHeight="400px"
|
3053
|
+
/>
|
3054
|
+
</div>
|
3055
|
+
)
|
3056
|
+
})()}
|
3057
|
+
|
3058
|
+
<CodeSnippet
|
3059
|
+
title="Configuración de Exportación Avanzada"
|
3060
|
+
code={`// Importar utilidades de exportación
|
3061
|
+
import {
|
3062
|
+
exportToCSV,
|
3063
|
+
exportToJSON,
|
3064
|
+
exportToExcel,
|
3065
|
+
exportToXML,
|
3066
|
+
exportSelected,
|
3067
|
+
getExportStats
|
3068
|
+
} from './table-export-utils'
|
3069
|
+
|
3070
|
+
// Configuración básica
|
3071
|
+
<DataTable2
|
3072
|
+
columns={columns}
|
3073
|
+
rows={data}
|
3074
|
+
showSidebar={true} // ← Habilita toolbar con exportación
|
3075
|
+
showSelectAll={true} // ← Permite seleccionar filas
|
3076
|
+
exportable={true} // ← Habilita exportación (opcional)
|
3077
|
+
/>
|
3078
|
+
|
3079
|
+
// Exportación programática
|
3080
|
+
const handleCustomExport = () => {
|
3081
|
+
// Exportar todos los datos a CSV
|
3082
|
+
const result = exportToCSV(data, columns, 'mi-reporte.csv')
|
3083
|
+
|
3084
|
+
if (result.success) {
|
3085
|
+
console.log(result.message) // "Exportado 100 filas a CSV"
|
3086
|
+
}
|
3087
|
+
}
|
3088
|
+
|
3089
|
+
// Exportar solo filas seleccionadas
|
3090
|
+
const handleExportSelected = () => {
|
3091
|
+
const selectedIds = [1, 3, 5] // IDs de filas seleccionadas
|
3092
|
+
const result = exportSelected(data, columns, selectedIds, 'json')
|
3093
|
+
|
3094
|
+
if (result.success) {
|
3095
|
+
console.log(result.message) // "Exportado 3 filas a JSON"
|
3096
|
+
}
|
3097
|
+
}
|
3098
|
+
|
3099
|
+
// Obtener estadísticas antes de exportar
|
3100
|
+
const stats = getExportStats(data, columns)
|
3101
|
+
console.log(stats)
|
3102
|
+
// {
|
3103
|
+
// totalRows: 100,
|
3104
|
+
// totalColumns: 7,
|
3105
|
+
// visibleColumns: 6,
|
3106
|
+
// estimatedSize: { csv: "25 KB", json: "45 KB" }
|
3107
|
+
// }
|
3108
|
+
|
3109
|
+
// Formatos disponibles:
|
3110
|
+
// - CSV: Valores separados por comas
|
3111
|
+
// - Excel: Formato TSV compatible con Excel
|
3112
|
+
// - JSON: Datos estructurados en JavaScript
|
3113
|
+
// - XML: Markup extensible con estructura de filas
|
3114
|
+
|
3115
|
+
// Funcionalidades automáticas:
|
3116
|
+
// ✅ Descarga automática de archivos
|
3117
|
+
// ✅ Validación de datos antes de exportar
|
3118
|
+
// ✅ Manejo de errores con mensajes informativos
|
3119
|
+
// ✅ Estadísticas en tiempo real
|
3120
|
+
// ✅ Exportación de filas seleccionadas
|
3121
|
+
// ✅ Formateo específico por tipo de datos`}
|
3122
|
+
/>
|
3123
|
+
</ExampleSection>
|
3124
|
+
</ExampleLayout>
|
700
3125
|
)
|
701
3126
|
}
|
702
3127
|
|