ywana-core8 0.1.79 → 0.1.80

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 (63) hide show
  1. package/dist/index.cjs +3244 -2215
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +2095 -1125
  4. package/dist/index.css.map +1 -1
  5. package/dist/index.modern.js +3244 -2215
  6. package/dist/index.modern.js.map +1 -1
  7. package/dist/index.umd.js +3244 -2215
  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/checkbox.example.js +93 -4
  19. package/src/html/checkbox.example.js.backup +316 -0
  20. package/src/html/chip.example.js +108 -4
  21. package/src/html/chip.example.js.backup +355 -0
  22. package/src/html/color.example.js +108 -4
  23. package/src/html/color.example.js.backup +527 -0
  24. package/src/html/components.example.js +123 -4
  25. package/src/html/components.example.js.backup +492 -0
  26. package/src/html/convert-examples.js +183 -0
  27. package/src/html/demo-sidebar.html +410 -0
  28. package/src/html/form.example.js +93 -4
  29. package/src/html/form.example.js.backup +385 -0
  30. package/src/html/header2.example.js +108 -4
  31. package/src/html/header2.example.js.backup +411 -0
  32. package/src/html/icon.example.js +77 -3
  33. package/src/html/icon.example.js.backup +268 -0
  34. package/src/html/list.example.js +93 -4
  35. package/src/html/list.example.js.backup +404 -0
  36. package/src/html/progress.example.js +74 -4
  37. package/src/html/progress.example.js.backup +424 -0
  38. package/src/html/property.example.js +123 -4
  39. package/src/html/property.example.js.backup +553 -0
  40. package/src/html/radio.example.js +108 -4
  41. package/src/html/radio.example.js.backup +389 -0
  42. package/src/html/section.example.js +42 -3
  43. package/src/html/section.example.js.backup +99 -0
  44. package/src/html/switch.example.js +108 -4
  45. package/src/html/switch.example.js.backup +461 -0
  46. package/src/html/tab.example.js +93 -4
  47. package/src/html/tab.example.js.backup +446 -0
  48. package/src/html/table-export-utils.js +483 -0
  49. package/src/html/table-summary-functions.js +363 -0
  50. package/src/html/table2.css +1449 -479
  51. package/src/html/table2.example.js +2937 -512
  52. package/src/html/table2.example.js.broken +1226 -0
  53. package/src/html/table2.js +1426 -1000
  54. package/src/html/test-resize.html +279 -0
  55. package/src/html/test-selection.html +387 -0
  56. package/src/html/textfield2.example.js +108 -4
  57. package/src/html/textfield2.example.js.backup +1370 -0
  58. package/src/html/tokenfield.example.js +108 -4
  59. package/src/html/tokenfield.example.js.backup +503 -0
  60. package/src/html/tree.css +2 -4
  61. package/src/html/tree.example.js +93 -4
  62. package/src/html/tree.example.js.backup +475 -0
  63. package/src/html/tree.js +19 -3
@@ -0,0 +1,483 @@
1
+ /**
2
+ * Utilidades de exportación para DataTable2
3
+ * Proporciona funciones para exportar datos a diferentes formatos
4
+ */
5
+
6
+ /**
7
+ * Detectar entorno de ejecución
8
+ */
9
+ const detectEnvironment = () => {
10
+ const isInIframe = window !== window.top
11
+ const isPreviewJs = window.location.href.includes('previewjs') ||
12
+ window.location.href.includes('localhost:3140')
13
+ const isLocalhost = window.location.hostname === 'localhost' ||
14
+ window.location.hostname === '127.0.0.1'
15
+
16
+ return {
17
+ isInIframe,
18
+ isPreviewJs,
19
+ isLocalhost,
20
+ isRestricted: isInIframe || isPreviewJs
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Exportar datos a CSV
26
+ */
27
+ export const exportToCSV = (data, columns, filename = 'table-export.csv') => {
28
+ try {
29
+ const env = detectEnvironment()
30
+
31
+ // Crear headers
32
+ const headers = columns.map(col => col.label || col.id)
33
+
34
+ // Crear filas de datos
35
+ const rows = data.map(row =>
36
+ columns.map(col => {
37
+ let value = row[col.id]
38
+
39
+ // Manejar valores especiales
40
+ if (value === null || value === undefined) {
41
+ value = ''
42
+ } else if (typeof value === 'object') {
43
+ value = JSON.stringify(value)
44
+ } else if (typeof value === 'string') {
45
+ // Escapar comillas dobles
46
+ value = `"${value.replace(/"/g, '""')}"`
47
+ } else {
48
+ value = String(value)
49
+ }
50
+
51
+ return value
52
+ })
53
+ )
54
+
55
+ // Combinar headers y datos
56
+ const csvContent = [
57
+ headers.join(','),
58
+ ...rows.map(row => row.join(','))
59
+ ].join('\n')
60
+
61
+ // Crear y descargar archivo
62
+ downloadFile(csvContent, filename, 'text/csv;charset=utf-8;')
63
+
64
+ const message = env.isRestricted
65
+ ? `Exportado ${data.length} filas a CSV (se abrirá en nueva ventana)`
66
+ : `Exportado ${data.length} filas a CSV`
67
+
68
+ return { success: true, message }
69
+ } catch (error) {
70
+ console.error('Error exporting to CSV:', error)
71
+ return { success: false, message: 'Error al exportar a CSV' }
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Exportar datos a JSON
77
+ */
78
+ export const exportToJSON = (data, columns, filename = 'table-export.json') => {
79
+ try {
80
+ const env = detectEnvironment()
81
+
82
+ // Filtrar solo las columnas visibles
83
+ const filteredData = data.map(row => {
84
+ const filteredRow = {}
85
+ columns.forEach(col => {
86
+ filteredRow[col.id] = row[col.id]
87
+ })
88
+ return filteredRow
89
+ })
90
+
91
+ const jsonContent = JSON.stringify(filteredData, null, 2)
92
+
93
+ downloadFile(jsonContent, filename, 'application/json;charset=utf-8;')
94
+
95
+ const message = env.isRestricted
96
+ ? `Exportado ${data.length} filas a JSON (se abrirá en nueva ventana)`
97
+ : `Exportado ${data.length} filas a JSON`
98
+
99
+ return { success: true, message }
100
+ } catch (error) {
101
+ console.error('Error exporting to JSON:', error)
102
+ return { success: false, message: 'Error al exportar a JSON' }
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Exportar datos a Excel (formato TSV compatible)
108
+ */
109
+ export const exportToExcel = (data, columns, filename = 'table-export.xlsx') => {
110
+ try {
111
+ // Crear headers
112
+ const headers = columns.map(col => col.label || col.id)
113
+
114
+ // Crear filas de datos (usando tabulaciones para Excel)
115
+ const rows = data.map(row =>
116
+ columns.map(col => {
117
+ let value = row[col.id]
118
+
119
+ if (value === null || value === undefined) {
120
+ value = ''
121
+ } else if (typeof value === 'object') {
122
+ value = JSON.stringify(value)
123
+ } else {
124
+ value = String(value)
125
+ }
126
+
127
+ return value
128
+ })
129
+ )
130
+
131
+ // Combinar con tabulaciones (TSV)
132
+ const tsvContent = [
133
+ headers.join('\t'),
134
+ ...rows.map(row => row.join('\t'))
135
+ ].join('\n')
136
+
137
+ // Usar extensión .xls para compatibilidad
138
+ const excelFilename = filename.replace('.xlsx', '.xls')
139
+ downloadFile(tsvContent, excelFilename, 'application/vnd.ms-excel;charset=utf-8;')
140
+
141
+ return { success: true, message: `Exportado ${data.length} filas a Excel` }
142
+ } catch (error) {
143
+ console.error('Error exporting to Excel:', error)
144
+ return { success: false, message: 'Error al exportar a Excel' }
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Exportar datos a XML
150
+ */
151
+ export const exportToXML = (data, columns, filename = 'table-export.xml') => {
152
+ try {
153
+ let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n'
154
+
155
+ data.forEach(row => {
156
+ xmlContent += ' <row>\n'
157
+ columns.forEach(col => {
158
+ const value = row[col.id]
159
+ const escapedValue = escapeXML(value)
160
+ xmlContent += ` <${col.id}>${escapedValue}</${col.id}>\n`
161
+ })
162
+ xmlContent += ' </row>\n'
163
+ })
164
+
165
+ xmlContent += '</data>'
166
+
167
+ downloadFile(xmlContent, filename, 'application/xml;charset=utf-8;')
168
+
169
+ return { success: true, message: `Exportado ${data.length} filas a XML` }
170
+ } catch (error) {
171
+ console.error('Error exporting to XML:', error)
172
+ return { success: false, message: 'Error al exportar a XML' }
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Exportar solo filas seleccionadas
178
+ */
179
+ export const exportSelected = (data, columns, selectedRows, format = 'csv') => {
180
+ const selectedData = data.filter(row => selectedRows.includes(row.id))
181
+
182
+ if (selectedData.length === 0) {
183
+ return { success: false, message: 'No hay filas seleccionadas para exportar' }
184
+ }
185
+
186
+ const filename = `selected-rows.${format}`
187
+
188
+ switch (format) {
189
+ case 'csv':
190
+ return exportToCSV(selectedData, columns, filename)
191
+ case 'json':
192
+ return exportToJSON(selectedData, columns, filename)
193
+ case 'excel':
194
+ return exportToExcel(selectedData, columns, filename)
195
+ case 'xml':
196
+ return exportToXML(selectedData, columns, filename)
197
+ default:
198
+ return { success: false, message: 'Formato no soportado' }
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Función auxiliar para descargar archivos
204
+ * Maneja diferentes entornos incluyendo preview.js y iframes
205
+ */
206
+ const downloadFile = (content, filename, mimeType) => {
207
+ try {
208
+ const blob = new Blob([content], { type: mimeType })
209
+
210
+ // Método 1: Intentar descarga directa (funciona en la mayoría de navegadores)
211
+ if (window.navigator && window.navigator.msSaveOrOpenBlob) {
212
+ // IE/Edge
213
+ window.navigator.msSaveOrOpenBlob(blob, filename)
214
+ return
215
+ }
216
+
217
+ // Método 2: Crear URL y link (estándar)
218
+ const url = URL.createObjectURL(blob)
219
+
220
+ // Verificar si estamos en un iframe o entorno restringido
221
+ const isInIframe = window !== window.top
222
+ const isPreviewJs = window.location.href.includes('previewjs') ||
223
+ window.location.href.includes('localhost:3140')
224
+
225
+ if (isInIframe || isPreviewJs) {
226
+ // Método alternativo para entornos restringidos
227
+ downloadFileAlternative(content, filename, mimeType, blob, url)
228
+ } else {
229
+ // Método estándar
230
+ downloadFileStandard(url, filename)
231
+ }
232
+
233
+ // Limpiar URL después de un tiempo
234
+ setTimeout(() => {
235
+ try {
236
+ URL.revokeObjectURL(url)
237
+ } catch (e) {
238
+ // Ignorar errores de limpieza
239
+ }
240
+ }, 2000)
241
+
242
+ } catch (error) {
243
+ console.error('Error downloading file:', error)
244
+ // Fallback: mostrar contenido en nueva ventana
245
+ showContentInNewWindow(content, filename, mimeType)
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Descarga estándar con link
251
+ */
252
+ const downloadFileStandard = (url, filename) => {
253
+ const link = document.createElement('a')
254
+ link.href = url
255
+ link.download = filename
256
+ link.style.display = 'none'
257
+ link.target = '_blank'
258
+ link.rel = 'noopener noreferrer'
259
+
260
+ document.body.appendChild(link)
261
+
262
+ // Usar setTimeout para asegurar que el elemento esté en el DOM
263
+ setTimeout(() => {
264
+ try {
265
+ link.click()
266
+ } catch (e) {
267
+ // Si click() falla, intentar dispatchEvent
268
+ const event = new MouseEvent('click', {
269
+ view: window,
270
+ bubbles: true,
271
+ cancelable: true
272
+ })
273
+ link.dispatchEvent(event)
274
+ }
275
+
276
+ // Remover el link después de un breve delay
277
+ setTimeout(() => {
278
+ if (link.parentNode) {
279
+ document.body.removeChild(link)
280
+ }
281
+ }, 100)
282
+ }, 10)
283
+ }
284
+
285
+ /**
286
+ * Método alternativo para entornos restringidos
287
+ */
288
+ const downloadFileAlternative = (content, filename, mimeType, blob, url) => {
289
+ try {
290
+ // Método 1: Intentar abrir en nueva ventana con data URL
291
+ const dataUrl = `data:${mimeType};charset=utf-8,${encodeURIComponent(content)}`
292
+
293
+ // Verificar tamaño del contenido (data URLs tienen límites)
294
+ if (content.length < 1000000) { // 1MB límite aproximado
295
+ const newWindow = window.open(dataUrl, '_blank')
296
+ if (newWindow) {
297
+ // Sugerir al usuario que guarde el archivo
298
+ setTimeout(() => {
299
+ try {
300
+ newWindow.document.title = `Descargar ${filename}`
301
+ const instruction = newWindow.document.createElement('div')
302
+ instruction.innerHTML = `
303
+ <div style="padding: 20px; font-family: Arial, sans-serif;">
304
+ <h3>Archivo listo para descargar</h3>
305
+ <p>Nombre: <strong>${filename}</strong></p>
306
+ <p>Para guardar: <kbd>Ctrl+S</kbd> (Windows) o <kbd>Cmd+S</kbd> (Mac)</p>
307
+ <button onclick="window.close()" style="padding: 10px 20px; margin-top: 10px;">Cerrar</button>
308
+ </div>
309
+ `
310
+ newWindow.document.body.insertBefore(instruction, newWindow.document.body.firstChild)
311
+ } catch (e) {
312
+ // Ignorar errores de manipulación del DOM en la nueva ventana
313
+ }
314
+ }, 100)
315
+ return
316
+ }
317
+ }
318
+
319
+ // Método 2: Copiar al portapapeles como fallback
320
+ copyToClipboard(content, filename)
321
+
322
+ } catch (error) {
323
+ console.error('Error in alternative download:', error)
324
+ // Último recurso: mostrar en consola
325
+ console.log(`Contenido del archivo ${filename}:`, content)
326
+ alert(`No se pudo descargar el archivo. El contenido se ha mostrado en la consola del navegador.`)
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Mostrar contenido en nueva ventana como último recurso
332
+ */
333
+ const showContentInNewWindow = (content, filename, mimeType) => {
334
+ try {
335
+ const newWindow = window.open('', '_blank')
336
+ if (newWindow) {
337
+ newWindow.document.write(`
338
+ <html>
339
+ <head>
340
+ <title>${filename}</title>
341
+ <style>
342
+ body { font-family: monospace; padding: 20px; }
343
+ .header { background: #f0f0f0; padding: 10px; margin-bottom: 20px; }
344
+ .content { white-space: pre-wrap; }
345
+ </style>
346
+ </head>
347
+ <body>
348
+ <div class="header">
349
+ <h3>Archivo: ${filename}</h3>
350
+ <p>Tipo: ${mimeType}</p>
351
+ <p>Para guardar: Ctrl+S (Windows) o Cmd+S (Mac)</p>
352
+ </div>
353
+ <div class="content">${content.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
354
+ </body>
355
+ </html>
356
+ `)
357
+ newWindow.document.close()
358
+ }
359
+ } catch (error) {
360
+ console.error('Error showing content in new window:', error)
361
+ alert('No se pudo descargar el archivo. Verifique la consola del navegador.')
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Copiar contenido al portapapeles
367
+ */
368
+ const copyToClipboard = async (content, filename) => {
369
+ try {
370
+ if (navigator.clipboard && navigator.clipboard.writeText) {
371
+ await navigator.clipboard.writeText(content)
372
+ alert(`Contenido del archivo "${filename}" copiado al portapapeles. Puedes pegarlo en un editor de texto y guardarlo.`)
373
+ } else {
374
+ // Fallback para navegadores sin Clipboard API
375
+ const textArea = document.createElement('textarea')
376
+ textArea.value = content
377
+ textArea.style.position = 'fixed'
378
+ textArea.style.opacity = '0'
379
+ document.body.appendChild(textArea)
380
+ textArea.select()
381
+
382
+ try {
383
+ document.execCommand('copy')
384
+ alert(`Contenido del archivo "${filename}" copiado al portapapeles.`)
385
+ } catch (e) {
386
+ console.log('Fallback copy failed:', e)
387
+ }
388
+
389
+ document.body.removeChild(textArea)
390
+ }
391
+ } catch (error) {
392
+ console.error('Error copying to clipboard:', error)
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Función auxiliar para escapar XML
398
+ */
399
+ const escapeXML = (value) => {
400
+ if (value === null || value === undefined) {
401
+ return ''
402
+ }
403
+
404
+ return String(value)
405
+ .replace(/&/g, '&amp;')
406
+ .replace(/</g, '&lt;')
407
+ .replace(/>/g, '&gt;')
408
+ .replace(/"/g, '&quot;')
409
+ .replace(/'/g, '&#39;')
410
+ }
411
+
412
+ /**
413
+ * Obtener estadísticas de exportación
414
+ */
415
+ export const getExportStats = (data, columns) => {
416
+ return {
417
+ totalRows: data.length,
418
+ totalColumns: columns.length,
419
+ visibleColumns: columns.filter(col => !col.hidden).length,
420
+ estimatedSize: {
421
+ csv: Math.round((data.length * columns.length * 10) / 1024) + ' KB',
422
+ json: Math.round((JSON.stringify(data).length) / 1024) + ' KB'
423
+ }
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Validar datos antes de exportar
429
+ */
430
+ export const validateExportData = (data, columns) => {
431
+ const errors = []
432
+
433
+ if (!data || data.length === 0) {
434
+ errors.push('No hay datos para exportar')
435
+ }
436
+
437
+ if (!columns || columns.length === 0) {
438
+ errors.push('No hay columnas definidas')
439
+ }
440
+
441
+ return {
442
+ isValid: errors.length === 0,
443
+ errors
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Formatos de exportación disponibles
449
+ */
450
+ export const exportFormats = {
451
+ csv: {
452
+ id: 'csv',
453
+ label: 'CSV',
454
+ description: 'Valores separados por comas',
455
+ icon: 'description',
456
+ mimeType: 'text/csv',
457
+ extension: '.csv'
458
+ },
459
+ excel: {
460
+ id: 'excel',
461
+ label: 'Excel',
462
+ description: 'Hoja de cálculo de Microsoft Excel',
463
+ icon: 'table_chart',
464
+ mimeType: 'application/vnd.ms-excel',
465
+ extension: '.xls'
466
+ },
467
+ json: {
468
+ id: 'json',
469
+ label: 'JSON',
470
+ description: 'JavaScript Object Notation',
471
+ icon: 'code',
472
+ mimeType: 'application/json',
473
+ extension: '.json'
474
+ },
475
+ xml: {
476
+ id: 'xml',
477
+ label: 'XML',
478
+ description: 'Extensible Markup Language',
479
+ icon: 'code',
480
+ mimeType: 'application/xml',
481
+ extension: '.xml'
482
+ }
483
+ }