@applicaster/zapp-react-native-utils 12.0.0-alpha.5035843303 → 12.0.0-alpha.5050333009
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/actionsExecutor/ActionExecutorContext.tsx +16 -8
- package/analyticsUtils/AnalyticsEvents/index.ts +13 -0
- package/analyticsUtils/AnalyticsEvents/searchEvents.ts +21 -0
- package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
- package/analyticsUtils/__tests__/analyticsManager.test.js +75 -73
- package/analyticsUtils/analyticsPlugins.ts +25 -0
- package/analyticsUtils/events.ts +12 -0
- package/analyticsUtils/helpers/hooks.ts +1 -1
- package/analyticsUtils/index.tsx +12 -0
- package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
- package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
- package/appUtils/HooksManager/{index.js → index.ts} +121 -112
- package/appUtils/HooksManager/types.ts +116 -0
- package/appUtils/RiverFocusManager/index.js +17 -12
- package/appUtils/accessibilityManager/const.ts +50 -0
- package/appUtils/accessibilityManager/hooks.ts +31 -0
- package/appUtils/accessibilityManager/index.ts +150 -0
- package/appUtils/accessibilityManager/logger.ts +5 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
- package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
- package/appUtils/focusManager/__tests__/index.test.ts +2 -3
- package/appUtils/focusManager/index.ts +76 -57
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +2 -3
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +76 -38
- package/appUtils/focusManager/utils/index.ts +100 -0
- package/appUtils/focusManagerAux/index.ts +76 -0
- package/appUtils/focusManagerAux/utils/index.ts +100 -0
- package/appUtils/{index.js → index.ts} +2 -0
- package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +1 -1
- package/appUtils/keyCodes/__tests__/keyCodes.test.js +80 -85
- package/appUtils/keyCodes/{index.js → index.ts} +2 -0
- package/appUtils/keyCodes/keys/index.ts +3 -0
- package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
- package/appUtils/keyCodes/keys/keys.android.ts +18 -0
- package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
- package/appUtils/keyCodes/keys/keys.ts +15 -0
- package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +9 -0
- package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +11 -1
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/platform/const.ts +25 -0
- package/appUtils/platform/index.ts +3 -0
- package/appUtils/platform/platformUtils.ts +171 -0
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +53 -0
- package/appUtils/playerManager/conts.ts +5 -0
- package/appUtils/playerManager/index.ts +11 -5
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +48 -7
- package/appUtils/playerManager/playerFactory.ts +15 -1
- package/appUtils/playerManager/playerNative.ts +40 -14
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +7 -1
- package/appUtils/playerManager/usePlayerState.tsx +13 -1
- package/appUtils/playerManager/usePlayerTitleSummaryOverlay.tsx +50 -0
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +5 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/__tests__/cellUtils.test.ts +48 -0
- package/cellUtils/__tests__/fixtures.ts +13 -0
- package/cellUtils/index.ts +40 -2
- package/dateUtils/index.ts +11 -0
- package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
- package/focusManager/index.ts +1 -1
- package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +2 -2
- package/functionUtils/__tests__/functionUtils.test.js +244 -248
- package/functionUtils/index.ts +15 -11
- package/headersUtils/index.ts +1 -0
- package/inflatedUrlUtils/index.ts +4 -4
- package/keyboardState/index.ts +0 -0
- package/localizationUtils/__tests__/localizationUtils.test.ts +78 -55
- package/localizationUtils/index.ts +13 -19
- package/logger/index.ts +1 -1
- package/manifestUtils/defaultManifestConfigurations/generalContent.js +6 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +10 -1
- package/manifestUtils/index.js +5 -1
- package/manifestUtils/secondaryImage/index.js +18 -15
- package/modalState/index.ts +53 -0
- package/navigationUtils/index.ts +118 -12
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/index.ts +9 -18
- package/package.json +3 -2
- package/playerUtils/configurationGenerator.ts +11 -1
- package/pluginUtils/index.ts +20 -7
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +2 -0
- package/reactHooks/autoscrolling/__tests__/{useTrackedView.test.ts → useTrackedView.test.tsx} +18 -1
- package/reactHooks/cell-click/__tests__/index.test.js +16 -3
- package/reactHooks/cell-click/index.ts +14 -14
- package/reactHooks/device/index.ts +0 -1
- package/reactHooks/device/useIsTablet.tsx +28 -3
- package/reactHooks/device/useMemoizedIsTablet.ts +1 -1
- package/reactHooks/device/useOrientation.tsx +3 -1
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
- package/reactHooks/feed/__tests__/useInflatedUrl.test.ts +18 -3
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/feed/useInflatedUrl.ts +44 -15
- package/reactHooks/flatList/useLoadNextPageIfNeeded.ts +16 -9
- package/reactHooks/index.ts +2 -0
- package/reactHooks/layout/__tests__/{index.test.js → index.test.tsx} +66 -59
- package/reactHooks/layout/getDeviceInfo/index.ts +4 -2
- package/reactHooks/layout/index.ts +2 -1
- package/reactHooks/layout/types/index.ts +2 -0
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +11 -16
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +12 -14
- package/reactHooks/layout/useDimensions/useDimensions.ts +13 -7
- package/reactHooks/navigation/__mocks__/index.ts +15 -0
- package/reactHooks/navigation/__mocks__/useNavigation.ts +7 -0
- package/reactHooks/navigation/__tests__/index.test.tsx +240 -81
- package/reactHooks/navigation/index.ts +246 -84
- package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +3 -1
- package/reactHooks/screen/__mocks__/useScreenContext.ts +3 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
- package/reactHooks/screen/index.ts +4 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
- package/reactHooks/screen/useScreenContext.ts +4 -4
- package/reactHooks/screen/useScreenData.ts +1 -1
- package/reactHooks/screen/useTargetScreenData.ts +7 -0
- package/reactHooks/state/useRivers.ts +3 -1
- package/reactHooks/useListenEventBusEvent.ts +6 -15
- package/reactHooks/videoModal/hooks/useVideoModalScreenData.tsx +10 -9
- package/reactUtils/index.ts +91 -0
- package/riverComponetsMeasurementProvider/index.tsx +2 -2
- package/stringUtils/index.ts +5 -0
- package/testUtils/getByTestId.ts +14 -0
- package/testUtils/index.tsx +38 -23
- package/types.ts +4 -0
- package/zappFrameworkUtils/loginPluginUtils.ts +12 -2
- package/appUtils/focusManager/FocusableEvents.ts +0 -8
- package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
- package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
- package/appUtils/keyCodes/keys.ios.js +0 -46
- package/reactHooks/device/useIsTablet.android.tsx +0 -14
- package/reactHooks/device/useIsTablet.ios.tsx +0 -7
- package/reactHooks/device/useIsTablet.web.tsx +0 -3
- /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
- /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
- /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
- /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
- /package/domUtils/{logger.js → logger.ts} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { accessibilityManagerLogger as logger } from "./logger";
|
|
3
|
+
import { TTSManager } from "../platform/platformUtils";
|
|
4
|
+
import { getLocalizations } from "../../localizationUtils";
|
|
5
|
+
import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
|
|
6
|
+
|
|
7
|
+
export interface AccessibilityState {
|
|
8
|
+
screenReaderEnabled: boolean;
|
|
9
|
+
reduceMotionEnabled: boolean;
|
|
10
|
+
boldTextEnabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AccessibilityProps {
|
|
14
|
+
accessibilityLabel?: string;
|
|
15
|
+
accessibilityHint?: string;
|
|
16
|
+
"aria-label"?: string;
|
|
17
|
+
"aria-description"?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class AccessibilityManager {
|
|
21
|
+
private static _instance: AccessibilityManager | null = null;
|
|
22
|
+
private state$ = new BehaviorSubject<AccessibilityState>({
|
|
23
|
+
screenReaderEnabled: false,
|
|
24
|
+
reduceMotionEnabled: false,
|
|
25
|
+
boldTextEnabled: false,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
private announcements$ = new BehaviorSubject<{
|
|
29
|
+
message: string;
|
|
30
|
+
localizedMessage?: string | undefined;
|
|
31
|
+
timestamp: number;
|
|
32
|
+
} | null>(null);
|
|
33
|
+
|
|
34
|
+
private ttsManager = TTSManager.getInstance();
|
|
35
|
+
private localizations: { [key: string]: string };
|
|
36
|
+
|
|
37
|
+
private constructor(localizations?: { [key: string]: string }) {
|
|
38
|
+
if (localizations) {
|
|
39
|
+
this.localizations = getLocalizations({ localizations });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static getInstance(): AccessibilityManager {
|
|
44
|
+
if (!AccessibilityManager._instance) {
|
|
45
|
+
AccessibilityManager._instance = new AccessibilityManager();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return AccessibilityManager._instance;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public updateLocalizations(localizations: { [key: string]: string }) {
|
|
52
|
+
this.localizations = getLocalizations({ localizations });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public getState(): AccessibilityState {
|
|
56
|
+
return this.state$.getValue();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public getStateAsObservable() {
|
|
60
|
+
return this.state$.asObservable();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
|
|
65
|
+
* keyOfLocalizedText is the key to the localized text
|
|
66
|
+
*/
|
|
67
|
+
public readText({
|
|
68
|
+
text,
|
|
69
|
+
keyOfLocalizedText,
|
|
70
|
+
}: {
|
|
71
|
+
text: string;
|
|
72
|
+
keyOfLocalizedText?: string;
|
|
73
|
+
}) {
|
|
74
|
+
logger.debug("Reading text", { text, keyOfLocalizedText });
|
|
75
|
+
|
|
76
|
+
if (keyOfLocalizedText) {
|
|
77
|
+
if (!this.localizations) {
|
|
78
|
+
logger.error(
|
|
79
|
+
"Attempting to use localized key without initialized localizations"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const localizedMessage = this.getLocalizedMessage(keyOfLocalizedText);
|
|
86
|
+
|
|
87
|
+
if (!localizedMessage) {
|
|
88
|
+
logger.warn(`No localization found for key: ${keyOfLocalizedText}`);
|
|
89
|
+
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
text = localizedMessage;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (this.ttsManager) {
|
|
97
|
+
this.ttsManager.readText(text);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.announcements$.next({
|
|
101
|
+
message: text,
|
|
102
|
+
localizedMessage: keyOfLocalizedText ? text : undefined,
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public getButtonAccessibilityProps(buttonName: string): AccessibilityProps {
|
|
108
|
+
const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
|
|
109
|
+
|
|
110
|
+
if (!buttonConfig) {
|
|
111
|
+
return {
|
|
112
|
+
accessibilityLabel: buttonName,
|
|
113
|
+
accessibilityHint: `Press button to perform action on ${buttonName}`,
|
|
114
|
+
"aria-label": buttonName,
|
|
115
|
+
"aria-description": `Press button to perform action on ${buttonName}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const labelKey = buttonConfig.label;
|
|
120
|
+
const hintKey = buttonConfig.hint;
|
|
121
|
+
|
|
122
|
+
const label = this.getLocalizedMessage(labelKey) || buttonName;
|
|
123
|
+
|
|
124
|
+
const hint =
|
|
125
|
+
this.getLocalizedMessage(hintKey) ||
|
|
126
|
+
`Press button to perform action on ${buttonName}`;
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
accessibilityLabel: label,
|
|
130
|
+
accessibilityHint: hint,
|
|
131
|
+
"aria-label": label,
|
|
132
|
+
"aria-description": hint,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public getLocalizedMessage(key: string): string | void {
|
|
137
|
+
if (!key) return;
|
|
138
|
+
|
|
139
|
+
if (!this.localizations) {
|
|
140
|
+
logger.error(
|
|
141
|
+
"Attempting to use localized key without initialized localizations",
|
|
142
|
+
{ key }
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return this.localizations[key];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
exports[`focusManager should be defined 1`] = `
|
|
4
4
|
Object {
|
|
5
5
|
"blur": [Function],
|
|
6
|
-
"disableAutomaticFocus": [Function],
|
|
7
6
|
"disableFocus": [Function],
|
|
8
|
-
"enableAutomaticFocus": [Function],
|
|
9
7
|
"enableFocus": [Function],
|
|
10
8
|
"focusableTree": Tree {
|
|
11
9
|
"loadingCounter": 0,
|
|
@@ -23,6 +21,7 @@ Object {
|
|
|
23
21
|
"getGroupById": [Function],
|
|
24
22
|
"getGroupRootById": [Function],
|
|
25
23
|
"invokeHandler": [Function],
|
|
24
|
+
"isCurrentFocusOnTheTopScreen": [Function],
|
|
26
25
|
"isFocusDisabled": [Function],
|
|
27
26
|
"isGroupItemFocused": [Function],
|
|
28
27
|
"longPress": [Function],
|
|
@@ -30,11 +29,11 @@ Object {
|
|
|
30
29
|
"on": [Function],
|
|
31
30
|
"press": [Function],
|
|
32
31
|
"pressOut": [Function],
|
|
32
|
+
"recoverFocus": [Function],
|
|
33
33
|
"register": [Function],
|
|
34
34
|
"removeHandler": [Function],
|
|
35
35
|
"setFocus": [Function],
|
|
36
36
|
"setInitialFocus": [Function],
|
|
37
|
-
"setupInitialFocus": [Function],
|
|
38
37
|
"unregister": [Function],
|
|
39
38
|
}
|
|
40
39
|
`;
|
|
@@ -14,10 +14,8 @@ describe("focusManager", () => {
|
|
|
14
14
|
expect(focusManager).toMatchSnapshot();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
it("
|
|
17
|
+
it("manages to land initial focus", () => {
|
|
18
18
|
const mockSetFocus = jest.fn();
|
|
19
|
-
focusManager.setupInitialFocus("1", true);
|
|
20
|
-
focusManager.enableAutomaticFocus();
|
|
21
19
|
|
|
22
20
|
focusManager.register({
|
|
23
21
|
id: "1",
|
|
@@ -31,31 +29,13 @@ describe("focusManager", () => {
|
|
|
31
29
|
},
|
|
32
30
|
});
|
|
33
31
|
|
|
32
|
+
const { success } = focusManager.setInitialFocus();
|
|
33
|
+
|
|
34
|
+
expect(success).toBe(true);
|
|
34
35
|
expect(mockSetFocus).toBeCalledTimes(1);
|
|
35
36
|
expect(mockSetFocus).toBeCalledWith(null);
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
it("enableAutomaticFocus should block setting focus", () => {
|
|
39
|
-
const mockSetFocus = jest.fn();
|
|
40
|
-
focusManager.setupInitialFocus("1", true);
|
|
41
|
-
focusManager.disableAutomaticFocus();
|
|
42
|
-
|
|
43
|
-
focusManager.register({
|
|
44
|
-
id: "1",
|
|
45
|
-
component: {
|
|
46
|
-
setFocus: mockSetFocus,
|
|
47
|
-
willReceiveFocus: function () {},
|
|
48
|
-
isMounted: function () {
|
|
49
|
-
return true;
|
|
50
|
-
},
|
|
51
|
-
props: { groupId: "121" },
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
expect(mockSetFocus).toBeCalledTimes(0);
|
|
56
|
-
expect(mockSetFocus).not.toBeCalledWith(null);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
39
|
describe("register", () => {});
|
|
60
40
|
describe("unregister", () => {});
|
|
61
41
|
describe("find", () => {});
|
|
@@ -18,7 +18,6 @@ describe("focusManager", () => {
|
|
|
18
18
|
"on",
|
|
19
19
|
"invokeHandler",
|
|
20
20
|
"removeHandler",
|
|
21
|
-
"setupInitialFocus",
|
|
22
21
|
"longPress",
|
|
23
22
|
"pressOut",
|
|
24
23
|
"enableFocus",
|
|
@@ -27,10 +26,10 @@ describe("focusManager", () => {
|
|
|
27
26
|
"isFocusDisabled",
|
|
28
27
|
"getCurrentFocusNode",
|
|
29
28
|
"focusableTree",
|
|
30
|
-
"disableAutomaticFocus",
|
|
31
|
-
"enableAutomaticFocus",
|
|
32
29
|
"getGroupRootById",
|
|
33
30
|
"isGroupItemFocused",
|
|
31
|
+
"recoverFocus",
|
|
32
|
+
"isCurrentFocusOnTheTopScreen",
|
|
34
33
|
];
|
|
35
34
|
|
|
36
35
|
expectedMethods.forEach((method) => {
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
+
import {
|
|
3
|
+
isNilOrEmpty,
|
|
4
|
+
isNotNil,
|
|
5
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
6
|
+
|
|
2
7
|
import { Tree } from "./treeDataStructure/Tree";
|
|
3
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
findFocusableNode,
|
|
10
|
+
findPriorityItem,
|
|
11
|
+
haveSameParentBeforeRoot,
|
|
12
|
+
} from "./treeDataStructure/Utils";
|
|
4
13
|
import { subscriber } from "../../functionUtils";
|
|
5
14
|
import * as FOCUS_EVENTS from "./events";
|
|
6
15
|
import { coreLogger } from "../../logger";
|
|
7
|
-
|
|
8
16
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
9
17
|
|
|
10
18
|
const isFocusEnabled = (focusableItem): boolean => {
|
|
@@ -44,21 +52,9 @@ export const focusManager = (function () {
|
|
|
44
52
|
// focusable node which currently holds the focus
|
|
45
53
|
let currentFocusNode = null;
|
|
46
54
|
let lastFocusedId = null;
|
|
47
|
-
let initialFocusOnContent = null;
|
|
48
55
|
let focusDisabled = false;
|
|
49
|
-
let focusAutoEnabled = true;
|
|
50
56
|
const eventHandler = subscriber();
|
|
51
57
|
|
|
52
|
-
function disableAutomaticFocus() {
|
|
53
|
-
logger.log({ message: "disableAutomaticFocus" });
|
|
54
|
-
focusAutoEnabled = false;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function enableAutomaticFocus() {
|
|
58
|
-
logger.log({ message: "focusManager: enableAutomaticFocus" });
|
|
59
|
-
focusAutoEnabled = true;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
58
|
function enableFocus() {
|
|
63
59
|
focusDisabled = false;
|
|
64
60
|
}
|
|
@@ -93,7 +89,7 @@ export const focusManager = (function () {
|
|
|
93
89
|
!currentFocusable.isGroup &&
|
|
94
90
|
currentFocusable.isMounted()
|
|
95
91
|
) {
|
|
96
|
-
currentFocusable.blur(
|
|
92
|
+
currentFocusable.blur(direction);
|
|
97
93
|
currentFocusable.hasLostFocus(getCurrentFocus(), direction);
|
|
98
94
|
}
|
|
99
95
|
}
|
|
@@ -226,32 +222,52 @@ export const focusManager = (function () {
|
|
|
226
222
|
|
|
227
223
|
logger.log({ message: "setInitialFocus", data: { preferredFocus } });
|
|
228
224
|
|
|
229
|
-
if (preferredFocus
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
focusableItem &&
|
|
235
|
-
!focusableItem.isGroup &&
|
|
236
|
-
isFocusEnabled(focusableItem)
|
|
237
|
-
) {
|
|
238
|
-
if (initialFocusOnContent) {
|
|
239
|
-
blur();
|
|
240
|
-
initialFocusOnContent = false;
|
|
241
|
-
}
|
|
225
|
+
if (isNilOrEmpty(preferredFocus)) {
|
|
226
|
+
return { success: false };
|
|
227
|
+
}
|
|
242
228
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
data: {
|
|
246
|
-
focusableItem,
|
|
247
|
-
},
|
|
248
|
-
});
|
|
229
|
+
const focusCandidate = preferredFocus[0];
|
|
230
|
+
const focusableItem = focusCandidate.component;
|
|
249
231
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
232
|
+
const currentFocusedId = R.path(["props", "id"], getCurrentFocus());
|
|
233
|
+
const nextFocusedId = R.path(["props", "id"], focusableItem);
|
|
234
|
+
|
|
235
|
+
if (
|
|
236
|
+
isNotNil(currentFocusedId) &&
|
|
237
|
+
isNotNil(nextFocusedId) &&
|
|
238
|
+
currentFocusedId === nextFocusedId
|
|
239
|
+
) {
|
|
240
|
+
// we are going to move focus to the same node(already focused) -> nothing to do
|
|
241
|
+
return { success: true };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (focusableItem?.isGroup) {
|
|
245
|
+
// focusCandidate is a FocusableGroup, we are not able to land focus on group(only on Focusable)
|
|
246
|
+
return { success: false };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (
|
|
250
|
+
focusableItem &&
|
|
251
|
+
!focusableItem.isGroup &&
|
|
252
|
+
isFocusEnabled(focusableItem)
|
|
253
|
+
) {
|
|
254
|
+
blur();
|
|
255
|
+
|
|
256
|
+
logger.log({
|
|
257
|
+
message: "setInitialFocus – currentFocusNode",
|
|
258
|
+
data: {
|
|
259
|
+
focusableItem,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
currentFocusNode = focusCandidate;
|
|
264
|
+
focusableItem.willReceiveFocus(focusableItem, null);
|
|
265
|
+
focusableItem && setFocus(focusCandidate.id, null);
|
|
266
|
+
|
|
267
|
+
return { success: true };
|
|
254
268
|
}
|
|
269
|
+
|
|
270
|
+
return { success: false };
|
|
255
271
|
}
|
|
256
272
|
|
|
257
273
|
function treeLoaded(val, node) {
|
|
@@ -261,17 +277,8 @@ export const focusManager = (function () {
|
|
|
261
277
|
val,
|
|
262
278
|
node,
|
|
263
279
|
currentFocusNode,
|
|
264
|
-
initialFocusOnContent,
|
|
265
280
|
},
|
|
266
281
|
});
|
|
267
|
-
|
|
268
|
-
if ((val && !currentFocusNode) || initialFocusOnContent) {
|
|
269
|
-
logger.log({ message: `focusAutoEnabled ${focusAutoEnabled}` });
|
|
270
|
-
|
|
271
|
-
if (focusAutoEnabled) {
|
|
272
|
-
setInitialFocus(initialFocusOnContent ? node : undefined);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
282
|
}
|
|
276
283
|
|
|
277
284
|
/**
|
|
@@ -402,11 +409,21 @@ export const focusManager = (function () {
|
|
|
402
409
|
group ? unregisterGroup(id) : unregisterItem(id);
|
|
403
410
|
}
|
|
404
411
|
|
|
405
|
-
function
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
412
|
+
function isCurrentFocusOnTheTopScreen() {
|
|
413
|
+
const routes = R.pathOr([], ["root", "children"], focusableTree);
|
|
414
|
+
|
|
415
|
+
if (routes.length <= 1) {
|
|
416
|
+
// there is only one or zero routes
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function recoverFocus() {
|
|
424
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
425
|
+
// We've failed to set focused node on the new screen => run focus recovery
|
|
426
|
+
setInitialFocus();
|
|
410
427
|
}
|
|
411
428
|
}
|
|
412
429
|
|
|
@@ -416,6 +433,11 @@ export const focusManager = (function () {
|
|
|
416
433
|
* @param {Object} direction of the scroll
|
|
417
434
|
*/
|
|
418
435
|
function moveFocus(direction) {
|
|
436
|
+
if (!isCurrentFocusOnTheTopScreen()) {
|
|
437
|
+
// We are opening new screen. We need to ignore movement of focus.
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
|
|
419
441
|
if (focusDisabled) return false;
|
|
420
442
|
|
|
421
443
|
if (currentFocusNode) {
|
|
@@ -444,8 +466,6 @@ export const focusManager = (function () {
|
|
|
444
466
|
} else {
|
|
445
467
|
currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
|
|
446
468
|
}
|
|
447
|
-
} else {
|
|
448
|
-
setInitialFocus();
|
|
449
469
|
}
|
|
450
470
|
}
|
|
451
471
|
|
|
@@ -505,7 +525,6 @@ export const focusManager = (function () {
|
|
|
505
525
|
on,
|
|
506
526
|
invokeHandler,
|
|
507
527
|
removeHandler,
|
|
508
|
-
setupInitialFocus,
|
|
509
528
|
longPress,
|
|
510
529
|
pressOut,
|
|
511
530
|
enableFocus,
|
|
@@ -514,9 +533,9 @@ export const focusManager = (function () {
|
|
|
514
533
|
isFocusDisabled,
|
|
515
534
|
getCurrentFocusNode,
|
|
516
535
|
focusableTree,
|
|
517
|
-
disableAutomaticFocus,
|
|
518
|
-
enableAutomaticFocus,
|
|
519
536
|
getGroupRootById,
|
|
520
537
|
isGroupItemFocused,
|
|
538
|
+
recoverFocus,
|
|
539
|
+
isCurrentFocusOnTheTopScreen,
|
|
521
540
|
};
|
|
522
541
|
})();
|
|
@@ -59,7 +59,7 @@ export class Tree {
|
|
|
59
59
|
/**
|
|
60
60
|
* Update node with provided data from newNode
|
|
61
61
|
* @private
|
|
62
|
-
* @param {Object}
|
|
62
|
+
* @param {Object} currentNode to update
|
|
63
63
|
* @param {Object} newNode node to take data from
|
|
64
64
|
*/
|
|
65
65
|
updateNode(currentNode, newNode) {
|
|
@@ -175,7 +175,6 @@ export class Tree {
|
|
|
175
175
|
* @private
|
|
176
176
|
* @param {Object} node to check
|
|
177
177
|
* @returns True if node has defined groupID
|
|
178
|
-
* @returns False if node has NOT defined groupID
|
|
179
178
|
*/
|
|
180
179
|
hasGroupID(node) {
|
|
181
180
|
return node.component && node.component.props.groupId;
|
|
@@ -191,7 +190,7 @@ export class Tree {
|
|
|
191
190
|
const findChildNode = this.findInTree(node.component.props.groupId);
|
|
192
191
|
|
|
193
192
|
if (findChildNode) {
|
|
194
|
-
this.root.children.pop(
|
|
193
|
+
this.root.children.pop();
|
|
195
194
|
node.children.push(findChildNode);
|
|
196
195
|
}
|
|
197
196
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { allPass, pathOr, startsWith, isNil } from "ramda";
|
|
2
2
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
|
+
import { isZero } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
4
|
+
import { QUICK_BRICK_NAVBAR } from "@applicaster/quick-brick-core/const";
|
|
3
5
|
|
|
4
6
|
import { rejectItemByPath } from "../../../../objectUtils";
|
|
5
7
|
import {
|
|
@@ -143,13 +145,52 @@ const hasChildren = (target) =>
|
|
|
143
145
|
|
|
144
146
|
const hasComponent = (target) => !!target?.component;
|
|
145
147
|
|
|
146
|
-
const hasComponentAndChildren =
|
|
148
|
+
const hasComponentAndChildren = allPass([hasComponent, hasChildren]);
|
|
149
|
+
|
|
150
|
+
const positiveOrZero = (value) => value > 0 || isZero(value);
|
|
151
|
+
|
|
152
|
+
const excludeFromFocusSearching = pathOr(false, [
|
|
153
|
+
"component",
|
|
154
|
+
"props",
|
|
155
|
+
"excludeFromFocusSearching",
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
const isNavBar = (target) => startsWith(QUICK_BRICK_NAVBAR, target?.id);
|
|
159
|
+
|
|
160
|
+
const ignoreMenuIfMultipleTargets = (nodes) => {
|
|
161
|
+
if (nodes.length > 1) {
|
|
162
|
+
return nodes.filter((n) => !isNavBar(n?.target));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return nodes;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const toDistance = ({ target, direction, comparisonRect }) => {
|
|
169
|
+
const componentRect = target.component?.getRect();
|
|
170
|
+
|
|
171
|
+
const orientationDistance = measureDistance(
|
|
172
|
+
direction,
|
|
173
|
+
comparisonRect,
|
|
174
|
+
componentRect
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (orientationDistance === -1) {
|
|
178
|
+
// ignores offscreen targets when moving up/down
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const euclidDistance = measureEuclidianDistanceToCenter(
|
|
183
|
+
comparisonRect,
|
|
184
|
+
componentRect
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return orientationDistance > 0 ? euclidDistance : orientationDistance;
|
|
188
|
+
};
|
|
147
189
|
|
|
148
190
|
/**
|
|
149
191
|
* Get target in shortest distance in specified direction
|
|
150
192
|
* @private
|
|
151
193
|
* @param {Object} direction of the navigation, which led to this focus change
|
|
152
|
-
* @param {Object} excludedNode node which should be excluded from target group
|
|
153
194
|
* @param {Array} targetNodes array of input target nodes
|
|
154
195
|
* @param {Object} comparisonRect rectangle to compare sides in direction.
|
|
155
196
|
* @returns {Object} target in direction.
|
|
@@ -164,7 +205,7 @@ export function getTargetInDirection(
|
|
|
164
205
|
const overrideTarget = currentNode?.component?.props[overridePropName];
|
|
165
206
|
|
|
166
207
|
if (overrideTarget) {
|
|
167
|
-
const target = targetNodes.find((n) => n
|
|
208
|
+
const target = targetNodes.find((n) => n?.id === overrideTarget);
|
|
168
209
|
|
|
169
210
|
if (target) return target;
|
|
170
211
|
}
|
|
@@ -173,43 +214,18 @@ export function getTargetInDirection(
|
|
|
173
214
|
|
|
174
215
|
const distances = targets
|
|
175
216
|
.filter(hasComponentAndChildren)
|
|
176
|
-
.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
componentRect
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
if (distance === -1) {
|
|
186
|
-
// ignores offscreen targets when moving up/down
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
distance:
|
|
192
|
-
measureDistance(direction, comparisonRect, componentRect) +
|
|
193
|
-
measureEuclidianDistanceToCenter(comparisonRect, componentRect) /
|
|
194
|
-
10000,
|
|
195
|
-
target,
|
|
196
|
-
};
|
|
197
|
-
})
|
|
198
|
-
.filter((obj) => obj && obj.distance >= 0);
|
|
217
|
+
.filter((target) => !excludeFromFocusSearching(target))
|
|
218
|
+
.map((target) => ({
|
|
219
|
+
distance: toDistance({ target, direction, comparisonRect }),
|
|
220
|
+
target,
|
|
221
|
+
}))
|
|
222
|
+
.filter(({ distance }) => positiveOrZero(distance));
|
|
199
223
|
|
|
200
224
|
const match = distances
|
|
201
225
|
.sort((a, b) => a.distance - b.distance) // sorts the distances
|
|
202
226
|
.filter(ignoreMenuIfMultipleTargets)[0]; // filters out multiple targets if necessary // gets the first element
|
|
203
227
|
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function ignoreMenuIfMultipleTargets(nodes) {
|
|
208
|
-
if (nodes.length > 1 && nodes.map((n) => n?.target?.id).includes("navbar")) {
|
|
209
|
-
return nodes.filter((n) => n?.target?.id !== "navbar");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return nodes;
|
|
228
|
+
return match?.target;
|
|
213
229
|
}
|
|
214
230
|
|
|
215
231
|
/**
|
|
@@ -283,8 +299,7 @@ export function findFocusableNode(direction, currentFocusNode) {
|
|
|
283
299
|
* @public
|
|
284
300
|
* @param {Object} direction of the navigation, which led to this focus change
|
|
285
301
|
* @param {Object} component - object to check props.prioritiseFocusOn
|
|
286
|
-
* @returns {Boolean} TRUE if object has priority in direction.
|
|
287
|
-
* @returns {Boolean} FALSE if object has NO priority in direction.
|
|
302
|
+
* @returns {Boolean} TRUE if object has priority in direction. FALSE if object has NO priority in direction.
|
|
288
303
|
*/
|
|
289
304
|
export function hasPriorityInDirection(direction, component) {
|
|
290
305
|
const { prioritiseFocusOn = 0 } = component.props;
|
|
@@ -300,3 +315,26 @@ export function hasPriorityInDirection(direction, component) {
|
|
|
300
315
|
|
|
301
316
|
return (prioritiseFocusOn & mappedDirection) > 0;
|
|
302
317
|
}
|
|
318
|
+
|
|
319
|
+
export const getNodeBeforeRoot = (node) => {
|
|
320
|
+
if (isNil(node)) {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (isNil(node.parent)) {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (node.parent.id === "root") {
|
|
329
|
+
return node;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return getNodeBeforeRoot(node.parent);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export const haveSameParentBeforeRoot = (currentNode, nextNode) => {
|
|
336
|
+
const routeForCurrentNode = getNodeBeforeRoot(currentNode);
|
|
337
|
+
const routeForNextNode = getNodeBeforeRoot(nextNode);
|
|
338
|
+
|
|
339
|
+
return routeForCurrentNode?.id === routeForNextNode?.id;
|
|
340
|
+
};
|