parar de usar checkFn, reemplazar con selectores

This commit is contained in:
void 2020-11-09 16:07:20 -03:00
parent 774cfdbf76
commit 5f8c368541
3 changed files with 61 additions and 76 deletions

View file

@ -1,24 +1,22 @@
// TODO: minimizar complejidad utilizando selectores css/querySelector
const marks = { const marks = {
bold: { bold: {
checkFn: el => el.tagName === "STRONG", selector: "strong",
createFn: () => document.createElement("STRONG"), createFn: () => document.createElement("STRONG"),
}, },
italic: { italic: {
checkFn: el => el.tagName === "EM", selector: "em",
createFn: () => document.createElement("EM"), createFn: () => document.createElement("EM"),
}, },
deleted: { deleted: {
checkFn: el => el.tagName === "DEL", selector: "del",
createFn: () => document.createElement("DEL"), createFn: () => document.createElement("DEL"),
}, },
underline: { underline: {
checkFn: el => el.tagName === "U", selector: "u",
createFn: () => document.createElement("U"), createFn: () => document.createElement("U"),
}, },
mark: { mark: {
checkFn: el => el.tagName === "MARK", selector: "mark",
createFn: () => document.createElement("MARK"), createFn: () => document.createElement("MARK"),
}, },
} }
@ -34,43 +32,43 @@ const tagNameSetFn = tagName => el => {
const blocks = { const blocks = {
p: { p: {
noButton: true, noButton: true,
checkFn: el => el.tagName == "P", selector: "P",
setFn: tagNameSetFn("P"), setFn: tagNameSetFn("P"),
}, },
h1: { h1: {
checkFn: el => el.tagName == "H1", selector: "H1",
setFn: tagNameSetFn("H1"), setFn: tagNameSetFn("H1"),
}, },
h2: { h2: {
checkFn: el => el.tagName == "H2", selector: "H2",
setFn: tagNameSetFn("H2"), setFn: tagNameSetFn("H2"),
}, },
h3: { h3: {
checkFn: el => el.tagName == "H3", selector: "H3",
setFn: tagNameSetFn("H3"), setFn: tagNameSetFn("H3"),
}, },
h4: { h4: {
checkFn: el => el.tagName == "H4", selector: "H4",
setFn: tagNameSetFn("H4"), setFn: tagNameSetFn("H4"),
}, },
h5: { h5: {
checkFn: el => el.tagName == "H5", selector: "H5",
setFn: tagNameSetFn("H5"), setFn: tagNameSetFn("H5"),
}, },
h6: { h6: {
checkFn: el => el.tagName == "H6", selector: "H6",
setFn: tagNameSetFn("H6"), setFn: tagNameSetFn("H6"),
}, },
ul: { ul: {
checkFn: el => el.tagName == "UL", selector: "UL",
setFn: tagNameSetFn("UL"), setFn: tagNameSetFn("UL"),
}, },
ol: { ol: {
checkFn: el => el.tagName == "OL", selector: "OL",
setFn: tagNameSetFn("OL"), setFn: tagNameSetFn("OL"),
}, },
img: { img: {
checkFn: el => el.tagName == "IMG", selector: "IMG",
createFn: editorEl => { createFn: editorEl => {
const el = document.createElement("IMG") const el = document.createElement("IMG")
el.src = "https://radio.sutty.nl/public/placeholder_992x992.png" el.src = "https://radio.sutty.nl/public/placeholder_992x992.png"
@ -79,7 +77,7 @@ const blocks = {
}, },
}, },
audio: { audio: {
checkFn: el => el.tagName == "AUDIO", selector: "AUDIO",
createFn: editorEl => { createFn: editorEl => {
const el = document.createElement("AUDIO") const el = document.createElement("AUDIO")
el.controls = true el.controls = true
@ -87,7 +85,7 @@ const blocks = {
}, },
}, },
video: { video: {
checkFn: el => el.tagName == "VIDEO", selector: "VIDEO",
createFn: editorEl => { createFn: editorEl => {
const el = document.createElement("VIDEO") const el = document.createElement("VIDEO")
el.controls = true el.controls = true
@ -96,7 +94,7 @@ const blocks = {
}, },
// PDF // PDF
pdf: { pdf: {
checkFn: el => el.tagName == "IFRAME", selector: "IFRAME",
createFn: editorEl => { createFn: editorEl => {
const el = document.createElement("IFRAME") const el = document.createElement("IFRAME")
return el return el
@ -112,15 +110,15 @@ const divWithStyleCreateFn = styleFn => () => {
const parentBlocks = { const parentBlocks = {
left: { left: {
checkFn: el => el.tagName === "DIV" && el.dataset.align === "left", selector: "div[data-align=left]",
createFn: divWithStyleCreateFn(el => el.dataset.align = "left"), createFn: divWithStyleCreateFn(el => el.dataset.align = "left"),
}, },
center: { center: {
checkFn: el => el.tagName === "DIV" && el.dataset.align === "center", selector: "div[data-align=center]",
createFn: divWithStyleCreateFn(el => el.dataset.align = "center"), createFn: divWithStyleCreateFn(el => el.dataset.align = "center"),
}, },
right: { right: {
checkFn: el => el.tagName === "DIV" && el.dataset.align === "right", selector: "div[data-align=right]",
createFn: divWithStyleCreateFn(el => el.dataset.align = "right"), createFn: divWithStyleCreateFn(el => el.dataset.align = "right"),
}, },
} }
@ -135,12 +133,10 @@ function rgb2hex(rgb) {
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
} }
// Encuentra lx primer hijx que esté en la selección que cumpla con checkFn // Encuentra lx primer hijx de `selector` que esté en la selección dentro de `node`
const findRecursiveChild = (checkFn, node) => { const findInSelection = (selector, node) => {
if (checkFn(node) && window.getSelection().containsNode(node)) return node for (const child of node.querySelectorAll(selector)) {
for (const child of node.childNodes) { if (window.getSelection().containsNode(child)) return child
const result = findRecursiveChild(checkFn, child)
if (result) return result
} }
} }
@ -148,7 +144,7 @@ const getSelected = contentEl => contentEl.querySelector(".selected")
const typesWithProperties = { const typesWithProperties = {
mark: { mark: {
checkFn: marks.mark.checkFn, selector: marks.mark.selector,
updateInput (el, editorEl) { updateInput (el, editorEl) {
const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`) const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`)
markColorInputEl.disabled = false markColorInputEl.disabled = false
@ -162,13 +158,13 @@ const typesWithProperties = {
setupInput (editorEl, contentEl) { setupInput (editorEl, contentEl) {
const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`) const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`)
markColorInputEl.addEventListener("change", event => { markColorInputEl.addEventListener("change", event => {
const markEl = findRecursiveChild(marks.mark.checkFn, contentEl) const markEl = findInSelection(marks.mark.selector, contentEl)
if (markEl) markEl.style.backgroundColor = markColorInputEl.value if (markEl) markEl.style.backgroundColor = markColorInputEl.value
}, false) }, false)
}, },
}, },
img: { img: {
checkFn: blocks.img.checkFn, selector: blocks.img.selector,
updateInput (el, editorEl) { updateInput (el, editorEl) {
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`) const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
imgFileEl.disabled = false imgFileEl.disabled = false
@ -189,8 +185,7 @@ const typesWithProperties = {
setupInput (editorEl, contentEl) { setupInput (editorEl, contentEl) {
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`) const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
imgFileEl.addEventListener("input", event => { imgFileEl.addEventListener("input", event => {
// const imgEl = findRecursiveChild(blocks.img.checkFn, contentEl) const imgEl = contentEl.querySelector("img.selected")
const imgEl = getSelected(contentEl)
if (!imgEl) return if (!imgEl) return
const file = imgFileEl.files[0] const file = imgFileEl.files[0]
@ -214,14 +209,13 @@ const typesWithProperties = {
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`) const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
imgAltEl.addEventListener("input", event => { imgAltEl.addEventListener("input", event => {
// const imgEl = findRecursiveChild(blocks.img.checkFn, contentEl) const imgEl = contentEl.querySelector("img.selected")
const imgEl = getSelected(contentEl)
if (imgEl) imgEl.alt = imgAltEl.value if (imgEl) imgEl.alt = imgAltEl.value
}, false) }, false)
}, },
}, },
audio: { audio: {
checkFn: blocks.audio.checkFn, selector: blocks.audio.selector,
updateInput (el, editorEl) { updateInput (el, editorEl) {
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`) const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
audioFileEl.disabled = false audioFileEl.disabled = false
@ -258,7 +252,7 @@ const typesWithProperties = {
}, },
}, },
video: { video: {
checkFn: blocks.video.checkFn, selector: blocks.video.selector,
updateInput (el, editorEl) { updateInput (el, editorEl) {
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`) const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
videoFileEl.disabled = false videoFileEl.disabled = false
@ -295,7 +289,7 @@ const typesWithProperties = {
}, },
}, },
pdf: { pdf: {
checkFn: blocks.pdf.checkFn, selector: blocks.pdf.selector,
updateInput (el, editorEl) { updateInput (el, editorEl) {
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`) const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
pdfFileEl.disabled = false pdfFileEl.disabled = false

View file

@ -82,11 +82,10 @@ function setupMarkButton (button, mark, contentEl) {
let parentEl = getElementParent(sel.anchorNode) let parentEl = getElementParent(sel.anchorNode)
//if (sel.anchorNode == parentEl) parentEl = parentEl.firstChild //if (sel.anchorNode == parentEl) parentEl = parentEl.firstChild
const parentChildren = Array.from(parentEl.childNodes)
const range = sel.getRangeAt(0) const range = sel.getRangeAt(0)
if (mark.checkFn(parentEl)) { if (parentEl.matches(mark.selector)) {
const [left, right] = splitNode(parentEl, range) const [left, right] = splitNode(parentEl, range)
right.range.insertNode(range.extractContents()) right.range.insertNode(range.extractContents())
@ -96,8 +95,12 @@ function setupMarkButton (button, mark, contentEl) {
sel.removeAllRanges() sel.removeAllRanges()
sel.addRange(selectionRange) sel.addRange(selectionRange)
} else { } else {
for (const child of parentChildren) { for (const child of parentEl.childNodes) {
if (mark.checkFn(child) && sel.containsNode(child)) { if (
(child instanceof Element)
&& child.matches(mark.selector)
&& sel.containsNode(child)
) {
moveChildren(child, parentEl, child) moveChildren(child, parentEl, child)
parentEl.removeChild(child) parentEl.removeChild(child)
// TODO: agregar a selección // TODO: agregar a selección
@ -115,7 +118,7 @@ function setupMarkButton (button, mark, contentEl) {
} }
for (const child of tagEl.childNodes) { for (const child of tagEl.childNodes) {
if (mark.checkFn(child)) { if (child instanceof Element && child.matches(mark.selector)) {
moveChildren(child, tagEl, child) moveChildren(child, tagEl, child)
tagEl.removeChild(child) tagEl.removeChild(child)
} }
@ -147,7 +150,7 @@ function setupBlockButton (button, block, contentEl, editorEl) {
while (!isDirectChild(contentEl, parentEl)) parentEl = parentEl.parentElement while (!isDirectChild(contentEl, parentEl)) parentEl = parentEl.parentElement
if (block.setFn) { if (block.setFn) {
if (block.checkFn(parentEl)) { if (parentEl.matches(block.selector)) {
tagNameSetFn("P")(parentEl) tagNameSetFn("P")(parentEl)
} else { } else {
block.setFn(parentEl) block.setFn(parentEl)
@ -177,7 +180,7 @@ function setupParentBlockButton (button, parentBlock, contentEl) {
let parentEl = sel.anchorNode let parentEl = sel.anchorNode
while (!isDirectChild(contentEl, parentEl)) parentEl = parentEl.parentElement while (!isDirectChild(contentEl, parentEl)) parentEl = parentEl.parentElement
if (parentBlock.checkFn(parentEl)) { if (parentEl.matches(parentBlock.selector)) {
moveChildren(parentEl, parentEl.parentElement, parentEl) moveChildren(parentEl, parentEl.parentElement, parentEl)
parentEl.parentElement.removeChild(parentEl) parentEl.parentElement.removeChild(parentEl)
} else if (elementIsParentBlock(parentEl)) { } else if (elementIsParentBlock(parentEl)) {
@ -195,7 +198,7 @@ function setupParentBlockButton (button, parentBlock, contentEl) {
const elementIsTypes = types => element => { const elementIsTypes = types => element => {
for (const type of Object.values(types)) { for (const type of Object.values(types)) {
if (type.checkFn(element)) return true if (element.matches(type.selector)) return true
} }
return false return false
} }
@ -288,7 +291,12 @@ function cleanNode (node, contentEl) {
child.parentNode.removeChild(child) child.parentNode.removeChild(child)
for (const mark of Object.values(marks)) { for (const mark of Object.values(marks)) {
if (mark.checkFn(child) && child.nextSibling && mark.checkFn(child.nextSibling)) { if (
child.matches(mark.selector)
&& child.nextSibling
&& (child.nextSibling instanceof Element)
&& child.nextSibling.matches(mark.selector)
) {
moveChildren(child.nextSibling, child, null) moveChildren(child.nextSibling, child, null)
child.nextSibling.parentNode.removeChild(child.nextSibling) child.nextSibling.parentNode.removeChild(child.nextSibling)
} }
@ -346,39 +354,25 @@ function setupEditor (editorEl) {
el.classList.remove("selected-unactive") el.classList.remove("selected-unactive")
} }
let parentEl = range.commonAncestorContainer
if (parentEl.nodeType !== Node.ELEMENT_TYPE) parentEl = parentEl.parentElement
for (const [name, type] of Object.entries(typesWithProperties)) { for (const [name, type] of Object.entries(typesWithProperties)) {
let i = 0 let i = 0
const results = parentEl.querySelectorAll(type.selector)
let result if (results.length) {
while (true) { for (const el of results) {
try { if (!sel.containsNode(el)) continue
result = findRecursiveChild(
el => type.checkFn(el)
&& !(el.classList.contains("selected")
|| el.classList.contains("selected-unactive")),
range.commonAncestorContainer
)
} catch (err) {
// Permission denied or something...
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Property_access_denied
console.log("Error raro", err)
break
}
if (result) {
if (i === 0) { if (i === 0) {
type.updateInput(result, editorEl) type.updateInput(el, editorEl)
result.classList.add("selected") el.classList.add("selected")
} else { } else {
result.classList.add("selected-unactive") el.classList.add("selected-unactive")
} }
i++ i++
} else {
break
} }
} } else {
if (i === 0) {
type.disableInput(editorEl) type.disableInput(editorEl)
} }
} }

View file

@ -13,9 +13,6 @@
padding: 0; padding: 0;
} }
/*.selected, .selected-unactive {
outline-offset: 1pt;
}*/
.selected { outline: #f206f9 solid medium; } .selected { outline: #f206f9 solid medium; }
.selected-unactive { outline: gray solid medium; } .selected-unactive { outline: gray solid medium; }