Skip to content

Commit

Permalink
217 advanced clipboard copy for datagrids (#563)
Browse files Browse the repository at this point in the history
* adds advanced clipboard copy for datagrids refs: #217
* adds copy selected or all table data, handles case where column names "?column?"
  • Loading branch information
plucik authored Nov 28, 2024
1 parent 9caa3cc commit ece3706
Showing 1 changed file with 136 additions and 15 deletions.
151 changes: 136 additions & 15 deletions pgmanage/app/static/pgmanage_frontend/src/components/QueryResultTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export default {
table: null,
heightSubtract: 200,
colWidthArray: [],
columns: []
};
},
computed: {
Expand Down Expand Up @@ -206,6 +207,107 @@ export default {
this.handleResize();
},
methods: {
copyTableData(format) {
const selectedData = this.getSelectedDataInDisplayOrder();
const data = selectedData.length > 0 ? selectedData : this.table.getData();
const headers = this.columns;
if (format === "json") {
const jsonOutput = this.generateJson(data, headers);
this.copyToClipboard(jsonOutput);
} else if (format === "csv") {
const csvOutput = this.generateCsv(data, headers);
this.copyToClipboard(csvOutput);
} else if (format === "markdown") {
const markdownOutput = this.generateMarkdown(data, headers);
this.copyToClipboard(markdownOutput);
}
},
getSelectedDataInDisplayOrder() {
const rowComponents = this.table.getSelectedRows();
const rowsWithPosition = rowComponents.map((row) => ({
data: row.getData(),
position: row.getPosition(),
}));
rowsWithPosition.sort((a, b) => a.position - b.position);
return rowsWithPosition.map((row) => row.data);
},
copyToClipboard(text) {
navigator.clipboard
.writeText(text)
.then(() => {
})
.catch((error) => {
showToast("error", error);
});
},
generateJson(data, headers) {
const columns = headers.map((col, index) => ({
field: index,
title: col,
}));
const mappedData = data.map((row) => {
const mappedRow = {};
columns.forEach((col) => {
mappedRow[col.title] = row[col.field];
});
return mappedRow;
});
return JSON.stringify(mappedData, null, 2);
},
generateCsv(data, headers) {
const csvRows = [];
// Add header row
csvRows.push(headers.join(settingsStore.csvDelimiter));
data.forEach((row) => {
csvRows.push(row.join(settingsStore.csvDelimiter));
});
return csvRows.join("\n");
},
generateMarkdown(data, headers) {
const columnWidths = headers.map((header, index) => {
const maxDataLength = data.reduce(
(max, row) => Math.max(max, (row[index] || "").toString().length),
0
);
return Math.max(header.length, maxDataLength);
});
// Helper to pad strings to a given length
const pad = (str, length) => str.toString().padEnd(length, " ");
const mdRows = [];
// Add padded header row
mdRows.push(
`| ${headers
.map((header, index) => pad(header, columnWidths[index]))
.join(" | ")} |`
);
// Add separator row
mdRows.push(
`| ${columnWidths.map((width) => "-".repeat(width)).join(" | ")} |`
);
data.forEach((row) => {
mdRows.push(
`| ${row
.map((cell, index) => pad(cell || "", columnWidths[index]))
.join(" | ")} |`
);
});
return mdRows.join("\n");
},
cellFormatter(cell, params, onRendered) {
let cellVal = cell.getValue()
if (isNil(cellVal)) {
Expand Down Expand Up @@ -304,23 +406,42 @@ export default {
this.updateTableData(data);
},
prepareColumns(colNames, colTypes) {
let cellContextMenu = [
{
label:
'<div style="position: absolute;"><i class="fas fa-copy cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">Copy</div>',
action: function (e, cell) {
cell.getTable().copyToClipboard("selected");
this.columns = colNames.map((colName, idx) => {
return colName === '?column?' ? `column-${idx}` : colName
})
let cellContextMenu = () => {
const isAnyRowsSelected = !!this.table.getSelectedData().length;
const copyText = `${isAnyRowsSelected ? "selected" : "table data"}`
return [
{
label: `<div style="position: absolute;"><i class="fas fa-copy cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">Copy ${copyText} as JSON</div>`,
action: () => this.copyTableData("json"),
},
},
{
label:
'<div style="position: absolute;"><i class="fas fa-edit cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">View Content</div>',
action: (e, cell) => {
cellDataModalStore.showModal(cell.getValue())
{
label: `<div style="position: absolute;"><i class="fas fa-copy cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">Copy ${copyText} as CSV</div>`,
action: () => this.copyTableData("csv"),
},
},
];
let columns = colNames.map((col, idx) => {
{
label: `<div style="position: absolute;"><i class="fas fa-copy cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">Copy ${copyText} as Markdown</div>`,
action: () => this.copyTableData("markdown"),
},
{
label:
'<div style="position: absolute;"><i class="fas fa-copy cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">Copy</div>',
action: function (e, cell) {
cell.getTable().copyToClipboard("selected");
},
},
{
label:
'<div style="position: absolute;"><i class="fas fa-edit cm-all" style="vertical-align: middle;"></i></div><div style="padding-left: 30px;">View Content</div>',
action: (e, cell) => {
cellDataModalStore.showModal(cell.getValue())
},
},
];
}
let columns = this.columns.map((col, idx) => {
let formatTitle = function(col, idx) {
if(colTypes?.length === 0 )
return col
Expand Down

0 comments on commit ece3706

Please sign in to comment.