I’m trying to set a placeholder decoration only if the node does not contain a span with the step-history-deleted
class which comes from another diffsetPlugin and it’s corresponding generated decoration set (basically showing deleted items when we track changes as spans inside of the node).
This is working technically, using view.nodeDOM
and querySelector
, but it’s highly unoptimized, there’s a visible flash before the placeholder is properly replaced. Is there a way to improve the speed of this? Or is there a way to access the decoration-set via the EditorView from another plugin?
/**
* @param {Node} document
* @returns {DecorationSet}
*/
export const getDecorations = (document, view) => {
const decorations = [];
document.descendants((node, position, parent) => {
if (hasTextPlaceholder(node) && shouldDisplayPlaceholder(node, parent)) {
try {
const domElementAtPosition = view.nodeDOM(position);
if (!domElementAtPosition.querySelector(`.${DELETE_CSS_CLASSNAME}`)) {
decorations.push(
Decoration.node(position, position + node.nodeSize, {
class: placeholderStyles,
})
);
}
} catch (err) {
// view and it's dom element can occasionally be undefined
// this adds placeholders if the prior conditions are true
decorations.push(
Decoration.node(position, position + node.nodeSize, {
class: placeholderStyles,
})
);
}
}
});
return DecorationSet.create(document, decorations);
};
/**
* Plugin used to make placeholder attributes of text block nodes functional in the editor.
*/
export class TextPlaceholderPlugin extends Plugin {
/**
* Constructor
*/
constructor() {
super({
state: {
init: (config, state) => {
this.decorations = getDecorations(state.doc);
return this.decorations;
},
apply(tr, pluginState) {
if (tr.docChanged) {
this.decorations = getDecorations(tr.doc, this.view);
return this.decorations;
}
return pluginState;
},
},
view: (view) => {
this.view = view;
return {
update: (view) => {
const newDecorations = getDecorations(view.state.doc, this.view);
if (!isEqual(this.decorations, newDecorations)) {
this.decorations = newDecorations;
return this.decorations;
}
return this.decorations;
},
};
},
props: {
decorations: () => this.decorations,
},
});
this.view = null;
this.decorations = null;
}
}