@geometra/renderer-canvas 1.59.1 → 1.61.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geometra/renderer-canvas",
3
- "version": "1.59.1",
3
+ "version": "1.61.0",
4
4
  "description": "Canvas2D renderer for Geometra — the geometry protocol for UI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,6 +35,6 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@geometra/client": "^1.6.0",
38
- "@geometra/core": "^1.59.1"
38
+ "@geometra/core": "^1.61.0"
39
39
  }
40
40
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=border-radius.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"border-radius.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/border-radius.test.ts"],"names":[],"mappings":""}
@@ -1,115 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { box } from '@geometra/core';
3
- import { CanvasRenderer } from '../renderer.js';
4
- class FakeCtx {
5
- ops = [];
6
- fillStyle = '';
7
- strokeStyle = '';
8
- lineWidth = 1;
9
- font = '12px sans-serif';
10
- textBaseline = 'top';
11
- globalAlpha = 1;
12
- shadowOffsetX = 0;
13
- shadowOffsetY = 0;
14
- shadowBlur = 0;
15
- shadowColor = '';
16
- scale() { this.ops.push({ op: 'scale' }); }
17
- setTransform() { this.ops.push({ op: 'setTransform' }); }
18
- fillRect() { this.ops.push({ op: 'fillRect' }); }
19
- fill() { this.ops.push({ op: 'fill' }); }
20
- stroke() { this.ops.push({ op: 'stroke' }); }
21
- beginPath() { this.ops.push({ op: 'beginPath' }); }
22
- closePath() { this.ops.push({ op: 'closePath' }); }
23
- rect() { this.ops.push({ op: 'rect' }); }
24
- clip() { this.ops.push({ op: 'clip' }); }
25
- save() { this.ops.push({ op: 'save' }); }
26
- restore() { this.ops.push({ op: 'restore' }); }
27
- moveTo(x, y) { this.ops.push({ op: 'moveTo', args: [x, y] }); }
28
- lineTo(x, y) { this.ops.push({ op: 'lineTo', args: [x, y] }); }
29
- quadraticCurveTo(cx, cy, x, y) {
30
- this.ops.push({ op: 'quadraticCurveTo', args: [cx, cy, x, y] });
31
- }
32
- strokeRect() { this.ops.push({ op: 'strokeRect' }); }
33
- fillText() { this.ops.push({ op: 'fillText' }); }
34
- measureText(s) { return { width: s.length * 8 }; }
35
- }
36
- function setWindowDpr(dpr) {
37
- Object.defineProperty(globalThis, 'window', {
38
- value: { devicePixelRatio: dpr },
39
- configurable: true,
40
- writable: true,
41
- });
42
- }
43
- describe('canvas border-radius', () => {
44
- it('uniform number radius produces equal corner arcs', () => {
45
- setWindowDpr(1);
46
- const ctx = new FakeCtx();
47
- const canvas = { style: {}, getContext: () => ctx };
48
- const tree = box({ width: 100, height: 80, backgroundColor: '#ff0000', borderRadius: 10 }, []);
49
- const layout = { x: 0, y: 0, width: 100, height: 80, children: [] };
50
- const renderer = new CanvasRenderer({ canvas });
51
- renderer.render(layout, tree);
52
- const moveTo = ctx.ops.find((o) => o.op === 'moveTo');
53
- expect(moveTo?.args).toEqual([10, 0]); // first corner arc starts at uniform r
54
- });
55
- it('per-corner object applies different radii per corner', () => {
56
- setWindowDpr(1);
57
- const ctx = new FakeCtx();
58
- const canvas = { style: {}, getContext: () => ctx };
59
- const tree = box({
60
- width: 100,
61
- height: 80,
62
- backgroundColor: '#ff0000',
63
- borderRadius: { topLeft: 20, topRight: 4, bottomRight: 8, bottomLeft: 12 },
64
- }, []);
65
- const layout = { x: 0, y: 0, width: 100, height: 80, children: [] };
66
- const renderer = new CanvasRenderer({ canvas });
67
- renderer.render(layout, tree);
68
- // First moveTo is at x=topLeft, y=0
69
- const moveTo = ctx.ops.find((o) => o.op === 'moveTo');
70
- expect(moveTo?.args).toEqual([20, 0]);
71
- // The final lineTo before the top-left quadraticCurveTo should be at x=0, y=topLeft
72
- const lineTos = ctx.ops.filter((o) => o.op === 'lineTo');
73
- // Path order: moveTo(tl, 0) -> lineTo(w-tr, 0) -> quadraticCurveTo(w, 0, w, tr) -> lineTo(w, h-br) -> quadraticCurveTo(w, h, w-br, h) -> lineTo(bl, h) -> quadraticCurveTo(0, h, 0, h-bl) -> lineTo(0, tl) -> quadraticCurveTo(0, 0, tl, 0)
74
- expect(lineTos).toHaveLength(4);
75
- expect(lineTos[0]?.args).toEqual([100 - 4, 0]); // lineTo(w - tr, 0)
76
- expect(lineTos[1]?.args).toEqual([100, 80 - 8]); // lineTo(w, h - br)
77
- expect(lineTos[2]?.args).toEqual([12, 80]); // lineTo(bl, h)
78
- expect(lineTos[3]?.args).toEqual([0, 20]); // lineTo(0, tl)
79
- });
80
- it('omitted corners default to 0', () => {
81
- setWindowDpr(1);
82
- const ctx = new FakeCtx();
83
- const canvas = { style: {}, getContext: () => ctx };
84
- const tree = box({
85
- width: 100,
86
- height: 80,
87
- backgroundColor: '#ff0000',
88
- borderRadius: { topLeft: 16 }, // others default to 0
89
- }, []);
90
- const layout = { x: 0, y: 0, width: 100, height: 80, children: [] };
91
- const renderer = new CanvasRenderer({ canvas });
92
- renderer.render(layout, tree);
93
- const moveTo = ctx.ops.find((o) => o.op === 'moveTo');
94
- expect(moveTo?.args).toEqual([16, 0]);
95
- const lineTos = ctx.ops.filter((o) => o.op === 'lineTo');
96
- expect(lineTos[0]?.args).toEqual([100, 0]); // tr = 0, so line goes fully to corner
97
- });
98
- it('clamps radius to half of smaller dimension', () => {
99
- setWindowDpr(1);
100
- const ctx = new FakeCtx();
101
- const canvas = { style: {}, getContext: () => ctx };
102
- const tree = box({
103
- width: 40,
104
- height: 60,
105
- backgroundColor: '#ff0000',
106
- borderRadius: 999, // clamp to min(w, h) / 2 = 20
107
- }, []);
108
- const layout = { x: 0, y: 0, width: 40, height: 60, children: [] };
109
- const renderer = new CanvasRenderer({ canvas });
110
- renderer.render(layout, tree);
111
- const moveTo = ctx.ops.find((o) => o.op === 'moveTo');
112
- expect(moveTo?.args).toEqual([20, 0]);
113
- });
114
- });
115
- //# sourceMappingURL=border-radius.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"border-radius.test.js","sourceRoot":"","sources":["../../src/__tests__/border-radius.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=browser-client.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/browser-client.test.ts"],"names":[],"mappings":""}
@@ -1,147 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { createBrowserCanvasClient } from '../browser-client.js';
3
- const { createClientMock, enableSelectionMock, enableAccessibilityMirrorMock, rendererDestroyMock, rendererRenderMock, selectionCleanupMock, accessibilityCleanupMock, CanvasRendererMock, } = vi.hoisted(() => ({
4
- createClientMock: vi.fn(),
5
- enableSelectionMock: vi.fn(),
6
- enableAccessibilityMirrorMock: vi.fn(),
7
- rendererDestroyMock: vi.fn(),
8
- rendererRenderMock: vi.fn(),
9
- selectionCleanupMock: vi.fn(),
10
- accessibilityCleanupMock: vi.fn(),
11
- CanvasRendererMock: vi.fn(),
12
- }));
13
- vi.mock('@geometra/client', () => ({
14
- createClient: createClientMock,
15
- }));
16
- vi.mock('../renderer.js', () => ({
17
- CanvasRenderer: CanvasRendererMock,
18
- enableSelection: enableSelectionMock,
19
- enableAccessibilityMirror: enableAccessibilityMirrorMock,
20
- }));
21
- class FakeEventTarget {
22
- listeners = new Map();
23
- addEventListener(type, listener) {
24
- if (!this.listeners.has(type))
25
- this.listeners.set(type, new Set());
26
- this.listeners.get(type).add(listener);
27
- }
28
- removeEventListener(type, listener) {
29
- this.listeners.get(type)?.delete(listener);
30
- }
31
- dispatch(type, event) {
32
- const list = this.listeners.get(type);
33
- if (!list)
34
- return;
35
- for (const listener of list)
36
- listener(event);
37
- }
38
- }
39
- function makeWindowTarget() {
40
- return new FakeEventTarget();
41
- }
42
- describe('createBrowserCanvasClient', () => {
43
- beforeEach(() => {
44
- createClientMock.mockReset();
45
- enableSelectionMock.mockReset();
46
- enableAccessibilityMirrorMock.mockReset();
47
- rendererDestroyMock.mockReset();
48
- rendererRenderMock.mockReset();
49
- selectionCleanupMock.mockReset();
50
- accessibilityCleanupMock.mockReset();
51
- CanvasRendererMock.mockReset();
52
- CanvasRendererMock.mockImplementation(function MockCanvasRenderer() {
53
- return {
54
- destroy: rendererDestroyMock,
55
- render: rendererRenderMock,
56
- lastLayout: null,
57
- lastTree: null,
58
- };
59
- });
60
- enableSelectionMock.mockReturnValue(selectionCleanupMock);
61
- enableAccessibilityMirrorMock.mockReturnValue(accessibilityCleanupMock);
62
- createClientMock.mockImplementation(() => ({
63
- layout: null,
64
- tree: null,
65
- close: vi.fn(),
66
- }));
67
- });
68
- it('wires browser selection, accessibility mirror, focus, and cleanup by default', () => {
69
- const focus = vi.fn();
70
- const win = makeWindowTarget();
71
- const body = {};
72
- const doc = { body, defaultView: win };
73
- const target = new FakeEventTarget();
74
- const canvas = {
75
- ownerDocument: doc,
76
- focus,
77
- addEventListener: target.addEventListener.bind(target),
78
- removeEventListener: target.removeEventListener.bind(target),
79
- hasAttribute: () => true,
80
- setAttribute: () => undefined,
81
- };
82
- const handle = createBrowserCanvasClient({
83
- canvas,
84
- url: 'ws://localhost:3200',
85
- binaryFraming: true,
86
- });
87
- expect(CanvasRendererMock).toHaveBeenCalledWith(expect.objectContaining({
88
- canvas,
89
- }));
90
- expect(enableSelectionMock).toHaveBeenCalledWith(canvas, handle.renderer, undefined);
91
- expect(enableAccessibilityMirrorMock).toHaveBeenCalledWith(body, handle.renderer, {});
92
- expect(createClientMock).toHaveBeenCalledWith(expect.objectContaining({
93
- canvas,
94
- renderer: handle.renderer,
95
- url: 'ws://localhost:3200',
96
- binaryFraming: true,
97
- }));
98
- target.dispatch('pointerdown');
99
- expect(focus).toHaveBeenCalledTimes(1);
100
- win.dispatch('beforeunload');
101
- expect(accessibilityCleanupMock).toHaveBeenCalledTimes(1);
102
- expect(selectionCleanupMock).toHaveBeenCalledTimes(1);
103
- expect(handle.client.close).toHaveBeenCalledTimes(1);
104
- handle.destroy();
105
- expect(accessibilityCleanupMock).toHaveBeenCalledTimes(1);
106
- expect(selectionCleanupMock).toHaveBeenCalledTimes(1);
107
- expect(handle.client.close).toHaveBeenCalledTimes(1);
108
- });
109
- it('supports opting out of mirror/selection helpers and reusing an existing renderer', () => {
110
- const focus = vi.fn();
111
- const win = makeWindowTarget();
112
- const doc = { body: {}, defaultView: win };
113
- const target = new FakeEventTarget();
114
- const canvas = {
115
- ownerDocument: doc,
116
- focus,
117
- addEventListener: target.addEventListener.bind(target),
118
- removeEventListener: target.removeEventListener.bind(target),
119
- hasAttribute: () => true,
120
- setAttribute: () => undefined,
121
- };
122
- const renderer = {
123
- destroy: rendererDestroyMock,
124
- render: rendererRenderMock,
125
- lastLayout: null,
126
- lastTree: null,
127
- };
128
- const handle = createBrowserCanvasClient({
129
- canvas,
130
- renderer,
131
- selection: false,
132
- accessibilityMirror: false,
133
- focusOnPointerDown: false,
134
- autoFocus: true,
135
- closeOnBeforeUnload: false,
136
- });
137
- expect(CanvasRendererMock).not.toHaveBeenCalled();
138
- expect(enableSelectionMock).not.toHaveBeenCalled();
139
- expect(enableAccessibilityMirrorMock).not.toHaveBeenCalled();
140
- expect(focus).toHaveBeenCalledTimes(1);
141
- target.dispatch('pointerdown');
142
- expect(focus).toHaveBeenCalledTimes(1);
143
- handle.destroy();
144
- expect(handle.client.close).toHaveBeenCalledTimes(1);
145
- });
146
- });
147
- //# sourceMappingURL=browser-client.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-client.test.js","sourceRoot":"","sources":["../../src/__tests__/browser-client.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=gestures.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gestures.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gestures.test.ts"],"names":[],"mappings":""}
@@ -1,134 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { attachGestureRecognizers } from '../gestures.js';
3
- class FakeEventTarget {
4
- listeners = new Map();
5
- addEventListener(type, listener) {
6
- if (!this.listeners.has(type))
7
- this.listeners.set(type, new Set());
8
- this.listeners.get(type).add(listener);
9
- }
10
- removeEventListener(type, listener) {
11
- this.listeners.get(type)?.delete(listener);
12
- }
13
- dispatch(type, event) {
14
- const list = this.listeners.get(type);
15
- if (!list)
16
- return;
17
- for (const listener of list)
18
- listener(event);
19
- }
20
- listenerCount(type) {
21
- return this.listeners.get(type)?.size ?? 0;
22
- }
23
- }
24
- function makeRecorder() {
25
- const events = [];
26
- return {
27
- events,
28
- pointerDown: sample => events.push({ type: 'down', sample }),
29
- pointerMove: sample => events.push({ type: 'move', sample }),
30
- pointerUp: sample => events.push({ type: 'up', sample }),
31
- pointerCancel: pointerId => events.push({ type: 'cancel', pointerId }),
32
- };
33
- }
34
- function makeCanvas(target, rect = { left: 10, top: 20 }) {
35
- return {
36
- addEventListener: target.addEventListener.bind(target),
37
- removeEventListener: target.removeEventListener.bind(target),
38
- getBoundingClientRect: () => rect,
39
- };
40
- }
41
- describe('attachGestureRecognizers', () => {
42
- it('converts pointerdown clientX/Y to canvas-space samples and fans out to all recognizers', () => {
43
- const canvasTarget = new FakeEventTarget();
44
- const docTarget = new FakeEventTarget();
45
- const canvas = makeCanvas(canvasTarget, { left: 10, top: 20 });
46
- const a = makeRecorder();
47
- const b = makeRecorder();
48
- const cleanup = attachGestureRecognizers(canvas, [a, b], {
49
- documentTarget: docTarget,
50
- now: () => 42,
51
- });
52
- canvasTarget.dispatch('pointerdown', { pointerId: 1, clientX: 30, clientY: 50 });
53
- expect(a.events).toEqual([{ type: 'down', sample: { id: 1, x: 20, y: 30, timestampMs: 42 } }]);
54
- expect(b.events).toEqual([{ type: 'down', sample: { id: 1, x: 20, y: 30, timestampMs: 42 } }]);
55
- cleanup();
56
- });
57
- it('routes pointermove/up/cancel through documentTarget when trackOutsideCanvas is true (default)', () => {
58
- const canvasTarget = new FakeEventTarget();
59
- const docTarget = new FakeEventTarget();
60
- const canvas = makeCanvas(canvasTarget);
61
- const r = makeRecorder();
62
- const cleanup = attachGestureRecognizers(canvas, [r], {
63
- documentTarget: docTarget,
64
- now: () => 0,
65
- });
66
- canvasTarget.dispatch('pointerdown', { pointerId: 7, clientX: 10, clientY: 20 });
67
- docTarget.dispatch('pointermove', { pointerId: 7, clientX: 60, clientY: 80 });
68
- docTarget.dispatch('pointerup', { pointerId: 7, clientX: 60, clientY: 80 });
69
- expect(r.events.map(e => e.type)).toEqual(['down', 'move', 'up']);
70
- // Moves for unknown pointer ids are filtered out.
71
- docTarget.dispatch('pointermove', { pointerId: 99, clientX: 0, clientY: 0 });
72
- expect(r.events.map(e => e.type)).toEqual(['down', 'move', 'up']);
73
- cleanup();
74
- expect(canvasTarget.listenerCount('pointerdown')).toBe(0);
75
- expect(docTarget.listenerCount('pointermove')).toBe(0);
76
- expect(docTarget.listenerCount('pointerup')).toBe(0);
77
- expect(docTarget.listenerCount('pointercancel')).toBe(0);
78
- });
79
- it('clamps all listeners to the canvas when trackOutsideCanvas is false', () => {
80
- const canvasTarget = new FakeEventTarget();
81
- const docTarget = new FakeEventTarget();
82
- const canvas = makeCanvas(canvasTarget);
83
- const r = makeRecorder();
84
- const cleanup = attachGestureRecognizers(canvas, [r], {
85
- trackOutsideCanvas: false,
86
- documentTarget: docTarget,
87
- now: () => 0,
88
- });
89
- canvasTarget.dispatch('pointerdown', { pointerId: 1, clientX: 10, clientY: 20 });
90
- docTarget.dispatch('pointermove', { pointerId: 1, clientX: 60, clientY: 80 });
91
- // Document move ignored because we only listen on canvas.
92
- expect(r.events.map(e => e.type)).toEqual(['down']);
93
- canvasTarget.dispatch('pointermove', { pointerId: 1, clientX: 60, clientY: 80 });
94
- expect(r.events.map(e => e.type)).toEqual(['down', 'move']);
95
- cleanup();
96
- });
97
- it('pointercancel forwards the pointer id and drops the active pointer', () => {
98
- const canvasTarget = new FakeEventTarget();
99
- const docTarget = new FakeEventTarget();
100
- const canvas = makeCanvas(canvasTarget);
101
- const r = makeRecorder();
102
- const cleanup = attachGestureRecognizers(canvas, [r], {
103
- documentTarget: docTarget,
104
- now: () => 0,
105
- });
106
- canvasTarget.dispatch('pointerdown', { pointerId: 3, clientX: 10, clientY: 20 });
107
- docTarget.dispatch('pointercancel', { pointerId: 3 });
108
- expect(r.events[r.events.length - 1]).toEqual({ type: 'cancel', pointerId: 3 });
109
- // After cancel, further moves for this id are ignored.
110
- docTarget.dispatch('pointermove', { pointerId: 3, clientX: 100, clientY: 100 });
111
- expect(r.events.map(e => e.type)).toEqual(['down', 'cancel']);
112
- cleanup();
113
- });
114
- it('uses the options.now timestamp source for every sample', () => {
115
- const canvasTarget = new FakeEventTarget();
116
- const docTarget = new FakeEventTarget();
117
- const canvas = makeCanvas(canvasTarget);
118
- const r = makeRecorder();
119
- let t = 0;
120
- const clock = () => {
121
- t += 10;
122
- return t;
123
- };
124
- const cleanup = attachGestureRecognizers(canvas, [r], {
125
- documentTarget: docTarget,
126
- now: clock,
127
- });
128
- canvasTarget.dispatch('pointerdown', { pointerId: 1, clientX: 10, clientY: 20 });
129
- docTarget.dispatch('pointermove', { pointerId: 1, clientX: 12, clientY: 22 });
130
- expect(r.events.map(e => e.sample?.timestampMs)).toEqual([10, 20]);
131
- cleanup();
132
- });
133
- });
134
- //# sourceMappingURL=gestures.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"gestures.test.js","sourceRoot":"","sources":["../../src/__tests__/gestures.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=input-forwarding.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"input-forwarding.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/input-forwarding.test.ts"],"names":[],"mappings":""}
@@ -1,170 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { enableInputForwarding } from '../renderer.js';
3
- class FakeEventTarget {
4
- listeners = new Map();
5
- addEventListener(type, listener) {
6
- if (!this.listeners.has(type))
7
- this.listeners.set(type, new Set());
8
- this.listeners.get(type).add(listener);
9
- }
10
- removeEventListener(type, listener) {
11
- this.listeners.get(type)?.delete(listener);
12
- }
13
- dispatch(type, event) {
14
- const list = this.listeners.get(type);
15
- if (!list)
16
- return;
17
- for (const listener of list)
18
- listener(event);
19
- }
20
- }
21
- function makeApp(overrides = {}) {
22
- return {
23
- layout: null,
24
- tree: null,
25
- update: () => undefined,
26
- dispatch: () => false,
27
- dispatchKey: () => false,
28
- dispatchComposition: () => false,
29
- destroy: () => undefined,
30
- ...overrides,
31
- };
32
- }
33
- describe('enableInputForwarding', () => {
34
- it('forwards pointerdown to app onClick dispatch with local coords', () => {
35
- const canvasTarget = new FakeEventTarget();
36
- const keyboardTarget = new FakeEventTarget();
37
- let dispatched = null;
38
- const app = makeApp({
39
- dispatch: (eventType, x, y) => {
40
- dispatched = { eventType, x, y };
41
- return true;
42
- },
43
- });
44
- const canvas = {
45
- addEventListener: canvasTarget.addEventListener.bind(canvasTarget),
46
- removeEventListener: canvasTarget.removeEventListener.bind(canvasTarget),
47
- getBoundingClientRect: () => ({ left: 10, top: 20 }),
48
- };
49
- const cleanup = enableInputForwarding(canvas, () => app, {
50
- keyboardTarget: keyboardTarget,
51
- });
52
- canvasTarget.dispatch('pointerdown', { clientX: 26, clientY: 45 });
53
- expect(dispatched).toEqual({ eventType: 'onClick', x: 16, y: 25 });
54
- cleanup();
55
- });
56
- it('forwards keydown and prevents default only when handled keys require it', () => {
57
- const canvasTarget = new FakeEventTarget();
58
- const keyboardTarget = new FakeEventTarget();
59
- let prevented = false;
60
- let keyCalls = 0;
61
- const app = makeApp({
62
- dispatchKey: () => {
63
- keyCalls++;
64
- return true;
65
- },
66
- });
67
- const canvas = {
68
- addEventListener: canvasTarget.addEventListener.bind(canvasTarget),
69
- removeEventListener: canvasTarget.removeEventListener.bind(canvasTarget),
70
- getBoundingClientRect: () => ({ left: 0, top: 0 }),
71
- };
72
- const cleanup = enableInputForwarding(canvas, () => app, {
73
- keyboardTarget: keyboardTarget,
74
- });
75
- keyboardTarget.dispatch('keydown', {
76
- key: 'a',
77
- code: 'KeyA',
78
- shiftKey: false,
79
- ctrlKey: true,
80
- metaKey: false,
81
- altKey: false,
82
- preventDefault: () => {
83
- prevented = true;
84
- },
85
- });
86
- expect(keyCalls).toBe(1);
87
- expect(prevented).toBe(true);
88
- prevented = false;
89
- keyboardTarget.dispatch('keydown', {
90
- key: 'x',
91
- code: 'KeyX',
92
- shiftKey: false,
93
- ctrlKey: false,
94
- metaKey: false,
95
- altKey: false,
96
- preventDefault: () => {
97
- prevented = true;
98
- },
99
- });
100
- expect(keyCalls).toBe(2);
101
- expect(prevented).toBe(false);
102
- cleanup();
103
- });
104
- it('forwards composition lifecycle events to app', () => {
105
- const canvasTarget = new FakeEventTarget();
106
- const keyboardTarget = new FakeEventTarget();
107
- const calls = [];
108
- const app = makeApp({
109
- dispatchComposition: (eventType, event) => {
110
- calls.push({ type: eventType, data: event.data });
111
- return true;
112
- },
113
- });
114
- const canvas = {
115
- addEventListener: canvasTarget.addEventListener.bind(canvasTarget),
116
- removeEventListener: canvasTarget.removeEventListener.bind(canvasTarget),
117
- getBoundingClientRect: () => ({ left: 0, top: 0 }),
118
- };
119
- const cleanup = enableInputForwarding(canvas, () => app, {
120
- keyboardTarget: keyboardTarget,
121
- });
122
- keyboardTarget.dispatch('compositionstart', { data: '' });
123
- keyboardTarget.dispatch('compositionupdate', { data: 'に' });
124
- keyboardTarget.dispatch('compositionend', { data: 'に' });
125
- expect(calls).toEqual([
126
- { type: 'onCompositionStart', data: '' },
127
- { type: 'onCompositionUpdate', data: 'に' },
128
- { type: 'onCompositionEnd', data: 'に' },
129
- ]);
130
- cleanup();
131
- });
132
- it('cleanup removes pointer and keyboard forwarding listeners', () => {
133
- const canvasTarget = new FakeEventTarget();
134
- const keyboardTarget = new FakeEventTarget();
135
- let pointerCalls = 0;
136
- let keyCalls = 0;
137
- const app = makeApp({
138
- dispatch: () => {
139
- pointerCalls++;
140
- return true;
141
- },
142
- dispatchKey: () => {
143
- keyCalls++;
144
- return true;
145
- },
146
- });
147
- const canvas = {
148
- addEventListener: canvasTarget.addEventListener.bind(canvasTarget),
149
- removeEventListener: canvasTarget.removeEventListener.bind(canvasTarget),
150
- getBoundingClientRect: () => ({ left: 0, top: 0 }),
151
- };
152
- const cleanup = enableInputForwarding(canvas, () => app, {
153
- keyboardTarget: keyboardTarget,
154
- });
155
- cleanup();
156
- canvasTarget.dispatch('pointerdown', { clientX: 10, clientY: 10 });
157
- keyboardTarget.dispatch('keydown', {
158
- key: 'a',
159
- code: 'KeyA',
160
- shiftKey: false,
161
- ctrlKey: false,
162
- metaKey: false,
163
- altKey: false,
164
- preventDefault: () => undefined,
165
- });
166
- expect(pointerCalls).toBe(0);
167
- expect(keyCalls).toBe(0);
168
- });
169
- });
170
- //# sourceMappingURL=input-forwarding.test.js.map