Can now copy and paste table cells

This commit is contained in:
Matthew Welch 2021-01-20 18:32:24 -08:00
parent 72bff75f0f
commit 96da8eadc4
2 changed files with 230 additions and 85 deletions

View File

@ -27,16 +27,14 @@ let menu_template = [
{
label: "Save",
accelerator: "Ctrl+S",
click: function () {
let window = BrowserWindow.getFocusedWindow();
click: function (item, window, event) {
window.webContents.send("save", false);
}
},
{
label: "Save As",
accelerator: "Ctrl+Shift+S",
click: function () {
let window = BrowserWindow.getFocusedWindow();
click: function (item, window, event) {
window.webContents.send("save", true);
}
},
@ -63,15 +61,26 @@ let menu_template = [
label: "Edit",
submenu: [
{
label: "Copy",
accelerator: "Ctrl+C",
click: function () {
let window = BrowserWindow.getFocusedWindow();
window.webContents.send("copy-to-clipboard");
}
label: "Cut",
role: "cut",
},
{
label: "Copy",
role: "copy",
},
{
label: "Paste",
role: "paste",
},
{
label: "Select All",
accelerator: "Ctrl+A",
click: function (item, window, event) {
window.webContents.send("select-all");
}
}
]
}
},
]
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
@ -121,7 +130,7 @@ function createSplashScreen() {
splash_screen.loadFile(path.join(app.getAppPath(), "src/splash.html"));
// Open the DevTools.
// splash_screen.webContents.openDevTools();
splash_screen.webContents.openDevTools();
// store.openInEditor();
}
@ -142,7 +151,7 @@ function createEditorWindow(json) {
main_window.webContents.send("open", json);
});
// Open the DevTools.
// main_window.webContents.openDevTools();
main_window.webContents.openDevTools();
return main_window;
}

View File

@ -3,7 +3,7 @@ let json_data;
let tables_data;
let current_table;
let selected_cells = [];
let selected_cell_data;
let selection_data;
let is_positioning = false;
let nav = $("nav");
let nav_scroll = nav.scrollLeft();
@ -32,8 +32,7 @@ function clearPage() {
$("#tables").empty();
nav.empty();
$("#details input, #details textarea").val("");
selected_cells = [];
selected_cell_data = null;
selection_data = null;
}
function refreshPage() {
@ -168,7 +167,7 @@ function generateTables() {
function updateDetailsPanel() {
if (!is_positioning) {
let data = getSelectedCellData(true);
let data = getSelectionData(true);
detail_tab_name.val(data["tab_name"]);
detail_table_name.val(data["table_name"]);
detail_table_description.val(data["table_description"]);
@ -215,17 +214,18 @@ function selectCells(cell_ids) {
if (!Array.isArray(cell_ids)) {
cell_ids = [cell_ids];
}
selected_cells = cell_ids;
updateDetailsPanel();
}
function getSelectedCellData(force_update=false) {
if (!force_update && selected_cell_data) {
return selected_cell_data;
function getSelectionData(force_update=false) {
let selected_cells = getSelectedCells();
if (!force_update && selection_data && selection_data["selection_count"] === selected_cells.length) {
return selection_data;
}
let cell_data = {};
$.each(tables_data, (i, table_data) => {
if (table_data["id"] === current_table) {
cell_data["selection_count"] = selected_cells.length;
cell_data["table_index"] = i;
cell_data["table_name"] = table_data["name"];
cell_data["tab_name"] = table_data["tab_name"];
@ -259,13 +259,15 @@ function getSelectedCellData(force_update=false) {
return false;
}
})
} else {
cell_data["row_index"] = -1;
}
}
return false;
}
})
selected_cell_data = cell_data;
return selected_cell_data;
selection_data = cell_data;
return selection_data;
}
function addTable() {
@ -300,7 +302,7 @@ function addTable() {
]
}
tables_data.push(table_data);
getSelectedCellData(true);
getSelectionData(true);
generateTable(table_data);
}
@ -369,7 +371,7 @@ function insertColumn(table_id, index=-1) {
tr.children(`td:nth-child(${index + 1})`)
.insert(cell)
});
getSelectedCellData(true);
getSelectionData(true);
return false;
}
})
@ -377,7 +379,7 @@ function insertColumn(table_id, index=-1) {
selectable.add(cells_to_add);
}
function insertRow(table_id, index) {
function insertRow(table_id, index=-1) {
let cells_to_add = [];
$.each(tables_data, (i, table_data) => {
if (table_data["id"] === table_id) {
@ -418,7 +420,7 @@ function insertRow(table_id, index) {
}
$(`#${table_id} > tbody > tr:nth-child(${index+1})`)
.insert(tr);
getSelectedCellData(true);
getSelectionData(true);
return false;
}
})
@ -465,8 +467,12 @@ function deleteRow(table_id, row_index) {
function cellContextMenu(event, cell_id) {
if (is_positioning) {return;}
if (!selected_cells.includes(cell_id)) {
selectCell(cell_id);
if (!getSelectedCells().includes(cell_id)) {
let selectable = getSelectable();
selectable.selectAll();
selectable.invert();
selectable.select($("#"+cell_id)[0]);
getSelectionData(true);
}
let menu = $("<div>")
.attr("id", "context-menu-"+cell_id)
@ -477,37 +483,37 @@ function cellContextMenu(event, cell_id) {
.append($("<button>")
.addClass("list-group-item list-group-item-action")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
insertRow(current_table, data["row_index"]);
})
.text("Insert row above"))
.append($("<button>")
.addClass("list-group-item list-group-item-action")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
insertRow(current_table, data["row_index"]+1);
})
.text("Insert row below"))
.append($("<button>")
.addClass("list-group-item list-group-item-action")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
insertColumn(current_table, data["col_index"]);
})
.text("Insert column left"))
.append($("<button>")
.addClass("list-group-item list-group-item-action")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
insertColumn(current_table, data["col_index"]+1);
})
.text("Insert column right"))
let data = getSelectedCellData();
let data = getSelectionData();
if (data["num_cols"] > 1) {
menu.append($("<button>")
.addClass("list-group-item list-group-item-action list-group-item-danger")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
deleteColumn(current_table, data["col_index"]);
menu.remove();
})
@ -516,8 +522,8 @@ function cellContextMenu(event, cell_id) {
if (data["num_rows"] > 1) {
menu.append($("<button>")
.addClass("list-group-item list-group-item-action list-group-item-danger")
.on("click", () => {
let data = getSelectedCellData();
.on("click", function() {
let data = getSelectionData();
deleteRow(current_table, data["row_index"]);
menu.remove();
})
@ -536,8 +542,12 @@ function cellContextMenu(event, cell_id) {
function headerContextMenu(event, header_id) {
if (is_positioning) {return;}
if (!selected_cells.includes(header_id)) {
selectCell(header_id);
if (!getSelectedCells().includes(header_id)) {
let selectable = getSelectable();
selectable.selectAll();
selectable.invert();
selectable.select($("#"+header_id)[0]);
getSelectionData(true);
}
let menu = $("<div>")
.attr("id", "context-menu-"+header_id)
@ -548,24 +558,24 @@ function headerContextMenu(event, header_id) {
.append($("<button>")
.addClass("list-group-item list-group-item-action")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
insertColumn(current_table, data["col_index"]);
})
.text("Insert column left"))
.append($("<button>")
.addClass("list-group-item list-group-item-action")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
insertColumn(current_table, data["col_index"]+1);
})
.text("Insert column right"));
let data = getSelectedCellData();
let data = getSelectionData();
if (data["num_cols"] > 1) {
menu.append($("<button>")
.addClass("list-group-item list-group-item-action list-group-item-danger")
.on("click", () => {
let data = getSelectedCellData();
let data = getSelectionData();
deleteColumn(current_table, data["col_index"]);
menu.remove();
})
@ -584,7 +594,7 @@ function headerContextMenu(event, header_id) {
function navContextMenu(event, table_id) {
if (is_positioning) {return;}
let data = getSelectedCellData();
let data = getSelectionData();
let menu = $("<div>")
.attr("id", "context-menu-"+table_id)
.addClass("border context-menu text-nowrap list-group")
@ -621,7 +631,7 @@ function navContextMenu(event, table_id) {
function cellInlineEdit(event) {
if (is_positioning) {return;}
let data = getSelectedCellData()
let data = getSelectionData()
let target = $(event.target);
let text_area = $("<textarea>")
.on("focusout", () => {
@ -645,12 +655,12 @@ function closeCellInlineEdit(save=false) {
let val = $("td.inline-edit > textarea").val()
$("td.inline-edit").html(val)
.removeClass("inline-edit");
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["rows"][data["row_index"]][data["row_key"]] = val;
selected_cell_data["cell_text"] = val;
selection_data["cell_text"] = val;
detail_cell_text.val(val);
} else {
let data = getSelectedCellData();
let data = getSelectionData();
$("td.inline-edit").removeClass("inline-edit")
.html(data["cell_text"]);
}
@ -658,7 +668,7 @@ function closeCellInlineEdit(save=false) {
function headerInlineEdit(event) {
if (is_positioning) {return;}
let data = getSelectedCellData()
let data = getSelectionData()
let target = $(event.target);
let text_area = $("<textarea>")
.on("focusout", () => {
@ -682,12 +692,12 @@ function closeHeaderInlineEdit(save=false) {
let val = $("th.inline-edit > textarea").val()
$("th.inline-edit").html(val)
.removeClass("inline-edit");
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["columns"][data["col_index"]]["title"] = val;
selected_cell_data["col_name"] = val;
selection_data["col_name"] = val;
detail_column_name.val(val);
} else {
let data = getSelectedCellData();
let data = getSelectionData();
$("th.inline-edit").removeClass("inline-edit")
.html(data["col_name"]);
}
@ -697,7 +707,7 @@ function moveTable(old_index, new_index) {
let table_data = tables_data[old_index];
tables_data.splice(old_index, 1);
tables_data.splice(new_index, 0, table_data);
getSelectedCellData(true);
getSelectionData(true);
}
function moveRow(table_id, old_index, new_index) {
@ -709,7 +719,7 @@ function moveRow(table_id, old_index, new_index) {
table_data["rows"].splice(new_index, 0, row);
}
}
getSelectedCellData(true);
getSelectionData(true);
}
function moveCol(table_id, old_index, new_index) {
@ -720,7 +730,7 @@ function moveCol(table_id, old_index, new_index) {
table_data["columns"].splice(new_index, 0, col);
}
}
getSelectedCellData(true)
getSelectionData(true)
refreshPage();
}
@ -804,7 +814,6 @@ function disableSelection() {
selectable.clear();
selectable.disable();
}
selected_cells = [];
updateDetailsPanel();
}
@ -833,13 +842,7 @@ function getSelectable() {
$("*").blur();
})
selectable.on("end", (event, selected, unselected) => {
let cells = [];
$.each(selected, (i, item) => {
cells.push($(item.node).attr("id"));
});
if (JSON.stringify(cells) !== JSON.stringify(selected_cells)) {
selectCells(cells);
}
updateDetailsPanel();
});
if (is_positioning) {
selectable.disable();
@ -864,11 +867,38 @@ function selectFirstCell() {
getSelectable().select($("table:visible > tbody > tr:first > td:first")[0]);
}
function copyToClipboard() {
function getDeselectingItems(invert) {
return getSelectable().getItems().filter(function(item) {
return invert ? !item.deselecting : item.deselecting;
});
}
function getDeselectingNodes() {
return getDeselectingItems().map(function(item) {
return item.node;
});
}
function getSelectedCells() {
let selectable = getSelectable();
let nodes = selectable.getSelectedNodes();
if (nodes.length === 0) {
nodes = getDeselectingNodes();
}
let selected = [];
for (let node of nodes) {
selected.push($(node).attr("id"));
}
return selected;
}
function copyToClipboard(cut=false) {
let selectable = getSelectable();
let items = selectable.getSelectedItems();
let selection = "";
let previous_row;
let previous_row_elm = $("<tr>");
let table = $("<table>");
for (let item of items) {
let el = $(item["node"]);
let row_pattern = /(row\d+)/;
@ -880,17 +910,83 @@ function copyToClipboard() {
row = "header";
}
let item_text = el.text();
if (cut) {
el.text("");
let data = getSelectionData();
let table_index = data["table_index"];
let col_index = el.index();
if (el.prop("tagName") === "TH") {
tables_data[table_index]["columns"][col_index]["title"] = "";
} else {
let row_key = tables_data[table_index]["columns"][col_index]["field"];
let row_index = el.parent().index();
tables_data[table_index]["rows"][row_index][row_key] = "";
}
}
if (!previous_row) {
selection += item_text;
previous_row_elm.append("<td>"+item_text+"</td>");
} else if (previous_row === row) {
selection += "\t"+item_text;
previous_row_elm.append("<td>"+item_text+"</td>");
} else {
selection += "\n"+item_text;
selection += "\r\n"+item_text;
table.append(previous_row_elm);
previous_row_elm = $("<tr>");
previous_row_elm.append("<td>"+item_text+"</td>");
}
previous_row = row;
}
clipboard.writeText(selection);
console.log(selection);
table.append(previous_row_elm);
clipboard.clear();
clipboard.write({
"text": selection,
"html": table.prop("outerHTML"),
})
}
function pasteFromClipboard() {
let text = clipboard.readText();
let rows = text.split("\r\n");
for (let i=0; i < rows.length; i++) {
rows[i] = rows[i].split("\t");
}
let selectable = getSelectable();
let selected_items = selectable.getSelectedItems();
selected_items.splice(0, 1);
if(selected_items.length > 0) {
selectable.deselect(selected_items);
}
let data = getSelectionData();
let row_index = data["row_index"];
let col_index = data["col_index"];
let num_rows_needed = row_index+1+rows.length-(data["num_rows"]+1);
let num_cols_needed = col_index+rows[0].length-data["num_cols"];
for (let i=0; i < num_rows_needed; i++) {
insertRow(current_table);
}
for (let i=0; i < num_cols_needed; i++) {
insertColumn(current_table);
}
let x = 0;
let y = 0;
for (let i=row_index; i < row_index+rows.length; i++) {
for (let j=col_index; j < col_index+rows[0].length; j++) {
let cell_elm;
if (i === -1) {
cell_elm = $(`#${current_table} > thead > tr > th:nth-child(${j+1})`);
tables_data[data["table_index"]]["columns"][j]["title"] = rows[y][x];
} else {
cell_elm = $(`#${current_table} > tbody > tr:nth-child(${i+1}) > td:nth-child(${j+1})`);
let row_key = tables_data[data["table_index"]]["columns"][j]["field"];
tables_data[data["table_index"]]["rows"][i][row_key] = rows[y][x];
}
cell_elm.text(rows[y][x]);
x++;
}
y++;
x = 0;
}
}
function getColumnIDs(columns) {
@ -917,6 +1013,10 @@ function getTableIDs() {
return ids;
}
function makeCopy(json) {
return JSON.parse(JSON.stringify(json));
}
nav.on("wheel", (event) => {
event.preventDefault();
if (event.originalEvent.deltaY > 0) {
@ -937,68 +1037,68 @@ nav.on("wheel", (event) => {
detail_tab_name.on("input", (event) => {
let input = $(event.target);
let new_tab_name = input.val();
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["tab_name"] = new_tab_name;
selected_cell_data["tab_name"] = new_tab_name;
selection_data["tab_name"] = new_tab_name;
});
detail_table_name.on("input", (event) => {
let input = $(event.target);
let new_table_name = input.val();
let data = getSelectedCellData();
let data = getSelectionData();
$(`#${current_table}-nav`).text(new_table_name);
tables_data[data["table_index"]]["name"] = new_table_name;
selected_cell_data["table_name"] = new_table_name;
selection_data["table_name"] = new_table_name;
});
detail_table_description.on("input", (event) => {
let input = $(event.target);
let new_table_description = input.val();
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["description"] = new_table_description;
selected_cell_data["table_description"] = new_table_description;
selection_data["table_description"] = new_table_description;
})
detail_table_hidden.on("input", (event) => {
let input = $(event.target);
let table_hidden = input.prop("checked");
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["hidden"] = table_hidden;
selected_cell_data["table_hidden"] = table_hidden;
selection_data["table_hidden"] = table_hidden;
});
detail_column_name.on("input", (event) => {
let input = $(event.target);
let new_col_name = input.val();
let data = getSelectedCellData();
let data = getSelectionData();
$(`#${data["col_id"]}`).text(new_col_name);
tables_data[data["table_index"]]["columns"][data["col_index"]]["title"] = new_col_name;
selected_cell_data["col_name"] = new_col_name;
selection_data["col_name"] = new_col_name;
});
detail_column_selectable.on("input", (event) => {
let input = $(event.target);
let column_selectable = input.prop("checked");
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["columns"][data["col_index"]]["selectable"] = column_selectable;
selected_cell_data["col_selectable"] = column_selectable;
selection_data["col_selectable"] = column_selectable;
});
detail_column_sortable.on("input", (event) => {
let input = $(event.target);
let column_sortable = input.prop("checked");
let data = getSelectedCellData();
let data = getSelectionData();
tables_data[data["table_index"]]["columns"][data["col_index"]]["sortable"] = column_sortable;
selected_cell_data["col_sortable"] = column_sortable;
selection_data["col_sortable"] = column_sortable;
});
detail_cell_text.on("input", (event) => {
let input = $(event.target);
let new_cell_text = input.val();
let data = getSelectedCellData();
let data = getSelectionData();
$(`#${data["cell_id"]}`).text(new_cell_text);
tables_data[data["table_index"]]["rows"][data["row_index"]][data["row_key"]] = new_cell_text;
selected_cell_data["cell_text"] = new_cell_text;
selection_data["cell_text"] = new_cell_text;
});
position_edit_btn.on("click", () => {
@ -1008,10 +1108,39 @@ position_edit_btn.on("click", () => {
$(document).on("copy", (event) => {
let input_focus = $("input:focus, textarea:focus");
if (input_focus.length === 0) {
event.preventDefault();
copyToClipboard();
}
})
$(document).on("cut", (event) => {
let input_focus = $("input:focus, textarea:focus");
if (input_focus.length === 0) {
event.preventDefault();
copyToClipboard(true);
}
})
$(document).on("paste", (event) => {
let input_focus = $("input:focus, textarea:focus");
if (input_focus.length === 0) {
event.preventDefault();
pasteFromClipboard();
}
})
$(document).on("keydown", (event) => {
if (event.ctrlKey && event.key.toLowerCase() === "a") {
let input_focus = $("input:focus, textarea:focus");
if (input_focus.length === 0) {
event.preventDefault();
getSelectable().selectAll();
} else {
event.preventDefault();
$(":focus").select();
}
}
})
ipcRenderer.on("open", (event, json) => {
openFile(json);
@ -1021,6 +1150,13 @@ ipcRenderer.on("save", (event, create_new_file) => {
saveFile(create_new_file);
});
ipcRenderer.on("copy-to-clipboard", (event) => {
copyToClipboard();
ipcRenderer.on("select-all", (event) => {
let keydown_event = jQuery.Event( "keydown", {
bubbles: true,
cancelable: true,
ctrlKey: true,
key: "a",
code: "KeyA",
});
$(document).trigger(keydown_event);
})