diff --git a/src/index.ts b/src/index.ts index ed69cd4..756db58 100644 --- a/src/index.ts +++ b/src/index.ts @@ -88,6 +88,20 @@ let menu_template:MenuItemConstructorOptions[] = [ click: function (item, window, event) { window.webContents.send("select-all"); } + }, + { + label: "Undo", + accelerator: "Ctrl+Z", + click: function (item, window, event) { + window.webContents.send("undo"); + } + }, + { + label: "Redo", + accelerator: "Ctrl+Y", + click: function (item, window, event) { + window.webContents.send("redo"); + } } ] }, diff --git a/src/js/editor.ts b/src/js/editor.ts index 74de4ef..d5c201d 100644 --- a/src/js/editor.ts +++ b/src/js/editor.ts @@ -1,13 +1,13 @@ -import { ipcRenderer, clipboard } from 'electron'; +import {ipcRenderer, clipboard} from 'electron'; import * as $ from "../../third_party/js/jquery-3.5.1.js"; import * as Sortable from "../../third_party/js/Sortable.js" import * as Selectable from "../../third_party/js/selectable.js" +import * as util from "util"; let json_data; let tables_data; let current_table; -let selected_cells = []; -let selection_data; let is_positioning = false; +let undo_history = {}; let nav = $("nav"); let tables = $("#tables"); let nav_scroll = nav.scrollLeft(); @@ -36,7 +36,6 @@ function clearPage() { tables.empty(); nav.empty(); $("#details input, #details textarea").val(""); - selection_data = null; } function refreshPage() { @@ -107,6 +106,7 @@ function generateTable(table_data) { } else { table_container.append(table_div); } + createUndoHistory(table_id); } function makeCell(cell_id, cell_text) { @@ -223,9 +223,6 @@ function selectCells(cell_ids) { 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) { @@ -270,8 +267,7 @@ function getSelectionData(force_update=false) { return false; } }) - selection_data = cell_data; - return selection_data; + return cell_data; } function addTable() { @@ -381,6 +377,7 @@ function insertColumn(table_id, index=-1) { }) let selectable = getSelectable(); selectable.add(cells_to_add); + addUndoHistory(current_table); } function insertRow(table_id, index=-1) { @@ -430,6 +427,7 @@ function insertRow(table_id, index=-1) { }) let selectable = getSelectable(); selectable.add(cells_to_add); + addUndoHistory(current_table); } function deleteTable(table_id) { @@ -441,6 +439,7 @@ function deleteTable(table_id) { } } } + deleteUndoHistory(table_id); refreshPage(); } @@ -660,8 +659,10 @@ function closeCellInlineEdit(save=false) { .removeClass("inline-edit"); let data = getSelectionData(); tables_data[data["table_index"]]["rows"][data["row_index"]][data["row_key"]] = val; - selection_data["cell_text"] = val; detail_cell_text.val(val); + if (data["cell_text"] != val) { + addUndoHistory(current_table); + } } else { let data = getSelectionData(); $("td.inline-edit").removeClass("inline-edit") @@ -697,8 +698,10 @@ function closeHeaderInlineEdit(save=false) { .removeClass("inline-edit"); let data = getSelectionData(); tables_data[data["table_index"]]["columns"][data["col_index"]]["title"] = val; - selection_data["col_name"] = val; detail_column_name.val(val); + if (data["col_name"] != val) { + addUndoHistory(current_table); + } } else { let data = getSelectionData(); $("th.inline-edit").removeClass("inline-edit") @@ -707,34 +710,43 @@ function closeHeaderInlineEdit(save=false) { } 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); - getSelectionData(true); + if (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); + getSelectionData(true); + addUndoHistory(current_table); + } } function moveRow(table_id, old_index, new_index) { - for (let table_data of tables_data) { - if (table_data["id"] === table_id) { - let row = table_data["rows"][old_index]; + if (old_index != new_index) { + for (let table_data of tables_data) { + if (table_data["id"] === table_id) { + let row = table_data["rows"][old_index]; - table_data["rows"].splice(old_index, 1); - table_data["rows"].splice(new_index, 0, row); + table_data["rows"].splice(old_index, 1); + table_data["rows"].splice(new_index, 0, row); + } } + getSelectionData(true); + addUndoHistory(current_table); } - getSelectionData(true); } function moveCol(table_id, old_index, new_index) { - for (let table_data of tables_data) { - if (table_data["id"] === table_id) { - let col = table_data["columns"][old_index]; - table_data["columns"].splice(old_index, 1); - table_data["columns"].splice(new_index, 0, col); + if (old_index != new_index) { + for (let table_data of tables_data) { + if (table_data["id"] === table_id) { + let col = table_data["columns"][old_index]; + table_data["columns"].splice(old_index, 1); + table_data["columns"].splice(new_index, 0, col); + } } + getSelectionData(true) + addUndoHistory(current_table); + refreshPage(); } - getSelectionData(true) - refreshPage(); } function enableMovement() { @@ -929,6 +941,7 @@ function copyToClipboard(cut=false) { } if (cut) { clearCells(getSelectedCells()); + addUndoHistory(current_table); } table.append(previous_row_elm); clipboard.clear(); @@ -981,6 +994,7 @@ function pasteFromClipboard() { y++; x = 0; } + addUndoHistory(current_table); } function selectAll(event) { @@ -995,12 +1009,10 @@ function selectAll(event) { } } -function backspaceDelete(event: JQuery.KeyDownEvent) { - if (!event.ctrlKey && !event.shiftKey && !event.altKey && (event.key == "Backspace" || event.key == "Delete")) { - let cells = getSelectedCells(); - if (cells.length > 0) { - clearCells(cells); - } +function backspaceDelete() { + let cells = getSelectedCells(); + if (cells.length > 0) { + clearCells(cells); } } @@ -1019,6 +1031,62 @@ function clearCells(cells: Array) { tables_data[table_index]["rows"][row_index][row_key] = ""; } } + addUndoHistory(current_table); +} + +function createUndoHistory(table_id) { + for (let i=0; i < tables_data.length; i++) { + if (tables_data[i]["id"] == table_id) { + if (!undo_history.hasOwnProperty(table_id)) { + undo_history[table_id] = {"index": 0, "history": []}; + undo_history[table_id]["history"].push(makeCopy(tables_data[i])); + } + } + } +} + +function addUndoHistory(table_id) { + for (let i=0; i < tables_data.length; i++) { + if (tables_data[i]["id"] === table_id) { + let index = undo_history[table_id]["index"]; + if (!util.isDeepStrictEqual(tables_data[i], undo_history[table_id]["history"][index])) { + undo_history[table_id]["history"].splice(0, index, makeCopy(tables_data[i])); + undo_history[table_id]["index"] = 0; + } + } + } +} + +function undo() { + let index = undo_history[current_table]["index"]; + if (undo_history[current_table]["history"].length > index+1) { + index++; + for (let i=0; i < tables_data.length; i++) { + if (tables_data[i]["id"] == current_table) { + tables_data[i] = makeCopy(undo_history[current_table]["history"][index]); + undo_history[current_table]["index"] = index; + } + } + refreshPage(); + } +} + +function redo() { + let index = undo_history[current_table]["index"]; + if (index > 0) { + index--; + for (let i=0; i < tables_data.length; i++) { + if (tables_data[i]["id"] == current_table) { + tables_data[i] = makeCopy(undo_history[current_table]["history"][index]); + undo_history[current_table]["index"] = index; + } + } + refreshPage(); + } +} + +function deleteUndoHistory(table_id) { + delete undo_history[table_id]; } function getColumnIDs(columns): Array { @@ -1071,8 +1139,10 @@ detail_tab_name.on("input", (event) => { let new_tab_name = input.val(); let data = getSelectionData(); tables_data[data["table_index"]]["tab_name"] = new_tab_name; - selection_data["tab_name"] = new_tab_name; -}); +}) + .on("focusout", (event) => { + addUndoHistory(current_table); + }); detail_table_name.on("input", (event) => { let input = $(event.target); @@ -1080,23 +1150,27 @@ detail_table_name.on("input", (event) => { let data = getSelectionData(); $(`#${current_table}-nav`).text(new_table_name); tables_data[data["table_index"]]["name"] = new_table_name; - selection_data["table_name"] = new_table_name; -}); +}) + .on("focusout", (event) => { + addUndoHistory(current_table); + }); detail_table_description.on("input", (event) => { let input = $(event.target); let new_table_description = input.val(); let data = getSelectionData(); tables_data[data["table_index"]]["description"] = new_table_description; - selection_data["table_description"] = new_table_description; }) + .on("focusout", (event) => { + addUndoHistory(current_table); + }); detail_table_hidden.on("input", (event) => { let input = $(event.target); let table_hidden = input.prop("checked"); let data = getSelectionData(); tables_data[data["table_index"]]["hidden"] = table_hidden; - selection_data["table_hidden"] = table_hidden; + addUndoHistory(current_table); }); detail_column_name.on("input", (event) => { @@ -1105,15 +1179,17 @@ detail_column_name.on("input", (event) => { let data = getSelectionData(); $(`#${data["col_id"]}`).text(new_col_name); tables_data[data["table_index"]]["columns"][data["col_index"]]["title"] = new_col_name; - selection_data["col_name"] = new_col_name; -}); +}) + .on("focusout", (event) => { + addUndoHistory(current_table); + }); detail_column_selectable.on("input", (event) => { let input = $(event.target); let column_selectable = input.prop("checked"); let data = getSelectionData(); tables_data[data["table_index"]]["columns"][data["col_index"]]["selectable"] = column_selectable; - selection_data["col_selectable"] = column_selectable; + addUndoHistory(current_table); }); detail_column_sortable.on("input", (event) => { @@ -1121,7 +1197,7 @@ detail_column_sortable.on("input", (event) => { let column_sortable = input.prop("checked"); let data = getSelectionData(); tables_data[data["table_index"]]["columns"][data["col_index"]]["sortable"] = column_sortable; - selection_data["col_sortable"] = column_sortable; + addUndoHistory(current_table); }); detail_cell_text.on("input", (event) => { @@ -1130,8 +1206,10 @@ detail_cell_text.on("input", (event) => { 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; - selection_data["cell_text"] = new_cell_text; -}); +}) + .on("focusout", (event) => { + addUndoHistory(current_table); + }); position_edit_btn.on("click", () => { toggleMovement(); @@ -1165,7 +1243,16 @@ $(document).on("keydown", (event: JQuery.KeyDownEvent) => { selectAll(event); let input_focus = $("input:focus, textarea:focus"); if (input_focus.length === 0) { - backspaceDelete(event); + event.preventDefault(); + if (!event.ctrlKey && !event.shiftKey && !event.altKey && (event.key == "Backspace" || event.key == "Delete")) { + backspaceDelete(); + } + if (!event.shiftKey && !event.altKey && event.ctrlKey && event.key == "z") { + undo(); + } + if (!event.shiftKey && !event.altKey && event.ctrlKey && event.key == "y") { + redo(); + } } }) @@ -1187,3 +1274,11 @@ ipcRenderer.on("select-all", (event) => { }); $(document).trigger(keydown_event); }) + +ipcRenderer.on("undo", (event) => { + undo(); +}) + +ipcRenderer.on("redo", (event) => { + redo(); +})