Background
I'm working on a web editor using Tiptap2. I've developed an external attributes panel that enables me to modify the attributes (padding, margin, background colour, etc.) of the selected block node. This panel includes several form elements, such as text fields, that correspond to the attributes and allow the values to be changed.
In this setup, I'm utilising Next.js with a client React component rendering the editor and other UI, then updating the corresponding block attribute when the value changes in the input or other form elements that are linked to the block attribute.
Problem
I'm encountering an issue where executing the command to update the attribute causes the editor to shift focus away from the input field and back to the editor node with the updated attribute. This can be particularly annoying if you need to type multiple values into a field and it loses focus after the first key is entered. For example, trying to add padding of 10, would loose focus after the 1 is entered.
Question
Could you advise me on how to update the node's attributes without it regaining focus? I have been looking at creating a ProseMirror plugin that uses appendTransaction to look for a 'preventFocus' meta value I set in my custom updateAttribute, and avoid changing focus, but I don't think this is the approach, as the focus I want to maintain is outside the editor.
new Plugin({
appendTransaction(transactions, oldState, newState) {
if (transactions.some(tr => tr.getMeta('preventFocus'))) {
// Create a new transaction that maintains the old selection
const selectionTr = newState.tr;
selectionTr.setSelection(oldState.selection);
return selectionTr;
}
}
});
Here is the updateAttribute command that is hijacking focus. I'm not using the Tiptap updateAttributes so that I can set tr.setMeta("preventFocus", true);, that the ProseMirror Plugin is looking for. But I have the same focus issue if using the Tiptap updateAttributes method.
updateAttribute:
(attributeName, attributeValue) =>
({ tr, state, dispatch }) => {
const { selection } = state;
const { from, to } = selection;
tr.doc.nodesBetween(from, to, (node, pos) => {
if (node.type.name === "section") {
const updatedAttrs = { ...node.attrs, [attributeName]: attributeValue };
tr.setMeta("preventFocus", true); // Set a custom meta flag to indicate this transaction shouldn't set focus
tr.setNodeMarkup( pos, null, updatedAttrs );
}
});
if (dispatch) {
dispatch(tr);
}
return tr;
},
Here is a reference image of the UI for context. You can see the fields on the right side that edit the padding and margin attributes, and should be keeping focus.
