From ef66f6ad5a46493908611a306c66d0a7dd857afb Mon Sep 17 00:00:00 2001 From: void Date: Fri, 13 Nov 2020 17:35:12 -0300 Subject: [PATCH] toolbar auxiliar --- app/assets/javascripts/01-types.js | 22 ++++++++ app/assets/javascripts/02-editor.js | 14 ++++- app/assets/stylesheets/editor.scss | 21 +++++++ app/views/application/markdown.haml | 86 +++++++++++++++-------------- 4 files changed, 100 insertions(+), 43 deletions(-) diff --git a/app/assets/javascripts/01-types.js b/app/assets/javascripts/01-types.js index ea6865b6..62749598 100644 --- a/app/assets/javascripts/01-types.js +++ b/app/assets/javascripts/01-types.js @@ -1,3 +1,15 @@ +function setAuxiliaryToolbar (editorEl, toolbarName) { + const toolbarEl = editorEl.querySelector(`*[data-editor-auxiliary-toolbar]`) + for (const otherEl of toolbarEl.childNodes) { + if (otherEl.nodeType !== Node.ELEMENT_NODE) continue + otherEl.classList.remove("editor-auxiliary-tool-active") + } + if (toolbarName) { + const auxEl = editorEl.querySelector(`*[data-editor-auxiliary="${toolbarName}"]`) + auxEl.classList.add("editor-auxiliary-tool-active") + } +} + const marks = { bold: { selector: "strong", @@ -146,6 +158,8 @@ const typesWithProperties = { mark: { selector: marks.mark.selector, updateInput (el, editorEl) { + setAuxiliaryToolbar(editorEl, "mark") + const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`) markColorInputEl.disabled = false markColorInputEl.value = el.style.backgroundColor ? rgb2hex(el.style.backgroundColor) : "#f206f9" @@ -166,6 +180,8 @@ const typesWithProperties = { img: { selector: blocks.img.selector, updateInput (el, editorEl) { + setAuxiliaryToolbar(editorEl, "img") + const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`) imgFileEl.disabled = false // XXX: No se puede cambiar el texto, ¡esto puede ser confuso! @@ -217,6 +233,8 @@ const typesWithProperties = { audio: { selector: blocks.audio.selector, updateInput (el, editorEl) { + setAuxiliaryToolbar(editorEl, "audio") + const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`) audioFileEl.disabled = false // XXX: No se puede cambiar el texto, ¡esto puede ser confuso! @@ -254,6 +272,8 @@ const typesWithProperties = { video: { selector: blocks.video.selector, updateInput (el, editorEl) { + setAuxiliaryToolbar(editorEl, "video") + const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`) videoFileEl.disabled = false // XXX: No se puede cambiar el texto, ¡esto puede ser confuso! @@ -291,6 +311,8 @@ const typesWithProperties = { pdf: { selector: blocks.pdf.selector, updateInput (el, editorEl) { + setAuxiliaryToolbar(editorEl, "pdf") + const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`) pdfFileEl.disabled = false // XXX: No se puede cambiar el texto, ¡esto puede ser confuso! diff --git a/app/assets/javascripts/02-editor.js b/app/assets/javascripts/02-editor.js index b0e9aa14..ad9dc42c 100644 --- a/app/assets/javascripts/02-editor.js +++ b/app/assets/javascripts/02-editor.js @@ -346,6 +346,11 @@ function setupEditor (editorEl) { const sel = window.getSelection() const range = sel.getRangeAt(0) + let parentEl = range.commonAncestorContainer + if (parentEl.nodeType !== Node.ELEMENT_NODE) parentEl = parentEl.parentElement + + if (!contentEl.contains(parentEl)) return + // Borrar todas las selecciones for (const el of contentEl.querySelectorAll(".selected")) { el.classList.remove("selected") @@ -354,8 +359,7 @@ function setupEditor (editorEl) { el.classList.remove("selected-unactive") } - let parentEl = range.commonAncestorContainer - if (parentEl.nodeType !== Node.ELEMENT_TYPE) parentEl = parentEl.parentElement + setAuxiliaryToolbar(editorEl) for (const [name, type] of Object.entries(typesWithProperties)) { let i = 0 @@ -420,6 +424,12 @@ function setupEditor (editorEl) { type.setupInput(editorEl, contentEl) } + document.addEventListener(editorBtn("mark"), () => setAuxiliaryToolbar(editorEl, "mark")) + document.addEventListener(editorBtn("img"), () => setAuxiliaryToolbar(editorEl, "img")) + document.addEventListener(editorBtn("audio"), () => setAuxiliaryToolbar(editorEl, "audio")) + document.addEventListener(editorBtn("video"), () => setAuxiliaryToolbar(editorEl, "video")) + document.addEventListener(editorBtn("pdf"), () => setAuxiliaryToolbar(editorEl, "pdf")) + cleanContent(contentEl) htmlEl.value = contentEl.innerHTML } diff --git a/app/assets/stylesheets/editor.scss b/app/assets/stylesheets/editor.scss index 7cc714f1..af7ec76a 100644 --- a/app/assets/stylesheets/editor.scss +++ b/app/assets/stylesheets/editor.scss @@ -23,6 +23,27 @@ margin: 0 auto; } + .editor-toolbar { + position: sticky; + top: 0px; + background: white; + } + + .editor-primary-toolbar, .editor-auxiliary-toolbar { + display: block; + overflow-x: auto; + white-space: nowrap; + } + + .editor-auxiliary-toolbar { + & > * { + display: none; + } + .editor-auxiliary-tool-active { + display: block; + } + } + ol, ul { margin: 0; } .editor-content { diff --git a/app/views/application/markdown.haml b/app/views/application/markdown.haml index ff78986e..64297077 100644 --- a/app/views/application/markdown.haml +++ b/app/views/application/markdown.haml @@ -1,52 +1,56 @@ = form_with do .editor - %button{:data => {:button => "bold"}} Bold - %button{:data => {:button => "italic"}} Italic - %button{:data => {:button => "deleted"}} Deleted - %button{:data => {:button => "underline"}} Underline - %button{:data => {:button => "mark"}} Mark - %button{:data => {:button => "h1"}} H1 - %button{:data => {:button => "h2"}} H2 - %button{:data => {:button => "h3"}} H3 - %button{:data => {:button => "h4"}} H4 - %button{:data => {:button => "h5"}} H5 - %button{:data => {:button => "h6"}} H6 - %button{:data => {:button => "ul"}} Lista desordenada - %button{:data => {:button => "ol"}} Lista ordenada - %button{:data => {:button => "left"}} Left - %button{:data => {:button => "center"}} Center - %button{:data => {:button => "right"}} Right - %br/ - // TODO: generar IDs para labels + .editor-toolbar + .editor-primary-toolbar + %button.btn{:data => {:button => "bold"}} Bold + %button.btn{:data => {:button => "italic"}} Italic + %button.btn{:data => {:button => "deleted"}} Deleted + %button.btn{:data => {:button => "underline"}} Underline + %button.btn{:data => {:button => "mark"}} Subrayar + %button.btn{:data => {:button => "h1"}} H1 + %button.btn{:data => {:button => "h2"}} H2 + %button.btn{:data => {:button => "h3"}} H3 + %button.btn{:data => {:button => "h4"}} H4 + %button.btn{:data => {:button => "h5"}} H5 + %button.btn{:data => {:button => "h6"}} H6 + %button.btn{:data => {:button => "ul"}} Lista desordenada + %button.btn{:data => {:button => "ol"}} Lista ordenada + %button.btn{:data => {:button => "left"}} Left + %button.btn{:data => {:button => "center"}} Center + %button.btn{:data => {:button => "right"}} Right + %button.btn{:data => {:button => "img"}} Imágen + %button.btn{:data => {:button => "video"}} Video + %button.btn{:data => {:button => "audio"}} Audio + %button.btn{:data => {:button => "pdf"}} PDF + // TODO: generar IDs para labels - %label{:for => "mark-color"} Color de resaltado: - %input{:type => "color", :data => {:prop => "mark-color"}}/ - %br/ + // HAML cringe + .editor-auxiliary-toolbar{:data => {:editor => {:auxiliary => {:toolbar => ""}}}} + %div{:data => {:editor => {:auxiliary => "mark"}}} + %label{:for => "mark-color"} Color de resaltado: + %input{:type => "color", :data => {:prop => "mark-color"}}/ - %label{:for => "img-file"} Archivo de la imágen: - %input{:type => "file", :data => {:prop => "img-file"}}/ - %label{:for => "img-alt"} Descripción de imágen: - %input{:placeholder => "Un álbum", :type => "text", :data => {:prop => "img-alt"}}/ - %button{:data => {:button => "img"}} Insertar imágen - %br/ + %div{:data => {:editor => {:auxiliary => "img"}}} + %label{:for => "img-file"} Archivo de la imágen: + %input{:type => "file", :data => {:prop => "img-file"}}/ + %label{:for => "img-alt"} Descripción de imágen: + %input{:placeholder => "Un álbum", :type => "text", :data => {:prop => "img-alt"}}/ - %label{:for => "audio-file"} Archivo de la audio: - %input{:type => "file", :data => {:prop => "audio-file"}}/ - %button{:data => {:button => "audio"}} Insertar audio - %br/ + %div{:data => {:editor => {:auxiliary => "audio"}}} + %label{:for => "audio-file"} Archivo de la audio: + %input{:type => "file", :data => {:prop => "audio-file"}}/ - %label{:for => "video-file"} Archivo de la video: - %input{:type => "file", :data => {:prop => "video-file"}}/ - %button{:data => {:button => "video"}} Insertar video - %br/ + %div{:data => {:editor => {:auxiliary => "video"}}} + %label{:for => "video-file"} Archivo de la video: + %input{:type => "file", :data => {:prop => "video-file"}}/ - %label{:for => "pdf-file"} Archivo de la PDF: - %input{:type => "file", :data => {:prop => "pdf-file"}}/ - %button{:data => {:button => "pdf"}} Insertar PDF - %br/ + %div{:data => {:editor => {:auxiliary => "pdf"}}} + %label{:for => "pdf-file"} Archivo de la PDF: + %input{:type => "file", :data => {:prop => "pdf-file"}}/ - %label{:for => "link-href"} URL de link: - %input{:type => "url", :data => {:prop => "link-href"}}/ + %div{:data => {:editor => {:auxiliary => "link"}}} + %label{:for => "link-href"} URL de link: + %input{:type => "url", :data => {:prop => "link-href"}}/ .editor-content{:contenteditable => "true"} %h1