Open Steveiwonder opened 3 years ago
Why do you think it should work this way?
A common use case for removing all content is to start from scratch. Why keeping the formatting then?
Hey @Reinmar, if you have formatted text in the editor, select all or some text, then start typing, you'd expect styling to be maintained, this is pretty common. Try it out in Word, PowerPoint, TinyMCE, Quill, DevExpress, Summernote, Google Docs and many others.
I have a solution which seems to behave in all of our use cases for anyone that would like to use it.
import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
import MoveOperation from "@ckeditor/ckeditor5-engine/src/model/operation/moveoperation";
import InsertOperation from "@ckeditor/ckeditor5-engine/src/model/operation/insertoperation";
import Text from "@ckeditor/ckeditor5-engine/src/model/text";
export const HIDDEN_CHARACTER = "\u200B";
export default class PreserveFontStyle extends Plugin {
init() {
this.editor.model.document.on("change:data", (evt, data) => {
if (!data.operations) {
return;
}
const insertOperations = data.operations.filter(
(x) => x instanceof InsertOperation
);
const moveOperations = data.operations.filter(
(x) => x instanceof MoveOperation
);
// handle new insert, remove zeroWidthSpace
if (insertOperations.length > 0) {
try {
const insertOperation = insertOperations[insertOperations.length - 1];
const textNode = insertOperation.position.textNode;
if (textNode) {
const data = textNode.data;
if (data.length > 1 && data.includes(HIDDEN_CHARACTER)) {
// strip out the hidden character now we don't need it.
this.editor.model.change((writer) => {
const newItem = writer.createText(
data.replace(HIDDEN_CHARACTER, ""),
textNode.getAttributes()
);
writer.remove(textNode);
this.editor.model.insertContent(newItem, textNode.range);
});
}
}
} catch (e) {
console.debug(e);
}
}
if (!moveOperations || moveOperations.length <= 0) {
return;
}
try {
// grab last move operation
const moveOperation = moveOperations[moveOperations.length - 1];
try {
console.log(moveOperation.sourcePosition.nodeAfter.data);
} catch {}
// ensure we are moving from "main" to the "graveyard"
if (
moveOperation.sourcePosition.root.rootName != "main" ||
moveOperation.targetPosition.root.rootName != "$graveyard"
) {
return;
}
// get the first node and ensure it's a text node
const deletedTextNode = moveOperation.targetPosition.nodeAfter;
if (!(deletedTextNode instanceof Text)) {
return;
}
// get all attributes to re-apply
const attrs = [...deletedTextNode.getAttributes()];
if (attrs.length == 0) {
return;
}
// now we need to check for the paragraph is empty, if it is, we insert an invisible character
// inserting this invisible character maintains styles
if (
!moveOperation.sourcePosition ||
!moveOperation.sourcePosition.parent
) {
return;
}
const parentParagraph = moveOperation.sourcePosition.parent;
if (!parentParagraph) {
return;
}
if (
parentParagraph.name == "paragraph" &&
parentParagraph.childCount == 0
) {
// if we are not the last paragraph or, the first character within the deleted text nodes data is not the hidden character
// insert the hidden character to preserve styling.
if (
!parentParagraph.previousSibling ||
(deletedTextNode.data &&
deletedTextNode.data[0] != HIDDEN_CHARACTER)
) {
this.editor.model.change((writer) => {
writer.insertText(HIDDEN_CHARACTER, attrs, parentParagraph, 0);
});
} else if (parentParagraph.previousSibling) {
// Remove any empty paragraphs
this.editor.model.change((writer) => {
writer.remove(parentParagraph);
});
}
return;
}
const insertedTextNode = moveOperation.sourcePosition.nodeAfter;
if (!insertedTextNode) {
return;
}
// when the parent paragraph has more than one child, do nothing
// prevents merging
if (insertedTextNode.parent.childCount != 1) {
return;
}
this.editor.model.change((writer) => {
writer.setAttributes(attrs, insertedTextNode);
});
} catch (e) {
console.debug(e);
}
});
}
}
Another issue with the same problem: https://github.com/ckeditor/ckeditor5/issues/9946
Hi @Reinmar, I think it could remove all format if you have no text and then remove text again(with backspace, delete...). The main reason I see to keep styles is to be able to, for example, change the text(not format) of the whole paragraph. Today you have to keep a character and start typing and only after you can remove the old character. This may also solve a problem where you change the text alignment the whole text(2 paragraphs) being the last one empty. You will see that the first one aligns correcly but the second one doesn't.
There has been no activity on this issue for the past year. We've marked it as stale and will close it in 30 days. We understand it may be relevant, so if you're interested in the solution, leave a comment or reaction under this issue.
📝 Provide detailed reproduction steps (if any)
✔️ Expected result
I expect to see the newly typed content with the last selected font styles (similar to word, powerpoint, etc)
❌ Actual result
The font has the editors default styles applied
❓ Possible solution
Don't remove the last span element that is the document editor, as this has the style
If you'd like to see this fixed sooner, add a 👍 reaction to this post.