canvasengine 2.0.0-beta.42 → 2.0.0-beta.44

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.
Files changed (77) hide show
  1. package/dist/{DebugRenderer-BosXI2Pd.js → DebugRenderer-P5sZ-0Tq.js} +2 -2
  2. package/dist/{DebugRenderer-BosXI2Pd.js.map → DebugRenderer-P5sZ-0Tq.js.map} +1 -1
  3. package/dist/components/Button.d.ts +3 -1
  4. package/dist/components/Button.d.ts.map +1 -1
  5. package/dist/components/Canvas.d.ts +0 -1
  6. package/dist/components/DOMElement.d.ts +0 -1
  7. package/dist/components/DOMElement.d.ts.map +1 -1
  8. package/dist/components/Graphic.d.ts +1 -2
  9. package/dist/components/Graphic.d.ts.map +1 -1
  10. package/dist/components/NineSliceSprite.d.ts +0 -1
  11. package/dist/components/ParticleEmitter.d.ts +0 -1
  12. package/dist/components/Text.d.ts +0 -1
  13. package/dist/components/TilingSprite.d.ts +0 -1
  14. package/dist/components/Video.d.ts +0 -1
  15. package/dist/components/index.d.ts +2 -1
  16. package/dist/components/index.d.ts.map +1 -1
  17. package/dist/components/types/DisplayObject.d.ts +12 -16
  18. package/dist/components/types/DisplayObject.d.ts.map +1 -1
  19. package/dist/components/types/index.d.ts +0 -1
  20. package/dist/directives/Controls.d.ts +0 -1
  21. package/dist/directives/Drag.d.ts +0 -1
  22. package/dist/directives/Flash.d.ts +0 -1
  23. package/dist/directives/FocusNavigation.d.ts +70 -0
  24. package/dist/directives/FocusNavigation.d.ts.map +1 -0
  25. package/dist/directives/GamepadControls.d.ts +0 -1
  26. package/dist/directives/JoystickControls.d.ts +0 -1
  27. package/dist/directives/KeyboardControls.d.ts +0 -1
  28. package/dist/directives/KeyboardControls.d.ts.map +1 -1
  29. package/dist/directives/Scheduler.d.ts +0 -1
  30. package/dist/directives/Shake.d.ts +0 -1
  31. package/dist/directives/Sound.d.ts +0 -1
  32. package/dist/directives/Transition.d.ts +0 -1
  33. package/dist/directives/ViewportCull.d.ts +0 -1
  34. package/dist/directives/ViewportFollow.d.ts +0 -1
  35. package/dist/directives/ViewportFollow.d.ts.map +1 -1
  36. package/dist/engine/FocusManager.d.ts +173 -0
  37. package/dist/engine/FocusManager.d.ts.map +1 -0
  38. package/dist/engine/animation.d.ts +0 -1
  39. package/dist/engine/bootstrap.d.ts +0 -1
  40. package/dist/engine/directive.d.ts +0 -1
  41. package/dist/engine/reactive.d.ts +0 -1
  42. package/dist/engine/reactive.d.ts.map +1 -1
  43. package/dist/engine/signal.d.ts +0 -1
  44. package/dist/engine/utils.d.ts +0 -1
  45. package/dist/hooks/useFocus.d.ts +60 -0
  46. package/dist/hooks/useFocus.d.ts.map +1 -0
  47. package/dist/hooks/useRef.d.ts +0 -1
  48. package/dist/{index-DNwqVzaq.js → index-VPoz4ufu.js} +6792 -6117
  49. package/dist/index-VPoz4ufu.js.map +1 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.global.js +4 -28
  53. package/dist/index.global.js.map +1 -1
  54. package/dist/index.js +67 -61
  55. package/dist/utils/RadialGradient.d.ts +0 -1
  56. package/package.json +4 -4
  57. package/src/components/Button.ts +7 -4
  58. package/src/components/Canvas.ts +1 -1
  59. package/src/components/DOMContainer.ts +27 -2
  60. package/src/components/DOMElement.ts +37 -29
  61. package/src/components/DisplayObject.ts +15 -3
  62. package/src/components/FocusContainer.ts +372 -0
  63. package/src/components/Graphic.ts +43 -48
  64. package/src/components/Sprite.ts +4 -2
  65. package/src/components/Viewport.ts +65 -26
  66. package/src/components/index.ts +2 -1
  67. package/src/components/types/DisplayObject.ts +7 -4
  68. package/src/directives/Controls.ts +1 -1
  69. package/src/directives/ControlsBase.ts +1 -1
  70. package/src/directives/FocusNavigation.ts +251 -0
  71. package/src/directives/KeyboardControls.ts +12 -8
  72. package/src/directives/ViewportFollow.ts +8 -5
  73. package/src/engine/FocusManager.ts +495 -0
  74. package/src/engine/reactive.ts +20 -19
  75. package/src/hooks/useFocus.ts +94 -0
  76. package/src/index.ts +2 -0
  77. package/dist/index-DNwqVzaq.js.map +0 -1
@@ -3,6 +3,7 @@ import { Subscription } from 'rxjs';
3
3
  import { createComponent, registerComponent, Element, Props } from '../engine/reactive';
4
4
  import { DisplayObject, ComponentInstance } from './DisplayObject';
5
5
  import { effect, Signal } from '@signe/reactive';
6
+ import { Graphics, Container } from 'pixi.js';
6
7
 
7
8
  const EVENTS = [
8
9
  'bounce-x-end',
@@ -43,27 +44,43 @@ export interface ViewportProps extends Props {
43
44
  [key: string]: any;
44
45
  }
45
46
 
46
- export class CanvasViewport extends DisplayObject(PixiViewport) {
47
+ export class CanvasViewport extends DisplayObject(Container) {
47
48
  private tickSubscription: Subscription
48
49
  overrideProps = ['wheel']
50
+ #mask: Graphics
51
+ public viewport: PixiViewport
49
52
 
50
53
  constructor() {
54
+ super()
51
55
  const defaultOptions = {
52
56
  noTicker: true,
53
57
  events: {
54
58
  domElement: {
55
- addEventListener: () => {}
59
+ addEventListener: () => { }
56
60
  }
57
61
  },
58
62
  }
59
63
  // @ts-ignore
60
- super(defaultOptions)
64
+ this.viewport = new PixiViewport(defaultOptions)
65
+ super.addChild(this.viewport)
66
+
67
+ this.#mask = new Graphics()
68
+ super.addChild(this.#mask)
69
+ this.mask = this.#mask
70
+ }
71
+
72
+ addChild<U extends any[]>(...children: U): U[0] {
73
+ return this.viewport.addChild(...children)
74
+ }
75
+
76
+ addChildAt<U extends any>(child: U, index: number): U {
77
+ return this.viewport.addChildAt(child, index)
61
78
  }
62
79
 
63
80
  onInit(props) {
64
81
  super.onInit(props)
65
82
  for (let event of EVENTS) {
66
- if (props[event]) this.on(event, props[event])
83
+ if (props[event]) this.viewport.on(event, props[event])
67
84
  }
68
85
  }
69
86
 
@@ -74,14 +91,19 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
74
91
  * @param {number} [index] - The index of the component among its siblings.
75
92
  */
76
93
  async onMount(element: Element<CanvasViewport>, index?: number): Promise<void> {
94
+ element.props.context.viewport = this.viewport
77
95
  await super.onMount(element, index);
78
96
  const { props } = element;
79
97
  const { tick, app, canvasSize } = props.context;
80
- let isDragging = false
81
-
98
+
82
99
  effect(() => {
83
- this.screenWidth = canvasSize().width
84
- this.screenHeight = canvasSize().height
100
+ if (props.screenWidth === undefined) {
101
+ this.viewport.screenWidth = canvasSize().width
102
+ }
103
+ if (props.screenHeight === undefined) {
104
+ this.viewport.screenHeight = canvasSize().height
105
+ }
106
+ this.updateMask()
85
107
  })
86
108
 
87
109
  effect(() => {
@@ -89,20 +111,19 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
89
111
  if (!_app) return
90
112
 
91
113
  const renderer = _app.renderer
92
-
114
+
93
115
  renderer.events.domElement.addEventListener(
94
116
  'wheel',
95
- this.input.wheelFunction
117
+ this.viewport.input.wheelFunction
96
118
  );
97
119
 
98
- this.options.events = renderer.events
120
+ this.viewport.options.events = renderer.events
99
121
  })
100
-
122
+
101
123
  this.tickSubscription = tick.observable.subscribe(({ value }) => {
102
- this.update(value.timestamp)
124
+ this.viewport.update(value.deltaTime)
103
125
  })
104
126
 
105
- element.props.context.viewport = this
106
127
  this.updateViewportSettings(props)
107
128
  }
108
129
 
@@ -113,46 +134,55 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
113
134
 
114
135
  private updateViewportSettings(props) {
115
136
  if (props.screenWidth !== undefined) {
116
- this.screenWidth = props.screenWidth
137
+ this.viewport.screenWidth = props.screenWidth
117
138
  }
118
139
  if (props.screenHeight !== undefined) {
119
- this.screenHeight = props.screenHeight
140
+ this.viewport.screenHeight = props.screenHeight
120
141
  }
142
+ this.updateMask()
121
143
  if (props.worldWidth !== undefined) {
122
- this.worldWidth = props.worldWidth
144
+ this.viewport.worldWidth = props.worldWidth
123
145
  }
124
146
  if (props.worldHeight !== undefined) {
125
- this.worldHeight = props.worldHeight
147
+ this.viewport.worldHeight = props.worldHeight
126
148
  }
127
149
  if (props.drag) {
128
- this.drag(props.drag)
150
+ this.viewport.drag(props.drag)
129
151
  }
130
152
  if (props.clamp) {
131
- this.clamp(props.clamp.value ?? props.clamp)
153
+ this.viewport.clamp(props.clamp.value ?? props.clamp)
132
154
  }
133
155
  if (props.wheel) {
134
156
  if (props.wheel === true) {
135
- this.wheel()
157
+ this.viewport.wheel()
136
158
  } else {
137
- this.wheel(props.wheel)
159
+ this.viewport.wheel(props.wheel)
138
160
  }
139
161
  }
140
162
  if (props.decelerate) {
141
163
  if (props.decelerate === true) {
142
- this.decelerate()
164
+ this.viewport.decelerate()
143
165
  } else {
144
- this.decelerate(props.decelerate)
166
+ this.viewport.decelerate(props.decelerate)
145
167
  }
146
168
  }
147
169
  if (props.pinch) {
148
170
  if (props.pinch === true) {
149
- this.pinch()
171
+ this.viewport.pinch()
150
172
  } else {
151
- this.pinch(props.pinch)
173
+ this.viewport.pinch(props.pinch)
152
174
  }
153
175
  }
154
176
  }
155
177
 
178
+ private updateMask() {
179
+ if (!this.#mask) return
180
+ this.#mask.clear()
181
+ this.#mask.beginFill(0xffffff)
182
+ this.#mask.drawRect(0, 0, this.viewport.screenWidth, this.viewport.screenHeight)
183
+ this.#mask.endFill()
184
+ }
185
+
156
186
  /**
157
187
  * Called when the component is about to be destroyed.
158
188
  * Unsubscribes from the tick observable.
@@ -166,6 +196,15 @@ export class CanvasViewport extends DisplayObject(PixiViewport) {
166
196
  }
167
197
  await super.onDestroy(parent, _afterDestroy);
168
198
  }
199
+
200
+ // Proxy methods for viewport plugins
201
+ follow(...args: any[]) {
202
+ return (this.viewport.follow as any)(...args)
203
+ }
204
+
205
+ get plugins() {
206
+ return this.viewport.plugins
207
+ }
169
208
  }
170
209
 
171
210
  registerComponent('Viewport', CanvasViewport)
@@ -14,4 +14,5 @@ export { type ComponentInstance } from './DisplayObject'
14
14
  export { DOMContainer } from './DOMContainer'
15
15
  export { DOMElement } from './DOMElement'
16
16
  export { Button, ButtonState, type ButtonProps, type ButtonStyle } from './Button'
17
- export { Joystick, type JoystickSettings, type JoystickChangeEvent } from './Joystick'
17
+ export { Joystick, type JoystickSettings } from './Joystick'
18
+ export { FocusContainer, type FocusContainerProps } from './FocusContainer'
@@ -15,6 +15,8 @@ export type ObjectPosition = string;
15
15
  export type TransformOrigin = string;
16
16
  export type PositionType = 'relative' | 'absolute' | 'static';
17
17
 
18
+ export type ObservablePointSignal = [number, number] | SignalOrPrimitive<[number, number]> | { x: number, y: number } | SignalOrPrimitive<{ x: number, y: number }>;
19
+
18
20
  export interface DisplayObjectProps {
19
21
  attach?: any;
20
22
  ref?: string;
@@ -48,9 +50,9 @@ export interface DisplayObjectProps {
48
50
  padding?: EdgeSize;
49
51
  border?: EdgeSize;
50
52
  absolute?: SignalOrPrimitive<boolean>;
51
- scale?: SignalOrPrimitive<{ x: number, y: number } | number>;
52
- anchor?: SignalOrPrimitive<{ x: number, y: number }>;
53
- skew?: SignalOrPrimitive<{ x: number, y: number }>;
53
+ scale?: ObservablePointSignal | number;
54
+ anchor?: ObservablePointSignal;
55
+ skew?: ObservablePointSignal;
54
56
  tint?: SignalOrPrimitive<number>;
55
57
  rotation?: SignalOrPrimitive<number>;
56
58
  angle?: SignalOrPrimitive<number>;
@@ -58,7 +60,7 @@ export interface DisplayObjectProps {
58
60
  roundPixels?: SignalOrPrimitive<boolean>;
59
61
  cursor?: SignalOrPrimitive<string>;
60
62
  visible?: SignalOrPrimitive<boolean>;
61
- pivot?: SignalOrPrimitive<{ x: number, y: number }>;
63
+ pivot?: ObservablePointSignal;
62
64
  filters?: any[];
63
65
  blendMode?: SignalOrPrimitive<PIXI.BLEND_MODES>;
64
66
  blur?: SignalOrPrimitive<number>;
@@ -100,4 +102,5 @@ export interface DisplayObjectProps {
100
102
  touchmove?: PIXI.FederatedEventHandler;
101
103
  touchstart?: PIXI.FederatedEventHandler;
102
104
  wheel?: PIXI.FederatedEventHandler<PIXI.FederatedWheelEvent>;
105
+ tabindex?: SignalOrPrimitive<number>;
103
106
  }
@@ -84,7 +84,7 @@ export class ControlsDirective extends Directive {
84
84
  /**
85
85
  * Mount hook (no specific action needed)
86
86
  */
87
- onMount(element: Element) {}
87
+ onMount(element: Element) { }
88
88
 
89
89
  /**
90
90
  * Update controls configuration
@@ -118,7 +118,7 @@ export abstract class ControlsBase {
118
118
  protected applyInput(keyName: string) {
119
119
  const boundKey = this.boundKeys[keyName];
120
120
  if (!boundKey) return;
121
-
121
+
122
122
  const { repeat, keyDown } = boundKey.options;
123
123
  // Default implementation - subclasses may override for state tracking
124
124
  if (keyDown) {
@@ -0,0 +1,251 @@
1
+ import { Directive, registerDirective, applyDirective } from "../engine/directive";
2
+ import { type Element } from "../engine/reactive";
3
+ import { focusManager } from "../engine/FocusManager";
4
+ import { ControlsDirective } from "./Controls";
5
+ import { Controls } from "./ControlsBase";
6
+ import { isSignal, Signal } from "@signe/reactive";
7
+ import { CanvasFocusContainer } from "../components/FocusContainer";
8
+
9
+ /**
10
+ * FocusNavigation directive for automatic focus navigation via Controls
11
+ *
12
+ * This directive integrates with the Controls system to automatically navigate
13
+ * between focusable elements using keyboard arrows or gamepad input.
14
+ *
15
+ * The directive is automatically applied when a FocusContainer has a `controls` prop.
16
+ * It wraps the existing Controls configuration to add focus navigation behavior.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Automatic navigation with Controls
21
+ * <FocusContainer tabindex={0} controls={controlsConfig}>
22
+ * <Button tabindex={0} text="Button 1" />
23
+ * <Button tabindex={1} text="Button 2" />
24
+ * </FocusContainer>
25
+ * ```
26
+ */
27
+ export class FocusNavigationDirective extends Directive {
28
+ private element: Element<CanvasFocusContainer> | null = null;
29
+ private controlsDirective: ControlsDirective | null = null;
30
+ private containerId: string = '';
31
+ private controlsSubscription: any = null;
32
+ private originalControls: Controls | null = null;
33
+
34
+ /**
35
+ * Initialize the focus navigation directive
36
+ *
37
+ * @param element - FocusContainer element
38
+ */
39
+ onInit(element: Element<CanvasFocusContainer>) {
40
+ this.element = element;
41
+
42
+ // Get container ID from component instance
43
+ const instance = element.componentInstance as CanvasFocusContainer;
44
+ if (instance && typeof instance.getContainerId === 'function') {
45
+ this.containerId = instance.getContainerId();
46
+ }
47
+
48
+ // Get controls from props
49
+ const controlsProp = element.props.controls;
50
+ if (!controlsProp) return;
51
+
52
+ // Get controls value (handle signals)
53
+ const controlsValue = isSignal(controlsProp) ? controlsProp() : controlsProp;
54
+ if (!controlsValue) return;
55
+
56
+ this.originalControls = controlsValue;
57
+
58
+ // Note: Controls directive will be created/initialized in onMount
59
+ // We'll set up navigation controls there
60
+ }
61
+
62
+ /**
63
+ * Set up navigation controls
64
+ *
65
+ * @param controls - Controls configuration
66
+ */
67
+ private setupNavigationControls(controls: Controls) {
68
+ if (!this.controlsDirective) {
69
+ console.warn('FocusNavigation: Controls directive not found, cannot set up navigation');
70
+ return;
71
+ }
72
+ controls = (controls.value ?? controls) as Controls;
73
+ // Create navigation controls by wrapping existing ones
74
+ const navigationControls: Controls = {
75
+ ...controls,
76
+ // Override or add navigation controls
77
+ up: {
78
+ ...controls.up,
79
+ repeat: controls.up?.repeat ?? true,
80
+ bind: controls.up?.bind ?? ['up', 'top_left', 'top_right'],
81
+ keyDown: (boundKey?: any) => {
82
+ // Navigate up/previous first
83
+ this.navigate('previous');
84
+ // Call original handler if exists
85
+ controls.up?.keyDown?.(boundKey);
86
+ }
87
+ },
88
+ down: {
89
+ ...controls.down,
90
+ repeat: controls.down?.repeat ?? true,
91
+ bind: controls.down?.bind ?? ['down', 'bottom_left', 'bottom_right'],
92
+ keyDown: (boundKey?: any) => {
93
+ // Navigate down/next first
94
+ this.navigate('next');
95
+ // Call original handler if exists
96
+ controls.down?.keyDown?.(boundKey);
97
+ }
98
+ },
99
+ left: {
100
+ ...controls.left,
101
+ repeat: controls.left?.repeat ?? true,
102
+ bind: controls.left?.bind ?? 'left',
103
+ keyDown: (boundKey?: any) => {
104
+ // Navigate previous (for horizontal lists)
105
+ this.navigate('previous');
106
+ // Call original handler if exists
107
+ controls.left?.keyDown?.(boundKey);
108
+ }
109
+ },
110
+ right: {
111
+ ...controls.right,
112
+ repeat: controls.right?.repeat ?? true,
113
+ bind: controls.right?.bind ?? 'right',
114
+ keyDown: (boundKey?: any) => {
115
+ // Navigate next (for horizontal lists)
116
+ this.navigate('next');
117
+ // Call original handler if exists
118
+ controls.right?.keyDown?.(boundKey);
119
+ }
120
+ },
121
+ action: {
122
+ ...controls.action,
123
+ bind: controls.action?.bind ?? ['space', 'enter'],
124
+ keyDown: (boundKey?: any) => {
125
+ // Trigger action on focused element (e.g., click)
126
+ this.triggerAction();
127
+ // Call original handler if exists
128
+ controls.action?.keyDown?.(boundKey);
129
+ }
130
+ }
131
+ };
132
+
133
+ // Update controls directive with navigation controls
134
+ this.controlsDirective.onUpdate({ controls: navigationControls }, this.element!);
135
+ }
136
+
137
+ /**
138
+ * Navigate to next or previous focusable element
139
+ *
140
+ * @param direction - Navigation direction
141
+ */
142
+ private navigate(direction: 'next' | 'previous') {
143
+ if (!this.containerId) return;
144
+ focusManager.navigate(this.containerId, direction);
145
+ }
146
+
147
+ /**
148
+ * Trigger action on currently focused element
149
+ */
150
+ private triggerAction() {
151
+ if (!this.containerId) return;
152
+
153
+ const focusedElementSignal = focusManager.getFocusedElementSignal(this.containerId);
154
+ if (!focusedElementSignal) return;
155
+
156
+ const focusedElement = focusedElementSignal();
157
+ if (!focusedElement) return;
158
+
159
+ // Try to trigger click/pointertap event on focused element
160
+ const instance = focusedElement.componentInstance;
161
+ if (instance && typeof instance.emit === 'function') {
162
+ // Emit pointertap event (equivalent to click)
163
+ instance.emit('pointertap', { target: instance });
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Mount hook
169
+ *
170
+ * @param element - FocusContainer element
171
+ */
172
+ onMount(element: Element<CanvasFocusContainer>) {
173
+ // Get container ID again (should be available now)
174
+ const instance = element.componentInstance as CanvasFocusContainer;
175
+ if (instance && typeof instance.getContainerId === 'function') {
176
+ this.containerId = instance.getContainerId();
177
+ }
178
+
179
+ // Get or create Controls directive
180
+ this.controlsDirective = element.directives?.controls as ControlsDirective;
181
+
182
+ // If Controls directive doesn't exist, create it
183
+ if (!this.controlsDirective) {
184
+ const controlsDirective = applyDirective(element, 'controls');
185
+ if (controlsDirective) {
186
+ if (!element.directives) {
187
+ element.directives = {};
188
+ }
189
+ element.directives.controls = controlsDirective;
190
+ this.controlsDirective = controlsDirective as ControlsDirective;
191
+ }
192
+ }
193
+
194
+ // Get controls from props
195
+ const controlsProp = element.props.controls;
196
+ if (controlsProp) {
197
+ const controlsValue = isSignal(controlsProp) ? controlsProp() : controlsProp;
198
+ if (controlsValue) {
199
+ this.originalControls = controlsValue;
200
+ this.setupNavigationControls(controlsValue);
201
+ }
202
+ }
203
+
204
+ // Handle controls prop updates if it's a signal
205
+ if (isSignal(controlsProp)) {
206
+ this.controlsSubscription = (controlsProp as Signal<Controls>).observable.subscribe((controls) => {
207
+ if (controls) {
208
+ this.originalControls = controls;
209
+ this.setupNavigationControls(controls);
210
+ }
211
+ });
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Update hook
217
+ *
218
+ * @param props - Updated properties
219
+ * @param element - FocusContainer element
220
+ */
221
+ onUpdate(props: any, element: Element<CanvasFocusContainer>) {
222
+ // Update controls if changed
223
+ if (props.controls !== undefined) {
224
+ const controlsValue = isSignal(props.controls) ? props.controls() : props.controls;
225
+ if (controlsValue) {
226
+ this.originalControls = controlsValue;
227
+ this.setupNavigationControls(controlsValue);
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Destroy hook
234
+ *
235
+ * @param element - FocusContainer element
236
+ */
237
+ onDestroy(element: Element<CanvasFocusContainer>) {
238
+ // Cleanup subscription
239
+ if (this.controlsSubscription) {
240
+ this.controlsSubscription.unsubscribe();
241
+ this.controlsSubscription = null;
242
+ }
243
+
244
+ this.element = null;
245
+ this.controlsDirective = null;
246
+ this.originalControls = null;
247
+ }
248
+ }
249
+
250
+ registerDirective('focusNavigation', FocusNavigationDirective);
251
+
@@ -357,11 +357,11 @@ export class KeyboardControls extends ControlsBase {
357
357
  left: boolean,
358
358
  right: boolean
359
359
  } = {
360
- up: false,
361
- down: false,
362
- left: false,
363
- right: false
364
- };
360
+ up: false,
361
+ down: false,
362
+ left: false,
363
+ right: false
364
+ };
365
365
 
366
366
  /**
367
367
  * Setup keyboard event listeners
@@ -391,7 +391,7 @@ export class KeyboardControls extends ControlsBase {
391
391
  const directionControl = this.boundKeys[direction];
392
392
  if (directionControl) {
393
393
  const { keyDown } = directionControl.options;
394
- if (keyDown ) {
394
+ if (keyDown) {
395
395
  this.applyInput(direction);
396
396
  }
397
397
  }
@@ -410,11 +410,15 @@ export class KeyboardControls extends ControlsBase {
410
410
  */
411
411
  protected applyInput(keyName: string) {
412
412
  const keyState = this.keyState[keyName];
413
- if (!keyState) return;
413
+ if (!keyState) {
414
+ return;
415
+ }
414
416
  const { isDown, count } = keyState;
415
417
  if (isDown) {
416
418
  const boundKey = this.boundKeys[keyName];
417
- if (!boundKey) return;
419
+ if (!boundKey) {
420
+ return;
421
+ }
418
422
  const { repeat, keyDown } = boundKey.options;
419
423
  if ((repeat || count == 0)) {
420
424
  let parameters = boundKey.parameters;
@@ -18,12 +18,15 @@ export class ViewportFollow extends Directive {
18
18
 
19
19
  }
20
20
  onMount(element: Element) {
21
- this.onUpdate(element.props.viewportFollow, element)
21
+ this.onUpdate(element.props.viewportFollow, element)
22
22
  }
23
23
  onUpdate(viewportFollow: any, element: Element) {
24
- const { viewport } = element.props.context
24
+ const viewport = element.props.context?.viewport
25
25
  if (!viewport) {
26
- throw error('ViewportFollow directive requires a Viewport component to be mounted in the same context')
26
+ if (viewportFollow) {
27
+ throw error('ViewportFollow directive requires a Viewport component to be mounted in the same context')
28
+ }
29
+ return
27
30
  }
28
31
  if (viewportFollow) {
29
32
  if (viewportFollow === true) {
@@ -46,8 +49,8 @@ export class ViewportFollow extends Directive {
46
49
  }
47
50
  onDestroy(element: Element) {
48
51
  const { viewportFollow } = element.props
49
- const { viewport } = element.props.context
50
- if (viewportFollow) viewport.plugins.remove('follow')
52
+ const viewport = element.props.context?.viewport
53
+ if (viewportFollow && viewport) viewport.plugins.remove('follow')
51
54
  }
52
55
  }
53
56