fix(creator-studio): respect line breaks when deleting blocks
This commit is contained in:
1 parent
561378312f
commit
b497eb4a59
3 files changed
+99
-15
No files matched your search
@@ -9,6 +9,7 @@
|
||||
/** 按键删除时作为整体处理的正文顶层块。 */
|
||||
const STRUCTURAL_SELECTOR = 'table,pre,hr,figure,ul,ol,.quote_q,.tinymce-hide,.new-dplayer,.wp-block-embed';
|
||||
const WRAPPED_MEDIA_SELECTOR = '.jio-math--block,img,iframe,video,audio,.jio-file-link';
|
||||
const NON_TEXT_CONTENT_SELECTOR = 'img,iframe,video,audio,table,pre,hr,figure,.jio-math,.jio-footnote-ref,.jio-file-link,.new-dplayer';
|
||||
|
||||
function isStructuralBlock(node: Element): boolean {
|
||||
if (node.matches(STRUCTURAL_SELECTOR)) {
|
||||
@@ -34,8 +35,20 @@ function isAtEdge(range: Range, container: Element, backward: boolean): boolean
|
||||
remainder.setStart(range.startContainer, range.startOffset);
|
||||
}
|
||||
const content = remainder.cloneContents();
|
||||
return !content.textContent?.trim()
|
||||
&& !content.querySelector('img,iframe,video,audio,table,pre,hr,figure,.jio-math,.jio-footnote-ref,.new-dplayer');
|
||||
return !content.textContent?.length
|
||||
&& !content.querySelector(`br:not([data-mce-bogus]),${NON_TEXT_CONTENT_SELECTOR}`);
|
||||
}
|
||||
|
||||
function isBlankParagraph(node: Element): boolean {
|
||||
return node.tagName === 'P' && !node.textContent?.replace(/\u00a0/g, ' ').trim()
|
||||
&& !node.querySelector(NON_TEXT_CONTENT_SELECTOR);
|
||||
}
|
||||
|
||||
function commitDeletion(editor: any, event: any, remove: () => void): void {
|
||||
event.preventDefault();
|
||||
editor.undoManager.transact(remove);
|
||||
editor.nodeChanged();
|
||||
editor.fire('change');
|
||||
}
|
||||
|
||||
function topLevelBlock(node: Node, body: HTMLElement): Element | null {
|
||||
@@ -71,12 +84,24 @@ export function removeAdjacentBlock(editor: any, event: any): void {
|
||||
const current = topLevelBlock(range.startContainer, body);
|
||||
let target: Element | null = null;
|
||||
if (current) {
|
||||
if (isBlankParagraph(current)) {
|
||||
const adjacent = backward ? current.previousElementSibling : current.nextElementSibling;
|
||||
if (adjacent && isStructuralBlock(adjacent) && isAtEdge(range, current, backward)) {
|
||||
commitDeletion(editor, event, () => {
|
||||
const offset = Array.prototype.indexOf.call(body.childNodes, current);
|
||||
editor.dom.remove(current);
|
||||
selection.setCursorLocation(body, offset);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
const node = range.startContainer.nodeType === 1
|
||||
? range.startContainer as Element : range.startContainer.parentElement;
|
||||
const nestedEditable = node?.closest('[contenteditable="true"]');
|
||||
const edgeContainer = nestedEditable && current.contains(nestedEditable)
|
||||
? nestedEditable : current;
|
||||
if (!isAtEdge(range, edgeContainer, backward)) {
|
||||
const wrappedMedia = current.tagName === 'P' && isStructuralBlock(current);
|
||||
if (!isAtEdge(range, edgeContainer, wrappedMedia ? !backward : backward)) {
|
||||
return;
|
||||
}
|
||||
// 表格单元格和列表项的边界键由 TinyMCE 处理。
|
||||
@@ -96,8 +121,7 @@ export function removeAdjacentBlock(editor: any, event: any): void {
|
||||
if (!target || !isStructuralBlock(target)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
editor.undoManager.transact(() => {
|
||||
commitDeletion(editor, event, () => {
|
||||
if (target === current) {
|
||||
const previous = target.previousElementSibling;
|
||||
const next = target.nextElementSibling;
|
||||
@@ -120,15 +144,13 @@ export function removeAdjacentBlock(editor: any, event: any): void {
|
||||
if (!current && !Array.from(body.children).some((child) => !isStructuralBlock(child))) {
|
||||
const paragraph = body.ownerDocument.createElement('p');
|
||||
paragraph.appendChild(body.ownerDocument.createElement('br'));
|
||||
body.insertBefore(paragraph, next);
|
||||
body.insertBefore(paragraph, next);
|
||||
selection.setCursorLocation(paragraph, 0);
|
||||
} else {
|
||||
selection.setRng(range);
|
||||
}
|
||||
}
|
||||
});
|
||||
editor.nodeChanged();
|
||||
editor.fire('change');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -154,14 +154,26 @@ export function hydrateBlocks(html: string): string {
|
||||
container.innerHTML = html;
|
||||
|
||||
container.querySelectorAll('pre').forEach((pre) => hydrateCodeBlock(pre));
|
||||
container.querySelectorAll('p').forEach((paragraph) => {
|
||||
const match = paragraph.textContent?.trim().match(/^\[jio_math display="1"\]([\s\S]*?)\[\/jio_math\]$/);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
const formula = match[1];
|
||||
const span = document.createElement('span');
|
||||
span.className = 'jio-math jio-math--block';
|
||||
span.setAttribute('data-jio-math-source', formula);
|
||||
span.setAttribute('data-jio-math-display', '1');
|
||||
span.setAttribute('contenteditable', 'false');
|
||||
span.innerHTML = typeset(formula);
|
||||
paragraph.replaceChildren(span);
|
||||
});
|
||||
|
||||
return container.innerHTML
|
||||
.replace(
|
||||
/\[jio_math display="([01])"\]([\s\S]*?)\[\/jio_math\]/g,
|
||||
(_match, display: string, formula: string) =>
|
||||
display === '0'
|
||||
? `<span class="jio-math" data-jio-math-source="${escapeAttr(formula)}" data-jio-math-display="0" contenteditable="false">${typeset(formula)}</span>`
|
||||
: `<p><span class="jio-math jio-math--block" data-jio-math-source="${escapeAttr(formula)}" data-jio-math-display="1" contenteditable="false">${typeset(formula)}</span></p>`
|
||||
/\[jio_math display="0"\]([\s\S]*?)\[\/jio_math\]/g,
|
||||
(_match, formula: string) =>
|
||||
`<span class="jio-math" data-jio-math-source="${escapeAttr(formula)}" data-jio-math-display="0" contenteditable="false">${typeset(formula)}</span>`
|
||||
)
|
||||
.replace(
|
||||
/\[jio_footnote\]([\s\S]*?)\[\/jio_footnote\]/g,
|
||||
|
||||
Reference in new issue
Block a user