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.
Files changed (70) hide show
  1. package/dist/index.cjs +3493 -2320
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +2096 -1125
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.modern.js +3493 -2320
  6. package/dist/index.modern.js.map +1 -1
  7. package/dist/index.umd.js +3493 -2320
  8. package/dist/index.umd.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/html/ExampleLayout.css +401 -0
  11. package/src/html/ExampleLayout.js +192 -0
  12. package/src/html/README-sidebar-navigation.md +195 -0
  13. package/src/html/accordion.example.js +123 -4
  14. package/src/html/accordion.example.js.backup +390 -0
  15. package/src/html/button.example.js +50 -3
  16. package/src/html/button.example.js.backup +374 -0
  17. package/src/html/button.example.new.js +416 -0
  18. package/src/html/button.js +22 -4
  19. package/src/html/checkbox.example.js +93 -4
  20. package/src/html/checkbox.example.js.backup +316 -0
  21. package/src/html/chip.example.js +108 -4
  22. package/src/html/chip.example.js.backup +355 -0
  23. package/src/html/color.example.js +108 -4
  24. package/src/html/color.example.js.backup +527 -0
  25. package/src/html/components.example.js +123 -4
  26. package/src/html/components.example.js.backup +492 -0
  27. package/src/html/convert-examples.js +183 -0
  28. package/src/html/demo-sidebar.html +410 -0
  29. package/src/html/form.example.js +93 -4
  30. package/src/html/form.example.js.backup +385 -0
  31. package/src/html/header.js +20 -3
  32. package/src/html/header2.example.js +108 -4
  33. package/src/html/header2.example.js.backup +411 -0
  34. package/src/html/icon.example.js +77 -3
  35. package/src/html/icon.example.js.backup +268 -0
  36. package/src/html/list.example.js +93 -4
  37. package/src/html/list.example.js.backup +404 -0
  38. package/src/html/progress.example.js +74 -4
  39. package/src/html/progress.example.js.backup +424 -0
  40. package/src/html/property.example.js +123 -4
  41. package/src/html/property.example.js.backup +553 -0
  42. package/src/html/radio.example.js +108 -4
  43. package/src/html/radio.example.js.backup +389 -0
  44. package/src/html/section.example.js +42 -3
  45. package/src/html/section.example.js.backup +99 -0
  46. package/src/html/switch.example.js +108 -4
  47. package/src/html/switch.example.js.backup +461 -0
  48. package/src/html/tab.example.js +93 -4
  49. package/src/html/tab.example.js.backup +446 -0
  50. package/src/html/table-export-utils.js +483 -0
  51. package/src/html/table-summary-functions.js +363 -0
  52. package/src/html/table2.css +1449 -479
  53. package/src/html/table2.example.js +2937 -512
  54. package/src/html/table2.example.js.broken +1226 -0
  55. package/src/html/table2.js +1426 -1000
  56. package/src/html/test-resize.html +279 -0
  57. package/src/html/test-selection.html +387 -0
  58. package/src/html/textfield.js +73 -7
  59. package/src/html/textfield2.example.js +108 -4
  60. package/src/html/textfield2.example.js.backup +1370 -0
  61. package/src/html/textfield2.js +19 -4
  62. package/src/html/tokenfield.example.js +108 -4
  63. package/src/html/tokenfield.example.js.backup +503 -0
  64. package/src/html/tooltip.js +21 -3
  65. package/src/html/tree.css +2 -4
  66. package/src/html/tree.example.js +93 -4
  67. package/src/html/tree.example.js.backup +475 -0
  68. package/src/html/tree.js +19 -3
  69. package/src/widgets/login/LoginBox.css +1 -0
  70. 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 { Text } from './text'
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: 'SELECTION',
103
+ type: 'String',
66
104
  sortable: true,
67
105
  filterable: true,
68
106
  editable: true,
69
107
  resizable: true,
70
108
  options: [
71
- { label: 'Active', value: 'active' },
72
- { label: 'Inactive', value: 'inactive' },
73
- { label: 'Pending', value: 'pending' }
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
- verified: true,
96
- info: () => <div>Additional info for John Doe</div>
97
- },
98
- {
99
- id: 2,
100
- name: 'Jane Smith',
101
- email: 'jane@example.com',
102
- age: 25,
103
- status: 'pending',
104
- verified: false,
105
- info: 'Jane is a new user pending verification'
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: 4,
117
- name: 'Alice Brown',
118
- email: 'alice@example.com',
119
- age: 28,
120
- status: 'inactive',
121
- verified: true,
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: 5,
126
- name: 'Charlie Wilson',
127
- email: 'charlie@example.com',
128
- age: 42,
129
- status: 'active',
130
- verified: true
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
- // Generar datos grandes para virtual scrolling
135
- const largeDataset = Array.from({ length: 1000 }, (_, i) => ({
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 => [...prev, row.id])
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 handleSort = (dragged, dropped) => {
150
- console.log('Sort:', dragged, dropped)
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 handleCheckAll = (ids, checked) => {
154
- console.log('Check all:', ids, checked)
155
- setSelectedRows(checked ? ids : [])
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 handleExport = (rows, columns) => {
159
- console.log('Export:', rows.length, 'rows')
160
- // Custom export logic here
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
- const handleSearch = (term) => {
164
- console.log('Search:', term)
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
- <div style={{ padding: '2rem', maxWidth: '1400px', maxHeight: '100vh', overflow: 'auto' }}>
174
- <h1>DataTable2 - Versión Mejorada (100% Compatible)</h1>
175
-
176
- <div style={{
177
- background: '#f8f9fa',
178
- padding: '1rem',
179
- borderRadius: '8px',
180
- marginBottom: '2rem',
181
- border: '1px solid #e9ecef'
182
- }}>
183
- <h3>✅ Mejoras Implementadas (100% Compatibilidad):</h3>
184
- <ul style={{ columns: 2, columnGap: '2rem' }}>
185
- <li>🛡️ <strong>PropTypes completos</strong> - Validación exhaustiva</li>
186
- <li>🔍 <strong>Búsqueda integrada</strong> - Search en tiempo real</li>
187
- <li>📊 <strong>Exportación CSV</strong> - Export automático o personalizado</li>
188
- <li>📱 <strong>Virtual Scrolling</strong> - Rendimiento para miles de filas</li>
189
- <li>🎨 <strong>Múltiples temas</strong> - default, dark, minimal</li>
190
- <li>📐 <strong>Densidad variable</strong> - compact, normal, comfortable</li>
191
- <li>🎯 <strong>Selección mejorada</strong> - single, multiple, none</li>
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
- disabled={isLoading}
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
- </section>
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
- {/* Temas y variantes */}
310
- <section style={{ marginBottom: '2rem' }}>
311
- <h3>🎨 Temas y Variantes</h3>
312
- <div style={{
313
- background: '#fff',
314
- padding: '1rem',
315
- border: '1px solid #ddd',
316
- borderRadius: '8px',
317
- display: 'grid',
318
- gridTemplateColumns: '1fr 1fr',
319
- gap: '1rem'
320
- }}>
321
- <div>
322
- <h4>Tema Dark</h4>
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
- bordered={true}
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
- {/* Densidades */}
346
- <section style={{ marginBottom: '2rem' }}>
347
- <h3>📐 Densidades y Tamaños (Redimensionables)</h3>
348
- <div style={{
349
- background: '#fff',
350
- padding: '1rem',
351
- border: '1px solid #ddd',
352
- borderRadius: '8px'
353
- }}>
354
- <div style={{ marginBottom: '2rem' }}>
355
- <h4>🔧 Compact (Altura: 28px) - Arrastra los bordes de las columnas para redimensionar</h4>
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
- columns={columns.slice(1, 5)} // Sin checkbox para mejor visualización
358
- rows={rows.slice(0, 3)}
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
- <div style={{ marginBottom: '2rem' }}>
368
- <h4>📏 Normal (Altura: 40px) - Redimensionamiento habilitado</h4>
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
- columns={columns.slice(1, 5)}
371
- rows={rows.slice(0, 3)}
372
- density="normal"
373
- rowHeight="medium"
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
- bordered={true}
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
- marginTop: '1rem',
393
- padding: '0.75rem',
394
- background: '#e3f2fd',
647
+ background: '#e8f5e8',
648
+ padding: '1rem',
395
649
  borderRadius: '4px',
396
- border: '1px solid #bbdefb'
650
+ border: '1px solid #4caf50',
651
+ marginBottom: '1rem'
397
652
  }}>
398
- <Text size="sm" color="prima">
399
- 💡 <strong>Tip:</strong> Pasa el mouse sobre el borde derecho de cualquier columna para ver el cursor de redimensionamiento.
400
- Arrastra para cambiar el ancho. Los cambios se registran en la consola.
401
- </Text>
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>&lt;DataTable&gt;</code> por
656
+ <code>&lt;DataTable2&gt;</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
- {/* Virtual Scrolling */}
407
- {showAdvanced && (
408
- <section style={{ marginBottom: '2rem' }}>
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
- columns={columns.slice(1, 6)} // Sin checkbox para simplificar
418
- rows={largeDataset}
419
- virtualScrolling={true}
420
- pageSize={50}
421
- searchable={true}
422
- exportable={true}
423
- stickyHeader={true}
424
- showRowNumbers={true}
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
- {/* Estados especiales */}
437
- {showAdvanced && (
438
- <section style={{ marginBottom: '2rem' }}>
439
- <h3>⚠️ Estados Especiales</h3>
440
- <div style={{
441
- background: '#fff',
442
- padding: '1rem',
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
- display: 'grid',
446
- gridTemplateColumns: '1fr 1fr',
447
- gap: '1rem'
793
+ marginBottom: '1rem'
448
794
  }}>
449
- <div>
450
- <h4>Loading State</h4>
451
- <DataTable2
452
- columns={columns.slice(0, 4)}
453
- rows={rows.slice(0, 3)}
454
- loading={true}
455
- skeleton={false}
456
- />
457
- </div>
458
- <div>
459
- <h4>Skeleton State</h4>
460
- <DataTable2
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: '#ffebee',
810
+ background: '#f8f9fa',
477
811
  padding: '1rem',
478
812
  borderRadius: '4px',
479
- border: '1px solid #ffcdd2'
813
+ border: '1px solid #e9ecef',
814
+ marginBottom: '1rem'
480
815
  }}>
481
- <h4>❌ DataTable Original</h4>
482
- <ul>
483
- <li>Funcionalidad básica de tabla</li>
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: '#e8f5e8',
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 #c8e6c9'
945
+ border: '1px solid #e9ecef',
946
+ marginBottom: '1rem'
502
947
  }}>
503
- <h4>✅ DataTable2 Mejorado</h4>
504
- <ul>
505
- <li>Mantiene funcionalidad original</li>
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
- {/* Garantía de compatibilidad */}
523
- <section style={{ marginBottom: '2rem' }}>
524
- <h3>🔒 Garantía de Compatibilidad 100%</h3>
525
- <div style={{
526
- background: '#fff3cd',
527
- padding: '1rem',
528
- borderRadius: '4px',
529
- border: '1px solid #ffeaa7'
530
- }}>
531
- <p><strong>Migración Sin Riesgo:</strong></p>
532
- <ul>
533
- <li>✅ <strong>Props originales</strong> - columns, rows, onRowSelection, onSort, onCheckAll, editable, outlined, expanded, multisort, filterable funcionan idéntico</li>
534
- <li>✅ <strong>Callbacks sin cambios</strong> - Misma firma y comportamiento</li>
535
- <li>✅ <strong>Estructura de datos</strong> - Columns y rows mantienen formato exacto</li>
536
- <li>✅ <strong>CSS compatible</strong> - Clases originales preservadas (.datatable8)</li>
537
- <li>✅ <strong>Nuevas características opcionales</strong> - Todas las mejoras son opt-in</li>
538
- <li>✅ <strong>Cero breaking changes</strong> - No se rompe código existente</li>
539
- <li>✅ <strong>Migración gradual</strong> - Puedes adoptar nuevas características cuando quieras</li>
540
- </ul>
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: '#d4edda',
979
+ background: '#fff3cd',
544
980
  padding: '1rem',
545
981
  borderRadius: '4px',
546
- marginTop: '1rem',
547
- border: '1px solid #c3e6cb'
982
+ border: '1px solid #ffeaa7',
983
+ marginBottom: '1rem'
548
984
  }}>
549
- <h4>📋 Pasos de Migración:</h4>
550
- <ol>
551
- <li><strong>Reemplaza el import:</strong> <code>import {`{DataTable2}`} from './table2'</code></li>
552
- <li><strong>Cambia el componente:</strong> <code>&lt;DataTable2 .../&gt;</code></li>
553
- <li><strong>¡Listo!</strong> Todo funciona igual + nuevas características disponibles</li>
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
- {/* Casos de uso */}
561
- <section style={{ marginBottom: '2rem' }}>
562
- <h3>🎯 Casos de Uso Recomendados</h3>
563
- <div style={{
564
- background: '#fff',
565
- padding: '1rem',
566
- border: '1px solid #ddd',
567
- borderRadius: '8px',
568
- display: 'grid',
569
- gridTemplateColumns: '1fr 1fr',
570
- gap: '1rem'
571
- }}>
572
- <div>
573
- <h4>📊 Dashboards Administrativos</h4>
574
- <ul>
575
- <li>Búsqueda en tiempo real</li>
576
- <li>Exportación de reportes</li>
577
- <li>Edición inline</li>
578
- <li>Selección múltiple</li>
579
- <li>Filtros avanzados</li>
580
- </ul>
581
-
582
- <h4>📱 Aplicaciones Móviles</h4>
583
- <ul>
584
- <li>Responsive design</li>
585
- <li>Touch-friendly</li>
586
- <li>Virtual scrolling</li>
587
- <li>Densidad compacta</li>
588
- </ul>
589
- </div>
590
- <div>
591
- <h4>🏢 Sistemas Empresariales</h4>
592
- <ul>
593
- <li>Miles de registros</li>
594
- <li>Accesibilidad completa</li>
595
- <li>Temas corporativos</li>
596
- <li>Estados de carga</li>
597
- <li>Validación robusta</li>
598
- </ul>
599
-
600
- <h4>🎨 Aplicaciones Modernas</h4>
601
- <ul>
602
- <li>Dark mode</li>
603
- <li>Animaciones suaves</li>
604
- <li>UX optimizada</li>
605
- <li>Performance superior</li>
606
- </ul>
607
- </div>
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
- </section>
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
- {/* Performance */}
612
- <section style={{ marginBottom: '2rem' }}>
613
- <h3>⚡ Mejoras de Performance</h3>
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: '#fff',
1607
+ background: '#e8f5e8',
616
1608
  padding: '1rem',
617
- border: '1px solid #ddd',
618
- borderRadius: '8px'
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
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '1rem', textAlign: 'center' }}>
621
- <div style={{ padding: '1rem', background: '#f8f9fa', borderRadius: '4px' }}>
622
- <h4>🚀 Virtual Scrolling</h4>
623
- <p><strong>10,000+ filas</strong></p>
624
- <p>Renderizado solo de elementos visibles</p>
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
- <div style={{ padding: '1rem', background: '#f8f9fa', borderRadius: '4px' }}>
627
- <h4>🔍 Búsqueda Optimizada</h4>
628
- <p><strong>Tiempo real</strong></p>
629
- <p>Filtrado eficiente con debounce</p>
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
- <div style={{ padding: '1rem', background: '#f8f9fa', borderRadius: '4px' }}>
632
- <h4>💾 Gestión de Estado</h4>
633
- <p><strong>React Hooks</strong></p>
634
- <p>useCallback y useMemo optimizados</p>
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
- </section>
2097
+ </ExampleSection>
639
2098
 
640
- {/* API Reference */}
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
- <table style={{ width: '100%', borderCollapse: 'collapse' }}>
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>searchable</code></td>
661
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
662
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
663
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Habilita búsqueda en tiempo real</td>
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>exportable</code></td>
667
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>boolean</td>
668
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>false</td>
669
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Habilita exportación CSV</td>
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>selectionMode</code></td>
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' }}>single</td>
681
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Modo de selección: single, multiple, none</td>
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 visual: default, dark, minimal</td>
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>density</code></td>
691
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>string</td>
692
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>normal</td>
693
- <td style={{ padding: '0.5rem', border: '1px solid #ddd' }}>Densidad: compact, normal, comfortable</td>
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
- </section>
699
- </div>
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