parar de usar checkFn, reemplazar con selectores
This commit is contained in:
parent
774cfdbf76
commit
5f8c368541
3 changed files with 61 additions and 76 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue