@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
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findKey, sortBy, assign, throttle, filter, first, difference, forEach, debounce, forOwn } from 'lodash-es';
|
|
2
2
|
|
|
3
3
|
/******************************************************************************
|
|
4
4
|
Copyright (c) Microsoft Corporation.
|
|
@@ -16,6 +16,20 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
|
16
16
|
***************************************************************************** */
|
|
17
17
|
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
18
18
|
|
|
19
|
+
var extendStatics = function(d, b) {
|
|
20
|
+
extendStatics = Object.setPrototypeOf ||
|
|
21
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
22
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
23
|
+
return extendStatics(d, b);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function __extends(d, b) {
|
|
27
|
+
if (typeof b !== "function" && b !== null)
|
|
28
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
29
|
+
extendStatics(d, b);
|
|
30
|
+
function __() { this.constructor = d; }
|
|
31
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
32
|
+
}
|
|
19
33
|
|
|
20
34
|
var __assign = function() {
|
|
21
35
|
__assign = Object.assign || function __assign(t) {
|
|
@@ -28,6 +42,44 @@ var __assign = function() {
|
|
|
28
42
|
return __assign.apply(this, arguments);
|
|
29
43
|
};
|
|
30
44
|
|
|
45
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
46
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
47
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
48
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
49
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
50
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
51
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function __generator(thisArg, body) {
|
|
56
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
57
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
58
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
59
|
+
function step(op) {
|
|
60
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
61
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
62
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
63
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
64
|
+
switch (op[0]) {
|
|
65
|
+
case 0: case 1: t = op; break;
|
|
66
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
67
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
68
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
69
|
+
default:
|
|
70
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
71
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
72
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
73
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
74
|
+
if (t[2]) _.ops.pop();
|
|
75
|
+
_.trys.pop(); continue;
|
|
76
|
+
}
|
|
77
|
+
op = body.call(thisArg, _);
|
|
78
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
79
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
31
83
|
function __spreadArray(to, from, pack) {
|
|
32
84
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
33
85
|
if (ar || !(i in from)) {
|
|
@@ -43,79 +95,6 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
43
95
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
44
96
|
};
|
|
45
97
|
|
|
46
|
-
var WritingDirection;
|
|
47
|
-
(function (WritingDirection) {
|
|
48
|
-
WritingDirection[WritingDirection["LTR"] = 0] = "LTR";
|
|
49
|
-
WritingDirection[WritingDirection["RTL"] = 1] = "RTL";
|
|
50
|
-
})(WritingDirection || (WritingDirection = {}));
|
|
51
|
-
var WritingDirection$1 = WritingDirection;
|
|
52
|
-
|
|
53
|
-
// We'll make VisualDebugger no-op for any environments lacking a DOM (e.g. SSR and React Native non-web platforms).
|
|
54
|
-
var hasDOM = typeof window !== 'undefined' && window.document;
|
|
55
|
-
var WIDTH = hasDOM ? window.innerWidth : 0;
|
|
56
|
-
var HEIGHT = hasDOM ? window.innerHeight : 0;
|
|
57
|
-
var VisualDebugger = /** @class */ (function () {
|
|
58
|
-
function VisualDebugger(writingDirection) {
|
|
59
|
-
if (hasDOM) {
|
|
60
|
-
this.debugCtx = VisualDebugger.createCanvas('sn-debug', '1010', writingDirection);
|
|
61
|
-
this.layoutsCtx = VisualDebugger.createCanvas('sn-layouts', '1000', writingDirection);
|
|
62
|
-
this.writingDirection = writingDirection;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
VisualDebugger.createCanvas = function (id, zIndex, writingDirection) {
|
|
66
|
-
var canvas = document.querySelector("#".concat(id)) || document.createElement('canvas');
|
|
67
|
-
canvas.setAttribute('id', id);
|
|
68
|
-
canvas.setAttribute('dir', writingDirection === WritingDirection$1.LTR ? 'ltr' : 'rtl');
|
|
69
|
-
var ctx = canvas.getContext('2d');
|
|
70
|
-
canvas.style.zIndex = zIndex;
|
|
71
|
-
canvas.style.position = 'fixed';
|
|
72
|
-
canvas.style.top = '0';
|
|
73
|
-
canvas.style.left = '0';
|
|
74
|
-
document.body.appendChild(canvas);
|
|
75
|
-
canvas.width = WIDTH;
|
|
76
|
-
canvas.height = HEIGHT;
|
|
77
|
-
return ctx;
|
|
78
|
-
};
|
|
79
|
-
VisualDebugger.prototype.clear = function () {
|
|
80
|
-
if (!hasDOM) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
this.debugCtx.clearRect(0, 0, WIDTH, HEIGHT);
|
|
84
|
-
};
|
|
85
|
-
VisualDebugger.prototype.clearLayouts = function () {
|
|
86
|
-
if (!hasDOM) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
this.layoutsCtx.clearRect(0, 0, WIDTH, HEIGHT);
|
|
90
|
-
};
|
|
91
|
-
VisualDebugger.prototype.drawLayout = function (layout, focusKey, parentFocusKey) {
|
|
92
|
-
if (!hasDOM) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
this.layoutsCtx.strokeStyle = 'green';
|
|
96
|
-
this.layoutsCtx.strokeRect(layout.left, layout.top, layout.width, layout.height);
|
|
97
|
-
this.layoutsCtx.font = '8px monospace';
|
|
98
|
-
this.layoutsCtx.fillStyle = 'red';
|
|
99
|
-
var horizontalStartDirection = this.writingDirection === WritingDirection$1.LTR ? 'left' : 'right';
|
|
100
|
-
var horizontalStartCoordinate = layout[horizontalStartDirection];
|
|
101
|
-
this.layoutsCtx.fillText(focusKey, horizontalStartCoordinate, layout.top + 10);
|
|
102
|
-
this.layoutsCtx.fillText(parentFocusKey, horizontalStartCoordinate, layout.top + 25);
|
|
103
|
-
this.layoutsCtx.fillText("".concat(horizontalStartDirection, ": ").concat(horizontalStartCoordinate), horizontalStartCoordinate, layout.top + 40);
|
|
104
|
-
this.layoutsCtx.fillText("top: ".concat(layout.top), horizontalStartCoordinate, layout.top + 55);
|
|
105
|
-
};
|
|
106
|
-
VisualDebugger.prototype.drawPoint = function (x, y, color, size) {
|
|
107
|
-
if (color === void 0) { color = 'blue'; }
|
|
108
|
-
if (size === void 0) { size = 10; }
|
|
109
|
-
if (!hasDOM) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
this.debugCtx.strokeStyle = color;
|
|
113
|
-
this.debugCtx.lineWidth = 3;
|
|
114
|
-
this.debugCtx.strokeRect(x - size / 2, y - size / 2, size, size);
|
|
115
|
-
};
|
|
116
|
-
return VisualDebugger;
|
|
117
|
-
}());
|
|
118
|
-
|
|
119
98
|
var ELEMENT_NODE = 1;
|
|
120
99
|
var getRect = function (node) {
|
|
121
100
|
var offsetParent = node.offsetParent;
|
|
@@ -198,6 +177,241 @@ var getBoundingClientRect = function (node) {
|
|
|
198
177
|
};
|
|
199
178
|
};
|
|
200
179
|
|
|
180
|
+
var getKeyCode = function (event) {
|
|
181
|
+
return event.keyCode || event.code || event.key;
|
|
182
|
+
};
|
|
183
|
+
var BaseWebAdapter = /** @class */ (function () {
|
|
184
|
+
function BaseWebAdapter(service) {
|
|
185
|
+
var _this = this;
|
|
186
|
+
this.service = service;
|
|
187
|
+
this.measureLayout = function (component) { return __awaiter(_this, void 0, void 0, function () {
|
|
188
|
+
return __generator(this, function (_a) {
|
|
189
|
+
return [2 /*return*/, (__assign(__assign({}, measureLayout(component.node)), { node: component.node }))];
|
|
190
|
+
});
|
|
191
|
+
}); };
|
|
192
|
+
this.blurNode = function (component) {
|
|
193
|
+
var _a, _b;
|
|
194
|
+
if (component.node && _this.service.options.shouldFocusDOMNode) {
|
|
195
|
+
(_b = (_a = component.node) === null || _a === void 0 ? void 0 : _a.removeAttribute) === null || _b === void 0 ? void 0 : _b.call(_a, 'data-focused');
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
this.focusNode = function (component) {
|
|
199
|
+
var _a, _b;
|
|
200
|
+
if (component.node && _this.service.options.shouldFocusDOMNode) {
|
|
201
|
+
component.node.focus(_this.service.options.domNodeFocusOptions);
|
|
202
|
+
}
|
|
203
|
+
(_b = (_a = component.node) === null || _a === void 0 ? void 0 : _a.setAttribute) === null || _b === void 0 ? void 0 : _b.call(_a, 'data-focused', 'true');
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
BaseWebAdapter.prototype.addEventListeners = function (_a) {
|
|
207
|
+
var _this = this;
|
|
208
|
+
var keyDown = _a.keyDown, keyUp = _a.keyUp;
|
|
209
|
+
this.keyDownEventListener = function (event) {
|
|
210
|
+
var keyCode = getKeyCode(event);
|
|
211
|
+
var key = findKey(_this.service.getKeyMap(), function (codeList) {
|
|
212
|
+
return codeList.includes(keyCode);
|
|
213
|
+
});
|
|
214
|
+
if (!key) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (!_this.service.options.shouldUseNativeEvents) {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
event.stopPropagation();
|
|
220
|
+
}
|
|
221
|
+
keyDown === null || keyDown === void 0 ? void 0 : keyDown(key, event);
|
|
222
|
+
};
|
|
223
|
+
this.keyUpEventListener = function (event) {
|
|
224
|
+
var keyCode = getKeyCode(event);
|
|
225
|
+
var key = findKey(_this.service.getKeyMap(), function (codeList) {
|
|
226
|
+
return codeList.includes(keyCode);
|
|
227
|
+
});
|
|
228
|
+
if (!key) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
keyUp === null || keyUp === void 0 ? void 0 : keyUp(key);
|
|
232
|
+
};
|
|
233
|
+
window.addEventListener('keyup', this.keyUpEventListener);
|
|
234
|
+
window.addEventListener('keydown', this.keyDownEventListener);
|
|
235
|
+
};
|
|
236
|
+
BaseWebAdapter.prototype.removeEventListeners = function () {
|
|
237
|
+
window.removeEventListener('keyup', this.keyUpEventListener);
|
|
238
|
+
window.removeEventListener('keydown', this.keyDownEventListener);
|
|
239
|
+
};
|
|
240
|
+
return BaseWebAdapter;
|
|
241
|
+
}());
|
|
242
|
+
|
|
243
|
+
var GetBoundingClientRectAdapter = /** @class */ (function (_super) {
|
|
244
|
+
__extends(GetBoundingClientRectAdapter, _super);
|
|
245
|
+
function GetBoundingClientRectAdapter() {
|
|
246
|
+
var _this = _super !== null && _super.apply(this, arguments) || this;
|
|
247
|
+
_this.measureLayout = function (component) { return __awaiter(_this, void 0, void 0, function () {
|
|
248
|
+
return __generator(this, function (_a) {
|
|
249
|
+
return [2 /*return*/, (__assign(__assign({}, getBoundingClientRect(component.node)), { node: component.node }))];
|
|
250
|
+
});
|
|
251
|
+
}); };
|
|
252
|
+
return _this;
|
|
253
|
+
}
|
|
254
|
+
return GetBoundingClientRectAdapter;
|
|
255
|
+
}(BaseWebAdapter));
|
|
256
|
+
|
|
257
|
+
var WritingDirection;
|
|
258
|
+
(function (WritingDirection) {
|
|
259
|
+
WritingDirection[WritingDirection["LTR"] = 0] = "LTR";
|
|
260
|
+
WritingDirection[WritingDirection["RTL"] = 1] = "RTL";
|
|
261
|
+
})(WritingDirection || (WritingDirection = {}));
|
|
262
|
+
var WritingDirection$1 = WritingDirection;
|
|
263
|
+
|
|
264
|
+
// We'll make VisualDebugger no-op for any environments lacking a DOM (e.g. SSR and React Native non-web platforms).
|
|
265
|
+
var hasDOM = typeof window !== 'undefined' && window.document;
|
|
266
|
+
var WIDTH = hasDOM ? window.innerWidth : 0;
|
|
267
|
+
var HEIGHT = hasDOM ? window.innerHeight : 0;
|
|
268
|
+
var VisualDebugger = /** @class */ (function () {
|
|
269
|
+
function VisualDebugger(writingDirection) {
|
|
270
|
+
if (hasDOM) {
|
|
271
|
+
this.debugCtx = VisualDebugger.createCanvas('sn-debug', '1010', writingDirection);
|
|
272
|
+
this.layoutsCtx = VisualDebugger.createCanvas('sn-layouts', '1000', writingDirection);
|
|
273
|
+
this.writingDirection = writingDirection;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
VisualDebugger.createCanvas = function (id, zIndex, writingDirection) {
|
|
277
|
+
var canvas = document.querySelector("#".concat(id)) || document.createElement('canvas');
|
|
278
|
+
canvas.setAttribute('id', id);
|
|
279
|
+
canvas.setAttribute('dir', writingDirection === WritingDirection$1.LTR ? 'ltr' : 'rtl');
|
|
280
|
+
var ctx = canvas.getContext('2d');
|
|
281
|
+
canvas.style.zIndex = zIndex;
|
|
282
|
+
canvas.style.position = 'fixed';
|
|
283
|
+
canvas.style.top = '0';
|
|
284
|
+
canvas.style.left = '0';
|
|
285
|
+
document.body.appendChild(canvas);
|
|
286
|
+
canvas.width = WIDTH;
|
|
287
|
+
canvas.height = HEIGHT;
|
|
288
|
+
return ctx;
|
|
289
|
+
};
|
|
290
|
+
VisualDebugger.prototype.clear = function () {
|
|
291
|
+
if (!hasDOM) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
this.debugCtx.clearRect(0, 0, WIDTH, HEIGHT);
|
|
295
|
+
};
|
|
296
|
+
VisualDebugger.prototype.clearLayouts = function () {
|
|
297
|
+
if (!hasDOM) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
this.layoutsCtx.clearRect(0, 0, WIDTH, HEIGHT);
|
|
301
|
+
};
|
|
302
|
+
VisualDebugger.prototype.drawLayout = function (layout, focusKey, parentFocusKey) {
|
|
303
|
+
if (!hasDOM) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.layoutsCtx.strokeStyle = 'green';
|
|
307
|
+
this.layoutsCtx.strokeRect(layout.left, layout.top, layout.width, layout.height);
|
|
308
|
+
this.layoutsCtx.font = '8px monospace';
|
|
309
|
+
this.layoutsCtx.fillStyle = 'red';
|
|
310
|
+
var horizontalStartDirection = this.writingDirection === WritingDirection$1.LTR ? 'left' : 'right';
|
|
311
|
+
var horizontalStartCoordinate = layout[horizontalStartDirection];
|
|
312
|
+
this.layoutsCtx.fillText(focusKey, horizontalStartCoordinate, layout.top + 10);
|
|
313
|
+
this.layoutsCtx.fillText(parentFocusKey, horizontalStartCoordinate, layout.top + 25);
|
|
314
|
+
this.layoutsCtx.fillText("".concat(horizontalStartDirection, ": ").concat(horizontalStartCoordinate), horizontalStartCoordinate, layout.top + 40);
|
|
315
|
+
this.layoutsCtx.fillText("top: ".concat(layout.top), horizontalStartCoordinate, layout.top + 55);
|
|
316
|
+
};
|
|
317
|
+
VisualDebugger.prototype.drawPoint = function (x, y, color, size) {
|
|
318
|
+
if (color === void 0) { color = 'blue'; }
|
|
319
|
+
if (size === void 0) { size = 10; }
|
|
320
|
+
if (!hasDOM) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
this.debugCtx.strokeStyle = color;
|
|
324
|
+
this.debugCtx.lineWidth = 3;
|
|
325
|
+
this.debugCtx.strokeRect(x - size / 2, y - size / 2, size, size);
|
|
326
|
+
};
|
|
327
|
+
return VisualDebugger;
|
|
328
|
+
}());
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Scheduler provides a simple way to queue and execute tasks in a strict sequence.
|
|
332
|
+
* - Regular tasks are run one after another; if a new task is scheduled before the current one starts, it replaces the pending next task.
|
|
333
|
+
* - Priority tasks are added to a separate queue and will be executed before any remaining regular tasks.
|
|
334
|
+
*/
|
|
335
|
+
var Scheduler = /** @class */ (function () {
|
|
336
|
+
function Scheduler() {
|
|
337
|
+
this.nextPriorityTasks = [];
|
|
338
|
+
}
|
|
339
|
+
Scheduler.prototype.tick = function () {
|
|
340
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
341
|
+
var _this = this;
|
|
342
|
+
var _a;
|
|
343
|
+
return __generator(this, function (_b) {
|
|
344
|
+
switch (_b.label) {
|
|
345
|
+
case 0:
|
|
346
|
+
_b.trys.push([0, , 2, 8]);
|
|
347
|
+
return [4 /*yield*/, ((_a = this.currentTask) === null || _a === void 0 ? void 0 : _a.call(this))];
|
|
348
|
+
case 1:
|
|
349
|
+
_b.sent();
|
|
350
|
+
return [3 /*break*/, 8];
|
|
351
|
+
case 2:
|
|
352
|
+
if (!(this.nextPriorityTasks.length > 0)) return [3 /*break*/, 4];
|
|
353
|
+
this.currentTask = function () {
|
|
354
|
+
return Promise.all(_this.nextPriorityTasks.map(function (task) { return task(); }));
|
|
355
|
+
};
|
|
356
|
+
this.nextPriorityTasks = [];
|
|
357
|
+
return [4 /*yield*/, this.tick()];
|
|
358
|
+
case 3:
|
|
359
|
+
_b.sent();
|
|
360
|
+
return [3 /*break*/, 7];
|
|
361
|
+
case 4:
|
|
362
|
+
if (!this.nextTask) return [3 /*break*/, 6];
|
|
363
|
+
this.currentTask = this.nextTask;
|
|
364
|
+
this.nextTask = undefined;
|
|
365
|
+
return [4 /*yield*/, this.tick()];
|
|
366
|
+
case 5:
|
|
367
|
+
_b.sent();
|
|
368
|
+
return [3 /*break*/, 7];
|
|
369
|
+
case 6:
|
|
370
|
+
this.currentTask = undefined;
|
|
371
|
+
_b.label = 7;
|
|
372
|
+
case 7: return [7 /*endfinally*/];
|
|
373
|
+
case 8: return [2 /*return*/];
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
Scheduler.prototype.bind = function (fn, context) {
|
|
379
|
+
var _this = this;
|
|
380
|
+
return function () {
|
|
381
|
+
var args = [];
|
|
382
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
383
|
+
args[_i] = arguments[_i];
|
|
384
|
+
}
|
|
385
|
+
return __awaiter(_this, void 0, void 0, function () {
|
|
386
|
+
var _this = this;
|
|
387
|
+
return __generator(this, function (_a) {
|
|
388
|
+
return [2 /*return*/, this.schedulePriority(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
389
|
+
return [2 /*return*/, fn.bind(context).apply(void 0, args)];
|
|
390
|
+
}); }); })];
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
Scheduler.prototype.schedule = function (task) {
|
|
396
|
+
if (this.currentTask) {
|
|
397
|
+
this.nextTask = task;
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
this.currentTask = task;
|
|
401
|
+
this.tick();
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
Scheduler.prototype.schedulePriority = function (task) {
|
|
405
|
+
if (this.currentTask) {
|
|
406
|
+
this.nextPriorityTasks.push(task);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
task();
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
return Scheduler;
|
|
413
|
+
}());
|
|
414
|
+
|
|
201
415
|
var _a;
|
|
202
416
|
var DIRECTION_LEFT = 'left';
|
|
203
417
|
var DIRECTION_RIGHT = 'right';
|
|
@@ -223,6 +437,7 @@ var DIAGONAL_SLICE_WEIGHT = 1;
|
|
|
223
437
|
*/
|
|
224
438
|
var MAIN_COORDINATE_WEIGHT = 5;
|
|
225
439
|
var AUTO_RESTORE_FOCUS_DELAY = 300;
|
|
440
|
+
var LAYOUT_STALE_TIME = 16; // 60fps
|
|
226
441
|
var DEBUG_FN_COLORS = ['#0FF', '#FF0', '#F0F'];
|
|
227
442
|
var THROTTLE_OPTIONS = {
|
|
228
443
|
leading: true,
|
|
@@ -253,8 +468,16 @@ var normalizeKeyMap = function (keyMap) {
|
|
|
253
468
|
});
|
|
254
469
|
return newKeyMap;
|
|
255
470
|
};
|
|
471
|
+
var DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS = {
|
|
472
|
+
debug: false,
|
|
473
|
+
throttle: 0,
|
|
474
|
+
throttleKeypresses: false,
|
|
475
|
+
domNodeFocusOptions: {},
|
|
476
|
+
shouldUseNativeEvents: false,
|
|
477
|
+
distanceCalculationMethod: 'corners'};
|
|
256
478
|
var SpatialNavigationService = /** @class */ (function () {
|
|
257
479
|
function SpatialNavigationService() {
|
|
480
|
+
this.scheduler = new Scheduler();
|
|
258
481
|
/**
|
|
259
482
|
* Storage for all focusable components
|
|
260
483
|
*/
|
|
@@ -270,10 +493,8 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
270
493
|
this.parentsHavingFocusedChild = [];
|
|
271
494
|
this.domNodeFocusOptions = {};
|
|
272
495
|
this.enabled = false;
|
|
273
|
-
this.nativeMode = false;
|
|
274
496
|
this.throttle = 0;
|
|
275
497
|
this.throttleKeypresses = false;
|
|
276
|
-
this.useGetBoundingClientRect = false;
|
|
277
498
|
this.shouldFocusDOMNode = false;
|
|
278
499
|
this.shouldUseNativeEvents = false;
|
|
279
500
|
this.writingDirection = WritingDirection$1.LTR;
|
|
@@ -289,8 +510,10 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
289
510
|
this.pause = this.pause.bind(this);
|
|
290
511
|
this.resume = this.resume.bind(this);
|
|
291
512
|
this.setFocus = this.setFocus.bind(this);
|
|
292
|
-
this.updateAllLayouts = this.
|
|
293
|
-
this.navigateByDirection = this.
|
|
513
|
+
this.updateAllLayouts = this.scheduler.bind(this.updateAllLayouts, this);
|
|
514
|
+
this.navigateByDirection = this.scheduler.bind(this.navigateByDirection, this);
|
|
515
|
+
this.addFocusable = this.scheduler.bind(this.addFocusable, this);
|
|
516
|
+
this.removeFocusable = this.scheduler.bind(this.removeFocusable, this);
|
|
294
517
|
this.init = this.init.bind(this);
|
|
295
518
|
this.setThrottle = this.setThrottle.bind(this);
|
|
296
519
|
this.destroy = this.destroy.bind(this);
|
|
@@ -307,6 +530,17 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
307
530
|
this.logIndex = 0;
|
|
308
531
|
this.distanceCalculationMethod = 'corners';
|
|
309
532
|
}
|
|
533
|
+
Object.defineProperty(SpatialNavigationService.prototype, "options", {
|
|
534
|
+
get: function () {
|
|
535
|
+
return {
|
|
536
|
+
shouldFocusDOMNode: this.shouldFocusDOMNode,
|
|
537
|
+
domNodeFocusOptions: this.domNodeFocusOptions,
|
|
538
|
+
shouldUseNativeEvents: this.shouldUseNativeEvents
|
|
539
|
+
};
|
|
540
|
+
},
|
|
541
|
+
enumerable: false,
|
|
542
|
+
configurable: true
|
|
543
|
+
});
|
|
310
544
|
/**
|
|
311
545
|
* Used to determine the coordinate that will be used to filter items that are over the "edge"
|
|
312
546
|
*/
|
|
@@ -495,56 +729,73 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
495
729
|
};
|
|
496
730
|
SpatialNavigationService.prototype.init = function (_a) {
|
|
497
731
|
var _this = this;
|
|
498
|
-
var _b = _a === void 0 ? {} : _a,
|
|
732
|
+
var _b = _a === void 0 ? {} : _a, debug = _b.debug, visualDebug = _b.visualDebug, throttleParam = _b.throttle, throttleKeypresses = _b.throttleKeypresses, useGetBoundingClientRect = _b.useGetBoundingClientRect, layoutAdapter = _b.layoutAdapter, shouldFocusDOMNode = _b.shouldFocusDOMNode, domNodeFocusOptions = _b.domNodeFocusOptions, shouldUseNativeEvents = _b.shouldUseNativeEvents, rtl = _b.rtl, distanceCalculationMethod = _b.distanceCalculationMethod, _c = _b.customDistanceCalculationFunction, customDistanceCalculationFunction = _c === void 0 ? undefined : _c;
|
|
499
733
|
if (!this.enabled) {
|
|
500
|
-
this.domNodeFocusOptions =
|
|
501
|
-
|
|
502
|
-
this.
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
734
|
+
this.domNodeFocusOptions =
|
|
735
|
+
domNodeFocusOptions !== null && domNodeFocusOptions !== void 0 ? domNodeFocusOptions : DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.domNodeFocusOptions;
|
|
736
|
+
this.throttleKeypresses =
|
|
737
|
+
throttleKeypresses !== null && throttleKeypresses !== void 0 ? throttleKeypresses : DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.throttleKeypresses;
|
|
738
|
+
/*
|
|
739
|
+
* If layoutAdapter is a constructor, create a new instance of the class
|
|
740
|
+
* If it's an object, merge it with the default adapter
|
|
741
|
+
*/
|
|
742
|
+
if (typeof layoutAdapter === 'function') {
|
|
743
|
+
var LayoutAdapterClass = layoutAdapter;
|
|
744
|
+
this.layoutAdapter = new LayoutAdapterClass(this);
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
if (useGetBoundingClientRect) {
|
|
748
|
+
console.warn('useGetBoundingClientRect is deprecated. Please use layoutAdapter API instead.');
|
|
749
|
+
this.layoutAdapter = new GetBoundingClientRectAdapter(this);
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
this.layoutAdapter = new BaseWebAdapter(this);
|
|
753
|
+
}
|
|
754
|
+
// Override specific methods
|
|
755
|
+
if (layoutAdapter) {
|
|
756
|
+
assign(this.layoutAdapter, layoutAdapter);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
this.shouldFocusDOMNode = shouldFocusDOMNode;
|
|
760
|
+
this.shouldUseNativeEvents =
|
|
761
|
+
shouldUseNativeEvents !== null && shouldUseNativeEvents !== void 0 ? shouldUseNativeEvents : DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.shouldUseNativeEvents;
|
|
507
762
|
this.writingDirection = rtl ? WritingDirection$1.RTL : WritingDirection$1.LTR;
|
|
508
|
-
this.distanceCalculationMethod =
|
|
763
|
+
this.distanceCalculationMethod =
|
|
764
|
+
distanceCalculationMethod !== null && distanceCalculationMethod !== void 0 ? distanceCalculationMethod : DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.distanceCalculationMethod;
|
|
509
765
|
this.customDistanceCalculationFunction =
|
|
510
766
|
customDistanceCalculationFunction;
|
|
511
|
-
this.debug = debug;
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
this.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
_this.visualDebugger.
|
|
522
|
-
forOwn(_this.focusableComponents, function (component, focusKey) {
|
|
523
|
-
_this.visualDebugger.drawLayout(component.layout, focusKey, component.parentFocusKey);
|
|
524
|
-
});
|
|
525
|
-
draw_1();
|
|
767
|
+
this.debug = debug !== null && debug !== void 0 ? debug : DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.debug;
|
|
768
|
+
this.throttle =
|
|
769
|
+
throttleParam !== null && throttleParam !== void 0 ? throttleParam : DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.throttle;
|
|
770
|
+
this.bindEventHandlers();
|
|
771
|
+
if (visualDebug) {
|
|
772
|
+
this.visualDebugger = new VisualDebugger(this.writingDirection);
|
|
773
|
+
var draw_1 = function () {
|
|
774
|
+
requestAnimationFrame(function () {
|
|
775
|
+
_this.visualDebugger.clearLayouts();
|
|
776
|
+
forOwn(_this.focusableComponents, function (component, focusKey) {
|
|
777
|
+
_this.visualDebugger.drawLayout(component.layout, focusKey, component.parentFocusKey);
|
|
526
778
|
});
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}
|
|
779
|
+
draw_1();
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
draw_1();
|
|
530
783
|
}
|
|
784
|
+
this.enabled = true;
|
|
531
785
|
}
|
|
532
786
|
};
|
|
533
787
|
SpatialNavigationService.prototype.setThrottle = function (_a) {
|
|
534
|
-
var _b = _a === void 0 ? {} : _a, _c = _b.throttle, throttleParam = _c === void 0 ?
|
|
788
|
+
var _b = _a === void 0 ? {} : _a, _c = _b.throttle, throttleParam = _c === void 0 ? DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.throttle : _c, _d = _b.throttleKeypresses, throttleKeypresses = _d === void 0 ? DEFAULT_SPATIAL_NAVIGATION_SERVICE_OPTIONS.throttleKeypresses : _d;
|
|
535
789
|
this.throttleKeypresses = throttleKeypresses;
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
this.throttle = throttleParam;
|
|
540
|
-
}
|
|
541
|
-
this.bindEventHandlers();
|
|
790
|
+
this.unbindEventHandlers();
|
|
791
|
+
if (Number.isInteger(throttleParam)) {
|
|
792
|
+
this.throttle = throttleParam;
|
|
542
793
|
}
|
|
794
|
+
this.bindEventHandlers();
|
|
543
795
|
};
|
|
544
796
|
SpatialNavigationService.prototype.destroy = function () {
|
|
545
797
|
if (this.enabled) {
|
|
546
798
|
this.enabled = false;
|
|
547
|
-
this.nativeMode = false;
|
|
548
799
|
this.throttle = 0;
|
|
549
800
|
this.throttleKeypresses = false;
|
|
550
801
|
this.focusKey = null;
|
|
@@ -558,92 +809,78 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
558
809
|
SpatialNavigationService.prototype.getEventType = function (keyCode) {
|
|
559
810
|
return findKey(this.getKeyMap(), function (codeList) { return codeList.includes(keyCode); });
|
|
560
811
|
};
|
|
561
|
-
SpatialNavigationService.getKeyCode = function (event) {
|
|
562
|
-
return event.keyCode || event.code || event.key;
|
|
563
|
-
};
|
|
564
812
|
SpatialNavigationService.prototype.bindEventHandlers = function () {
|
|
565
813
|
var _this = this;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (this.throttle) {
|
|
610
|
-
this.keyDownEventListenerThrottled = throttle(this.keyDownEventListener.bind(this), this.throttle, THROTTLE_OPTIONS);
|
|
611
|
-
}
|
|
612
|
-
// When throttling then make sure to only throttle key down and cancel any queued functions in case of key up
|
|
613
|
-
this.keyUpEventListener = function (event) {
|
|
614
|
-
var keyCode = SpatialNavigationService.getKeyCode(event);
|
|
615
|
-
var eventType = _this.getEventType(keyCode);
|
|
616
|
-
delete _this.pressedKeys[eventType];
|
|
814
|
+
this.keyDownEventListener = function (key, event) {
|
|
815
|
+
_this.scheduler.schedule(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
816
|
+
var preventDefaultNavigation;
|
|
817
|
+
return __generator(this, function (_a) {
|
|
818
|
+
switch (_a.label) {
|
|
819
|
+
case 0:
|
|
820
|
+
if (this.paused === true) {
|
|
821
|
+
return [2 /*return*/];
|
|
822
|
+
}
|
|
823
|
+
if (this.debug) {
|
|
824
|
+
this.logIndex += 1;
|
|
825
|
+
}
|
|
826
|
+
this.pressedKeys[key] = this.pressedKeys[key]
|
|
827
|
+
? this.pressedKeys[key] + 1
|
|
828
|
+
: 1;
|
|
829
|
+
if (key === KEY_ENTER && this.focusKey) {
|
|
830
|
+
this.onEnterPress({ pressedKeys: this.pressedKeys });
|
|
831
|
+
return [2 /*return*/];
|
|
832
|
+
}
|
|
833
|
+
preventDefaultNavigation = this.onArrowPress(key, { pressedKeys: this.pressedKeys }) === false;
|
|
834
|
+
if (this.visualDebugger) {
|
|
835
|
+
this.visualDebugger.clear();
|
|
836
|
+
}
|
|
837
|
+
if (!preventDefaultNavigation) return [3 /*break*/, 1];
|
|
838
|
+
this.log('keyDownEventListener', 'default navigation prevented');
|
|
839
|
+
return [3 /*break*/, 3];
|
|
840
|
+
case 1: return [4 /*yield*/, this.smartNavigate(key, null, { event: event })];
|
|
841
|
+
case 2:
|
|
842
|
+
_a.sent();
|
|
843
|
+
_a.label = 3;
|
|
844
|
+
case 3: return [2 /*return*/];
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}); });
|
|
848
|
+
};
|
|
849
|
+
// Apply throttle only if the option we got is > 0 to avoid limiting the listener to every animation frame
|
|
850
|
+
if (this.throttle) {
|
|
851
|
+
this.keyDownEventListenerThrottled = throttle(this.keyDownEventListener.bind(this), this.throttle, THROTTLE_OPTIONS);
|
|
852
|
+
}
|
|
853
|
+
// When throttling then make sure to only throttle key down and cancel any queued functions in case of key up
|
|
854
|
+
this.keyUpEventListener = function (key) {
|
|
855
|
+
_this.scheduler.schedule(function () {
|
|
856
|
+
delete _this.pressedKeys[key];
|
|
617
857
|
if (_this.throttle && !_this.throttleKeypresses) {
|
|
618
858
|
_this.keyDownEventListenerThrottled.cancel();
|
|
619
859
|
}
|
|
620
|
-
if (
|
|
860
|
+
if (key === KEY_ENTER && _this.focusKey) {
|
|
621
861
|
_this.onEnterRelease();
|
|
622
862
|
}
|
|
623
|
-
if (_this.focusKey &&
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
863
|
+
if (_this.focusKey &&
|
|
864
|
+
(key === DIRECTION_LEFT ||
|
|
865
|
+
key === DIRECTION_RIGHT ||
|
|
866
|
+
key === DIRECTION_UP ||
|
|
867
|
+
key === DIRECTION_DOWN)) {
|
|
868
|
+
_this.onArrowRelease(key);
|
|
628
869
|
}
|
|
629
|
-
};
|
|
630
|
-
|
|
631
|
-
|
|
870
|
+
});
|
|
871
|
+
};
|
|
872
|
+
this.layoutAdapter.addEventListeners({
|
|
873
|
+
keyDown: this.throttle
|
|
632
874
|
? this.keyDownEventListenerThrottled
|
|
633
|
-
: this.keyDownEventListener
|
|
634
|
-
|
|
875
|
+
: this.keyDownEventListener,
|
|
876
|
+
keyUp: this.keyUpEventListener
|
|
877
|
+
});
|
|
635
878
|
};
|
|
636
879
|
SpatialNavigationService.prototype.unbindEventHandlers = function () {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
var listener = this.throttle
|
|
642
|
-
? this.keyDownEventListenerThrottled
|
|
643
|
-
: this.keyDownEventListener;
|
|
644
|
-
window.removeEventListener('keydown', listener);
|
|
645
|
-
this.keyDownEventListener = null;
|
|
646
|
-
}
|
|
880
|
+
this.layoutAdapter.removeEventListeners();
|
|
881
|
+
this.keyUpEventListener = null;
|
|
882
|
+
this.keyDownEventListener = null;
|
|
883
|
+
this.keyDownEventListenerThrottled = null;
|
|
647
884
|
};
|
|
648
885
|
SpatialNavigationService.prototype.onEnterPress = function (keysDetails) {
|
|
649
886
|
var component = this.focusableComponents[this.focusKey];
|
|
@@ -713,107 +950,135 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
713
950
|
* navigateByDirection('right') // The focus is moved to right
|
|
714
951
|
*/
|
|
715
952
|
SpatialNavigationService.prototype.navigateByDirection = function (direction, focusDetails) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
953
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
954
|
+
var validDirections;
|
|
955
|
+
return __generator(this, function (_a) {
|
|
956
|
+
switch (_a.label) {
|
|
957
|
+
case 0:
|
|
958
|
+
if (this.paused === true || !this.enabled) {
|
|
959
|
+
return [2 /*return*/];
|
|
960
|
+
}
|
|
961
|
+
validDirections = [
|
|
962
|
+
DIRECTION_DOWN,
|
|
963
|
+
DIRECTION_UP,
|
|
964
|
+
DIRECTION_LEFT,
|
|
965
|
+
DIRECTION_RIGHT
|
|
966
|
+
];
|
|
967
|
+
if (!validDirections.includes(direction)) return [3 /*break*/, 2];
|
|
968
|
+
this.log('navigateByDirection', 'direction', direction);
|
|
969
|
+
return [4 /*yield*/, this.smartNavigate(direction, null, focusDetails)];
|
|
970
|
+
case 1:
|
|
971
|
+
_a.sent();
|
|
972
|
+
return [3 /*break*/, 3];
|
|
973
|
+
case 2:
|
|
974
|
+
this.log('navigateByDirection', "Invalid direction. You passed: `".concat(direction, "`, but you can use only these: "), validDirections);
|
|
975
|
+
_a.label = 3;
|
|
976
|
+
case 3: return [2 /*return*/];
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
});
|
|
732
980
|
};
|
|
733
981
|
/**
|
|
734
982
|
* This function navigates between siblings OR goes up by the Tree
|
|
735
983
|
* Based on the Direction
|
|
736
984
|
*/
|
|
737
985
|
SpatialNavigationService.prototype.smartNavigate = function (direction, fromParentFocusKey, focusDetails) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
986
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
987
|
+
var isVerticalDirection, isIncrementalDirection, currentComponent, forcedKey, parentFocusKey_1, focusKey, layout, currentCutoffCoordinate_1, threshold_1, siblings, refCorners, sortedSiblings, nextComponent, parentComponent, focusBoundaryDirections;
|
|
988
|
+
var _this = this;
|
|
989
|
+
return __generator(this, function (_a) {
|
|
990
|
+
switch (_a.label) {
|
|
991
|
+
case 0:
|
|
992
|
+
isVerticalDirection = direction === DIRECTION_DOWN || direction === DIRECTION_UP;
|
|
993
|
+
isIncrementalDirection = direction === DIRECTION_DOWN ||
|
|
994
|
+
(this.writingDirection === WritingDirection$1.LTR
|
|
995
|
+
? direction === DIRECTION_RIGHT
|
|
996
|
+
: direction === DIRECTION_LEFT);
|
|
997
|
+
this.log('smartNavigate', 'direction', direction);
|
|
998
|
+
this.log('smartNavigate', 'fromParentFocusKey', fromParentFocusKey);
|
|
999
|
+
this.log('smartNavigate', 'this.focusKey', this.focusKey);
|
|
1000
|
+
currentComponent = this.focusableComponents[fromParentFocusKey || this.focusKey];
|
|
1001
|
+
/**
|
|
1002
|
+
* When there's no currently focused component, an attempt is made, to force focus one of
|
|
1003
|
+
* the Focusable Containers, that have "forceFocus" flag enabled.
|
|
1004
|
+
*/
|
|
1005
|
+
if (!fromParentFocusKey && !currentComponent) {
|
|
1006
|
+
forcedKey = this.getForcedFocusKey();
|
|
1007
|
+
if (forcedKey) {
|
|
1008
|
+
this.setFocus(forcedKey);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
this.log('smartNavigate', 'Aborted due to missing current component and force-focusable key');
|
|
1012
|
+
}
|
|
1013
|
+
// Current component is null (e.g. nothing to navigate from)
|
|
1014
|
+
return [2 /*return*/];
|
|
1015
|
+
}
|
|
1016
|
+
this.log('smartNavigate', 'currentComponent', currentComponent ? currentComponent.focusKey : undefined, currentComponent ? currentComponent.node : undefined, currentComponent);
|
|
1017
|
+
if (!currentComponent) return [3 /*break*/, 5];
|
|
1018
|
+
return [4 /*yield*/, this.updateLayout(currentComponent.focusKey)];
|
|
1019
|
+
case 1:
|
|
1020
|
+
_a.sent();
|
|
1021
|
+
parentFocusKey_1 = currentComponent.parentFocusKey, focusKey = currentComponent.focusKey, layout = currentComponent.layout;
|
|
1022
|
+
currentCutoffCoordinate_1 = SpatialNavigationService.getCutoffCoordinate(isVerticalDirection, isIncrementalDirection, false, layout, this.writingDirection);
|
|
1023
|
+
threshold_1 = Date.now() - LAYOUT_STALE_TIME;
|
|
1024
|
+
return [4 /*yield*/, Promise.all(Object.values(this.focusableComponents)
|
|
1025
|
+
.filter(function (component) {
|
|
1026
|
+
return component.parentFocusKey === parentFocusKey_1 &&
|
|
1027
|
+
component.focusable &&
|
|
1028
|
+
component.layoutUpdatedAt <= threshold_1;
|
|
1029
|
+
})
|
|
1030
|
+
.map(function (component) { return _this.updateLayout(component.focusKey); }))];
|
|
1031
|
+
case 2:
|
|
1032
|
+
_a.sent();
|
|
1033
|
+
siblings = filter(this.focusableComponents, function (component) {
|
|
1034
|
+
if (component.parentFocusKey === parentFocusKey_1 &&
|
|
1035
|
+
component.focusKey !== currentComponent.focusKey &&
|
|
1036
|
+
component.focusable &&
|
|
1037
|
+
component.layout) {
|
|
1038
|
+
var siblingCutoffCoordinate = SpatialNavigationService.getCutoffCoordinate(isVerticalDirection, isIncrementalDirection, true, component.layout, _this.writingDirection);
|
|
1039
|
+
return isVerticalDirection
|
|
1040
|
+
? isIncrementalDirection
|
|
1041
|
+
? siblingCutoffCoordinate >= currentCutoffCoordinate_1 // vertical next
|
|
1042
|
+
: siblingCutoffCoordinate <= currentCutoffCoordinate_1 // vertical previous
|
|
1043
|
+
: _this.writingDirection === WritingDirection$1.LTR
|
|
1044
|
+
? isIncrementalDirection
|
|
1045
|
+
? siblingCutoffCoordinate >= currentCutoffCoordinate_1 // horizontal LTR next
|
|
1046
|
+
: siblingCutoffCoordinate <= currentCutoffCoordinate_1 // horizontal LTR previous
|
|
1047
|
+
: isIncrementalDirection
|
|
1048
|
+
? siblingCutoffCoordinate <= currentCutoffCoordinate_1 // horizontal RTL next
|
|
1049
|
+
: siblingCutoffCoordinate >= currentCutoffCoordinate_1; // horizontal RTL previous
|
|
1050
|
+
}
|
|
1051
|
+
return false;
|
|
1052
|
+
});
|
|
1053
|
+
if (this.debug) {
|
|
1054
|
+
this.log('smartNavigate', 'currentCutoffCoordinate', currentCutoffCoordinate_1);
|
|
1055
|
+
this.log('smartNavigate', 'siblings', "".concat(siblings.length, " elements:"), siblings.map(function (sibling) { return sibling.focusKey; }).join(', '), siblings.map(function (sibling) { return sibling.node; }), siblings.map(function (sibling) { return sibling; }));
|
|
1056
|
+
}
|
|
1057
|
+
if (this.visualDebugger) {
|
|
1058
|
+
refCorners = SpatialNavigationService.getRefCorners(direction, false, layout);
|
|
1059
|
+
this.visualDebugger.drawPoint(refCorners.a.x, refCorners.a.y);
|
|
1060
|
+
this.visualDebugger.drawPoint(refCorners.b.x, refCorners.b.y);
|
|
1061
|
+
}
|
|
1062
|
+
sortedSiblings = this.sortSiblingsByPriority(siblings, layout, direction, focusKey);
|
|
1063
|
+
nextComponent = first(sortedSiblings);
|
|
1064
|
+
this.log('smartNavigate', 'nextComponent', nextComponent ? nextComponent.focusKey : undefined, nextComponent ? nextComponent.node : undefined, nextComponent);
|
|
1065
|
+
if (!nextComponent) return [3 /*break*/, 3];
|
|
1066
|
+
this.setFocus(nextComponent.focusKey, focusDetails);
|
|
1067
|
+
return [3 /*break*/, 5];
|
|
1068
|
+
case 3:
|
|
1069
|
+
parentComponent = this.focusableComponents[parentFocusKey_1];
|
|
1070
|
+
focusBoundaryDirections = (parentComponent === null || parentComponent === void 0 ? void 0 : parentComponent.isFocusBoundary)
|
|
1071
|
+
? parentComponent.focusBoundaryDirections || [direction]
|
|
1072
|
+
: [];
|
|
1073
|
+
if (!(!parentComponent || !focusBoundaryDirections.includes(direction))) return [3 /*break*/, 5];
|
|
1074
|
+
return [4 /*yield*/, this.smartNavigate(direction, parentFocusKey_1, focusDetails)];
|
|
1075
|
+
case 4:
|
|
1076
|
+
_a.sent();
|
|
1077
|
+
_a.label = 5;
|
|
1078
|
+
case 5: return [2 /*return*/];
|
|
789
1079
|
}
|
|
790
|
-
return false;
|
|
791
1080
|
});
|
|
792
|
-
|
|
793
|
-
this.log('smartNavigate', 'currentCutoffCoordinate', currentCutoffCoordinate_1);
|
|
794
|
-
this.log('smartNavigate', 'siblings', "".concat(siblings.length, " elements:"), siblings.map(function (sibling) { return sibling.focusKey; }).join(', '), siblings.map(function (sibling) { return sibling.node; }), siblings.map(function (sibling) { return sibling; }));
|
|
795
|
-
}
|
|
796
|
-
if (this.visualDebugger) {
|
|
797
|
-
var refCorners = SpatialNavigationService.getRefCorners(direction, false, layout);
|
|
798
|
-
this.visualDebugger.drawPoint(refCorners.a.x, refCorners.a.y);
|
|
799
|
-
this.visualDebugger.drawPoint(refCorners.b.x, refCorners.b.y);
|
|
800
|
-
}
|
|
801
|
-
var sortedSiblings = this.sortSiblingsByPriority(siblings, layout, direction, focusKey);
|
|
802
|
-
var nextComponent = first(sortedSiblings);
|
|
803
|
-
this.log('smartNavigate', 'nextComponent', nextComponent ? nextComponent.focusKey : undefined, nextComponent ? nextComponent.node : undefined, nextComponent);
|
|
804
|
-
if (nextComponent) {
|
|
805
|
-
this.setFocus(nextComponent.focusKey, focusDetails);
|
|
806
|
-
}
|
|
807
|
-
else {
|
|
808
|
-
var parentComponent = this.focusableComponents[parentFocusKey_1];
|
|
809
|
-
var focusBoundaryDirections = (parentComponent === null || parentComponent === void 0 ? void 0 : parentComponent.isFocusBoundary)
|
|
810
|
-
? parentComponent.focusBoundaryDirections || [direction]
|
|
811
|
-
: [];
|
|
812
|
-
if (!parentComponent || !focusBoundaryDirections.includes(direction)) {
|
|
813
|
-
this.smartNavigate(direction, parentFocusKey_1, focusDetails);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
1081
|
+
});
|
|
817
1082
|
};
|
|
818
1083
|
SpatialNavigationService.prototype.saveLastFocusedChildKey = function (component, focusKey) {
|
|
819
1084
|
if (component) {
|
|
@@ -868,51 +1133,64 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
868
1133
|
* Based on "targetFocusKey" which means the "intended component to focus"
|
|
869
1134
|
*/
|
|
870
1135
|
SpatialNavigationService.prototype.getNextFocusKey = function (targetFocusKey) {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1136
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1137
|
+
var targetComponent, children, lastFocusedChildKey, preferredChildFocusKey, childKey;
|
|
1138
|
+
var _this = this;
|
|
1139
|
+
return __generator(this, function (_a) {
|
|
1140
|
+
switch (_a.label) {
|
|
1141
|
+
case 0:
|
|
1142
|
+
targetComponent = this.focusableComponents[targetFocusKey];
|
|
1143
|
+
/**
|
|
1144
|
+
* Security check, if component doesn't exist, stay on the same focusKey
|
|
1145
|
+
*/
|
|
1146
|
+
if (!targetComponent) {
|
|
1147
|
+
return [2 /*return*/, targetFocusKey];
|
|
1148
|
+
}
|
|
1149
|
+
children = filter(this.focusableComponents, function (component) {
|
|
1150
|
+
return component.parentFocusKey === targetFocusKey && component.focusable;
|
|
1151
|
+
});
|
|
1152
|
+
if (!(children.length > 0)) return [3 /*break*/, 2];
|
|
1153
|
+
lastFocusedChildKey = targetComponent.lastFocusedChildKey, preferredChildFocusKey = targetComponent.preferredChildFocusKey;
|
|
1154
|
+
this.log('getNextFocusKey', 'lastFocusedChildKey is', lastFocusedChildKey);
|
|
1155
|
+
this.log('getNextFocusKey', 'preferredChildFocusKey is', preferredChildFocusKey);
|
|
1156
|
+
/**
|
|
1157
|
+
* First of all trying to focus last focused child
|
|
1158
|
+
*/
|
|
1159
|
+
if (lastFocusedChildKey &&
|
|
1160
|
+
targetComponent.saveLastFocusedChild &&
|
|
1161
|
+
this.isParticipatingFocusableComponent(lastFocusedChildKey)) {
|
|
1162
|
+
this.log('getNextFocusKey', 'lastFocusedChildKey will be focused', lastFocusedChildKey);
|
|
1163
|
+
return [2 /*return*/, this.getNextFocusKey(lastFocusedChildKey)];
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* If there is no lastFocusedChild, trying to focus the preferred focused key
|
|
1167
|
+
*/
|
|
1168
|
+
if (preferredChildFocusKey &&
|
|
1169
|
+
this.isParticipatingFocusableComponent(preferredChildFocusKey)) {
|
|
1170
|
+
this.log('getNextFocusKey', 'preferredChildFocusKey will be focused', preferredChildFocusKey);
|
|
1171
|
+
return [2 /*return*/, this.getNextFocusKey(preferredChildFocusKey)];
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Otherwise, trying to focus something by coordinates
|
|
1175
|
+
*/
|
|
1176
|
+
return [4 /*yield*/, Promise.all(children.map(function (component) { return _this.updateLayout(component.focusKey); }))];
|
|
1177
|
+
case 1:
|
|
1178
|
+
/**
|
|
1179
|
+
* Otherwise, trying to focus something by coordinates
|
|
1180
|
+
*/
|
|
1181
|
+
_a.sent();
|
|
1182
|
+
childKey = getChildClosestToOrigin(children, this.writingDirection).focusKey;
|
|
1183
|
+
this.log('getNextFocusKey', 'childKey will be focused', childKey);
|
|
1184
|
+
return [2 /*return*/, this.getNextFocusKey(childKey)];
|
|
1185
|
+
case 2:
|
|
1186
|
+
/**
|
|
1187
|
+
* If no children, just return targetFocusKey back
|
|
1188
|
+
*/
|
|
1189
|
+
this.log('getNextFocusKey', 'targetFocusKey', targetFocusKey);
|
|
1190
|
+
return [2 /*return*/, targetFocusKey];
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
881
1193
|
});
|
|
882
|
-
if (children.length > 0) {
|
|
883
|
-
var lastFocusedChildKey = targetComponent.lastFocusedChildKey, preferredChildFocusKey = targetComponent.preferredChildFocusKey;
|
|
884
|
-
this.log('getNextFocusKey', 'lastFocusedChildKey is', lastFocusedChildKey);
|
|
885
|
-
this.log('getNextFocusKey', 'preferredChildFocusKey is', preferredChildFocusKey);
|
|
886
|
-
/**
|
|
887
|
-
* First of all trying to focus last focused child
|
|
888
|
-
*/
|
|
889
|
-
if (lastFocusedChildKey &&
|
|
890
|
-
targetComponent.saveLastFocusedChild &&
|
|
891
|
-
this.isParticipatingFocusableComponent(lastFocusedChildKey)) {
|
|
892
|
-
this.log('getNextFocusKey', 'lastFocusedChildKey will be focused', lastFocusedChildKey);
|
|
893
|
-
return this.getNextFocusKey(lastFocusedChildKey);
|
|
894
|
-
}
|
|
895
|
-
/**
|
|
896
|
-
* If there is no lastFocusedChild, trying to focus the preferred focused key
|
|
897
|
-
*/
|
|
898
|
-
if (preferredChildFocusKey &&
|
|
899
|
-
this.isParticipatingFocusableComponent(preferredChildFocusKey)) {
|
|
900
|
-
this.log('getNextFocusKey', 'preferredChildFocusKey will be focused', preferredChildFocusKey);
|
|
901
|
-
return this.getNextFocusKey(preferredChildFocusKey);
|
|
902
|
-
}
|
|
903
|
-
/**
|
|
904
|
-
* Otherwise, trying to focus something by coordinates
|
|
905
|
-
*/
|
|
906
|
-
children.forEach(function (component) { return _this.updateLayout(component.focusKey); });
|
|
907
|
-
var childKey = getChildClosestToOrigin(children, this.writingDirection).focusKey;
|
|
908
|
-
this.log('getNextFocusKey', 'childKey will be focused', childKey);
|
|
909
|
-
return this.getNextFocusKey(childKey);
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* If no children, just return targetFocusKey back
|
|
913
|
-
*/
|
|
914
|
-
this.log('getNextFocusKey', 'targetFocusKey', targetFocusKey);
|
|
915
|
-
return targetFocusKey;
|
|
916
1194
|
};
|
|
917
1195
|
SpatialNavigationService.prototype.addFocusable = function (_a) {
|
|
918
1196
|
var focusKey = _a.focusKey, node = _a.node, parentFocusKey = _a.parentFocusKey, onEnterPress = _a.onEnterPress, onEnterRelease = _a.onEnterRelease, onArrowPress = _a.onArrowPress, onArrowRelease = _a.onArrowRelease, onFocus = _a.onFocus, onBlur = _a.onBlur, saveLastFocusedChild = _a.saveLastFocusedChild, trackChildren = _a.trackChildren, onUpdateFocus = _a.onUpdateFocus, onUpdateHasFocusedChild = _a.onUpdateHasFocusedChild, preferredChildFocusKey = _a.preferredChildFocusKey, autoRestoreFocus = _a.autoRestoreFocus, forceFocus = _a.forceFocus, focusable = _a.focusable, isFocusBoundary = _a.isFocusBoundary, focusBoundaryDirections = _a.focusBoundaryDirections;
|
|
@@ -937,6 +1215,7 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
937
1215
|
autoRestoreFocus: autoRestoreFocus,
|
|
938
1216
|
forceFocus: forceFocus,
|
|
939
1217
|
lastFocusedChildKey: null,
|
|
1218
|
+
layoutUpdatedAt: 0,
|
|
940
1219
|
layout: {
|
|
941
1220
|
x: 0,
|
|
942
1221
|
y: 0,
|
|
@@ -950,16 +1229,12 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
950
1229
|
* Node ref is also duplicated in layout to be reported in onFocus callback
|
|
951
1230
|
*/
|
|
952
1231
|
node: node
|
|
953
|
-
}
|
|
954
|
-
layoutUpdated: false
|
|
1232
|
+
}
|
|
955
1233
|
};
|
|
956
1234
|
if (!node) {
|
|
957
1235
|
// eslint-disable-next-line no-console
|
|
958
1236
|
console.warn('Component added without a node reference. This will result in its coordinates being empty and may cause lost focus. Check the "ref" passed to "useFocusable": ', this.focusableComponents[focusKey]);
|
|
959
1237
|
}
|
|
960
|
-
if (this.nativeMode) {
|
|
961
|
-
return;
|
|
962
|
-
}
|
|
963
1238
|
this.updateLayout(focusKey);
|
|
964
1239
|
this.log('addFocusable', 'Component added: ', this.focusableComponents[focusKey]);
|
|
965
1240
|
/**
|
|
@@ -1001,9 +1276,6 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
1001
1276
|
if (parentComponent && parentComponent.lastFocusedChildKey === focusKey) {
|
|
1002
1277
|
parentComponent.lastFocusedChildKey = null;
|
|
1003
1278
|
}
|
|
1004
|
-
if (this.nativeMode) {
|
|
1005
|
-
return;
|
|
1006
|
-
}
|
|
1007
1279
|
/**
|
|
1008
1280
|
* If the component was also focused at this time, OR had focused child, focus its parent -> it will focus another child
|
|
1009
1281
|
* Normally the order of components unmount is children -> parents, but sometimes parent can be removed before the child
|
|
@@ -1022,33 +1294,43 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
1022
1294
|
}
|
|
1023
1295
|
};
|
|
1024
1296
|
SpatialNavigationService.prototype.getNodeLayoutByFocusKey = function (focusKey) {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
this
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1297
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1298
|
+
var component;
|
|
1299
|
+
return __generator(this, function (_a) {
|
|
1300
|
+
switch (_a.label) {
|
|
1301
|
+
case 0:
|
|
1302
|
+
component = this.focusableComponents[focusKey];
|
|
1303
|
+
if (!component) return [3 /*break*/, 2];
|
|
1304
|
+
return [4 /*yield*/, this.updateLayout(component.focusKey)];
|
|
1305
|
+
case 1:
|
|
1306
|
+
_a.sent();
|
|
1307
|
+
return [2 /*return*/, component.layout];
|
|
1308
|
+
case 2: return [2 /*return*/, null];
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1031
1312
|
};
|
|
1032
1313
|
SpatialNavigationService.prototype.setCurrentFocusedKey = function (newFocusKey, focusDetails) {
|
|
1033
|
-
var
|
|
1314
|
+
var _this = this;
|
|
1034
1315
|
if (this.isFocusableComponent(this.focusKey) &&
|
|
1035
1316
|
newFocusKey !== this.focusKey) {
|
|
1036
|
-
var
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
(
|
|
1040
|
-
|
|
1317
|
+
var oldComponent_1 = this.focusableComponents[this.focusKey];
|
|
1318
|
+
oldComponent_1.onUpdateFocus(false);
|
|
1319
|
+
this.layoutAdapter.blurNode(oldComponent_1);
|
|
1320
|
+
this.getNodeLayoutByFocusKey(this.focusKey).then(function (layout) {
|
|
1321
|
+
oldComponent_1.onBlur(layout, focusDetails);
|
|
1322
|
+
_this.log('setCurrentFocusedKey', 'onBlur', oldComponent_1);
|
|
1323
|
+
});
|
|
1041
1324
|
}
|
|
1042
1325
|
this.focusKey = newFocusKey;
|
|
1043
1326
|
if (this.isFocusableComponent(this.focusKey)) {
|
|
1044
|
-
var
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
this.log('setCurrentFocusedKey', 'onFocus', newComponent);
|
|
1327
|
+
var newComponent_1 = this.focusableComponents[this.focusKey];
|
|
1328
|
+
this.layoutAdapter.focusNode(newComponent_1);
|
|
1329
|
+
newComponent_1.onUpdateFocus(true);
|
|
1330
|
+
this.getNodeLayoutByFocusKey(this.focusKey).then(function (layout) {
|
|
1331
|
+
newComponent_1.onFocus(layout, focusDetails);
|
|
1332
|
+
_this.log('setCurrentFocusedKey', 'onFocus', newComponent_1);
|
|
1333
|
+
});
|
|
1052
1334
|
}
|
|
1053
1335
|
};
|
|
1054
1336
|
SpatialNavigationService.prototype.updateParentsHasFocusedChild = function (focusKey, focusDetails) {
|
|
@@ -1117,13 +1399,19 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
1117
1399
|
this.focusableComponents[focusKey].focusable);
|
|
1118
1400
|
};
|
|
1119
1401
|
SpatialNavigationService.prototype.onIntermediateNodeBecameFocused = function (focusKey, focusDetails) {
|
|
1402
|
+
var _this = this;
|
|
1120
1403
|
if (this.isParticipatingFocusableComponent(focusKey)) {
|
|
1121
|
-
this.
|
|
1404
|
+
this.getNodeLayoutByFocusKey(focusKey).then(function (layout) {
|
|
1405
|
+
_this.focusableComponents[focusKey].onFocus(layout, focusDetails);
|
|
1406
|
+
});
|
|
1122
1407
|
}
|
|
1123
1408
|
};
|
|
1124
1409
|
SpatialNavigationService.prototype.onIntermediateNodeBecameBlurred = function (focusKey, focusDetails) {
|
|
1410
|
+
var _this = this;
|
|
1125
1411
|
if (this.isParticipatingFocusableComponent(focusKey)) {
|
|
1126
|
-
this.
|
|
1412
|
+
this.getNodeLayoutByFocusKey(focusKey).then(function (layout) {
|
|
1413
|
+
_this.focusableComponents[focusKey].onBlur(layout, focusDetails);
|
|
1414
|
+
});
|
|
1127
1415
|
}
|
|
1128
1416
|
};
|
|
1129
1417
|
SpatialNavigationService.prototype.pause = function () {
|
|
@@ -1132,54 +1420,90 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
1132
1420
|
SpatialNavigationService.prototype.resume = function () {
|
|
1133
1421
|
this.paused = false;
|
|
1134
1422
|
};
|
|
1135
|
-
SpatialNavigationService.prototype.setFocus = function (
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1423
|
+
SpatialNavigationService.prototype.setFocus = function (focusKey_1) {
|
|
1424
|
+
return __awaiter(this, arguments, void 0, function (focusKey, focusDetails) {
|
|
1425
|
+
var newFocusKey;
|
|
1426
|
+
if (focusDetails === void 0) { focusDetails = {}; }
|
|
1427
|
+
return __generator(this, function (_a) {
|
|
1428
|
+
switch (_a.label) {
|
|
1429
|
+
case 0:
|
|
1430
|
+
// Cancel any pending auto-restore focus calls if we are setting focus manually
|
|
1431
|
+
this.setFocusDebounced.cancel();
|
|
1432
|
+
if (!this.enabled) {
|
|
1433
|
+
return [2 /*return*/];
|
|
1434
|
+
}
|
|
1435
|
+
this.log('setFocus', 'focusKey', focusKey);
|
|
1436
|
+
/**
|
|
1437
|
+
* When focusKey is not provided or is equal to `ROOT_FOCUS_KEY`, an attempt is made,
|
|
1438
|
+
* to force focus one of the Focusable Containers, that have "forceFocus" flag enabled.
|
|
1439
|
+
* A component closest to the top left viewport corner (0,0) is force-focused.
|
|
1440
|
+
*/
|
|
1441
|
+
if (!focusKey || focusKey === ROOT_FOCUS_KEY) {
|
|
1442
|
+
// eslint-disable-next-line no-param-reassign
|
|
1443
|
+
focusKey = this.getForcedFocusKey();
|
|
1444
|
+
// If there is no force-focusable key either then we abort
|
|
1445
|
+
if (!focusKey) {
|
|
1446
|
+
this.log('setFocus', 'Aborted due to missing force-focusable key');
|
|
1447
|
+
return [2 /*return*/];
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return [4 /*yield*/, this.getNextFocusKey(focusKey)];
|
|
1451
|
+
case 1:
|
|
1452
|
+
newFocusKey = _a.sent();
|
|
1453
|
+
if (!newFocusKey) {
|
|
1454
|
+
this.log('setFocus', 'Aborted due to missing next focus key');
|
|
1455
|
+
return [2 /*return*/];
|
|
1456
|
+
}
|
|
1457
|
+
this.log('setFocus', 'newFocusKey', newFocusKey);
|
|
1458
|
+
this.setCurrentFocusedKey(newFocusKey, focusDetails);
|
|
1459
|
+
this.updateParentsHasFocusedChild(newFocusKey, focusDetails);
|
|
1460
|
+
this.updateParentsLastFocusedChild(newFocusKey);
|
|
1461
|
+
return [2 /*return*/];
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
});
|
|
1157
1465
|
};
|
|
1158
1466
|
SpatialNavigationService.prototype.updateAllLayouts = function () {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
return
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1467
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1468
|
+
var _this = this;
|
|
1469
|
+
return __generator(this, function (_a) {
|
|
1470
|
+
switch (_a.label) {
|
|
1471
|
+
case 0:
|
|
1472
|
+
if (!this.enabled) {
|
|
1473
|
+
return [2 /*return*/];
|
|
1474
|
+
}
|
|
1475
|
+
return [4 /*yield*/, Promise.all(Object.keys(this.focusableComponents).map(function (focusKey) {
|
|
1476
|
+
return _this.updateLayout(focusKey);
|
|
1477
|
+
}))];
|
|
1478
|
+
case 1:
|
|
1479
|
+
_a.sent();
|
|
1480
|
+
return [2 /*return*/];
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1165
1483
|
});
|
|
1166
1484
|
};
|
|
1167
1485
|
SpatialNavigationService.prototype.updateLayout = function (focusKey) {
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
return
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1486
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1487
|
+
var component, _a;
|
|
1488
|
+
return __generator(this, function (_b) {
|
|
1489
|
+
switch (_b.label) {
|
|
1490
|
+
case 0:
|
|
1491
|
+
component = this.focusableComponents[focusKey];
|
|
1492
|
+
if (!component) {
|
|
1493
|
+
return [2 /*return*/];
|
|
1494
|
+
}
|
|
1495
|
+
_a = component;
|
|
1496
|
+
return [4 /*yield*/, this.layoutAdapter.measureLayout(component)];
|
|
1497
|
+
case 1:
|
|
1498
|
+
_a.layout = _b.sent();
|
|
1499
|
+
component.layoutUpdatedAt = Date.now();
|
|
1500
|
+
return [2 /*return*/];
|
|
1501
|
+
}
|
|
1502
|
+
});
|
|
1503
|
+
});
|
|
1177
1504
|
};
|
|
1178
1505
|
SpatialNavigationService.prototype.updateFocusable = function (focusKey, _a) {
|
|
1179
1506
|
var node = _a.node, preferredChildFocusKey = _a.preferredChildFocusKey, focusable = _a.focusable, isFocusBoundary = _a.isFocusBoundary, focusBoundaryDirections = _a.focusBoundaryDirections, onEnterPress = _a.onEnterPress, onEnterRelease = _a.onEnterRelease, onArrowPress = _a.onArrowPress, onFocus = _a.onFocus, onBlur = _a.onBlur;
|
|
1180
|
-
if (this.nativeMode) {
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
1507
|
var component = this.focusableComponents[focusKey];
|
|
1184
1508
|
if (component) {
|
|
1185
1509
|
component.preferredChildFocusKey = preferredChildFocusKey;
|
|
@@ -1191,14 +1515,13 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
1191
1515
|
component.onArrowPress = onArrowPress;
|
|
1192
1516
|
component.onFocus = onFocus;
|
|
1193
1517
|
component.onBlur = onBlur;
|
|
1518
|
+
// Reset layout updated at to force a layout update
|
|
1519
|
+
component.layoutUpdatedAt = 0;
|
|
1194
1520
|
if (node) {
|
|
1195
1521
|
component.node = node;
|
|
1196
1522
|
}
|
|
1197
1523
|
}
|
|
1198
1524
|
};
|
|
1199
|
-
SpatialNavigationService.prototype.isNativeMode = function () {
|
|
1200
|
-
return this.nativeMode;
|
|
1201
|
-
};
|
|
1202
1525
|
SpatialNavigationService.prototype.doesFocusableExist = function (focusKey) {
|
|
1203
1526
|
return !!this.focusableComponents[focusKey];
|
|
1204
1527
|
};
|
|
@@ -1217,4 +1540,4 @@ var SpatialNavigationService = /** @class */ (function () {
|
|
|
1217
1540
|
var SpatialNavigation = new SpatialNavigationService();
|
|
1218
1541
|
var init = SpatialNavigation.init, setThrottle = SpatialNavigation.setThrottle, destroy = SpatialNavigation.destroy, setKeyMap = SpatialNavigation.setKeyMap, setFocus = SpatialNavigation.setFocus, navigateByDirection = SpatialNavigation.navigateByDirection, pause = SpatialNavigation.pause, resume = SpatialNavigation.resume, updateAllLayouts = SpatialNavigation.updateAllLayouts, getCurrentFocusKey = SpatialNavigation.getCurrentFocusKey, doesFocusableExist = SpatialNavigation.doesFocusableExist, updateRtl = SpatialNavigation.updateRtl;
|
|
1219
1542
|
|
|
1220
|
-
export { ROOT_FOCUS_KEY, SpatialNavigation, destroy, doesFocusableExist, getCurrentFocusKey, init, navigateByDirection, pause, resume, setFocus, setKeyMap, setThrottle, updateAllLayouts, updateRtl };
|
|
1543
|
+
export { BaseWebAdapter, GetBoundingClientRectAdapter, ROOT_FOCUS_KEY, SpatialNavigation, SpatialNavigationService, destroy, doesFocusableExist, getCurrentFocusKey, init, navigateByDirection, pause, resume, setFocus, setKeyMap, setThrottle, updateAllLayouts, updateRtl };
|