mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-15 04:21:41 +00:00
Merge branch 'editor-nuevo' of 0xacab.org:sutty/sutty into editor-nuevo
This commit is contained in:
commit
cc0dcf47a7
3 changed files with 49 additions and 53 deletions
|
@ -145,13 +145,6 @@ function rgb2hex(rgb) {
|
|||
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
|
||||
}
|
||||
|
||||
// Encuentra lx primer hijx de `selector` que esté en la selección dentro de `node`
|
||||
const findInSelection = (selector, node) => {
|
||||
for (const child of node.querySelectorAll(selector)) {
|
||||
if (window.getSelection().containsNode(child)) return child
|
||||
}
|
||||
}
|
||||
|
||||
const getSelected = contentEl => contentEl.querySelector(".selected")
|
||||
|
||||
const typesWithProperties = {
|
||||
|
@ -172,7 +165,7 @@ const typesWithProperties = {
|
|||
setupInput (editorEl, contentEl) {
|
||||
const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`)
|
||||
markColorInputEl.addEventListener("change", event => {
|
||||
const markEl = findInSelection(marks.mark.selector, contentEl)
|
||||
const markEl = contentEl.querySelector(marks.mark.selector + ".selected")
|
||||
if (markEl) markEl.style.backgroundColor = markColorInputEl.value
|
||||
}, false)
|
||||
},
|
||||
|
|
|
@ -236,6 +236,14 @@ function cleanContent (contentEl) {
|
|||
} else if (!elementIsBlock(child)) {
|
||||
child.tagName = "P"
|
||||
}
|
||||
} else if (child.nodeType === Node.TEXT_NODE) {
|
||||
const wasSelected = sel.getRangeAt(0).intersectsNode(child)
|
||||
|
||||
const el = document.createElement("p")
|
||||
el.appendChild(child)
|
||||
contentEl.insertBefore(el, child.nextSibling)
|
||||
|
||||
if (wasSelected) sel.collapse(el, child.data.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,12 +254,6 @@ function cleanContent (contentEl) {
|
|||
* * Wrappea el contenido de un UL o OL en un LI si no lo está
|
||||
*/
|
||||
function fixContent (contentEl) {
|
||||
if (!contentEl.firstChild) {
|
||||
const newEl = document.createElement("p")
|
||||
contentEl.appendChild(newEl)
|
||||
window.getSelection().collapse(newEl)
|
||||
}
|
||||
|
||||
for (const child of contentEl.childNodes) {
|
||||
if (child.tagName) {
|
||||
if (elementIsParentBlock(child)) {
|
||||
|
@ -324,6 +326,42 @@ function cleanNode (node, contentEl) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Generar el clickListener para este editor.
|
||||
*/
|
||||
function generateClickListener (editorEl, contentEl) {
|
||||
/* El event listener para los typesWithProperties.
|
||||
*/
|
||||
return function clickListener (event) {
|
||||
// Borrar todas las selecciones
|
||||
for (const el of contentEl.querySelectorAll(".selected")) {
|
||||
el.classList.remove("selected")
|
||||
}
|
||||
|
||||
setAuxiliaryToolbar(editorEl)
|
||||
|
||||
let selectedType
|
||||
let selectedEl
|
||||
|
||||
for (const [name, type] of Object.entries(typesWithProperties)) {
|
||||
type.disableInput(editorEl)
|
||||
|
||||
let el = event.target
|
||||
while (el && !el.matches(type.selector)) el = el.parentElement
|
||||
if (el && contentEl.contains(el)) {
|
||||
selectedType = type
|
||||
selectedEl = el
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedType) {
|
||||
event.preventDefault()
|
||||
selectedType.updateInput(selectedEl, editorEl)
|
||||
event.target.classList.add("selected")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupEditor (editorEl) {
|
||||
// XXX: ¡Esto afecta a todo el documento! ¿Quizás usar un iframe para el editor?
|
||||
document.execCommand('defaultParagraphSeparator', false, 'p')
|
||||
|
@ -342,46 +380,11 @@ function setupEditor (editorEl) {
|
|||
})
|
||||
document.addEventListener("selectionchange", event => {
|
||||
cleanContent(contentEl)
|
||||
|
||||
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")
|
||||
}
|
||||
for (const el of contentEl.querySelectorAll(".selected-unactive")) {
|
||||
el.classList.remove("selected-unactive")
|
||||
}
|
||||
|
||||
setAuxiliaryToolbar(editorEl)
|
||||
|
||||
for (const [name, type] of Object.entries(typesWithProperties)) {
|
||||
let i = 0
|
||||
const results = parentEl.querySelectorAll(type.selector)
|
||||
|
||||
if (results.length) {
|
||||
for (const el of results) {
|
||||
if (!sel.containsNode(el)) continue
|
||||
if (i === 0) {
|
||||
type.updateInput(el, editorEl)
|
||||
el.classList.add("selected")
|
||||
} else {
|
||||
el.classList.add("selected-unactive")
|
||||
}
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
type.disableInput(editorEl)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const clickListener = generateClickListener(editorEl, contentEl)
|
||||
contentEl.addEventListener("click", clickListener, true)
|
||||
|
||||
const htmlEl = editorEl.querySelector("textarea")
|
||||
const observer = new MutationObserver((mutationList, observer) => {
|
||||
cleanContent(contentEl)
|
||||
|
@ -432,6 +435,7 @@ function setupEditor (editorEl) {
|
|||
|
||||
cleanContent(contentEl)
|
||||
htmlEl.value = contentEl.innerHTML
|
||||
fixContent(contentEl)
|
||||
}
|
||||
|
||||
// TODO: por ahora confiamos, quizás queremos filtrar estilos?
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
}
|
||||
|
||||
.selected { outline: #f206f9 solid medium; }
|
||||
.selected-unactive { outline: gray solid medium; }
|
||||
|
||||
img, video, iframe, audio {
|
||||
width: 100%;
|
||||
|
|
Loading…
Reference in a new issue