Skip to content

Commit 680a2ef

Browse files
authored
Focus management refactoring
1 parent fb2fb9d commit 680a2ef

22 files changed

+923
-541
lines changed

.changeset/eager-carrots-repair.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@plexinc/react-native-lightning": patch
3+
"@plexinc/react-lightning": patch
4+
---
5+
6+
Refactored focus management to be a little more performant and reliable

packages/react-lightning/src/components/Canvas/CanvasRoot.tsx

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useRef } from 'react';
22
import { FocusGroup } from '../../focus/FocusGroup';
3-
import { FocusPathProvider } from '../../focus/FocusPathProvider';
3+
import { FocusKeyManager } from '../../focus/FocusKeyManager';
4+
import { FocusManager } from '../../focus/FocusManager';
5+
import { FocusManagerContext } from '../../focus/FocusManagerContext';
46
import { KeyEventProvider } from '../../input/KeyEventProvider';
57
import { KeyMapContext } from '../../input/KeyMapContext';
68
import { KeyPressHandler } from '../../input/KeyPressHandler';
@@ -14,10 +16,21 @@ export const CanvasRoot = ({
1416
height,
1517
}: CanvasProps) => {
1618
const ref = useRef<LightningElement>(null);
19+
const focusManager = useRef<FocusManager<LightningElement>>(
20+
new FocusManager(),
21+
);
22+
const focusKeyManager = useRef<FocusKeyManager<LightningElement>>(
23+
new FocusKeyManager(focusManager.current),
24+
);
1725

1826
return (
1927
<KeyMapContext.Provider value={keyMap}>
20-
<FocusPathProvider rootRef={ref}>
28+
<FocusManagerContext.Provider
29+
value={{
30+
focusManager: focusManager.current,
31+
focusKeyManager: focusKeyManager.current,
32+
}}
33+
>
2134
<KeyEventProvider>
2235
<KeyPressHandler>
2336
<FocusGroup
@@ -32,7 +45,7 @@ export const CanvasRoot = ({
3245
</FocusGroup>
3346
</KeyPressHandler>
3447
</KeyEventProvider>
35-
</FocusPathProvider>
48+
</FocusManagerContext.Provider>
3649
</KeyMapContext.Provider>
3750
);
3851
};

packages/react-lightning/src/element/LightningViewElement.ts

+21-26
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,12 @@ function createTexture(
101101
let idCounter = 0;
102102

103103
export class LightningViewElement<
104-
TStyleProps extends LightningViewElementStyle = LightningViewElementStyle,
105-
TProps extends
106-
LightningViewElementProps<TStyleProps> = LightningViewElementProps<TStyleProps>,
107-
> implements
108-
Focusable,
109-
Pick<EventEmitter<LightningElementEvents>, 'on' | 'off' | 'emit'>
104+
TStyleProps extends LightningViewElementStyle = LightningViewElementStyle,
105+
TProps extends
106+
LightningViewElementProps<TStyleProps> = LightningViewElementProps<TStyleProps>,
107+
>
108+
extends EventEmitter<LightningElementEvents>
109+
implements Focusable
110110
{
111111
public static allElements: Record<number, LightningElement> = {};
112112

@@ -118,7 +118,6 @@ export class LightningViewElement<
118118
public readonly rawProps: TProps;
119119
public readonly props: TProps;
120120

121-
protected readonly _emitter = new EventEmitter<LightningElementEvents>();
122121
protected readonly _renderer: RendererMain;
123122

124123
protected _parent: LightningElement | null = null;
@@ -131,7 +130,7 @@ export class LightningViewElement<
131130
private _shaderDef?: ShaderDef;
132131
private _textureDef?: TextureDef;
133132
private _focused = false;
134-
private _focusable = true;
133+
private _focusable = false;
135134
private _visible = true;
136135

137136
public get visible(): boolean {
@@ -154,7 +153,7 @@ export class LightningViewElement<
154153
this.blur();
155154
}
156155

157-
this.emit('focusableChanged', this._focusable);
156+
this.emit('focusableChanged', this, this._focusable);
158157
}
159158

160159
public get focused() {
@@ -240,18 +239,14 @@ export class LightningViewElement<
240239
return this.node.id === 1;
241240
}
242241

243-
public on = this._emitter.on.bind(this._emitter);
244-
public off = this._emitter.off.bind(this._emitter);
245-
public addEventListener = this._emitter.on.bind(this._emitter);
246-
public removeEventListener = this._emitter.off.bind(this._emitter);
247-
public emit = this._emitter.emit.bind(this._emitter);
248-
249242
public constructor(
250243
initialProps: TProps,
251244
renderer: RendererMain,
252245
plugins: Plugin<LightningElement>[],
253246
fiber: Fiber,
254247
) {
248+
super();
249+
255250
this._renderer = renderer;
256251
this._plugins = plugins ?? [];
257252

@@ -294,7 +289,7 @@ export class LightningViewElement<
294289

295290
LightningViewElement.allElements[this.id] = this;
296291

297-
this._emitter.emit('initialized');
292+
this.emit('initialized');
298293
}
299294

300295
public destroy() {
@@ -312,7 +307,7 @@ export class LightningViewElement<
312307

313308
delete LightningViewElement.allElements[this.id];
314309

315-
this._emitter.emit('destroy');
310+
this.emit('destroy');
316311
}
317312

318313
public insertChild(
@@ -331,7 +326,7 @@ export class LightningViewElement<
331326

332327
child.parent = this;
333328

334-
this._emitter.emit('childAdded', child, index);
329+
this.emit('childAdded', child, index);
335330
}
336331

337332
public removeChild(child: LightningElement) {
@@ -343,22 +338,22 @@ export class LightningViewElement<
343338

344339
child.node.parent = null;
345340

346-
this._emitter.emit('childRemoved', child, index);
341+
this.emit('childRemoved', child, index);
347342
}
348343

349344
public focus(): void {
350345
if (!this._focused) {
351346
this.props.onFocusCapture?.(this);
352347
this._focused = true;
353-
this._emitter.emit('focusChanged', true);
348+
this.emit('focusChanged', this, true);
354349
this.props.onFocus?.(this);
355350
}
356351
}
357352

358353
public blur(): void {
359354
if (this._focused) {
360355
this._focused = false;
361-
this._emitter.emit('focusChanged', false);
356+
this.emit('focusChanged', this, false);
362357
this.props.onBlur?.(this);
363358
}
364359
}
@@ -507,7 +502,7 @@ export class LightningViewElement<
507502
}
508503

509504
if (this.focusable !== prevFocusable) {
510-
this.emit('focusableChanged', this.focusable);
505+
this.emit('focusableChanged', this, this.focusable);
511506
}
512507
};
513508

@@ -559,7 +554,7 @@ export class LightningViewElement<
559554
}
560555

561556
if (payload.style && Object.keys(payload.style).length) {
562-
this._emitter.emit(
557+
this.emit(
563558
'stylesChanged',
564559
this.props.style as Partial<LightningElementStyle>,
565560
);
@@ -580,12 +575,12 @@ export class LightningViewElement<
580575

581576
private _onTextureLoaded: NodeLoadedEventHandler = (node, event) => {
582577
this._handleTextureLoaded(event);
583-
this._emitter.emit('textureLoaded', node, event);
578+
this.emit('textureLoaded', node, event);
584579
this.props.onTextureReady?.(event.dimensions);
585580
};
586581

587582
private _onTextureFailed: NodeFailedEventHandler = (...args) => {
588-
this._emitter.emit('textureFailed', ...args);
583+
this.emit('textureFailed', ...args);
589584
};
590585

591586
private _onLayout = (dimensions: Rect) => {
@@ -599,7 +594,7 @@ export class LightningViewElement<
599594
const animation = this.node.animate(props, animationSettings || {});
600595

601596
animation.once('stopped', (controller) => {
602-
this._emitter.emit('animationFinished', controller);
597+
this.emit('animationFinished', controller);
603598
});
604599

605600
return animation;

packages/react-lightning/src/focus/FocusContextType.tsx

-11
This file was deleted.

packages/react-lightning/src/focus/FocusEventHandlers.tsx

-3
This file was deleted.

0 commit comments

Comments
 (0)