@noriginmedia/norigin-spatial-navigation-core 3.0.0 → 3.1.0-beta.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/dist/Scheduler.d.ts +16 -0
- package/dist/SpatialNavigation.d.ts +52 -56
- package/dist/adapter/getboundingclientrect.d.ts +15 -0
- package/dist/adapter/types.d.ts +15 -0
- package/dist/adapter/web.d.ts +23 -0
- package/dist/index.cjs +723 -397
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +722 -399
- package/dist/measureLayout.d.ts +1 -2
- package/package.json +4 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type Task = () => unknown | Promise<unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Scheduler provides a simple way to queue and execute tasks in a strict sequence.
|
|
4
|
+
* - Regular tasks are run one after another; if a new task is scheduled before the current one starts, it replaces the pending next task.
|
|
5
|
+
* - Priority tasks are added to a separate queue and will be executed before any remaining regular tasks.
|
|
6
|
+
*/
|
|
7
|
+
export default class Scheduler {
|
|
8
|
+
private currentTask;
|
|
9
|
+
private nextTask;
|
|
10
|
+
private nextPriorityTasks;
|
|
11
|
+
private tick;
|
|
12
|
+
bind<A extends unknown[], R>(fn: (...args: A) => R | Promise<R>, context: any): (...args: A) => Promise<void>;
|
|
13
|
+
schedule(task: Task): void;
|
|
14
|
+
schedulePriority(task: Task): void;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import { type LayoutAdapter } from './adapter/types';
|
|
1
2
|
import WritingDirection from './WritingDirection';
|
|
3
|
+
export interface NodeTypeOverrides {
|
|
4
|
+
}
|
|
5
|
+
export type NodeType = NodeTypeOverrides extends {
|
|
6
|
+
node: infer N;
|
|
7
|
+
} ? N : HTMLElement;
|
|
2
8
|
export type Direction = 'up' | 'down' | 'left' | 'right';
|
|
3
9
|
type DistanceCalculationMethod = 'center' | 'edges' | 'corners';
|
|
4
10
|
type DistanceCalculationFunction = (refCorners: Corners, siblingCorners: Corners, isVerticalDirection: boolean, distanceCalculationMethod: DistanceCalculationMethod) => number;
|
|
@@ -6,17 +12,17 @@ export declare const ROOT_FOCUS_KEY = "SN:ROOT";
|
|
|
6
12
|
export interface FocusableComponentLayout {
|
|
7
13
|
left: number;
|
|
8
14
|
top: number;
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
right: number;
|
|
16
|
+
bottom: number;
|
|
11
17
|
width: number;
|
|
12
18
|
height: number;
|
|
13
19
|
x: number;
|
|
14
20
|
y: number;
|
|
15
|
-
node:
|
|
21
|
+
node: NodeType;
|
|
16
22
|
}
|
|
17
|
-
interface FocusableComponent {
|
|
23
|
+
export interface FocusableComponent {
|
|
18
24
|
focusKey: string;
|
|
19
|
-
node:
|
|
25
|
+
node: NodeType;
|
|
20
26
|
parentFocusKey: string;
|
|
21
27
|
onEnterPress: (details?: KeyPressDetails) => void;
|
|
22
28
|
onEnterRelease: () => void;
|
|
@@ -36,10 +42,10 @@ interface FocusableComponent {
|
|
|
36
42
|
forceFocus: boolean;
|
|
37
43
|
lastFocusedChildKey?: string;
|
|
38
44
|
layout?: FocusableComponentLayout;
|
|
39
|
-
|
|
45
|
+
layoutUpdatedAt?: number;
|
|
40
46
|
}
|
|
41
47
|
interface FocusableComponentUpdatePayload {
|
|
42
|
-
node:
|
|
48
|
+
node: NodeType;
|
|
43
49
|
preferredChildFocusKey?: string;
|
|
44
50
|
focusable: boolean;
|
|
45
51
|
isFocusBoundary: boolean;
|
|
@@ -85,7 +91,28 @@ export type BackwardsCompatibleKeyMap = {
|
|
|
85
91
|
export type KeyMap = {
|
|
86
92
|
[index: string]: (string | number)[];
|
|
87
93
|
};
|
|
88
|
-
|
|
94
|
+
export type SpatialNavigationServiceOptions = {
|
|
95
|
+
debug: boolean;
|
|
96
|
+
visualDebug: boolean;
|
|
97
|
+
throttle: number;
|
|
98
|
+
throttleKeypresses: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* @deprecated Use layoutAdapter API instead.
|
|
101
|
+
*/
|
|
102
|
+
useGetBoundingClientRect: boolean;
|
|
103
|
+
shouldFocusDOMNode: boolean;
|
|
104
|
+
domNodeFocusOptions: FocusOptions;
|
|
105
|
+
shouldUseNativeEvents: boolean;
|
|
106
|
+
rtl: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Can be a class (constructor) that implements LayoutAdapter,
|
|
109
|
+
* or an object that partially implements LayoutAdapter.
|
|
110
|
+
*/
|
|
111
|
+
layoutAdapter?: (new (...args: any[]) => LayoutAdapter) | Partial<LayoutAdapter>;
|
|
112
|
+
distanceCalculationMethod: DistanceCalculationMethod;
|
|
113
|
+
customDistanceCalculationFunction?: DistanceCalculationFunction;
|
|
114
|
+
};
|
|
115
|
+
export declare class SpatialNavigationService {
|
|
89
116
|
private focusableComponents;
|
|
90
117
|
private visualDebugger;
|
|
91
118
|
/**
|
|
@@ -104,12 +131,6 @@ declare class SpatialNavigationService {
|
|
|
104
131
|
*/
|
|
105
132
|
private domNodeFocusOptions;
|
|
106
133
|
private enabled;
|
|
107
|
-
/**
|
|
108
|
-
* Used in the React Native environment
|
|
109
|
-
* In this mode, the library works as a "read-only" helper to sync focused
|
|
110
|
-
* states for the components when they are focused by the native focus engine
|
|
111
|
-
*/
|
|
112
|
-
private nativeMode;
|
|
113
134
|
/**
|
|
114
135
|
* Throttling delay for key presses in milliseconds
|
|
115
136
|
*/
|
|
@@ -126,10 +147,7 @@ declare class SpatialNavigationService {
|
|
|
126
147
|
* Flag used to block key events from this service
|
|
127
148
|
*/
|
|
128
149
|
private paused;
|
|
129
|
-
|
|
130
|
-
* Enables/disables getBoundingClientRect
|
|
131
|
-
*/
|
|
132
|
-
private useGetBoundingClientRect;
|
|
150
|
+
private layoutAdapter;
|
|
133
151
|
private keyDownEventListener;
|
|
134
152
|
private keyDownEventListenerThrottled;
|
|
135
153
|
private keyUpEventListener;
|
|
@@ -140,6 +158,12 @@ declare class SpatialNavigationService {
|
|
|
140
158
|
private writingDirection;
|
|
141
159
|
private distanceCalculationMethod;
|
|
142
160
|
private customDistanceCalculationFunction?;
|
|
161
|
+
private scheduler;
|
|
162
|
+
get options(): {
|
|
163
|
+
shouldFocusDOMNode: boolean;
|
|
164
|
+
domNodeFocusOptions: FocusOptions;
|
|
165
|
+
shouldUseNativeEvents: boolean;
|
|
166
|
+
};
|
|
143
167
|
/**
|
|
144
168
|
* Used to determine the coordinate that will be used to filter items that are over the "edge"
|
|
145
169
|
*/
|
|
@@ -171,27 +195,13 @@ declare class SpatialNavigationService {
|
|
|
171
195
|
*/
|
|
172
196
|
sortSiblingsByPriority(siblings: FocusableComponent[], currentLayout: FocusableComponentLayout, direction: string, focusKey: string): FocusableComponent[];
|
|
173
197
|
constructor();
|
|
174
|
-
init({ debug, visualDebug,
|
|
175
|
-
debug?: boolean;
|
|
176
|
-
visualDebug?: boolean;
|
|
177
|
-
nativeMode?: boolean;
|
|
178
|
-
throttle?: number;
|
|
179
|
-
throttleKeypresses?: boolean;
|
|
180
|
-
useGetBoundingClientRect?: boolean;
|
|
181
|
-
shouldFocusDOMNode?: boolean;
|
|
182
|
-
domNodeFocusOptions?: {};
|
|
183
|
-
shouldUseNativeEvents?: boolean;
|
|
184
|
-
rtl?: boolean;
|
|
185
|
-
distanceCalculationMethod?: DistanceCalculationMethod;
|
|
186
|
-
customDistanceCalculationFunction?: DistanceCalculationFunction;
|
|
187
|
-
}): void;
|
|
198
|
+
init({ debug, visualDebug, throttle: throttleParam, throttleKeypresses, useGetBoundingClientRect, layoutAdapter, shouldFocusDOMNode, domNodeFocusOptions, shouldUseNativeEvents, rtl, distanceCalculationMethod, customDistanceCalculationFunction }?: Partial<SpatialNavigationServiceOptions>): void;
|
|
188
199
|
setThrottle({ throttle: throttleParam, throttleKeypresses }?: {
|
|
189
200
|
throttle?: number;
|
|
190
201
|
throttleKeypresses?: boolean;
|
|
191
202
|
}): void;
|
|
192
203
|
destroy(): void;
|
|
193
204
|
getEventType(keyCode: number | string): string;
|
|
194
|
-
static getKeyCode(event: KeyboardEvent): string | number;
|
|
195
205
|
bindEventHandlers(): void;
|
|
196
206
|
unbindEventHandlers(): void;
|
|
197
207
|
onEnterPress(keysDetails: KeyPressDetails): void;
|
|
@@ -204,12 +214,12 @@ declare class SpatialNavigationService {
|
|
|
204
214
|
* @example
|
|
205
215
|
* navigateByDirection('right') // The focus is moved to right
|
|
206
216
|
*/
|
|
207
|
-
navigateByDirection(direction: string, focusDetails: FocusDetails): void
|
|
217
|
+
navigateByDirection(direction: string, focusDetails: FocusDetails): Promise<void>;
|
|
208
218
|
/**
|
|
209
219
|
* This function navigates between siblings OR goes up by the Tree
|
|
210
220
|
* Based on the Direction
|
|
211
221
|
*/
|
|
212
|
-
smartNavigate(direction: string, fromParentFocusKey: string, focusDetails: FocusDetails): void
|
|
222
|
+
smartNavigate(direction: string, fromParentFocusKey: string, focusDetails: FocusDetails): Promise<void>;
|
|
213
223
|
saveLastFocusedChildKey(component: FocusableComponent, focusKey: string): void;
|
|
214
224
|
log(functionName: string, debugString: string, ...rest: any[]): void;
|
|
215
225
|
/**
|
|
@@ -226,10 +236,10 @@ declare class SpatialNavigationService {
|
|
|
226
236
|
* It's either the target node OR the one down by the Tree if node has children components
|
|
227
237
|
* Based on "targetFocusKey" which means the "intended component to focus"
|
|
228
238
|
*/
|
|
229
|
-
getNextFocusKey(targetFocusKey: string): string
|
|
239
|
+
getNextFocusKey(targetFocusKey: string): Promise<string>;
|
|
230
240
|
addFocusable({ focusKey, node, parentFocusKey, onEnterPress, onEnterRelease, onArrowPress, onArrowRelease, onFocus, onBlur, saveLastFocusedChild, trackChildren, onUpdateFocus, onUpdateHasFocusedChild, preferredChildFocusKey, autoRestoreFocus, forceFocus, focusable, isFocusBoundary, focusBoundaryDirections }: FocusableComponent): void;
|
|
231
241
|
removeFocusable({ focusKey }: FocusableComponentRemovePayload): void;
|
|
232
|
-
getNodeLayoutByFocusKey(focusKey: string): FocusableComponentLayout
|
|
242
|
+
getNodeLayoutByFocusKey(focusKey: string): Promise<FocusableComponentLayout>;
|
|
233
243
|
setCurrentFocusedKey(newFocusKey: string, focusDetails: FocusDetails): void;
|
|
234
244
|
updateParentsHasFocusedChild(focusKey: string, focusDetails: FocusDetails): void;
|
|
235
245
|
updateParentsLastFocusedChild(focusKey: string): void;
|
|
@@ -245,11 +255,10 @@ declare class SpatialNavigationService {
|
|
|
245
255
|
onIntermediateNodeBecameBlurred(focusKey: string, focusDetails: FocusDetails): void;
|
|
246
256
|
pause(): void;
|
|
247
257
|
resume(): void;
|
|
248
|
-
setFocus(focusKey: string, focusDetails?: FocusDetails): void
|
|
249
|
-
updateAllLayouts(): void
|
|
250
|
-
updateLayout(focusKey: string): void
|
|
258
|
+
setFocus(focusKey: string, focusDetails?: FocusDetails): Promise<void>;
|
|
259
|
+
updateAllLayouts(): Promise<void>;
|
|
260
|
+
updateLayout(focusKey: string): Promise<void>;
|
|
251
261
|
updateFocusable(focusKey: string, { node, preferredChildFocusKey, focusable, isFocusBoundary, focusBoundaryDirections, onEnterPress, onEnterRelease, onArrowPress, onFocus, onBlur }: FocusableComponentUpdatePayload): void;
|
|
252
|
-
isNativeMode(): boolean;
|
|
253
262
|
doesFocusableExist(focusKey: string): boolean;
|
|
254
263
|
/**
|
|
255
264
|
* This function updates the writing direction
|
|
@@ -261,21 +270,8 @@ declare class SpatialNavigationService {
|
|
|
261
270
|
* Export singleton
|
|
262
271
|
*/
|
|
263
272
|
export declare const SpatialNavigation: SpatialNavigationService;
|
|
264
|
-
export declare const init: ({ debug, visualDebug,
|
|
265
|
-
debug?: boolean;
|
|
266
|
-
visualDebug?: boolean;
|
|
267
|
-
nativeMode?: boolean;
|
|
268
|
-
throttle?: number;
|
|
269
|
-
throttleKeypresses?: boolean;
|
|
270
|
-
useGetBoundingClientRect?: boolean;
|
|
271
|
-
shouldFocusDOMNode?: boolean;
|
|
272
|
-
domNodeFocusOptions?: {};
|
|
273
|
-
shouldUseNativeEvents?: boolean;
|
|
274
|
-
rtl?: boolean;
|
|
275
|
-
distanceCalculationMethod?: DistanceCalculationMethod;
|
|
276
|
-
customDistanceCalculationFunction?: DistanceCalculationFunction;
|
|
277
|
-
}) => void, setThrottle: ({ throttle: throttleParam, throttleKeypresses }?: {
|
|
273
|
+
export declare const init: ({ debug, visualDebug, throttle: throttleParam, throttleKeypresses, useGetBoundingClientRect, layoutAdapter, shouldFocusDOMNode, domNodeFocusOptions, shouldUseNativeEvents, rtl, distanceCalculationMethod, customDistanceCalculationFunction }?: Partial<SpatialNavigationServiceOptions>) => void, setThrottle: ({ throttle: throttleParam, throttleKeypresses }?: {
|
|
278
274
|
throttle?: number;
|
|
279
275
|
throttleKeypresses?: boolean;
|
|
280
|
-
}) => void, destroy: () => void, setKeyMap: (keyMap: BackwardsCompatibleKeyMap) => void, setFocus: (focusKey: string, focusDetails?: FocusDetails) => void
|
|
276
|
+
}) => void, destroy: () => void, setKeyMap: (keyMap: BackwardsCompatibleKeyMap) => void, setFocus: (focusKey: string, focusDetails?: FocusDetails) => Promise<void>, navigateByDirection: (direction: string, focusDetails: FocusDetails) => Promise<void>, pause: () => void, resume: () => void, updateAllLayouts: () => Promise<void>, getCurrentFocusKey: () => string, doesFocusableExist: (focusKey: string) => boolean, updateRtl: (rtl: boolean) => void;
|
|
281
277
|
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FocusableComponent } from '../SpatialNavigation';
|
|
2
|
+
import BaseWebAdapter from './web';
|
|
3
|
+
export default class GetBoundingClientRectAdapter extends BaseWebAdapter {
|
|
4
|
+
measureLayout: (component: FocusableComponent) => Promise<{
|
|
5
|
+
node: HTMLElement;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
left: number;
|
|
11
|
+
top: number;
|
|
12
|
+
right: any;
|
|
13
|
+
bottom: any;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FocusableComponent, type FocusableComponentLayout } from '../SpatialNavigation';
|
|
2
|
+
export type Key = 'left' | 'right' | 'up' | 'down' | 'enter';
|
|
3
|
+
export type KeyDownEventListener = (key: Key, event: Event) => void;
|
|
4
|
+
export type KeyUpEventListener = (key: Key) => void;
|
|
5
|
+
export type AddEventListenersOptions = {
|
|
6
|
+
keyDown?: KeyDownEventListener;
|
|
7
|
+
keyUp?: KeyUpEventListener;
|
|
8
|
+
};
|
|
9
|
+
export interface LayoutAdapter {
|
|
10
|
+
addEventListeners: (options: AddEventListenersOptions) => void;
|
|
11
|
+
removeEventListeners: () => void;
|
|
12
|
+
measureLayout: (component: FocusableComponent) => Promise<FocusableComponentLayout>;
|
|
13
|
+
blurNode: (component: FocusableComponent) => void;
|
|
14
|
+
focusNode: (component: FocusableComponent) => void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type FocusableComponent, type SpatialNavigationService } from '../SpatialNavigation';
|
|
2
|
+
import { type AddEventListenersOptions, type LayoutAdapter } from './types';
|
|
3
|
+
export default class BaseWebAdapter implements LayoutAdapter {
|
|
4
|
+
private service;
|
|
5
|
+
constructor(service: SpatialNavigationService);
|
|
6
|
+
private keyDownEventListener;
|
|
7
|
+
private keyUpEventListener;
|
|
8
|
+
addEventListeners({ keyDown, keyUp }: AddEventListenersOptions): void;
|
|
9
|
+
removeEventListeners(): void;
|
|
10
|
+
measureLayout: (component: FocusableComponent) => Promise<{
|
|
11
|
+
node: HTMLElement;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
left: number;
|
|
17
|
+
top: number;
|
|
18
|
+
right: any;
|
|
19
|
+
bottom: any;
|
|
20
|
+
}>;
|
|
21
|
+
blurNode: (component: FocusableComponent) => void;
|
|
22
|
+
focusNode: (component: FocusableComponent) => void;
|
|
23
|
+
}
|