mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-06 23:44:36 +00:00
153 lines
4.4 KiB
TypeScript
153 lines
4.4 KiB
TypeScript
import assert from "node:assert";
|
|
import { describe, it } from "node:test";
|
|
import { Editor, type EditorTheme } from "../src/components/editor.ts";
|
|
import { Input } from "../src/components/input.ts";
|
|
import { SelectList, type SelectListTheme } from "../src/components/select-list.ts";
|
|
import { SettingsList, type SettingsListTheme } from "../src/components/settings-list.ts";
|
|
import { Container, type TuiMouseEvent, type TuiMouseEventType } from "../src/tui.ts";
|
|
import { TuiAltScreen } from "../src/tui-alt-screen.ts";
|
|
import { VirtualTerminal } from "./virtual-terminal.ts";
|
|
|
|
function mouse(type: TuiMouseEventType, x: number, y: number, width = 80, height = 10): TuiMouseEvent {
|
|
return {
|
|
type,
|
|
button: "left",
|
|
x,
|
|
y,
|
|
screenX: x,
|
|
screenY: y,
|
|
width,
|
|
height,
|
|
shift: false,
|
|
alt: false,
|
|
ctrl: false,
|
|
...(type === "click" ? { clickCount: 1 } : {}),
|
|
};
|
|
}
|
|
|
|
const selectTheme: SelectListTheme = {
|
|
selectedPrefix: (text) => text,
|
|
selectedText: (text) => text,
|
|
description: (text) => text,
|
|
scrollInfo: (text) => text,
|
|
noMatch: (text) => text,
|
|
};
|
|
|
|
const settingsTheme: SettingsListTheme = {
|
|
label: (text) => text,
|
|
value: (text) => text,
|
|
description: (text) => text,
|
|
cursor: "> ",
|
|
hint: (text) => text,
|
|
};
|
|
|
|
const editorTheme: EditorTheme = {
|
|
borderColor: (text) => text,
|
|
selectList: selectTheme,
|
|
};
|
|
|
|
class InputOverlay extends Container {
|
|
readonly input = new Input();
|
|
|
|
constructor() {
|
|
super();
|
|
this.addChild(this.input);
|
|
}
|
|
|
|
handleInput(data: string): void {
|
|
this.input.handleInput(data);
|
|
}
|
|
}
|
|
|
|
describe("mouse-aware components", () => {
|
|
it("positions a single-line input cursor on press", () => {
|
|
const input = new Input();
|
|
input.setValue("hello");
|
|
input.render(20);
|
|
|
|
assert.strictEqual(input.handleMouse(mouse("press", 4, 0, 20, 1))?.handled, true);
|
|
input.handleInput("X");
|
|
assert.strictEqual(input.getValue(), "heXllo");
|
|
});
|
|
|
|
it("selects and activates list rows", () => {
|
|
const list = new SelectList(
|
|
[
|
|
{ value: "a", label: "A" },
|
|
{ value: "b", label: "B" },
|
|
{ value: "c", label: "C" },
|
|
{ value: "d", label: "D" },
|
|
{ value: "e", label: "E" },
|
|
],
|
|
3,
|
|
selectTheme,
|
|
);
|
|
let selected: string | undefined;
|
|
list.onSelect = (item) => {
|
|
selected = item.value;
|
|
};
|
|
|
|
assert.strictEqual(list.handleMouse(mouse("press", 1, 2, 40, 3))?.handled, true);
|
|
assert.strictEqual(list.getSelectedItem()?.value, "c");
|
|
assert.strictEqual(list.handleMouse(mouse("click", 1, 2, 40, 3))?.handled, true);
|
|
assert.strictEqual(selected, "c");
|
|
});
|
|
|
|
it("activates settings rows", () => {
|
|
const changes: Array<{ id: string; value: string }> = [];
|
|
const list = new SettingsList(
|
|
[
|
|
{ id: "mode", label: "Mode", currentValue: "one", values: ["one", "two"] },
|
|
{ id: "other", label: "Other", currentValue: "off", values: ["off", "on"] },
|
|
{ id: "third", label: "Third", currentValue: "low", values: ["low", "high"] },
|
|
{ id: "fourth", label: "Fourth", currentValue: "x", values: ["x", "y"] },
|
|
],
|
|
3,
|
|
settingsTheme,
|
|
(id, value) => changes.push({ id, value }),
|
|
() => {},
|
|
);
|
|
|
|
list.handleMouse(mouse("press", 1, 2, 40, 5));
|
|
list.handleMouse(mouse("click", 1, 2, 40, 5));
|
|
assert.deepStrictEqual(changes, [{ id: "third", value: "high" }]);
|
|
});
|
|
|
|
it("keeps a delegating overlay focused when its nested input is clicked", async () => {
|
|
const terminal = new VirtualTerminal(20, 4);
|
|
const tui = new TuiAltScreen(terminal);
|
|
const overlay = new InputOverlay();
|
|
overlay.input.setValue("hi");
|
|
tui.start();
|
|
tui.showOverlay(overlay, { anchor: "top-left", width: 20 });
|
|
await terminal.waitForRender();
|
|
|
|
terminal.sendInput("\x1b[<0;5;1M");
|
|
terminal.sendInput("\x1b[<0;5;1m");
|
|
terminal.sendInput("!");
|
|
await terminal.waitForRender();
|
|
|
|
assert.strictEqual(overlay.input.getValue(), "hi!");
|
|
assert.strictEqual(tui.getFocusedComponent(), overlay);
|
|
tui.stop();
|
|
});
|
|
|
|
it("positions and focuses the multiline editor through alternate-screen dispatch", async () => {
|
|
const terminal = new VirtualTerminal(20, 6);
|
|
const tui = new TuiAltScreen(terminal);
|
|
const editor = new Editor(tui, editorTheme);
|
|
editor.setText("hello");
|
|
tui.addChild(editor);
|
|
tui.start();
|
|
await terminal.waitForRender();
|
|
|
|
terminal.sendInput("\x1b[<0;3;2M");
|
|
terminal.sendInput("\x1b[<0;3;2m");
|
|
terminal.sendInput("X");
|
|
await terminal.waitForRender();
|
|
|
|
assert.strictEqual(editor.getText(), "heXllo");
|
|
assert.strictEqual(tui.getFocusedComponent(), editor);
|
|
tui.stop();
|
|
});
|
|
});
|