@flighthq/gui 0.4.1-edge.28.7f8e221
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -0
- package/README.md +17 -0
- package/dist/buttonController.d.ts +7 -0
- package/dist/buttonController.d.ts.map +1 -0
- package/dist/buttonController.js +83 -0
- package/dist/buttonController.js.map +1 -0
- package/dist/comboBoxController.d.ts +7 -0
- package/dist/comboBoxController.d.ts.map +1 -0
- package/dist/comboBoxController.js +46 -0
- package/dist/comboBoxController.js.map +1 -0
- package/dist/contract.d.ts +16 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +16 -0
- package/dist/contract.js.map +1 -0
- package/dist/guiController.d.ts +24 -0
- package/dist/guiController.d.ts.map +1 -0
- package/dist/guiController.js +101 -0
- package/dist/guiController.js.map +1 -0
- package/dist/guiTestHelper.d.ts +5 -0
- package/dist/guiTestHelper.d.ts.map +1 -0
- package/dist/guiTestHelper.js +57 -0
- package/dist/guiTestHelper.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/listController.d.ts +8 -0
- package/dist/listController.d.ts.map +1 -0
- package/dist/listController.js +82 -0
- package/dist/listController.js.map +1 -0
- package/dist/progressBarController.d.ts +6 -0
- package/dist/progressBarController.d.ts.map +1 -0
- package/dist/progressBarController.js +42 -0
- package/dist/progressBarController.js.map +1 -0
- package/dist/radioGroupController.d.ts +7 -0
- package/dist/radioGroupController.d.ts.map +1 -0
- package/dist/radioGroupController.js +50 -0
- package/dist/radioGroupController.js.map +1 -0
- package/dist/scrollBarController.d.ts +7 -0
- package/dist/scrollBarController.d.ts.map +1 -0
- package/dist/scrollBarController.js +124 -0
- package/dist/scrollBarController.js.map +1 -0
- package/dist/scrollViewController.d.ts +8 -0
- package/dist/scrollViewController.d.ts.map +1 -0
- package/dist/scrollViewController.js +99 -0
- package/dist/scrollViewController.js.map +1 -0
- package/dist/sliderController.d.ts +7 -0
- package/dist/sliderController.d.ts.map +1 -0
- package/dist/sliderController.js +105 -0
- package/dist/sliderController.js.map +1 -0
- package/dist/splitPaneController.d.ts +7 -0
- package/dist/splitPaneController.d.ts.map +1 -0
- package/dist/splitPaneController.js +87 -0
- package/dist/splitPaneController.js.map +1 -0
- package/dist/tabBarController.d.ts +7 -0
- package/dist/tabBarController.d.ts.map +1 -0
- package/dist/tabBarController.js +43 -0
- package/dist/tabBarController.js.map +1 -0
- package/dist/textInputController.d.ts +9 -0
- package/dist/textInputController.d.ts.map +1 -0
- package/dist/textInputController.js +132 -0
- package/dist/textInputController.js.map +1 -0
- package/dist/toggleController.d.ts +7 -0
- package/dist/toggleController.d.ts.map +1 -0
- package/dist/toggleController.js +57 -0
- package/dist/toggleController.js.map +1 -0
- package/dist/tooltipController.d.ts +6 -0
- package/dist/tooltipController.d.ts.map +1 -0
- package/dist/tooltipController.js +62 -0
- package/dist/tooltipController.js.map +1 -0
- package/dist/treeViewController.d.ts +10 -0
- package/dist/treeViewController.d.ts.map +1 -0
- package/dist/treeViewController.js +128 -0
- package/dist/treeViewController.js.map +1 -0
- package/dist/windowController.d.ts +7 -0
- package/dist/windowController.d.ts.map +1 -0
- package/dist/windowController.js +119 -0
- package/dist/windowController.js.map +1 -0
- package/package.json +53 -0
- package/src/buttonController.test.ts +90 -0
- package/src/comboBoxController.test.ts +71 -0
- package/src/guiController.test.ts +162 -0
- package/src/guiTreeShaking.test.ts +35 -0
- package/src/listController.test.ts +96 -0
- package/src/progressBarController.test.ts +49 -0
- package/src/radioGroupController.test.ts +61 -0
- package/src/scrollBarController.test.ts +86 -0
- package/src/scrollViewController.test.ts +80 -0
- package/src/sliderController.test.ts +74 -0
- package/src/splitPaneController.test.ts +85 -0
- package/src/tabBarController.test.ts +50 -0
- package/src/textInputController.test.ts +93 -0
- package/src/toggleController.test.ts +72 -0
- package/src/tooltipController.test.ts +56 -0
- package/src/treeViewController.test.ts +105 -0
- package/src/windowController.test.ts +75 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createGuiTestNode, emitGuiPointer } from './guiTestHelper';
|
|
2
|
+
import {
|
|
3
|
+
createTooltipController,
|
|
4
|
+
disposeTooltipController,
|
|
5
|
+
hideTooltipController,
|
|
6
|
+
showTooltipController,
|
|
7
|
+
} from './tooltipController';
|
|
8
|
+
|
|
9
|
+
describe('createTooltipController', () => {
|
|
10
|
+
it('shows after delay at the pointer offset and hides on exit', () => {
|
|
11
|
+
vi.useFakeTimers();
|
|
12
|
+
const content = createGuiTestNode();
|
|
13
|
+
const target = createGuiTestNode();
|
|
14
|
+
createTooltipController({ content, delay: 50, offset: { x: 3, y: 4 }, target });
|
|
15
|
+
emitGuiPointer(target, 'onPointerOver', { worldX: 10, worldY: 20 });
|
|
16
|
+
expect(content.visible).toBe(false);
|
|
17
|
+
vi.advanceTimersByTime(50);
|
|
18
|
+
expect([content.visible, content.x, content.y]).toEqual([true, 13, 24]);
|
|
19
|
+
emitGuiPointer(target, 'onPointerOut');
|
|
20
|
+
expect(content.visible).toBe(false);
|
|
21
|
+
vi.useRealTimers();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('disposeTooltipController', () => {
|
|
26
|
+
it('cancels delayed display', () => {
|
|
27
|
+
vi.useFakeTimers();
|
|
28
|
+
const content = createGuiTestNode();
|
|
29
|
+
const target = createGuiTestNode();
|
|
30
|
+
const controller = createTooltipController({ content, delay: 50, target });
|
|
31
|
+
emitGuiPointer(target, 'onPointerOver');
|
|
32
|
+
disposeTooltipController(controller);
|
|
33
|
+
vi.advanceTimersByTime(50);
|
|
34
|
+
expect(content.visible).toBe(false);
|
|
35
|
+
vi.useRealTimers();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('hideTooltipController', () => {
|
|
40
|
+
it('hides immediately', () => {
|
|
41
|
+
const content = createGuiTestNode();
|
|
42
|
+
const controller = createTooltipController({ content, target: createGuiTestNode() });
|
|
43
|
+
showTooltipController(controller);
|
|
44
|
+
hideTooltipController(controller);
|
|
45
|
+
expect(content.visible).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('showTooltipController', () => {
|
|
50
|
+
it('shows immediately when called', () => {
|
|
51
|
+
const content = createGuiTestNode();
|
|
52
|
+
const controller = createTooltipController({ content, target: createGuiTestNode() });
|
|
53
|
+
showTooltipController(controller);
|
|
54
|
+
expect(content.visible).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { connectSignal } from '@flighthq/signals/contract';
|
|
2
|
+
import type { TreeViewControllerItem } from '@flighthq/types/contract';
|
|
3
|
+
|
|
4
|
+
import { createGuiTestNode, emitGuiKeyboard, emitGuiPointer } from './guiTestHelper';
|
|
5
|
+
import {
|
|
6
|
+
createTreeViewController,
|
|
7
|
+
disposeTreeViewController,
|
|
8
|
+
getTreeViewControllerSelectedItem,
|
|
9
|
+
getTreeViewControllerSignals,
|
|
10
|
+
isTreeViewControllerItemExpanded,
|
|
11
|
+
setTreeViewControllerItemExpanded,
|
|
12
|
+
setTreeViewControllerSelectedItem,
|
|
13
|
+
toggleTreeViewControllerItem,
|
|
14
|
+
} from './treeViewController';
|
|
15
|
+
|
|
16
|
+
function tree() {
|
|
17
|
+
const child: TreeViewControllerItem = { visual: createGuiTestNode() };
|
|
18
|
+
const root: TreeViewControllerItem = { children: [child], visual: createGuiTestNode() };
|
|
19
|
+
return { child, root };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('createTreeViewController', () => {
|
|
23
|
+
it('models expansion separately from flat ListController state', () => {
|
|
24
|
+
const { child, root } = tree();
|
|
25
|
+
const controller = createTreeViewController({ items: [root] });
|
|
26
|
+
expect(child.visual.visible).toBe(false);
|
|
27
|
+
emitGuiPointer(root.visual, 'onDoubleClick');
|
|
28
|
+
expect(isTreeViewControllerItemExpanded(controller, root)).toBe(true);
|
|
29
|
+
expect(child.visual.visible).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('navigates hierarchy with left and right', () => {
|
|
33
|
+
const { child, root } = tree();
|
|
34
|
+
const controller = createTreeViewController({ items: [root], selectedItem: root });
|
|
35
|
+
emitGuiKeyboard(root.visual, 'onKeyDown', 'ArrowRight');
|
|
36
|
+
emitGuiKeyboard(root.visual, 'onKeyDown', 'ArrowRight');
|
|
37
|
+
expect(getTreeViewControllerSelectedItem(controller)).toBe(child);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('disposeTreeViewController', () => {
|
|
42
|
+
it('detaches item input', () => {
|
|
43
|
+
const { root } = tree();
|
|
44
|
+
const controller = createTreeViewController({ items: [root] });
|
|
45
|
+
disposeTreeViewController(controller);
|
|
46
|
+
emitGuiPointer(root.visual, 'onClick');
|
|
47
|
+
expect(getTreeViewControllerSelectedItem(controller)).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('getTreeViewControllerSelectedItem', () => {
|
|
52
|
+
it('returns configured selection', () => {
|
|
53
|
+
const { root } = tree();
|
|
54
|
+
expect(getTreeViewControllerSelectedItem(createTreeViewController({ items: [root], selectedItem: root }))).toBe(
|
|
55
|
+
root,
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('getTreeViewControllerSignals', () => {
|
|
61
|
+
it('emits activation separately from expansion', () => {
|
|
62
|
+
const { root } = tree();
|
|
63
|
+
const controller = createTreeViewController({ items: [root] });
|
|
64
|
+
let activated = false;
|
|
65
|
+
connectSignal(getTreeViewControllerSignals(controller).onActivate, () => (activated = true));
|
|
66
|
+
emitGuiPointer(root.visual, 'onDoubleClick');
|
|
67
|
+
expect(activated).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('isTreeViewControllerItemExpanded', () => {
|
|
72
|
+
it('reads authored initial expansion', () => {
|
|
73
|
+
const { root } = tree();
|
|
74
|
+
root.expanded = true;
|
|
75
|
+
expect(isTreeViewControllerItemExpanded(createTreeViewController({ items: [root] }), root)).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('setTreeViewControllerItemExpanded', () => {
|
|
80
|
+
it('ignores leaves', () => {
|
|
81
|
+
const { child, root } = tree();
|
|
82
|
+
const controller = createTreeViewController({ items: [root] });
|
|
83
|
+
setTreeViewControllerItemExpanded(controller, child, true);
|
|
84
|
+
expect(isTreeViewControllerItemExpanded(controller, child)).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('setTreeViewControllerSelectedItem', () => {
|
|
89
|
+
it('rejects foreign items', () => {
|
|
90
|
+
const { root } = tree();
|
|
91
|
+
const controller = createTreeViewController({ items: [root] });
|
|
92
|
+
setTreeViewControllerSelectedItem(controller, { visual: createGuiTestNode() });
|
|
93
|
+
expect(getTreeViewControllerSelectedItem(controller)).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('toggleTreeViewControllerItem', () => {
|
|
98
|
+
it('toggles expansion in both directions', () => {
|
|
99
|
+
const { root } = tree();
|
|
100
|
+
const controller = createTreeViewController({ items: [root] });
|
|
101
|
+
toggleTreeViewControllerItem(controller, root);
|
|
102
|
+
toggleTreeViewControllerItem(controller, root);
|
|
103
|
+
expect(isTreeViewControllerItemExpanded(controller, root)).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { connectSignal } from '@flighthq/signals/contract';
|
|
2
|
+
|
|
3
|
+
import { createButtonController } from './buttonController';
|
|
4
|
+
import { createGuiTestNode, emitGuiPointer } from './guiTestHelper';
|
|
5
|
+
import {
|
|
6
|
+
createWindowController,
|
|
7
|
+
disposeWindowController,
|
|
8
|
+
getWindowControllerSignals,
|
|
9
|
+
setWindowControllerPosition,
|
|
10
|
+
setWindowControllerSize,
|
|
11
|
+
} from './windowController';
|
|
12
|
+
|
|
13
|
+
describe('createWindowController', () => {
|
|
14
|
+
it('moves from a title bar and resizes from a handle', () => {
|
|
15
|
+
const frame = createGuiTestNode(200, 100);
|
|
16
|
+
const titleBar = createGuiTestNode();
|
|
17
|
+
const resizeHandle = createGuiTestNode();
|
|
18
|
+
createWindowController({ frame, resizeHandle, titleBar });
|
|
19
|
+
emitGuiPointer(titleBar, 'onPointerDown', { worldX: 10, worldY: 10 });
|
|
20
|
+
emitGuiPointer(titleBar, 'onPointerMove', { worldX: 30, worldY: 40 });
|
|
21
|
+
expect([frame.x, frame.y]).toEqual([20, 30]);
|
|
22
|
+
emitGuiPointer(resizeHandle, 'onPointerDown', { worldX: 0, worldY: 0 });
|
|
23
|
+
emitGuiPointer(resizeHandle, 'onPointerMove', { worldX: 100, worldY: 50 });
|
|
24
|
+
expect([frame.scaleX, frame.scaleY]).toEqual([1.5, 1.5]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('forwards a composed close button', () => {
|
|
28
|
+
const closeNode = createGuiTestNode();
|
|
29
|
+
const controller = createWindowController({
|
|
30
|
+
closeButton: createButtonController({ upState: closeNode }),
|
|
31
|
+
frame: createGuiTestNode(),
|
|
32
|
+
});
|
|
33
|
+
let closed = false;
|
|
34
|
+
connectSignal(getWindowControllerSignals(controller).onClose, () => (closed = true));
|
|
35
|
+
emitGuiPointer(closeNode, 'onClick');
|
|
36
|
+
expect(closed).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('disposeWindowController', () => {
|
|
41
|
+
it('detaches title dragging', () => {
|
|
42
|
+
const frame = createGuiTestNode();
|
|
43
|
+
const titleBar = createGuiTestNode();
|
|
44
|
+
const controller = createWindowController({ frame, titleBar });
|
|
45
|
+
disposeWindowController(controller);
|
|
46
|
+
emitGuiPointer(titleBar, 'onPointerDown', { worldX: 0 });
|
|
47
|
+
emitGuiPointer(titleBar, 'onPointerMove', { worldX: 20 });
|
|
48
|
+
expect(frame.x).toBe(0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('getWindowControllerSignals', () => {
|
|
53
|
+
it('returns stable signals', () => {
|
|
54
|
+
const controller = createWindowController({ frame: createGuiTestNode() });
|
|
55
|
+
expect(getWindowControllerSignals(controller)).toBe(getWindowControllerSignals(controller));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('setWindowControllerPosition', () => {
|
|
60
|
+
it('sets the frame transform', () => {
|
|
61
|
+
const frame = createGuiTestNode();
|
|
62
|
+
const controller = createWindowController({ frame });
|
|
63
|
+
setWindowControllerPosition(controller, 4, 5);
|
|
64
|
+
expect([frame.x, frame.y]).toEqual([4, 5]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('setWindowControllerSize', () => {
|
|
69
|
+
it('honors minimum dimensions', () => {
|
|
70
|
+
const frame = createGuiTestNode(100, 100);
|
|
71
|
+
const controller = createWindowController({ frame, minimumHeight: 50, minimumWidth: 50 });
|
|
72
|
+
setWindowControllerSize(controller, 10, 20);
|
|
73
|
+
expect([frame.scaleX, frame.scaleY]).toEqual([0.5, 0.5]);
|
|
74
|
+
});
|
|
75
|
+
});
|