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