Skip to content

Instantly share code, notes, and snippets.

@devsnek
Created May 15, 2021 22:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devsnek/72136ba7ce0e950d8d567ca329d2305c to your computer and use it in GitHub Desktop.
Save devsnek/72136ba7ce0e950d8d567ca329d2305c to your computer and use it in GitHub Desktop.
import Yoga from 'yoga-layout-prebuilt';
import { useEffect, useRef } from 'react';
import { useStdin, useStdout, useApp } from 'ink';
import { Program } from 'blessed';
// TODO: yogaNode.getComputedLayout() returns all 0s except
// for the width and height, which means we have to manually
// track the positions of elements in the code below. I'm
// not sure why this is, but it probably needs some sort of
// upstream fix in Ink.
function capture(node, event, held, offsetX = 0, offsetY = 0) {
const { yogaNode, childNodes } = node;
if (!yogaNode) {
return false;
}
if (yogaNode.getDisplay() === Yoga.DISPLAY_NONE) {
return false;
}
const nodeX = offsetX + yogaNode.getComputedLeft();
if (event.x < nodeX) {
return false;
}
const nodeY = offsetY + yogaNode.getComputedTop();
if (event.y < nodeY) {
return false;
}
if (event.x >= nodeX + yogaNode.getComputedWidth()) {
return false;
}
if (event.y >= nodeY + yogaNode.getComputedHeight()) {
return false;
}
switch (event.action) {
case 'mousedown':
held.add(node);
break;
case 'mouseup':
if (held.has(node)) {
if (node.attributes.onClick?.()) {
return true;
}
}
break;
case 'wheeldown':
case 'wheelup':
if (node.attributes.onScroll?.()) {
return true;
}
break;
default:
break;
}
for (const c of childNodes) {
if (capture(c, event, held, nodeX, nodeY)) {
return true;
}
}
return false;
}
export function useInput(rootRef) {
const { stdin } = useStdin();
const { stdout } = useStdout();
const { exit } = useApp();
const { current: state } = useRef({
mouseDown: false,
held: new Set(),
});
useEffect(() => {
const program = new Program({
input: stdin,
output: stdout,
});
program.enableMouse();
program.on('mouse', (event) => {
if (!rootRef.current) {
return;
}
// normalize mousemove while held down
if (event.action === 'mousedown' && state.mouseDown) {
event.action = 'mousemove';
}
switch (event.action) {
case 'mousedown':
state.mouseDown = true;
break;
case 'mouseup':
state.mouseDown = false;
break;
case 'wheelup':
case 'wheeldown':
break;
case 'mousemove':
return;
default:
throw new RangeError(event.action);
}
capture(rootRef.current, event, state.held);
if (event.action === 'mouseup') {
state.held.clear();
}
});
program.on('keypress', (key, event) => {
if (event.ctrl && event.name === 'c') {
program.destroy();
process.nextTick(() => {
process.exit(0);
});
}
});
return () => program.destroy();
}, [stdin, stdout, exit]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment