codemirror-interact
codemirror-interact copied to clipboard
Proposal: allow drag to continue outside of editor area
As a user editing a number with the interactive number dragger widget, I want to be able to continue dragging outside the code editor, so that I can get a greater range.
For example, if a number is positioned close to the left edge of the editor and I want to make it smaller, I don't have that much room to drag left. However in the UI I'm working on, there is a sidebar, so in theory it should be possible to intercept mousemove events from the top level document rather than from the code editor area to extend the range.
This is the current "rule" I'm using
// a rule for a number dragger
{
// the regexp matching the value
regexp: /(?<!\#)-?\b\d+\.?\d*\b/g,
// set cursor to "ew-resize" on hover
cursor: 'ew-resize',
// change number value based on mouse X movement on drag
onDrag: (text, setText, e) => {
if (onInteract) onInteract();
const newVal = Number(text) + e.movementX;
if (isNaN(newVal)) return;
setText(newVal.toString());
},
},
This code works, but has a bug when the number of digits change:
// a rule for a number dragger
{
// the regexp matching the value
regexp: /(?<!\#)-?\b\d+\.?\d*\b/g,
// set cursor to "ew-resize" on hover
cursor: 'ew-resize',
// change number value based on mouse X movement on drag
// This works fine with the 999 to 1000 transition
// onDrag: (text, setText, e) => {
// if (onInteract) onInteract();
// const newVal = Number(text) + e.movementX;
// if (isNaN(newVal)) return;
// setText(newVal.toString());
// },
// This DOES NOT works fine with the 999 to 1000 transition
// TODO figure out why
onClick(text, setText, e) {
let newVal = Number(text);
const handleDrag = (e: MouseEvent) => {
if (onInteract) onInteract();
newVal += e.movementX;
if (isNaN(newVal)) return;
setText(newVal.toString());
};
// add an event listener to the document for mouse move
document.addEventListener(
'mousemove',
handleDrag,
);
// when the mouse is released, remove the event listener
document.addEventListener('mouseup', () => {
document.removeEventListener(
'mousemove',
handleDrag,
);
});
},
},
If it starts with, say 900, then is dragged past 1000, each new change adds a digit. Likewise if it decreases from say 100 to 99, subsequent characters get deleted. Any ideas why that might be happening?
I think it has to do with this logic:
updateText(target) {
return (text) => {
view.dispatch({
effects: setInteract.of({ ...target, text }),
changes: {
from: target.pos,
to: target.pos + target.text.length, // The target length has changed, but how can we update it?
insert: text,
},
});
};
},
If only there were a way to redefine setText to be this.updateText(this.target) on each drag event, but from the rule definition rather than from within the class itself. I think the issue is that it's working with a stale definition of this.target, which I guess changes every time you call setText.