@applicaster/zapp-react-native-ui-components 15.1.0-rc.1 → 16.0.0-alpha.9803580571
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/Components/BaseFocusable/index.ios.ts +12 -2
- package/Components/Cell/FocusableWrapper.tsx +3 -0
- package/Components/Cell/TvOSCellComponent.tsx +6 -3
- package/Components/Focusable/Focusable.tsx +4 -2
- package/Components/Focusable/FocusableTvOS.tsx +18 -1
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
- package/Components/FocusableGroup/FocusableTvOS.tsx +30 -1
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
- package/Components/Layout/TV/LayoutBackground.tsx +5 -2
- package/Components/Layout/TV/NavBarContainer.tsx +1 -10
- package/Components/Layout/TV/ScreenContainer.tsx +2 -6
- package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/index.tsx +3 -4
- package/Components/Layout/TV/index.web.tsx +3 -4
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -10
- package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -0
- package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
- package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
- package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +34 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
- package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
- package/Components/MasterCell/DefaultComponents/index.ts +9 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +125 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +37 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +393 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +343 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +122 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
- package/Components/MasterCell/MappingFunctions/index.js +3 -2
- package/Components/MasterCell/README.md +4 -0
- package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
- package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
- package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
- package/Components/MasterCell/dataAdapter.ts +4 -1
- package/Components/MasterCell/elementMapper.tsx +52 -7
- package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
- package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
- package/Components/MasterCell/utils/index.ts +85 -15
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
- package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
- package/Components/PlayerContainer/PlayerContainer.tsx +14 -13
- package/Components/River/ComponentsMap/ComponentsMap.tsx +6 -19
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +19 -88
- package/Components/River/River.tsx +9 -82
- package/Components/River/TV/River.tsx +31 -14
- package/Components/River/TV/index.tsx +8 -4
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
- package/Components/River/TV/utils/index.ts +4 -0
- package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -0
- package/Components/River/__tests__/componentsMap.test.js +38 -0
- package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
- package/Components/River/hooks/index.ts +1 -0
- package/Components/River/hooks/usePullToRefresh.ts +51 -0
- package/Components/Screen/TV/index.web.tsx +4 -2
- package/Components/Screen/__tests__/Screen.test.tsx +65 -42
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
- package/Components/Screen/hooks.ts +2 -3
- package/Components/Screen/index.tsx +2 -3
- package/Components/Screen/orientationHandler.ts +3 -3
- package/Components/ScreenResolver/index.tsx +9 -5
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/Tabs/TabContent.tsx +7 -4
- package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
- package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
- package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
- package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
- package/Components/TopCutoffOverlay/index.tsx +55 -0
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
- package/Components/VideoModal/utils.ts +12 -9
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
- package/Components/Viewport/ViewportAware/index.tsx +16 -7
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -6
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
- package/Contexts/ScreenContext/index.tsx +25 -18
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Decorators/Analytics/index.tsx +6 -5
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
- package/Decorators/ConfigurationWrapper/const.ts +1 -0
- package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/package.json +5 -5
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
- package/Components/River/TV/withTVEventHandler.tsx +0 -36
- package/Helpers/DataSourceHelper/index.js +0 -19
|
@@ -22,6 +22,7 @@ type Props = {
|
|
|
22
22
|
onFocus?: FocusManager.FocusEventCB;
|
|
23
23
|
onBlur?: FocusManager.FocusEventCB;
|
|
24
24
|
selected?: boolean;
|
|
25
|
+
skipFocusManagerRegistration?: boolean;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export class BaseFocusable<
|
|
@@ -61,10 +62,14 @@ export class BaseFocusable<
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
componentDidMount() {
|
|
64
|
-
const { id } = this.props;
|
|
65
|
+
const { id, skipFocusManagerRegistration } = this.props;
|
|
65
66
|
const component = this;
|
|
66
67
|
this.node = this.ref.current;
|
|
67
68
|
|
|
69
|
+
if (skipFocusManagerRegistration) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
focusManager.register({
|
|
69
74
|
id,
|
|
70
75
|
component: component,
|
|
@@ -118,7 +123,12 @@ export class BaseFocusable<
|
|
|
118
123
|
|
|
119
124
|
componentWillUnmount() {
|
|
120
125
|
this._isMounted = false;
|
|
121
|
-
const { id } = this.props;
|
|
126
|
+
const { id, skipFocusManagerRegistration } = this.props;
|
|
127
|
+
|
|
128
|
+
if (skipFocusManagerRegistration) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
122
132
|
focusManager.unregister(id, { group: this.isGroup || false });
|
|
123
133
|
}
|
|
124
134
|
|
|
@@ -10,6 +10,7 @@ type Props = {
|
|
|
10
10
|
children: (focused: boolean) => React.ReactNode;
|
|
11
11
|
onFocus: (arg1: any, index?: number) => void;
|
|
12
12
|
onBlur: Callback;
|
|
13
|
+
skipFocusManagerRegistration?: boolean;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export const FocusableWrapper = ({
|
|
@@ -20,6 +21,7 @@ export const FocusableWrapper = ({
|
|
|
20
21
|
applyWrapper,
|
|
21
22
|
onFocus,
|
|
22
23
|
onBlur,
|
|
24
|
+
skipFocusManagerRegistration,
|
|
23
25
|
}: Props) => {
|
|
24
26
|
if (applyWrapper) {
|
|
25
27
|
return (
|
|
@@ -34,6 +36,7 @@ export const FocusableWrapper = ({
|
|
|
34
36
|
// @ts-ignore
|
|
35
37
|
offsetUpdater={noop}
|
|
36
38
|
isFocusable
|
|
39
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
37
40
|
>
|
|
38
41
|
{(focused) => children(focused)}
|
|
39
42
|
</Focusable>
|
|
@@ -80,6 +80,7 @@ type Props = {
|
|
|
80
80
|
componentsMapOffset: number;
|
|
81
81
|
applyFocusableWrapper: boolean;
|
|
82
82
|
hasFocusableInside: boolean;
|
|
83
|
+
skipFocusManagerRegistration?: boolean;
|
|
83
84
|
};
|
|
84
85
|
|
|
85
86
|
type State = {
|
|
@@ -194,7 +195,6 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
194
195
|
groupId,
|
|
195
196
|
component,
|
|
196
197
|
index,
|
|
197
|
-
componentsMapOffset,
|
|
198
198
|
} = this.props;
|
|
199
199
|
|
|
200
200
|
this.setScreenLayout(componentAnchorPointY, screenLayout);
|
|
@@ -221,8 +221,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
221
221
|
const totalOffset =
|
|
222
222
|
headerOffset +
|
|
223
223
|
toNumberWithDefaultZero(componentAnchorPointY) +
|
|
224
|
-
extraAnchorPointYOffset
|
|
225
|
-
toNumberWithDefaultZero(componentsMapOffset) +
|
|
224
|
+
extraAnchorPointYOffset +
|
|
226
225
|
componentMarginTop +
|
|
227
226
|
componentPaddingTop;
|
|
228
227
|
|
|
@@ -266,6 +265,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
266
265
|
behavior,
|
|
267
266
|
applyFocusableWrapper,
|
|
268
267
|
hasFocusableInside,
|
|
268
|
+
skipFocusManagerRegistration,
|
|
269
269
|
} = this.props;
|
|
270
270
|
|
|
271
271
|
const { id } = item;
|
|
@@ -291,6 +291,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
291
291
|
onFocus={handleFocus}
|
|
292
292
|
onBlur={onBlur || this.onBlur}
|
|
293
293
|
applyWrapper={applyFocusableWrapper}
|
|
294
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
294
295
|
>
|
|
295
296
|
{(focused) => (
|
|
296
297
|
<CellWithFocusable
|
|
@@ -305,6 +306,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
305
306
|
focused={focused || this.props.focused}
|
|
306
307
|
behavior={behavior}
|
|
307
308
|
isFocusable={isFocusable}
|
|
309
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
308
310
|
/>
|
|
309
311
|
)}
|
|
310
312
|
</FocusableWrapper>
|
|
@@ -327,6 +329,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
327
329
|
offsetUpdater={offsetUpdater}
|
|
328
330
|
style={baseCellStyles}
|
|
329
331
|
isFocusable={isFocusable}
|
|
332
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
330
333
|
>
|
|
331
334
|
{(focused) => (
|
|
332
335
|
<FocusableCell
|
|
@@ -8,6 +8,8 @@ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withF
|
|
|
8
8
|
import { StyleSheet, ViewStyle } from "react-native";
|
|
9
9
|
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
10
10
|
|
|
11
|
+
import { isSearchInputId } from "@applicaster/zapp-react-native-utils/searchUtils";
|
|
12
|
+
|
|
11
13
|
type Props = {
|
|
12
14
|
initialFocus?: boolean;
|
|
13
15
|
id: string;
|
|
@@ -106,7 +108,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
106
108
|
onMouseEnter() {
|
|
107
109
|
const { id } = this.props;
|
|
108
110
|
|
|
109
|
-
if (id
|
|
111
|
+
if (!isSearchInputId(id)) {
|
|
110
112
|
this.mouse = true;
|
|
111
113
|
this.props?.handleFocus?.({ mouse: true });
|
|
112
114
|
|
|
@@ -120,7 +122,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
120
122
|
onMouseLeave() {
|
|
121
123
|
const { id } = this.props;
|
|
122
124
|
|
|
123
|
-
if (id
|
|
125
|
+
if (!isSearchInputId(id)) {
|
|
124
126
|
this.mouse = false;
|
|
125
127
|
this.blur(null);
|
|
126
128
|
}
|
|
@@ -10,8 +10,12 @@ import {
|
|
|
10
10
|
forceFocusableFocus,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
|
|
12
12
|
import { findNodeHandle, ViewStyle } from "react-native";
|
|
13
|
+
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
import {
|
|
16
|
+
emitFocused,
|
|
17
|
+
emitNativeRegistered,
|
|
18
|
+
} from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
15
19
|
|
|
16
20
|
type Props = {
|
|
17
21
|
id: string;
|
|
@@ -39,6 +43,7 @@ type Props = {
|
|
|
39
43
|
hasReceivedFocus: () => void;
|
|
40
44
|
offsetUpdater: (arg1: string, arg2: number) => number;
|
|
41
45
|
style: ViewStyle;
|
|
46
|
+
skipFocusManagerRegistration?: boolean;
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -53,6 +58,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
53
58
|
this.nextFocusableReactTags = {};
|
|
54
59
|
this.preferredFocus = this.preferredFocus.bind(this);
|
|
55
60
|
this.measureView = this.measureView.bind(this);
|
|
61
|
+
this.onRegistered = this.onRegistered.bind(this);
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
/**
|
|
@@ -84,6 +90,9 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
84
90
|
});
|
|
85
91
|
}
|
|
86
92
|
|
|
93
|
+
const id: string = nativeEvent.itemID;
|
|
94
|
+
emitFocused(id);
|
|
95
|
+
|
|
87
96
|
onFocus(nativeEvent);
|
|
88
97
|
}
|
|
89
98
|
|
|
@@ -169,6 +178,13 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
169
178
|
});
|
|
170
179
|
}
|
|
171
180
|
|
|
181
|
+
onRegistered({ nativeEvent }) {
|
|
182
|
+
const groupId = nativeEvent?.groupId;
|
|
183
|
+
const id = nativeEvent?.itemId;
|
|
184
|
+
|
|
185
|
+
emitNativeRegistered({ id, groupId, isGroup: false });
|
|
186
|
+
}
|
|
187
|
+
|
|
172
188
|
render() {
|
|
173
189
|
const {
|
|
174
190
|
children,
|
|
@@ -203,6 +219,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
203
219
|
focusable={isFocusable}
|
|
204
220
|
{...this.nextFocusableReactTags}
|
|
205
221
|
{...otherProps}
|
|
222
|
+
onRegistered={this.onRegistered}
|
|
206
223
|
>
|
|
207
224
|
{typeof children === "function" ? children(focused) : children}
|
|
208
225
|
</FocusableItemNative>
|
|
@@ -2,6 +2,10 @@ import * as React from "react";
|
|
|
2
2
|
import { FocusableGroupNative } from "@applicaster/zapp-react-native-ui-components/Components/NativeFocusables";
|
|
3
3
|
import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable";
|
|
4
4
|
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
5
|
+
import { LayoutContext } from "@applicaster/zapp-react-native-tvos-app/Context/LayoutContext";
|
|
6
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
|
|
7
|
+
import { isScreenPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
|
|
8
|
+
import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
5
9
|
|
|
6
10
|
const { log_verbose } = createLogger({
|
|
7
11
|
subsystem: "General",
|
|
@@ -33,9 +37,16 @@ type Props = {
|
|
|
33
37
|
screenData: { screenId: string; parentScreenId: string };
|
|
34
38
|
};
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
class FocusableGroupComponent extends BaseFocusable<Props> {
|
|
37
41
|
public readonly isGroup: boolean = true;
|
|
38
42
|
|
|
43
|
+
onRegistered = ({ nativeEvent }) => {
|
|
44
|
+
const groupId = nativeEvent?.groupId;
|
|
45
|
+
const id = nativeEvent?.itemId;
|
|
46
|
+
|
|
47
|
+
emitNativeRegistered({ id, groupId, isGroup: true });
|
|
48
|
+
};
|
|
49
|
+
|
|
39
50
|
render() {
|
|
40
51
|
const {
|
|
41
52
|
children,
|
|
@@ -68,9 +79,27 @@ export class FocusableGroup extends BaseFocusable<Props> {
|
|
|
68
79
|
onGroupBlur={onGroupBlur}
|
|
69
80
|
style={style}
|
|
70
81
|
{...otherProps}
|
|
82
|
+
onRegistered={this.onRegistered}
|
|
71
83
|
>
|
|
72
84
|
{children}
|
|
73
85
|
</FocusableGroupNative>
|
|
74
86
|
);
|
|
75
87
|
}
|
|
76
88
|
}
|
|
89
|
+
|
|
90
|
+
export const withFocusDisabled = (Component) => {
|
|
91
|
+
return function WithFocusDisabled(props) {
|
|
92
|
+
// @ts-ignore
|
|
93
|
+
const { screenFocusBlocked } = React.useContext(LayoutContext.ReactContext);
|
|
94
|
+
|
|
95
|
+
const { pathname } = useRoute();
|
|
96
|
+
|
|
97
|
+
const isPlayerPresented = isScreenPlayable(pathname);
|
|
98
|
+
|
|
99
|
+
const blockScreenFocus = isPlayerPresented === false && screenFocusBlocked;
|
|
100
|
+
|
|
101
|
+
return <Component {...props} isFocusDisabled={blockScreenFocus} />;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const FocusableGroup = withFocusDisabled(FocusableGroupComponent);
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
useAppData,
|
|
4
|
+
useContentTypes,
|
|
5
|
+
usePlugins,
|
|
6
|
+
} from "@applicaster/zapp-react-native-redux/hooks";
|
|
7
|
+
import {
|
|
8
|
+
useNavigation,
|
|
9
|
+
useRivers,
|
|
10
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
4
11
|
|
|
5
12
|
import { BufferAnimation } from "../PlayerContainer/BufferAnimation";
|
|
6
13
|
import { PlayerContainer } from "../PlayerContainer";
|
|
@@ -28,12 +35,10 @@ export function HandlePlayable({
|
|
|
28
35
|
mode,
|
|
29
36
|
groupId,
|
|
30
37
|
}: Props): React.ReactElement | null {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"appData",
|
|
36
|
-
]);
|
|
38
|
+
const plugins = usePlugins();
|
|
39
|
+
const contentTypes = useContentTypes();
|
|
40
|
+
const rivers = useRivers();
|
|
41
|
+
const appData = useAppData();
|
|
37
42
|
|
|
38
43
|
const { closeVideoModal } = useNavigation();
|
|
39
44
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
3
2
|
import { getBackgroundImageUrl } from "../utils";
|
|
4
3
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
4
|
+
import {
|
|
5
|
+
selectRemoteConfigurations,
|
|
6
|
+
useAppSelector,
|
|
7
|
+
} from "@applicaster/zapp-react-native-redux";
|
|
5
8
|
|
|
6
9
|
export const LayoutBackground = ({
|
|
7
10
|
Background,
|
|
@@ -12,7 +15,7 @@ export const LayoutBackground = ({
|
|
|
12
15
|
}) => {
|
|
13
16
|
const theme = useTheme();
|
|
14
17
|
|
|
15
|
-
const
|
|
18
|
+
const remoteConfigurations = useAppSelector(selectRemoteConfigurations);
|
|
16
19
|
|
|
17
20
|
const backgroundColor = theme.app_background_color;
|
|
18
21
|
const backgroundImageUrl = getBackgroundImageUrl(remoteConfigurations);
|
|
@@ -15,12 +15,6 @@ const styles = StyleSheet.create({
|
|
|
15
15
|
flex: 1,
|
|
16
16
|
width: "100%",
|
|
17
17
|
},
|
|
18
|
-
themeStyles: {
|
|
19
|
-
// limits the height of the focusable container of the TopMenuBarTV component
|
|
20
|
-
// to prevent it from being overlapped by the screen content,
|
|
21
|
-
// as it makes TopMenuBarTV unfocusable on tvOS
|
|
22
|
-
maxHeight: 1,
|
|
23
|
-
},
|
|
24
18
|
});
|
|
25
19
|
|
|
26
20
|
export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
@@ -31,10 +25,7 @@ export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
|
|
|
31
25
|
}, [onReady]);
|
|
32
26
|
|
|
33
27
|
return isVisible ? (
|
|
34
|
-
<View
|
|
35
|
-
testID="nav-bar-container"
|
|
36
|
-
style={[styles.container, styles.themeStyles]}
|
|
37
|
-
>
|
|
28
|
+
<View testID="nav-bar-container" style={styles.container}>
|
|
38
29
|
{children}
|
|
39
30
|
</View>
|
|
40
31
|
) : null;
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
routeIsPlayerScreen,
|
|
19
19
|
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
20
20
|
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
21
|
-
import {
|
|
21
|
+
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
22
22
|
import { NavBarContainer } from "./NavBarContainer";
|
|
23
23
|
|
|
24
24
|
type ComponentsExtraProps = {
|
|
@@ -111,11 +111,7 @@ export const ScreenContainer = React.memo(function ScreenContainer({
|
|
|
111
111
|
const { activeRiver } = navigator;
|
|
112
112
|
const { title, visible } = useNavbarState();
|
|
113
113
|
|
|
114
|
-
const
|
|
115
|
-
"appState",
|
|
116
|
-
"remoteConfigurations",
|
|
117
|
-
"plugins",
|
|
118
|
-
]);
|
|
114
|
+
const plugins = usePlugins();
|
|
119
115
|
|
|
120
116
|
const navigationProps = getNavigationProps({
|
|
121
117
|
navigator,
|
|
@@ -3,18 +3,13 @@
|
|
|
3
3
|
exports[`NavBarContainer renders 1`] = `
|
|
4
4
|
<View
|
|
5
5
|
style={
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
"maxHeight": 1,
|
|
16
|
-
},
|
|
17
|
-
]
|
|
6
|
+
{
|
|
7
|
+
"flex": 1,
|
|
8
|
+
"position": "absolute",
|
|
9
|
+
"top": 0,
|
|
10
|
+
"width": "100%",
|
|
11
|
+
"zIndex": 10,
|
|
12
|
+
}
|
|
18
13
|
}
|
|
19
14
|
testID="nav-bar-container"
|
|
20
15
|
>
|
|
@@ -14,18 +14,13 @@ exports[`ScreenContainer renders 1`] = `
|
|
|
14
14
|
>
|
|
15
15
|
<View
|
|
16
16
|
style={
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"maxHeight": 1,
|
|
27
|
-
},
|
|
28
|
-
]
|
|
17
|
+
{
|
|
18
|
+
"flex": 1,
|
|
19
|
+
"position": "absolute",
|
|
20
|
+
"top": 0,
|
|
21
|
+
"width": "100%",
|
|
22
|
+
"zIndex": 10,
|
|
23
|
+
}
|
|
29
24
|
}
|
|
30
25
|
testID="nav-bar-container"
|
|
31
26
|
>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
|
|
3
3
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
4
4
|
|
|
5
5
|
import { LayoutContainer } from "./LayoutContainer";
|
|
@@ -10,6 +10,7 @@ import { PathnameContext } from "../../../Contexts/PathnameContext";
|
|
|
10
10
|
import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
|
|
11
11
|
import { ScreenContextProvider } from "../../../Contexts/ScreenContext";
|
|
12
12
|
import { LayoutBackground } from "./LayoutBackground";
|
|
13
|
+
import { selectAppReady } from "@applicaster/zapp-react-native-redux";
|
|
13
14
|
|
|
14
15
|
type Components = {
|
|
15
16
|
NavBar: React.ComponentType<any>;
|
|
@@ -29,9 +30,7 @@ type Props = {
|
|
|
29
30
|
const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
30
31
|
const navigator = useNavigation();
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
"appState",
|
|
34
|
-
]);
|
|
33
|
+
const appReady = useAppSelector(selectAppReady);
|
|
35
34
|
|
|
36
35
|
if (!appReady) {
|
|
37
36
|
return null;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { pathOr } from "ramda";
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
4
|
|
|
6
5
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
7
6
|
import { StackNavigator } from "../../Navigator";
|
|
8
7
|
import { LayoutBackground } from "./LayoutBackground";
|
|
8
|
+
import { selectAppReady } from "@applicaster/zapp-react-native-redux";
|
|
9
9
|
|
|
10
10
|
type Components = {
|
|
11
11
|
NavBar: React.ComponentType<any>;
|
|
@@ -17,8 +17,7 @@ type Props = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const Layout = ({ Components }: Props) => {
|
|
20
|
-
const
|
|
21
|
-
const appReady = pathOr(false, ["appReady"], appState);
|
|
20
|
+
const appReady = useAppSelector(selectAppReady);
|
|
22
21
|
|
|
23
22
|
if (!appReady) {
|
|
24
23
|
return null;
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
4
|
import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
5
|
-
import {
|
|
5
|
+
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
6
6
|
import {
|
|
7
7
|
inflateString,
|
|
8
8
|
objectToReadableString,
|
|
@@ -40,7 +40,7 @@ export async function inflateUrl(url) {
|
|
|
40
40
|
|
|
41
41
|
export function LinkHandler(props: Props) {
|
|
42
42
|
const screenData = props?.screenData;
|
|
43
|
-
const
|
|
43
|
+
const plugins = usePlugins();
|
|
44
44
|
|
|
45
45
|
const ScreenPlugin = findPluginByIdentifier(
|
|
46
46
|
WEBVIEW_SCREEN_IDENTIFIER,
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Config Builder -> React Component Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to migrate MasterCell config-builders to React components incrementally, without breaking existing config-node rendering.
|
|
4
|
+
|
|
5
|
+
## Why This Exists
|
|
6
|
+
|
|
7
|
+
MasterCell currently supports config-node builders such as:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
{
|
|
11
|
+
type: "View",
|
|
12
|
+
style: {},
|
|
13
|
+
additionalProps: {},
|
|
14
|
+
data: [],
|
|
15
|
+
elements: []
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This is still valid and should continue to work.
|
|
20
|
+
|
|
21
|
+
For incremental migration, MasterCell also supports inline React nodes:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
{
|
|
25
|
+
type: "ReactComponent",
|
|
26
|
+
style: {},
|
|
27
|
+
additionalProps: {
|
|
28
|
+
component: MyComponent
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This lets you migrate one builder at a time.
|
|
34
|
+
|
|
35
|
+
## Rendering Pipeline
|
|
36
|
+
|
|
37
|
+
1. Builder returns node config (`type`, `style`, `additionalProps`, `data`, `elements`).
|
|
38
|
+
2. `configInflater` converts `additionalProps` into runtime `props` and resolves `data`.
|
|
39
|
+
3. `elementMapper` renders:
|
|
40
|
+
4. String `type` nodes via component registry (`components[type]`).
|
|
41
|
+
5. `type: "ReactComponent"` nodes via `props.component`.
|
|
42
|
+
|
|
43
|
+
## Migration Pattern
|
|
44
|
+
|
|
45
|
+
1. Keep existing builder API unchanged.
|
|
46
|
+
2. Add a dedicated React component for the node being migrated.
|
|
47
|
+
3. Change builder output from string `type` to `type: "ReactComponent"`.
|
|
48
|
+
4. Pass component reference through `additionalProps.component`.
|
|
49
|
+
5. Keep behavioral props unchanged (`testID`, roles, accessibility props).
|
|
50
|
+
6. Set `renderChildren` explicitly:
|
|
51
|
+
7. `false` when React component renders everything itself.
|
|
52
|
+
8. `true` (or omit) when builder still provides `elements` children.
|
|
53
|
+
9. Set `requiresCellUUID: true` only if the component needs `cellUUID`.
|
|
54
|
+
10. Update builder-shape tests and rendering tests together.
|
|
55
|
+
|
|
56
|
+
## Example Using `TextLabelsContainer.ts`
|
|
57
|
+
|
|
58
|
+
Current builder (config-node only):
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
export const TextLabelsContainer = ({ actionIdentifier, testID, style, extraProps }) => ({
|
|
62
|
+
type: "View",
|
|
63
|
+
additionalProps: {
|
|
64
|
+
mobileActionRole: "label_container",
|
|
65
|
+
},
|
|
66
|
+
elements: [
|
|
67
|
+
{
|
|
68
|
+
type: "Text",
|
|
69
|
+
style,
|
|
70
|
+
additionalProps: {
|
|
71
|
+
label: { context: actionIdentifier, name: "label_1" },
|
|
72
|
+
state: "default",
|
|
73
|
+
mobileActionRole: "label",
|
|
74
|
+
testID: testID ? `${testID}-label` : undefined,
|
|
75
|
+
...extraProps,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Incremental migration target:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { TextLabelsContainerView } from "./TextLabelsContainerView";
|
|
86
|
+
|
|
87
|
+
export const TextLabelsContainer = ({ actionIdentifier, testID, style, extraProps }) => ({
|
|
88
|
+
type: "ReactComponent",
|
|
89
|
+
additionalProps: {
|
|
90
|
+
component: TextLabelsContainerView,
|
|
91
|
+
renderChildren: false,
|
|
92
|
+
mobileActionRole: "label_container",
|
|
93
|
+
actionIdentifier,
|
|
94
|
+
testID,
|
|
95
|
+
textStyle: style,
|
|
96
|
+
textProps: extraProps,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
And React component:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
export const TextLabelsContainerView = ({
|
|
105
|
+
actionIdentifier,
|
|
106
|
+
testID,
|
|
107
|
+
textStyle,
|
|
108
|
+
textProps,
|
|
109
|
+
mobileActionRole,
|
|
110
|
+
}) => (
|
|
111
|
+
<View mobileActionRole={mobileActionRole}>
|
|
112
|
+
<Text
|
|
113
|
+
style={textStyle}
|
|
114
|
+
label={{ context: actionIdentifier, name: "label_1" }}
|
|
115
|
+
state="default"
|
|
116
|
+
mobileActionRole="label"
|
|
117
|
+
testID={testID ? `${testID}-label` : undefined}
|
|
118
|
+
{...textProps}
|
|
119
|
+
/>
|
|
120
|
+
</View>
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This keeps external behavior and props contract intact while moving implementation to React.
|
|
125
|
+
|
|
126
|
+
## Rules for Safe Incremental Migration
|
|
127
|
+
|
|
128
|
+
1. Do not change role markers used by parent logic (for example `mobileActionRole`).
|
|
129
|
+
2. Do not rename `testID` contracts during migration.
|
|
130
|
+
3. Keep output shape stable for parent builders (`null` handling, `elements` presence).
|
|
131
|
+
4. Keep data-mapping semantics identical (`label`, `state`, `entry`, etc.).
|
|
132
|
+
5. Migrate one node per PR where possible.
|
|
133
|
+
|
|
134
|
+
## Testing Checklist
|
|
135
|
+
|
|
136
|
+
1. Builder test: node type and required props (`component`, role, `testID`) are correct.
|
|
137
|
+
2. Rendering test: `configInflater + elementMapper` still renders expected UI.
|
|
138
|
+
3. Behavior test: parent container logic that clones/filters children still works.
|
|
139
|
+
4. Regression test: hidden/empty states still return `null` as before.
|
|
140
|
+
|
|
141
|
+
## Important Limitation
|
|
142
|
+
|
|
143
|
+
`additionalProps.component` is a function reference and is suitable for local JS-built trees.
|
|
144
|
+
If a tree must be fully serialized JSON, use a string `componentKey` and resolve it in a registry instead of storing a function directly.
|