From 6e679f79b660d87e53b94ee9172c4ee42728d584 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Wed, 14 Jun 2017 21:44:18 -0700 Subject: [PATCH] Add Editor class to generalize in-place editors. --- frontends/etiquette_flask/static/common.css | 15 ++ frontends/etiquette_flask/static/common.js | 190 ++++++++++++++++++++ 2 files changed, 205 insertions(+) diff --git a/frontends/etiquette_flask/static/common.css b/frontends/etiquette_flask/static/common.css index 9e09fab..f7f17cb 100644 --- a/frontends/etiquette_flask/static/common.css +++ b/frontends/etiquette_flask/static/common.css @@ -31,6 +31,11 @@ li margin-bottom: 4px; } +.editor_input +{ + width: 100%; + max-width: 800px; +} .header_element { display: flex; @@ -43,6 +48,10 @@ li { background-color: #ffffd4; } +.hidden +{ + display: none !important; +} #content_body { display: flex; @@ -51,6 +60,8 @@ li } #search_go_button, .add_tag_button, +.editor_open_button, +.editor_save_button, .green_button { border-top: 2px solid #c2ffc3; @@ -61,6 +72,8 @@ li } #search_go_button:active, .add_tag_button:active, +.editor_open_button:active, +.editor_save_button:active, .green_button:active { border-top: 2px solid #259427; @@ -70,6 +83,7 @@ li } .remove_tag_button, .remove_tag_button_perm, +.editor_cancel_button, .red_button { border-top: 2px solid #ffacac; @@ -81,6 +95,7 @@ li } .remove_tag_button:active, .remove_tag_button_perm:active, +.editor_cancel_button:active, .red_button:active { border-top: 2px solid #bd1b1b; diff --git a/frontends/etiquette_flask/static/common.js b/frontends/etiquette_flask/static/common.js index 97ba1b4..21e328e 100644 --- a/frontends/etiquette_flask/static/common.js +++ b/frontends/etiquette_flask/static/common.js @@ -1,3 +1,193 @@ +function Editor(elements, on_open, on_save, on_cancel) +{ + /* + This class wraps around display elements like headers and paragraphs, and + creates inputs / textareas to edit them with. + + The placeholder text for the edit elements comes from the + data-editor-placeholder attribute of the display elements if available. + + The on_open, on_save and on_cancel callbacks will receive two arguments: + 1. This editor object. + 2. the edit elements as either: + If the display elements ALL have data-editor-id attributes, + then a dictionary of {data-editor-id: edit_element, ...}. + Otherwise, an array of [edit_element, ...] in their original order. + + When your callbacks are used, the default `open`, `save`, `cancel` + methods are not called automatically. You should call them from within + your function. + */ + this.cancel = function() + { + this.close(); + }; + + this.close = function() + { + for (var index = 0; index < this.display_elements.length; index += 1) + { + this.display_elements[index].classList.remove("hidden"); + this.edit_elements[index].classList.add("hidden"); + } + this.open_button.classList.remove("hidden") + this.save_button.classList.add("hidden"); + this.cancel_button.classList.add("hidden"); + }; + + this.hide_spinner = function() + { + this.spinner.classList.add("hidden"); + }; + + this.open = function() + { + for (var index = 0; index < this.display_elements.length; index += 1) + { + var display_element = this.display_elements[index]; + var edit_element = this.edit_elements[index]; + display_element.classList.add("hidden"); + edit_element.classList.remove("hidden"); + + edit_element.value = display_element.innerText; + } + this.open_button.classList.add("hidden") + this.save_button.classList.remove("hidden"); + this.cancel_button.classList.remove("hidden"); + }; + + this.save = function() + { + for (var index = 0; index < this.display_elements.length; index += 1) + { + var display_element = this.display_elements[index]; + var edit_element = this.edit_elements[index]; + + display_element.innerText = edit_element.value; + } + + this.close(); + }; + + this.show_spinner = function() + { + this.spinner.classList.remove("hidden"); + }; + + this.display_elements = []; + this.edit_elements = []; + this.can_use_element_map = true; + this.edit_element_map = {}; + + for (var index = 0; index < elements.length; index += 1) + { + var display_element = elements[index]; + var edit_element; + if (display_element.tagName == "P") + { + edit_element = document.createElement("textarea"); + edit_element.rows = 6; + } + else + { + edit_element = document.createElement("input"); + edit_element.type = "text"; + } + edit_element.classList.add("editor_input"); + edit_element.classList.add("hidden"); + if (display_element.dataset.editorPlaceholder !== undefined) + { + edit_element.placeholder = display_element.dataset.editorPlaceholder; + } + if (this.can_use_element_map) + { + if (display_element.dataset.editorId !== undefined) + { + this.edit_element_map[display_element.dataset.editorId] = edit_element; + } + else + { + this.can_use_element_map = false; + this.edit_element_map = null; + } + } + + display_element.parentElement.insertBefore(edit_element, display_element.nextSibling); + + this.display_elements.push(display_element); + this.edit_elements.push(edit_element); + } + + var self = this; + var binder = function(func, fallback) + { + if (func == undefined) + { + return fallback; + } + + var bound = function() + { + if (this.can_use_element_map) + { + func(self, self.edit_element_map); + } + else + { + func(self, self.edit_elements); + } + } + return bound; + } + + this.bound_open = binder(on_open, this.open); + this.bound_save = binder(on_save, this.save); + this.bound_cancel = binder(on_cancel, this.cancel); + + var last_element = this.edit_elements[this.edit_elements.length - 1]; + var toolbox = document.createElement("div"); + last_element.parentElement.insertBefore(toolbox, last_element.nextSibling); + + this.open_button = document.createElement("button"); + this.open_button.innerText = "Edit"; + this.open_button.classList.add("editor_open_button"); + this.open_button.onclick = this.bound_open.bind(this); + toolbox.appendChild(this.open_button); + + this.save_button = document.createElement("button"); + this.save_button.innerText = "Save"; + this.save_button.classList.add("editor_save_button"); + this.save_button.classList.add("hidden"); + this.save_button.onclick = this.bound_save.bind(this); + toolbox.appendChild(this.save_button); + + this.cancel_button = document.createElement("button"); + this.cancel_button.innerText = "Cancel"; + this.cancel_button.classList.add("editor_cancel_button"); + this.cancel_button.classList.add("hidden"); + this.cancel_button.onclick = this.bound_cancel.bind(this); + toolbox.appendChild(this.cancel_button); + + this.spinner = document.createElement("span"); + this.spinner.innerText = "Submitting..."; + this.spinner.classList.add("editor_spinner"); + this.spinner.classList.add("hidden"); + toolbox.appendChild(this.spinner); + + for (var index = 0; index < this.edit_elements.length; index += 1) + { + var edit_element = this.edit_elements[index]; + if (edit_element.tagName == "TEXTAREA") + { + bind_box_to_button(edit_element, this.save_button, true); + } + else + { + bind_box_to_button(edit_element, this.save_button, false); + } + } +} + function create_message_bubble(message_area, message_positivity, message_text, lifespan) { if (lifespan === undefined)