From 2921c142de9fd94902e82b8612e46c88dc59f589 Mon Sep 17 00:00:00 2001 From: David Larlet Date: Mon, 8 Jul 2024 14:58:16 -0400 Subject: [PATCH] feat: ability to export tableeditor data as CSV --- umap/static/umap/css/icon.css | 4 +++ umap/static/umap/js/modules/tableeditor.js | 39 ++++++++++++++++++++-- umap/static/umap/js/modules/utils.js | 9 +++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/umap/static/umap/css/icon.css b/umap/static/umap/css/icon.css index fb22329f..ad4e4cfb 100644 --- a/umap/static/umap/css/icon.css +++ b/umap/static/umap/css/icon.css @@ -118,6 +118,10 @@ .icon-upload { background-position: -144px -97px; } +.icon-download { + transform: rotate(180deg); + background-position: -146px -95px; +} .icon-zoom { background-position: -1px -49px; } diff --git a/umap/static/umap/js/modules/tableeditor.js b/umap/static/umap/js/modules/tableeditor.js index 622dee21..f5db47b0 100644 --- a/umap/static/umap/js/modules/tableeditor.js +++ b/umap/static/umap/js/modules/tableeditor.js @@ -1,7 +1,7 @@ import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js' import { translate } from './i18n.js' import ContextMenu from './ui/contextmenu.js' -import { WithTemplate, loadTemplate } from './utils.js' +import { WithTemplate, loadTemplate, downloadBlob } from './utils.js' const TEMPLATE = ` @@ -201,6 +201,13 @@ export default class TableEditor extends WithTemplate { filterButton.addEventListener('click', () => this.map.browser.open('filters')) actions.push(filterButton) + const downloadButton = loadTemplate(` + `) + downloadButton.addEventListener('click', () => this.exportAsCSV()) + actions.push(downloadButton) + this.map.fullPanel.open({ content: this.table, className: 'umap-table-editor', @@ -295,7 +302,7 @@ export default class TableEditor extends WithTemplate { getSelectedRows() { return Array.from( this.elements.body.querySelectorAll('input[type=checkbox]:checked') - ).map((checkbox) => checkbox.parentNode.parentNode) + ).map((checkbox) => checkbox.closest('tr')) } getFocus() { @@ -331,4 +338,32 @@ export default class TableEditor extends WithTemplate { } }) } + + _rowToCSV(row) { + console.log(row) + return row + .map((content) => content.replaceAll('"', '""')) // escape double quotes + .map((content) => `"${content}"`) // quote it + .join(',') // comma-separated + } + + exportAsCSV() { + const headers = this._rowToCSV( + Array.from(this.elements.header.querySelectorAll('th')) + .slice(1) // Remove initial select-all checkbox column + .map((header) => header.textContent) + .map((header) => header.slice(0, -1)) // Remove trailing `…` + ) + const rows = Array.from(this.elements.body.querySelectorAll('tr')) + .map((line) => + Array.from(line.querySelectorAll('td')).map((cell) => cell.textContent) + ) + .map(this._rowToCSV) + const csv = [headers, ...rows].join('\r\n') + downloadBlob( + csv, + `umap-export-${this.datalayer.umap_id}-${this.map.options.umap_id}.csv`, + 'text/csv;charset=utf-8;' + ) + } } diff --git a/umap/static/umap/js/modules/utils.js b/umap/static/umap/js/modules/utils.js index 9b7b11f1..121c24c9 100644 --- a/umap/static/umap/js/modules/utils.js +++ b/umap/static/umap/js/modules/utils.js @@ -397,3 +397,12 @@ export class WithTemplate { return this.element } } + +export function downloadBlob(content, filename, contentType) { + const blob = new Blob([content], { type: contentType }) + const url = URL.createObjectURL(blob) + const tmp = document.createElement('a') + tmp.href = url + tmp.setAttribute('download', filename) + tmp.click() +}