From 37218c1e81bfe6dcdd715a390c61832baac12ec7 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 21 Mar 2019 12:31:01 +0000 Subject: [PATCH] Add UI for input tabs. Can add, remove and switch tabs --- src/web/InputWaiter.mjs | 183 +++++++++++++++++++++++ src/web/Manager.mjs | 23 +-- src/web/html/index.html | 45 ++++-- src/web/stylesheets/components/_pane.css | 1 + src/web/stylesheets/layout/_io.css | 86 +++++++++-- 5 files changed, 304 insertions(+), 34 deletions(-) diff --git a/src/web/InputWaiter.mjs b/src/web/InputWaiter.mjs index 17b48b6f..fa1b53e5 100755 --- a/src/web/InputWaiter.mjs +++ b/src/web/InputWaiter.mjs @@ -349,6 +349,189 @@ class InputWaiter { window.dispatchEvent(this.manager.statechange); } + /** + * Handler for adding a new input tab. + * + */ + addTab() { + const tabWrapper = document.getElementById("input-tabs"); + const tabsList = tabWrapper.children[0]; + const lastTabNum = tabsList.lastElementChild.id.replace("input-tab-", ""); + const newTabNum = parseInt(lastTabNum, 10) + 1; + + tabWrapper.style.display = "block"; + + // Resize highlighter + // document.getElementById("input-highlighter").style.height = "calc(100% - var(--tab-height) - var(--title-height))"; + + const activeTabElements = document.getElementsByClassName("active-input-tab"); + for (let i = 0; i < activeTabElements.length; i++) { + activeTabElements.item(i).classList.remove("active-input-tab"); + } + + const newTab = document.createElement("li"); + newTab.id = `input-tab-${newTabNum}`; + newTab.classList.add("active-input-tab"); + + const newTabContent = document.createElement("div"); + newTabContent.classList.add("input-tab-content"); + newTabContent.innerText = `Tab ${newTabNum}`; + + const newTabCloseBtn = document.createElement("button"); + newTabCloseBtn.className = "btn btn-primary bmd-btn-icon btn-close-tab"; + newTabCloseBtn.id = `btn-close-tab-${newTabNum}`; + + const newTabCloseBtnIcon = document.createElement("i"); + newTabCloseBtnIcon.classList.add("material-icons"); + newTabCloseBtnIcon.innerText = "clear"; + + newTabCloseBtn.appendChild(newTabCloseBtnIcon); + newTab.appendChild(newTabContent); + newTab.appendChild(newTabCloseBtn); + + tabsList.appendChild(newTab); + + const multiWrapper = document.getElementById("multi-input-wrapper"); + + const activeAreaElements = document.getElementsByClassName("active-input-area"); + for (let i = 0; i < activeAreaElements.length; i++) { + activeAreaElements.item(i).classList.remove("active-input-area"); + } + + const newTextAreaWrapper = document.createElement("div"); + newTextAreaWrapper.className = "textarea-wrapper no-select input-wrapper active-input-area"; + newTextAreaWrapper.id = `tab-input-area-${newTabNum}`; + + const newTextArea = document.createElement("textarea"); + newTextArea.id = `input-text-${newTabNum}`; + newTextArea.spellcheck = "false"; + newTextArea.classList.add("input-text"); + + const newFileArea = document.createElement("div"); + newFileArea.id = `input-file-${newTabNum}`; + newFileArea.classList.add("input-file"); + + const newFileOverlay = document.createElement("div"); + newFileOverlay.classList.add("file-overlay"); + + const newFileWrapper = document.createElement("div"); + newFileWrapper.style.position = "relative"; + newFileWrapper.style.height = "100%"; + + const newFileCard = document.createElement("div"); + newFileCard.className = "io-card card"; + + const newFileThumb = document.createElement("img"); + newFileThumb["aria-hidden"] = "true"; + newFileThumb.src = require("./static/images/file-128x128.png"); + newFileThumb.alt = "File icon"; + newFileThumb.id = `input-file-thumbnail-${newTabNum}`; + + const newFileCardBody = document.createElement("div"); + newFileCardBody.class = "card-body"; + + const newFileCloseButton = document.createElement("button"); + newFileCloseButton.type = "button"; + newFileCloseButton.class = "close"; + newFileCloseButton.id = `input-file-close-${newTabNum}`; + newFileCloseButton.innerHTML = "×"; + + newFileCardBody.appendChild(newFileCloseButton); + + const cardInfo = ` + Name:
+ Size:
+ Type:
+ Loaded: `; + + newFileCardBody.innerHTML = newFileCardBody.innerHTML + cardInfo; + + newFileCard.appendChild(newFileThumb); + newFileCard.appendChild(newFileCardBody); + newFileWrapper.appendChild(newFileCard); + newFileArea.appendChild(newFileOverlay); + newFileArea.appendChild(newFileWrapper); + + newTextAreaWrapper.appendChild(newTextArea); + newTextAreaWrapper.appendChild(newFileArea); + multiWrapper.appendChild(newTextAreaWrapper); + + // file inputs! + } + + /** + * Handler for removing an input tab + * + * @param {event} mouseEvent + */ + removeTab(mouseEvent) { + if (!mouseEvent.path) { + return; + } + const closeBtn = mouseEvent.path[1]; + const liItem = closeBtn.parentElement; + const tabList = liItem.parentElement; + if (tabList.children.length > 1) { + if (liItem.classList.contains("active-input-tab")) { + // If current tab is active, change the active tab and input to another tab + let newActiveAreaId; + if (liItem.previousElementSibling) { + liItem.previousElementSibling.classList.add("active-input-tab"); + const newActiveTabNum = liItem.previousElementSibling.id.replace("input-tab-", ""); + newActiveAreaId = `tab-input-area-${newActiveTabNum}`; + } else if (liItem.nextElementSibling) { + liItem.nextElementSibling.classList.add("active-input-tab"); + const newActiveTabNum = liItem.nextElementSibling.id.replace("input-tab-", ""); + newActiveAreaId = `tab-input-area-${newActiveTabNum}`; + } + + if (newActiveAreaId) { + document.getElementById(newActiveAreaId).classList.add("active-input-area"); + } + } + const tabNum = liItem.id.replace("input-tab-", ""); + const multiInputArea = document.getElementById("multi-input-wrapper"); + const inputAreaId = `tab-input-area-${tabNum}`; + const inputArea = document.getElementById(inputAreaId); + + tabList.removeChild(liItem); + multiInputArea.removeChild(inputArea); + } + if (tabList.children.length === 1) { + document.getElementById("input-tabs").style.display = "none"; + // document.getElementById("input-highlighter").style.height = "calc(100% - var(--title-height))"; + } + } + + /** + * Handler for changing tabs + * + * @param {event} mouseEvent + */ + changeTab(mouseEvent) { + if (!mouseEvent.path) { + return; + } + const tabContent = mouseEvent.path[0]; + const liItem = tabContent.parentElement; + const tabNum = liItem.id.replace("input-tab-", ""); + + const activeTabsList = document.getElementsByClassName("active-input-tab"); + for (let i = 0; i < activeTabsList.length; i++) { + activeTabsList.item(i).classList.remove("active-input-tab"); + } + + const activeAreaList = document.getElementsByClassName("active-input-area"); + for (let i = 0; i < activeAreaList.length; i++) { + activeAreaList.item(i).classList.remove("active-input-area"); + } + liItem.classList.add("active-input-tab"); + + const newActiveAreaId = `tab-input-area-${tabNum}`; + document.getElementById(newActiveAreaId).classList.add("active-input-area"); + + } + } export default InputWaiter; diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 5fa0e8c1..572e9c32 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -142,19 +142,22 @@ class Manager { this.addDynamicListener("textarea.arg", "drop", this.recipe.textArgDrop, this.recipe); // Input - this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input); - this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input); + // this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input); + // this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input); document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app)); document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input)); this.addListeners("#open-file", "change", this.input.inputOpen, this.input); - this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input); - this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input); - this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input); - document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter)); - document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter)); - document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter)); - this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter); - document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input)); + // this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input); + // this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input); + // this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input); + // document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter)); + // document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter)); + // document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter)); + // this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter); + // document.querySelector("#input-file .close").addEventListener("click", this.input.clearIoClick.bind(this.input)); + document.getElementById("btn-new-tab").addEventListener("click", this.input.addTab); + this.addDynamicListener("#input-tabs ul li .btn-close-tab i", "click", this.input.removeTab, this.input); + this.addDynamicListener("#input-tabs ul li .input-tab-content", "click", this.input.changeTab, this.input); // Output document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output)); diff --git a/src/web/html/index.html b/src/web/html/index.html index 119e8ada..4c92a1ec 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -229,6 +229,9 @@
+
-
-
- -
-
-
-
- -
- - Name:
- Size:
- Type:
- Loaded: +
+ +
+
+ +
+
+
+
+ +
+ + Name:
+ Size:
+ Type:
+ Loaded: +
diff --git a/src/web/stylesheets/components/_pane.css b/src/web/stylesheets/components/_pane.css index f98e2f3f..65caf6e6 100755 --- a/src/web/stylesheets/components/_pane.css +++ b/src/web/stylesheets/components/_pane.css @@ -8,6 +8,7 @@ :root { --title-height: 48px; + --tab-height: 40px; } .title { diff --git a/src/web/stylesheets/layout/_io.css b/src/web/stylesheets/layout/_io.css index 2578b57d..d5e5bc3d 100755 --- a/src/web/stylesheets/layout/_io.css +++ b/src/web/stylesheets/layout/_io.css @@ -6,7 +6,7 @@ * @license Apache-2.0 */ -#input-text, +.input-text, #output-text, #output-html { position: relative; @@ -30,12 +30,69 @@ -moz-padding-start: 1px; /* Fixes bug in Firefox */ } -.textarea-wrapper { - position: absolute; - top: var(--title-height); - bottom: 0; +#multi-input-wrapper { + display: flex; + flex-direction: column; + height: calc(100% - var(--title-height)); +} + +#input-tabs ul { + list-style: none; + background-color: var(--title-background-colour); + padding: 0; + margin: 0; + overflow-x: auto; + overflow-y: hidden; + display: flex; + flex-direction: row; + border-bottom: 1px solid var(--primary-border-colour); + height: var(--tab-height); +} + +#input-tabs ul li { + display: flex; + flex-direction: row; width: 100%; + min-width: 120px; + float: left; + padding: 0px; + text-align: center; + border-right: 1px solid var(--primary-border-colour); + height: var(--tab-height); + vertical-align: middle; +} + +#input-tabs ul li:hover { + cursor: pointer; + background-color: var(--primary-background-colour); +} + +.active-input-tab { + font-weight: bold; +} + +.input-tab-content { + width: 100%; + padding-left: 5px; + padding-right: 5px; + padding-top: 10px; + padding-bottom: 10px; + height: var(--tab-height); + vertical-align: middle; +} + +.btn-close-tab { + height: var(--tab-height); + vertical-align: middle; + width: fit-content; +} + +.textarea-wrapper { + width: 100%; + height: 100%; + box-sizing: border-box; overflow: hidden; + pointer-events: auto; } .textarea-wrapper textarea, @@ -45,13 +102,21 @@ color: var(--fixed-width-font-colour); } +.input-wrapper { + display: none; +} + +.active-input-area { + display: inline-block; +} + #input-highlighter, #output-highlighter { position: absolute; left: 0; - top: 0; + bottom: 0; width: 100%; - height: 100%; + height: calc(100% - var(--title-height)); padding: 3px; margin: 0; overflow: hidden; @@ -61,6 +126,7 @@ color: #fff; background-color: transparent; border: none; + pointer-events: none; } #output-loader { @@ -101,13 +167,13 @@ transition: all 0.5s ease; } -#input-file, +.input-file, #output-file { position: absolute; left: 0; - top: 0; + bottom: 0; width: 100%; - height: 100%; + height: calc(100% - var(--tab-height) - var(--title-height)); display: none; }