Prevent the caret from moving outside a custom nodeview for arrow keys

I’m trying to keep the caret inside my custom nodeview as long as I click outside the nodeview, in fact the arrow keys should be default prevented if the caret moves outside the nodeview.

Setting isolating property true solved the issue partially for Enter & Delete keys. What would be the best way to achieve the same experience for arrow keys?

Bind custom commands to arrow keys that check whether their action would take you out of the node, and return true (preventing further handling) when that’s the case.

Thank you for your prompt reply, Yeah that’s pretty much straight forward, but the problem is how to know if the caret is somewhere in the first line ( or the last line ) of the nodeview ?

I can return true (preventing further handling) for,

  • 1st pos + ArrowLeft
  • last pos + ArrowRight

but the problem is

  • 1st line + ArrowUp
  • last line + ArrowDown

I couldn’t find a way to check if the caret is in the first or the last line of the nodeview. Please help!

The EditorView.endOfTextblock helper method can sometimes help with this.

That method didn’t help, I managed to do it with coordsAtPos and posAtCoords methods. It doesn’t look great but it’s okay for the moment and I’m still looking for a better solution.

const threshold = 20;
const coords =  view.coordsAtPos( pos );
let checkPos = view.posAtCoords({ left: coords.left, top: coords.top - threshold });
if ( checkPos.pos < startPosOfNode ) {
    return true;
}
checkPos = view.posAtCoords({ left: coords.left, top: coords.top + threshold });
if ( checkPos.pos > endPosOfNode ) {
   return true;
}

handleKeyDown in your plugin

export const handleKeyDown = keydownHandler({
	ArrowLeft: arrow('left'),
	ArrowRight: arrow('right'),
	ArrowUp: arrow('up'),
	ArrowDown: arrow('down'),
});

function arrow(dir) {
	return function(state, dispatch, view) {
		if (!view) return false;

		var $head=state.selection.$head;
		var parent=$head.parent;
		if (['yourlimitedtypes'].indexOf(parent.type.name)<0) return false;

		var index=$head.index();
		
		if (index==0) { // first line
			if (dir=='up') return true;
			if (dir=='left' && $head.parentOffset==0) return true;
		}

		if (index==parent.childCount-1) { // last line
			if (dir=='down') return true;
		}

		if (index==parent.childCount) { // last fake char
			if (dir=='down') return true;
			if (dir=='right') return true;
			if (dir=='up' && index==1) return true; // fake last one line
		}

		return false;
	}
}