@applicaster/zapp-react-native-utils 13.0.0-alpha.4528460103 → 13.0.0-alpha.4574923605
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/appUtils/contextKeysManager/index.ts +2 -2
- package/appUtils/contextKeysManager/utils/index.ts +2 -1
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +1 -0
- package/appUtils/focusManager/__tests__/focusManager.test.js +82 -0
- package/appUtils/focusManager/__tests__/index.test.ts +1 -0
- package/appUtils/focusManager/index.ts +16 -3
- package/appUtils/focusManager/utils/index.ts +14 -0
- package/appUtils/focusManagerAux/index.ts +2 -2
- package/package.json +2 -2
- package/reactHooks/navigation/index.ts +7 -19
- package/reactHooks/navigation/useProfilerLogging.ts +15 -0
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
|
|
17
17
|
export { StorageLevel };
|
|
18
18
|
|
|
19
|
-
type KeyNameObj = {
|
|
19
|
+
export type KeyNameObj = {
|
|
20
20
|
namespace: string;
|
|
21
21
|
key: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
type KeyName = string | KeyNameObj;
|
|
24
|
+
export type KeyName = string | KeyNameObj;
|
|
25
25
|
|
|
26
26
|
type ParsedKeyAndNamespace = {
|
|
27
27
|
namespace?: string;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
import { DEFAULT_NAMESPACE } from "../consts";
|
|
3
|
+
import { KeyName, KeyNameObj } from "../index";
|
|
3
4
|
|
|
4
5
|
const lastDotRegex = /\.([^.]+)$/;
|
|
5
6
|
const splitByLastDot = R.compose(R.init, R.split(lastDotRegex));
|
|
6
7
|
|
|
7
|
-
export function getNamespaceAndKey(namespacedKey) {
|
|
8
|
+
export function getNamespaceAndKey(namespacedKey: KeyName): KeyNameObj {
|
|
8
9
|
if (typeof namespacedKey !== "string") return namespacedKey;
|
|
9
10
|
|
|
10
11
|
const namespacedKeyProps = namespacedKey.includes(".")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { focusManager } from "../index.ts";
|
|
2
2
|
import { focusManager as focusManagerIOS } from "../index.ios.ts";
|
|
3
|
+
import { findFirstFocusableItem } from "../utils";
|
|
3
4
|
|
|
4
5
|
describe("focusManagerIOS", () => {
|
|
5
6
|
it("should be defined", () => {
|
|
@@ -8,6 +9,67 @@ describe("focusManagerIOS", () => {
|
|
|
8
9
|
});
|
|
9
10
|
});
|
|
10
11
|
|
|
12
|
+
const focusableTree = {
|
|
13
|
+
root: {
|
|
14
|
+
children: [
|
|
15
|
+
{
|
|
16
|
+
children: [
|
|
17
|
+
{
|
|
18
|
+
children: [
|
|
19
|
+
{
|
|
20
|
+
children: [],
|
|
21
|
+
component: {
|
|
22
|
+
isGroup: true,
|
|
23
|
+
},
|
|
24
|
+
id: "route/1/1",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
children: [],
|
|
28
|
+
component: {
|
|
29
|
+
isGroup: true,
|
|
30
|
+
},
|
|
31
|
+
id: "route/1/2",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
component: {
|
|
35
|
+
isGroup: true,
|
|
36
|
+
},
|
|
37
|
+
id: "route/1",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
children: [
|
|
41
|
+
{
|
|
42
|
+
children: [],
|
|
43
|
+
component: {
|
|
44
|
+
isGroup: true,
|
|
45
|
+
},
|
|
46
|
+
id: "route/2/1",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
children: [],
|
|
50
|
+
component: {
|
|
51
|
+
isGroup: false,
|
|
52
|
+
},
|
|
53
|
+
id: "route/2/2",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
component: {
|
|
57
|
+
isGroup: true,
|
|
58
|
+
},
|
|
59
|
+
id: "route/2",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
component: {
|
|
63
|
+
isGroup: true,
|
|
64
|
+
},
|
|
65
|
+
id: "/river/1234",
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
component: null,
|
|
69
|
+
id: "root",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
11
73
|
describe("focusManager", () => {
|
|
12
74
|
it("should be defined", () => {
|
|
13
75
|
expect(focusManager).toBeDefined();
|
|
@@ -45,4 +107,24 @@ describe("focusManager", () => {
|
|
|
45
107
|
it("placeholder test", () => {
|
|
46
108
|
expect(true).toBe(true);
|
|
47
109
|
});
|
|
110
|
+
|
|
111
|
+
it("findFirstFocusableItem returns focusableItem", () => {
|
|
112
|
+
const expectedItemId =
|
|
113
|
+
focusableTree.root.children[0].children[1].children[1].id;
|
|
114
|
+
|
|
115
|
+
const focusableItem = findFirstFocusableItem(focusableTree.root.children);
|
|
116
|
+
|
|
117
|
+
expect(
|
|
118
|
+
focusableItem !== undefined && expectedItemId === focusableItem.id
|
|
119
|
+
).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("findFirstFocusableItem does not return focusableItem", () => {
|
|
123
|
+
focusableTree.root.children[0].children[1].children[1].component.isGroup =
|
|
124
|
+
true;
|
|
125
|
+
|
|
126
|
+
const focusableItem = findFirstFocusableItem(focusableTree.root.children);
|
|
127
|
+
|
|
128
|
+
expect(focusableItem === undefined).toBe(true);
|
|
129
|
+
});
|
|
48
130
|
});
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { subscriber } from "../../functionUtils";
|
|
14
14
|
import { coreLogger } from "../../logger";
|
|
15
15
|
import { ACTION } from "./utils/enums";
|
|
16
|
+
import { findFirstFocusableItem } from "./utils";
|
|
16
17
|
|
|
17
18
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
18
19
|
|
|
@@ -492,20 +493,29 @@ export const focusManager = (function () {
|
|
|
492
493
|
return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
|
|
493
494
|
}
|
|
494
495
|
|
|
495
|
-
function
|
|
496
|
+
function resetFocus() {
|
|
496
497
|
if (!isCurrentFocusOnTheTopScreen()) {
|
|
497
|
-
// We've failed to set focused node on the new screen => run focus
|
|
498
|
+
// We've failed to set focused node on the new screen => run focus reset
|
|
498
499
|
setInitialFocus();
|
|
499
500
|
}
|
|
500
501
|
}
|
|
501
502
|
|
|
503
|
+
/**
|
|
504
|
+
* sets the focus on first focusable item
|
|
505
|
+
* @public
|
|
506
|
+
*/
|
|
507
|
+
function recoverFocus() {
|
|
508
|
+
const focusableItem = findFirstFocusableItem(focusableTree.root.children);
|
|
509
|
+
focusableItem && setFocus(focusableItem.id);
|
|
510
|
+
}
|
|
511
|
+
|
|
502
512
|
/**
|
|
503
513
|
* moves the focus in a provided direction
|
|
504
514
|
* @public
|
|
505
515
|
* @param {Object} direction of the scroll
|
|
506
516
|
*/
|
|
507
517
|
function moveFocus(direction) {
|
|
508
|
-
if (!isCurrentFocusOnTheTopScreen()) {
|
|
518
|
+
if (!isCurrentFocusOnTheTopScreen() && currentFocusNode) {
|
|
509
519
|
// We are opening new screen. We need to ignore movement of focus.
|
|
510
520
|
return false;
|
|
511
521
|
}
|
|
@@ -528,6 +538,8 @@ export const focusManager = (function () {
|
|
|
528
538
|
} else {
|
|
529
539
|
currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
|
|
530
540
|
}
|
|
541
|
+
} else {
|
|
542
|
+
recoverFocus();
|
|
531
543
|
}
|
|
532
544
|
}
|
|
533
545
|
|
|
@@ -572,5 +584,6 @@ export const focusManager = (function () {
|
|
|
572
584
|
isGroupItemFocused,
|
|
573
585
|
recoverFocus,
|
|
574
586
|
isCurrentFocusOnTheTopScreen,
|
|
587
|
+
resetFocus,
|
|
575
588
|
};
|
|
576
589
|
})();
|
|
@@ -98,3 +98,17 @@ export const waitForContent = (focusableTree) => {
|
|
|
98
98
|
conditionFn: contentHasAnyChildren,
|
|
99
99
|
});
|
|
100
100
|
};
|
|
101
|
+
|
|
102
|
+
export const findFirstFocusableItem = (items) => {
|
|
103
|
+
for (const item of items) {
|
|
104
|
+
if (!item.component.isGroup) {
|
|
105
|
+
return item;
|
|
106
|
+
} else {
|
|
107
|
+
if (item.children) {
|
|
108
|
+
const focusableItem = findFirstFocusableItem(item.children);
|
|
109
|
+
|
|
110
|
+
if (focusableItem) return focusableItem;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
@@ -54,7 +54,7 @@ export async function setFocusOnContent(currentRoute: string) {
|
|
|
54
54
|
// For doing it we have to wait registration of first focusable node into focusableTree
|
|
55
55
|
// we wait until content node starts having children
|
|
56
56
|
|
|
57
|
-
const { setInitialFocus, focusableTree } = focusManager;
|
|
57
|
+
const { setInitialFocus, focusableTree, getCurrentFocus } = focusManager;
|
|
58
58
|
|
|
59
59
|
try {
|
|
60
60
|
await waitForActiveScreen(currentRoute, focusableTree);
|
|
@@ -71,6 +71,6 @@ export async function setFocusOnContent(currentRoute: string) {
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
} catch (error) {
|
|
74
|
-
setFocusOnContent(currentRoute);
|
|
74
|
+
!getCurrentFocus() && setFocusOnContent(currentRoute);
|
|
75
75
|
}
|
|
76
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "13.0.0-alpha.
|
|
3
|
+
"version": "13.0.0-alpha.4574923605",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "13.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "13.0.0-alpha.4574923605",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { BackHandler
|
|
1
|
+
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { BackHandler } from "react-native";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
useContentTypes,
|
|
@@ -31,6 +31,8 @@ export { useGetBottomTabBarHeight } from "./useGetBottomTabBarHeight";
|
|
|
31
31
|
|
|
32
32
|
export { useIsScreenActive } from "./useIsScreenActive";
|
|
33
33
|
|
|
34
|
+
export { useProfilerLogging } from "./useProfilerLogging";
|
|
35
|
+
|
|
34
36
|
export {
|
|
35
37
|
useUniqueRouteSuffix,
|
|
36
38
|
useNavbarId,
|
|
@@ -121,10 +123,8 @@ export function isNavBarVisible(
|
|
|
121
123
|
return true;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
const { ProfilerBridge } = NativeModules;
|
|
125
|
-
|
|
126
126
|
export const useBackHandler = (cb: () => boolean) => {
|
|
127
|
-
|
|
127
|
+
useEffect(() => {
|
|
128
128
|
BackHandler.addEventListener("hardwareBackPress", cb);
|
|
129
129
|
|
|
130
130
|
return () => {
|
|
@@ -158,7 +158,7 @@ export const useZappHooksForEntry = (
|
|
|
158
158
|
setIsHooksExecutionInProgress,
|
|
159
159
|
setIsPresentingFullScreen,
|
|
160
160
|
setIsRunningInBackground,
|
|
161
|
-
}: HookModalContextT =
|
|
161
|
+
}: HookModalContextT = useContext(ZappHookModalContext.ReactContext);
|
|
162
162
|
|
|
163
163
|
const {
|
|
164
164
|
appData: { layoutVersion },
|
|
@@ -256,22 +256,10 @@ export const useZappHooksForEntry = (
|
|
|
256
256
|
}, [entry.id]);
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
-
export const useProfilerLogging = () => {
|
|
260
|
-
const logTimestamp = (data: string) =>
|
|
261
|
-
InteractionManager.runAfterInteractions(() => {
|
|
262
|
-
if (ProfilerBridge && data) {
|
|
263
|
-
ProfilerBridge.updateTimer("navigation", data, Date.now());
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
return logTimestamp;
|
|
268
|
-
};
|
|
269
|
-
|
|
270
259
|
export const useRunIfLongPress = (
|
|
271
260
|
options = { timeout: LONG_KEY_PRESS_TIMEOUT }
|
|
272
261
|
) => {
|
|
273
|
-
const pressTimeout =
|
|
274
|
-
React.useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
|
|
262
|
+
const pressTimeout = useRef<Nullable<ReturnType<typeof setTimeout>>>(null);
|
|
275
263
|
|
|
276
264
|
const resetTimeout = () => {
|
|
277
265
|
clearTimeout(pressTimeout.current as ReturnType<typeof setTimeout>);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { InteractionManager, NativeModules } from "react-native";
|
|
3
|
+
|
|
4
|
+
const { ProfilerBridge } = NativeModules;
|
|
5
|
+
|
|
6
|
+
export const useProfilerLogging = () =>
|
|
7
|
+
useCallback(
|
|
8
|
+
(data: string) =>
|
|
9
|
+
InteractionManager.runAfterInteractions(() => {
|
|
10
|
+
if (ProfilerBridge && data) {
|
|
11
|
+
ProfilerBridge.updateTimer("navigation", data, Date.now());
|
|
12
|
+
}
|
|
13
|
+
}),
|
|
14
|
+
[]
|
|
15
|
+
);
|