@applicaster/zapp-react-native-utils 14.0.0-alpha.8419134002 → 14.0.0-alpha.9256258513
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/accessibilityManager/index.ts +9 -0
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +0 -15
- package/appUtils/playerManager/useChapterMarker.tsx +0 -1
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +16 -0
- package/focusManager/FocusManager.ts +22 -10
- package/focusManager/Tree.ts +25 -21
- package/focusManager/__tests__/FocusManager.test.ts +50 -8
- package/index.d.ts +3 -0
- package/package.json +2 -2
- package/playerUtils/index.ts +51 -0
- package/reactHooks/layout/isTablet/index.ts +12 -5
- package/reactHooks/navigation/index.ts +7 -3
|
@@ -143,12 +143,15 @@ export class AccessibilityManager {
|
|
|
143
143
|
|
|
144
144
|
if (!buttonConfig) {
|
|
145
145
|
return {
|
|
146
|
+
accessible: true,
|
|
146
147
|
accessibilityLabel: buttonName,
|
|
147
148
|
accessibilityHint: `Press button to perform action on ${buttonName}`,
|
|
148
149
|
"aria-label": buttonName,
|
|
149
150
|
"aria-description": `Press button to perform action on ${buttonName}`,
|
|
150
151
|
accessibilityRole: "button" as AccessibilityRole,
|
|
151
152
|
"aria-role": "button",
|
|
153
|
+
role: "button",
|
|
154
|
+
tabindex: 0,
|
|
152
155
|
};
|
|
153
156
|
}
|
|
154
157
|
|
|
@@ -162,23 +165,29 @@ export class AccessibilityManager {
|
|
|
162
165
|
`Press button to perform action on ${buttonName}`;
|
|
163
166
|
|
|
164
167
|
return {
|
|
168
|
+
accessible: true,
|
|
165
169
|
accessibilityLabel: label,
|
|
166
170
|
accessibilityHint: hint,
|
|
167
171
|
"aria-label": label,
|
|
168
172
|
"aria-description": hint,
|
|
169
173
|
accessibilityRole: "button" as AccessibilityRole,
|
|
170
174
|
"aria-role": "button",
|
|
175
|
+
role: "button",
|
|
176
|
+
tabindex: 0,
|
|
171
177
|
};
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
public getInputAccessibilityProps(inputName: string): AccessibilityProps {
|
|
175
181
|
return {
|
|
182
|
+
accessible: true,
|
|
176
183
|
accessibilityLabel: inputName,
|
|
177
184
|
accessibilityHint: `Enter text into ${inputName}`,
|
|
178
185
|
"aria-label": inputName,
|
|
179
186
|
"aria-description": `Enter text into ${inputName}`,
|
|
180
187
|
accessibilityRole: "textbox" as AccessibilityRole,
|
|
181
188
|
"aria-role": "textbox",
|
|
189
|
+
role: "textbox",
|
|
190
|
+
tabindex: 0,
|
|
182
191
|
};
|
|
183
192
|
}
|
|
184
193
|
|
|
@@ -18,13 +18,6 @@ export const { log_verbose, log_debug, log_info, log_error } = createLogger({
|
|
|
18
18
|
parent: utilsLogger,
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
type ActionChapter = {
|
|
22
|
-
type: string;
|
|
23
|
-
options?: {
|
|
24
|
-
title: string;
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
|
|
28
21
|
type ChapterMarkerOriginal = {
|
|
29
22
|
id: string;
|
|
30
23
|
title: string;
|
|
@@ -33,14 +26,6 @@ type ChapterMarkerOriginal = {
|
|
|
33
26
|
actions: ActionChapter[];
|
|
34
27
|
};
|
|
35
28
|
|
|
36
|
-
export type ChapterMarkerEvent = {
|
|
37
|
-
id: string;
|
|
38
|
-
title: string;
|
|
39
|
-
start_time: number;
|
|
40
|
-
end_time: number;
|
|
41
|
-
actions: ActionChapter[];
|
|
42
|
-
};
|
|
43
|
-
|
|
44
29
|
export type TitleSummaryEvent = {
|
|
45
30
|
title: string | number;
|
|
46
31
|
summary: string | number;
|
|
@@ -5,6 +5,9 @@ import { playerManager } from "./index";
|
|
|
5
5
|
import { useValidatePlayerConfig } from "../../playerUtils/useValidatePlayerConfig";
|
|
6
6
|
import { PlayerRole } from "./conts";
|
|
7
7
|
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
8
|
+
import { TVSeekController } from "../../reactHooks/player/TVSeekControlller/TVSeekController";
|
|
9
|
+
import { KeyInputHandler } from "../keyInputHandler/KeyInputHandler";
|
|
10
|
+
import { isTV } from "../../reactUtils";
|
|
8
11
|
|
|
9
12
|
// TODO: Rename to ControllerType
|
|
10
13
|
export const usePlayerControllerSetup = ({
|
|
@@ -76,5 +79,18 @@ export const usePlayerControllerSetup = ({
|
|
|
76
79
|
};
|
|
77
80
|
}, [playerId, playerController]);
|
|
78
81
|
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!isTV()) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (playerController) {
|
|
88
|
+
const seekController = new TVSeekController(playerController);
|
|
89
|
+
playerController.seekController = seekController;
|
|
90
|
+
|
|
91
|
+
return KeyInputHandler.getInstance().addListener(seekController);
|
|
92
|
+
}
|
|
93
|
+
}, [playerController]);
|
|
94
|
+
|
|
79
95
|
return playerController;
|
|
80
96
|
};
|
|
@@ -176,18 +176,30 @@ class FocusManager {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
registerFocusable(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
isFocusableCell
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
registerFocusable({
|
|
180
|
+
touchableRef,
|
|
181
|
+
parentFocusableRef,
|
|
182
|
+
isFocusableCell,
|
|
183
|
+
parentFocusableId,
|
|
184
|
+
}: {
|
|
185
|
+
touchableRef: FocusManager.TouchableReactRef;
|
|
186
|
+
parentFocusableRef: FocusManager.TouchableReactRef;
|
|
187
|
+
isFocusableCell: boolean;
|
|
188
|
+
parentFocusableId: string;
|
|
189
|
+
}) {
|
|
190
|
+
const focusableId = getFocusableId(touchableRef);
|
|
191
|
+
|
|
185
192
|
const focusableComponent = FocusManager.findFocusable(focusableId);
|
|
186
193
|
|
|
187
|
-
if (!focusableComponent &&
|
|
188
|
-
this.focusableComponents.push(
|
|
194
|
+
if (!focusableComponent && touchableRef) {
|
|
195
|
+
this.focusableComponents.push(touchableRef);
|
|
189
196
|
|
|
190
|
-
this.tree.add(
|
|
197
|
+
this.tree.add(
|
|
198
|
+
touchableRef,
|
|
199
|
+
parentFocusableRef,
|
|
200
|
+
isFocusableCell,
|
|
201
|
+
parentFocusableId
|
|
202
|
+
);
|
|
191
203
|
} else {
|
|
192
204
|
logger.warning("Focusable component already registered", {
|
|
193
205
|
id: focusableId,
|
|
@@ -267,7 +279,7 @@ class FocusManager {
|
|
|
267
279
|
|
|
268
280
|
if (nextFocus) {
|
|
269
281
|
// HACK: hack to fix the hack below
|
|
270
|
-
// HACK: putting call to the end of the event loop so the next component has a
|
|
282
|
+
// HACK: putting call to the end of the event loop so the next component has a chance to be registered
|
|
271
283
|
setTimeout(() => {
|
|
272
284
|
FocusManager.instance.setFocus(nextFocus, {
|
|
273
285
|
direction: "down",
|
package/focusManager/Tree.ts
CHANGED
|
@@ -8,37 +8,41 @@ export class Tree {
|
|
|
8
8
|
this.tree = focusManagerTree;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
add(
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
add(
|
|
12
|
+
touchableRef: FocusManager.TouchableReactRef,
|
|
13
|
+
parentFocusableRef: FocusManager.TouchableReactRef,
|
|
14
|
+
isFocusableCell: boolean,
|
|
15
|
+
parentFocusableId: string
|
|
16
|
+
) {
|
|
17
|
+
const focusableId = getFocusableId(touchableRef);
|
|
18
|
+
const parentId = getFocusableId(parentFocusableRef) || parentFocusableId;
|
|
14
19
|
const focusableComponentInTree = this.find(focusableId);
|
|
15
20
|
|
|
16
21
|
// update node if it already exists
|
|
17
22
|
if (focusableComponentInTree) {
|
|
18
|
-
focusableComponentInTree.updateNode(
|
|
23
|
+
focusableComponentInTree.updateNode(touchableRef);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
if (!this.find(parentId)) {
|
|
27
|
+
// create temporary node to the root of the tree
|
|
28
|
+
this.tree.push(new TreeNode(null, parentId, null, isFocusableCell));
|
|
29
|
+
}
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
const parentNode = this.find(parentId);
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
if (parentNode) {
|
|
34
|
+
if (focusableComponentInTree) {
|
|
35
|
+
focusableComponentInTree.isFocusableCell = isFocusableCell;
|
|
36
|
+
focusableComponentInTree.parentId = parentNode.id;
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
parentNode.addChild(focusableComponentInTree);
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
40
|
+
// remove root object from the list
|
|
41
|
+
this.tree = this.tree.filter(
|
|
42
|
+
(node) => node !== focusableComponentInTree
|
|
43
|
+
);
|
|
44
|
+
} else {
|
|
45
|
+
parentNode.addChild(touchableRef, focusableId, isFocusableCell);
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { focusManager } from "../FocusManager";
|
|
2
2
|
|
|
3
|
+
const isFocusableCell = true;
|
|
4
|
+
const parentFocusableId = "parentFocusableId";
|
|
5
|
+
|
|
3
6
|
const group = {
|
|
4
7
|
current: {
|
|
5
8
|
props: {
|
|
@@ -62,13 +65,47 @@ jest.useFakeTimers();
|
|
|
62
65
|
|
|
63
66
|
describe("FocusManager", () => {
|
|
64
67
|
beforeAll(() => {
|
|
65
|
-
focusManager.registerFocusable(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
focusManager.registerFocusable({
|
|
69
|
+
touchableRef: group,
|
|
70
|
+
parentFocusableRef: { current: null },
|
|
71
|
+
isFocusableCell,
|
|
72
|
+
parentFocusableId,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
focusManager.registerFocusable({
|
|
76
|
+
touchableRef: child1,
|
|
77
|
+
parentFocusableRef: group,
|
|
78
|
+
isFocusableCell,
|
|
79
|
+
parentFocusableId,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
focusManager.registerFocusable({
|
|
83
|
+
touchableRef: child2,
|
|
84
|
+
parentFocusableRef: group,
|
|
85
|
+
isFocusableCell,
|
|
86
|
+
parentFocusableId,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
focusManager.registerFocusable({
|
|
90
|
+
touchableRef: child3,
|
|
91
|
+
parentFocusableRef: child2,
|
|
92
|
+
isFocusableCell,
|
|
93
|
+
parentFocusableId,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
focusManager.registerFocusable({
|
|
97
|
+
touchableRef: child4,
|
|
98
|
+
parentFocusableRef: child2,
|
|
99
|
+
isFocusableCell,
|
|
100
|
+
parentFocusableId,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
focusManager.registerFocusable({
|
|
104
|
+
touchableRef: child5,
|
|
105
|
+
parentFocusableRef: child2,
|
|
106
|
+
isFocusableCell,
|
|
107
|
+
parentFocusableId,
|
|
108
|
+
});
|
|
72
109
|
});
|
|
73
110
|
|
|
74
111
|
it("focusManager should be defined", () => {
|
|
@@ -199,7 +236,12 @@ describe("FocusManager", () => {
|
|
|
199
236
|
});
|
|
200
237
|
|
|
201
238
|
it("focusManager registerFocusable should register", () => {
|
|
202
|
-
focusManager.registerFocusable(
|
|
239
|
+
focusManager.registerFocusable({
|
|
240
|
+
touchableRef: child5,
|
|
241
|
+
parentFocusableRef: child2,
|
|
242
|
+
isFocusableCell,
|
|
243
|
+
parentFocusableId,
|
|
244
|
+
});
|
|
203
245
|
|
|
204
246
|
expect(
|
|
205
247
|
focusManager.isFocusableChildOf(child5.current.props.id, child2)
|
package/index.d.ts
CHANGED
|
@@ -139,10 +139,13 @@ declare type AccessibilityState = {
|
|
|
139
139
|
};
|
|
140
140
|
|
|
141
141
|
declare type AccessibilityProps = {
|
|
142
|
+
accessible?: boolean;
|
|
142
143
|
accessibilityLabel?: string;
|
|
143
144
|
accessibilityHint?: string;
|
|
144
145
|
"aria-label"?: string;
|
|
145
146
|
"aria-description"?: string;
|
|
146
147
|
accessibilityRole?: string;
|
|
147
148
|
"aria-role"?: string;
|
|
149
|
+
role?: string;
|
|
150
|
+
tabindex?: number;
|
|
148
151
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "14.0.0-alpha.
|
|
3
|
+
"version": "14.0.0-alpha.9256258513",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@applicaster/applicaster-types": "14.0.0-alpha.
|
|
30
|
+
"@applicaster/applicaster-types": "14.0.0-alpha.9256258513",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|
package/playerUtils/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { isFilledArray } from "@applicaster/zapp-react-native-utils/arrayUtils";
|
|
|
5
5
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
6
6
|
|
|
7
7
|
import { getBoolFromConfigValue } from "../configurationUtils";
|
|
8
|
+
import { Dimensions } from "react-native";
|
|
8
9
|
|
|
9
10
|
export { getPlayerActionButtons } from "./getPlayerActionButtons";
|
|
10
11
|
|
|
@@ -97,3 +98,53 @@ export const isAudioItem = (item: Option<ZappEntry>) => {
|
|
|
97
98
|
export const isInlineTV = (screenData) => {
|
|
98
99
|
return isTV() && isFilledArray(screenData?.ui_components);
|
|
99
100
|
};
|
|
101
|
+
|
|
102
|
+
const isPercentage = (value: string | number): boolean => {
|
|
103
|
+
if (typeof value === "string") {
|
|
104
|
+
return value.includes("%");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return false;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getPercentageOf = (percent: string, value: number) => {
|
|
111
|
+
const percentageValue = parseFloat(percent.replace("%", ""));
|
|
112
|
+
|
|
113
|
+
if (isNaN(percentageValue)) {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (value * percentageValue) / 100;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
type DimensionsT = {
|
|
121
|
+
width: number | string;
|
|
122
|
+
height: number | string | undefined;
|
|
123
|
+
aspectRatio?: number;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const getTabletWidth = (
|
|
127
|
+
tablet_landscape_sidebar_width,
|
|
128
|
+
dimensions: DimensionsT
|
|
129
|
+
) => {
|
|
130
|
+
const { width: SCREEN_WIDTH } = Dimensions.get("screen");
|
|
131
|
+
|
|
132
|
+
const { width } = dimensions;
|
|
133
|
+
let widthValue = Number(width);
|
|
134
|
+
|
|
135
|
+
if (isPercentage(width)) {
|
|
136
|
+
widthValue = getPercentageOf(width.toString(), SCREEN_WIDTH);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sidebarWidth = Number(tablet_landscape_sidebar_width?.replace("%", ""));
|
|
140
|
+
|
|
141
|
+
if (tablet_landscape_sidebar_width?.includes("%")) {
|
|
142
|
+
return widthValue * (1 - sidebarWidth / 100);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (Number.isNaN(sidebarWidth)) {
|
|
146
|
+
return widthValue * 0.65;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return widthValue - sidebarWidth;
|
|
150
|
+
};
|
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import { NativeModules, Platform } from "react-native";
|
|
2
2
|
|
|
3
|
+
export const isAndroidTablet = () => {
|
|
4
|
+
const { initialProps } = NativeModules.QuickBrickCommunicationModule;
|
|
5
|
+
|
|
6
|
+
return initialProps?.is_tablet;
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
/**
|
|
4
10
|
* Determines wether the given device is a tablet based on dimensions, orientation, and platform
|
|
5
11
|
* @param {Object} dimensions - Dimensions object passed to the function
|
|
6
12
|
* @param {String} orientation - Orientation enum passed to the function
|
|
7
13
|
* @returns {Boolean} isTablet - returns whether the given device is a tablet
|
|
8
14
|
*/
|
|
9
|
-
export const isTablet = (
|
|
15
|
+
export const isTablet = (
|
|
16
|
+
dimensions?: { width: number; height: number },
|
|
17
|
+
orientation?: string
|
|
18
|
+
) => {
|
|
10
19
|
if (Platform?.OS === "ios") {
|
|
11
20
|
return Platform?.isPad;
|
|
12
21
|
} else if (Platform?.OS === "android") {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return initialProps?.is_tablet;
|
|
22
|
+
return isAndroidTablet();
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
const { width } = dimensions;
|
|
25
|
+
const { width } = dimensions || {};
|
|
19
26
|
|
|
20
27
|
if (width < 600) return false;
|
|
21
28
|
if (width >= 600 && width < 840) return orientation === "portrait";
|
|
@@ -14,7 +14,7 @@ import { HOOKS_EVENTS } from "../../appUtils/HooksManager/constants";
|
|
|
14
14
|
import { getRiverFromRoute, getTargetRoute } from "../../navigationUtils";
|
|
15
15
|
import { useConnectionInfo } from "../connection";
|
|
16
16
|
|
|
17
|
-
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
17
|
+
import { isTV, isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
18
18
|
import { useNavbarState } from "../screen";
|
|
19
19
|
|
|
20
20
|
export { useNavigation } from "./useNavigation";
|
|
@@ -127,10 +127,14 @@ export function isNavBarVisible(
|
|
|
127
127
|
|
|
128
128
|
export const useBackHandler = (cb: () => boolean) => {
|
|
129
129
|
useEffect(() => {
|
|
130
|
-
|
|
130
|
+
if (!isWeb()) {
|
|
131
|
+
BackHandler.addEventListener("hardwareBackPress", cb);
|
|
132
|
+
}
|
|
131
133
|
|
|
132
134
|
return () => {
|
|
133
|
-
|
|
135
|
+
if (!isWeb()) {
|
|
136
|
+
BackHandler.removeEventListener("hardwareBackPress", cb);
|
|
137
|
+
}
|
|
134
138
|
};
|
|
135
139
|
}, [cb]);
|
|
136
140
|
};
|