@applicaster/zapp-react-native-ui-components 15.1.0-rc.1 → 16.0.0-rc.1
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
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type ActionButtonSlot = number;
|
|
2
|
+
|
|
3
|
+
export type ActionButtonDescriptor = {
|
|
4
|
+
slot: ActionButtonSlot;
|
|
5
|
+
renderIndex: number;
|
|
6
|
+
specificPrefix: string;
|
|
7
|
+
stylePrefix: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type ActionButtonsContainerLayout = {
|
|
11
|
+
horizontalAlign: string;
|
|
12
|
+
margins: {
|
|
13
|
+
top: number;
|
|
14
|
+
right: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
left: number;
|
|
17
|
+
};
|
|
18
|
+
stacking: "horizontal" | "vertical";
|
|
19
|
+
horizontalGutter: number;
|
|
20
|
+
verticalGutter: number;
|
|
21
|
+
independentStyles: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ActionButtonsModel = {
|
|
25
|
+
enabledSlots: ActionButtonSlot[];
|
|
26
|
+
buttonsCount: number;
|
|
27
|
+
container: ActionButtonsContainerLayout;
|
|
28
|
+
buttons: ActionButtonDescriptor[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type BuildActionButtonsModelOptions = {
|
|
32
|
+
configuration: Record<string, unknown>;
|
|
33
|
+
value: (key: string) => unknown;
|
|
34
|
+
containerPrefix: string;
|
|
35
|
+
buttonPrefix: string;
|
|
36
|
+
maxButtons?: number;
|
|
37
|
+
};
|
|
@@ -135,17 +135,11 @@ export const BorderContainerView = (props: Props) => {
|
|
|
135
135
|
|
|
136
136
|
const isImageOnlyCell = useMemo(
|
|
137
137
|
() =>
|
|
138
|
-
!hasFocusableInside(entry) &&
|
|
139
|
-
!hasTextLabels &&
|
|
140
138
|
state === "focused" &&
|
|
141
|
-
!
|
|
142
|
-
|
|
143
|
-
hasFocusableInside,
|
|
144
|
-
|
|
145
|
-
hasTextLabels,
|
|
146
|
-
state,
|
|
147
|
-
isMeasurement?.measuringInProgress,
|
|
148
|
-
]
|
|
139
|
+
!hasTextLabels &&
|
|
140
|
+
!isMeasurement?.measuringInProgress &&
|
|
141
|
+
!hasFocusableInside(entry),
|
|
142
|
+
[entry, hasTextLabels, state, isMeasurement?.measuringInProgress]
|
|
149
143
|
);
|
|
150
144
|
|
|
151
145
|
useEffect(() => {
|
|
@@ -2,7 +2,6 @@ import * as React from "react";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
4
|
import { TouchableOpacity } from "react-native";
|
|
5
|
-
// import { SvgUri } from "react-native-svg";
|
|
6
5
|
|
|
7
6
|
import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
|
|
8
7
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
@@ -35,20 +34,6 @@ type Props = {
|
|
|
35
34
|
cellUUID?: string;
|
|
36
35
|
};
|
|
37
36
|
|
|
38
|
-
// function Svg({
|
|
39
|
-
// uri,
|
|
40
|
-
// style,
|
|
41
|
-
// ...props
|
|
42
|
-
// }: {
|
|
43
|
-
// uri: {},
|
|
44
|
-
// style: { width: Number, height: number },
|
|
45
|
-
// }) {
|
|
46
|
-
// const width = style?.width;
|
|
47
|
-
// const height = style?.height;
|
|
48
|
-
|
|
49
|
-
// return <SvgUri width={width} height={height} uri={uri} />;
|
|
50
|
-
// }
|
|
51
|
-
|
|
52
37
|
const components = {
|
|
53
38
|
Image,
|
|
54
39
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { ContainerProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function ButtonContainerView({
|
|
6
|
+
style,
|
|
7
|
+
contentStyle,
|
|
8
|
+
children,
|
|
9
|
+
}: ContainerProps) {
|
|
10
|
+
return (
|
|
11
|
+
<View style={style}>
|
|
12
|
+
<View style={contentStyle}>{children}</View>
|
|
13
|
+
</View>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import * as R from "ramda";
|
|
4
|
+
import { useInitialFocus } from "@applicaster/zapp-react-native-utils/focusManager";
|
|
5
|
+
import {
|
|
6
|
+
useIsRTL,
|
|
7
|
+
applyRTLStylesIfNeeded,
|
|
8
|
+
} from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
cloneChildrenWithIds,
|
|
12
|
+
insertBetween,
|
|
13
|
+
unwrapDataProviderChild,
|
|
14
|
+
useFilterChildren,
|
|
15
|
+
} from "../../utils";
|
|
16
|
+
|
|
17
|
+
import type { ContainerProps, ContainerChildren } from "./types";
|
|
18
|
+
import { HorizontalSeparator } from "./components/HorizontalSeparator";
|
|
19
|
+
|
|
20
|
+
const generateId = (cellUUID, suffixId) => `${cellUUID}--${suffixId}`;
|
|
21
|
+
|
|
22
|
+
export function ButtonContainerView({
|
|
23
|
+
style,
|
|
24
|
+
children,
|
|
25
|
+
...otherProps
|
|
26
|
+
}: ContainerProps) {
|
|
27
|
+
const isRTL = useIsRTL();
|
|
28
|
+
|
|
29
|
+
const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
|
|
30
|
+
const filteredChildren = useFilterChildren<ContainerChildren>(children);
|
|
31
|
+
|
|
32
|
+
const buttonIds = filteredChildren.map((child) => {
|
|
33
|
+
const wrappedChild = unwrapDataProviderChild(child) as ContainerChildren;
|
|
34
|
+
const { cellUUID, suffixId } = wrappedChild.props;
|
|
35
|
+
|
|
36
|
+
return generateId(cellUUID, suffixId);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
useInitialFocus(otherProps.state === "focused", R.head(buttonIds));
|
|
40
|
+
|
|
41
|
+
if (R.isEmpty(filteredChildren)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
47
|
+
{insertBetween(
|
|
48
|
+
(index) => (
|
|
49
|
+
<HorizontalSeparator
|
|
50
|
+
key={`separator_${index}`}
|
|
51
|
+
width={horizontalGutter}
|
|
52
|
+
/>
|
|
53
|
+
),
|
|
54
|
+
cloneChildrenWithIds(buttonIds, filteredChildren)
|
|
55
|
+
)}
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View } from "react-native";
|
|
3
3
|
import * as R from "ramda";
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
6
|
-
HorizontalSeparatorProps,
|
|
7
|
-
ContainerProps,
|
|
8
|
-
ContainerChildren,
|
|
9
|
-
} from "./types";
|
|
4
|
+
import { insertBetween, useFilterChildren } from "../../utils";
|
|
5
|
+
import type { ContainerProps, ContainerChildren } from "./types";
|
|
10
6
|
import {
|
|
11
7
|
useIsRTL,
|
|
12
8
|
applyRTLStylesIfNeeded,
|
|
13
9
|
} from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
14
|
-
|
|
15
|
-
const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
|
|
16
|
-
<View style={{ width }} />
|
|
17
|
-
);
|
|
10
|
+
import { HorizontalSeparator } from "./components/HorizontalSeparator";
|
|
18
11
|
|
|
19
12
|
export function ButtonContainerView({
|
|
20
13
|
style,
|
|
@@ -23,7 +16,6 @@ export function ButtonContainerView({
|
|
|
23
16
|
}: ContainerProps) {
|
|
24
17
|
const isRTL = useIsRTL();
|
|
25
18
|
const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
|
|
26
|
-
|
|
27
19
|
const filteredChildren = useFilterChildren<ContainerChildren>(children);
|
|
28
20
|
|
|
29
21
|
if (R.isEmpty(filteredChildren)) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ButtonContainerView } from "./index.tv";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ReactElement, ReactNode } from "react";
|
|
2
|
+
import { ViewStyle } from "react-native";
|
|
3
|
+
|
|
4
|
+
export type ContainerChildProps = {
|
|
5
|
+
item: any;
|
|
6
|
+
pluginIdentifier: string;
|
|
7
|
+
cellUUID: string;
|
|
8
|
+
suffixId: string;
|
|
9
|
+
nextFocusLeft?: string;
|
|
10
|
+
nextFocusRight?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ContainerProps = Record<string, any> & {
|
|
14
|
+
buttonsToggleEnabled: boolean;
|
|
15
|
+
skipButtons: boolean;
|
|
16
|
+
style: ViewStyle;
|
|
17
|
+
contentStyle?: ViewStyle;
|
|
18
|
+
children: ContainerChildren[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type ButtonProps = Record<string, any> & {
|
|
22
|
+
style: ViewStyle;
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
item: any;
|
|
25
|
+
cellUUID: string;
|
|
26
|
+
groupId?: string;
|
|
27
|
+
suffixId: string;
|
|
28
|
+
normalStyles?: ViewStyle;
|
|
29
|
+
focusedStyles?: ViewStyle;
|
|
30
|
+
nextFocusLeft?: string;
|
|
31
|
+
nextFocusRight?: string;
|
|
32
|
+
pluginIdentifier: string;
|
|
33
|
+
disableFocus?: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type HorizontalSeparatorProps = {
|
|
37
|
+
width: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type ContainerChildren = ReactElement<ContainerChildProps>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
|
|
4
|
+
type Props = Record<string, any> & {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates the provider payload from the resolved runtime props.
|
|
10
|
+
* `children` is structural and is therefore excluded. `_dataKey` is produced
|
|
11
|
+
* by `configInflater` from the data mapping propName and identifies the prop
|
|
12
|
+
* that carries the resolved entry value.
|
|
13
|
+
*/
|
|
14
|
+
const createDataProviderProps = ({ children: _children, ...props }: Props) => ({
|
|
15
|
+
_dataKey: props._dataKey,
|
|
16
|
+
[props._dataKey as string]: props[props._dataKey as string],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Merges provider values into an element's existing `dataProviderProps`
|
|
21
|
+
* without overwriting keys that were supplied explicitly.
|
|
22
|
+
*/
|
|
23
|
+
const mergeDataProviderProps = (
|
|
24
|
+
targetProps: Record<string, unknown>,
|
|
25
|
+
forwardedProps: Record<string, unknown>
|
|
26
|
+
) => {
|
|
27
|
+
const dataProviderProps = Object.keys(forwardedProps).reduce(
|
|
28
|
+
(acc, key) => {
|
|
29
|
+
if (typeof acc[key] === "undefined") {
|
|
30
|
+
acc[key] = forwardedProps[key];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return acc;
|
|
34
|
+
},
|
|
35
|
+
{ ...(targetProps.dataProviderProps as Record<string, unknown>) } as Record<
|
|
36
|
+
string,
|
|
37
|
+
unknown
|
|
38
|
+
>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
dataProviderProps: dataProviderProps,
|
|
43
|
+
...(targetProps[dataProviderProps._dataKey as string] === undefined
|
|
44
|
+
? {
|
|
45
|
+
[dataProviderProps._dataKey as string]:
|
|
46
|
+
forwardedProps[dataProviderProps._dataKey as string],
|
|
47
|
+
}
|
|
48
|
+
: {}),
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Applies the provider props to the wrapped child's direct children only.
|
|
54
|
+
* The shallow stop is intentional: descendants below this level must receive
|
|
55
|
+
* data explicitly from their parent component instead of implicit propagation.
|
|
56
|
+
*/
|
|
57
|
+
const cloneSecondLevel = (
|
|
58
|
+
nodes: React.ReactNode,
|
|
59
|
+
forwardedProps: Record<string, unknown>
|
|
60
|
+
) =>
|
|
61
|
+
React.Children.map(nodes, (child) => {
|
|
62
|
+
if (!React.isValidElement(child)) {
|
|
63
|
+
return child;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (child.type === View) {
|
|
67
|
+
return child;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return React.cloneElement(
|
|
71
|
+
child,
|
|
72
|
+
mergeDataProviderProps(
|
|
73
|
+
child.props as Record<string, unknown>,
|
|
74
|
+
forwardedProps
|
|
75
|
+
) as never
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Specification:
|
|
81
|
+
* `DataProvider` bridges MasterCell node-tree `data` mappings into runtime
|
|
82
|
+
* React props for a shallow wrapped subtree under `dataProviderProps`.
|
|
83
|
+
*
|
|
84
|
+
* Purpose:
|
|
85
|
+
* Use `DataProvider` when a subtree root and that root's direct children must
|
|
86
|
+
* receive the same resolved runtime prop without propagating that prop deeper
|
|
87
|
+
* into the tree.
|
|
88
|
+
*
|
|
89
|
+
* Input contract:
|
|
90
|
+
* - `DataProvider` receives runtime props from normal MasterCell inflation.
|
|
91
|
+
* - The values under `dataProviderProps` are derived from the resolved props
|
|
92
|
+
* it receives.
|
|
93
|
+
* - The inner keys are not hardcoded and may include `entry`, `item`, or any
|
|
94
|
+
* other prop name produced by the node-tree `data` mapping.
|
|
95
|
+
*
|
|
96
|
+
* Forwarding contract:
|
|
97
|
+
* - inject the mapped value as a direct prop on the wrapped child
|
|
98
|
+
* - inject `dataProviderProps` into the wrapped child
|
|
99
|
+
* - inject both the direct prop and `dataProviderProps` into that child's
|
|
100
|
+
* direct children
|
|
101
|
+
* - stop at that level
|
|
102
|
+
* - do not overwrite explicit keys that already exist inside
|
|
103
|
+
* `dataProviderProps` on wrapped elements
|
|
104
|
+
*
|
|
105
|
+
* Non-forwarded props:
|
|
106
|
+
* - `children`
|
|
107
|
+
*
|
|
108
|
+
* Example:
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* {
|
|
112
|
+
* type: "DataProvider",
|
|
113
|
+
* data: [
|
|
114
|
+
* {
|
|
115
|
+
* func: "identity",
|
|
116
|
+
* args: [],
|
|
117
|
+
* propName: "entry",
|
|
118
|
+
* },
|
|
119
|
+
* ],
|
|
120
|
+
* elements: [
|
|
121
|
+
* {
|
|
122
|
+
* type: "ButtonContainerView",
|
|
123
|
+
* elements: [Button({ ... })],
|
|
124
|
+
* },
|
|
125
|
+
* ],
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* Expected runtime result:
|
|
130
|
+
* - `ButtonContainerView` receives `entry` and `dataProviderProps.entry`
|
|
131
|
+
* - each direct `Button` child receives `entry` and `dataProviderProps.entry`
|
|
132
|
+
* - deeper descendants such as `Asset` or `TextLabel` do not receive
|
|
133
|
+
* `entry` or `dataProviderProps.entry` unless their parent passes it
|
|
134
|
+
* explicitly
|
|
135
|
+
*/
|
|
136
|
+
export const DataProvider = ({ children, ...props }: Props) => {
|
|
137
|
+
const forwardedProps = createDataProviderProps(props);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<>
|
|
141
|
+
{React.Children.map(children, (child) => {
|
|
142
|
+
if (!React.isValidElement(child)) {
|
|
143
|
+
return child;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const injectedChildren = cloneSecondLevel(
|
|
147
|
+
child.props.children,
|
|
148
|
+
forwardedProps
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return React.cloneElement(child, {
|
|
152
|
+
...mergeDataProviderProps(
|
|
153
|
+
child.props as Record<string, unknown>,
|
|
154
|
+
forwardedProps
|
|
155
|
+
),
|
|
156
|
+
...(typeof injectedChildren !== "undefined"
|
|
157
|
+
? { children: injectedChildren }
|
|
158
|
+
: {}),
|
|
159
|
+
});
|
|
160
|
+
})}
|
|
161
|
+
</>
|
|
162
|
+
);
|
|
163
|
+
};
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, ImageStyle } from "react-native";
|
|
3
3
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
4
|
-
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
-
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
6
4
|
import { useFocusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/index.android";
|
|
7
5
|
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
8
6
|
|
|
9
|
-
const { Logger } = getXray();
|
|
10
|
-
|
|
11
7
|
import { recursiveCloneElementsWithState } from "../../utils";
|
|
12
8
|
|
|
13
|
-
const logger = new Logger("plugin", "plugins/navigation-action");
|
|
14
|
-
|
|
15
9
|
type ButtonProps = Record<string, any> & {
|
|
16
10
|
style: ImageStyle;
|
|
17
11
|
};
|
|
@@ -33,7 +27,7 @@ const getNextFocusRight = ({ nextFocusRight, parentFocus, isRTL }) => {
|
|
|
33
27
|
};
|
|
34
28
|
|
|
35
29
|
export function FocusableViewComponent(
|
|
36
|
-
{ style, children,
|
|
30
|
+
{ style, children, ...otherProps }: ButtonProps,
|
|
37
31
|
ref
|
|
38
32
|
) {
|
|
39
33
|
const {
|
|
@@ -44,7 +38,6 @@ export function FocusableViewComponent(
|
|
|
44
38
|
focusedStyles,
|
|
45
39
|
nextFocusLeft,
|
|
46
40
|
nextFocusRight,
|
|
47
|
-
pluginIdentifier,
|
|
48
41
|
disableFocus,
|
|
49
42
|
onButtonFocus,
|
|
50
43
|
} = otherProps;
|
|
@@ -52,20 +45,6 @@ export function FocusableViewComponent(
|
|
|
52
45
|
const parentFocus = useFocusable();
|
|
53
46
|
const isRTL = useIsRTL();
|
|
54
47
|
|
|
55
|
-
const actionContext = useActions(pluginIdentifier);
|
|
56
|
-
|
|
57
|
-
const onPress = () => {
|
|
58
|
-
if (!actionContext) {
|
|
59
|
-
logger.warning(
|
|
60
|
-
`Cannot resolve action context for ${pluginIdentifier} - please make sure the plugin is installed and up to date`
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
actionContext?.invokeAction?.(item);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
48
|
const onFocus = React.useCallback(
|
|
70
49
|
(ref) => {
|
|
71
50
|
onButtonFocus?.(ref);
|
|
@@ -82,7 +61,7 @@ export function FocusableViewComponent(
|
|
|
82
61
|
id={generateId(cellUUID, suffixId)}
|
|
83
62
|
disableFocus={disableFocus}
|
|
84
63
|
groupId={groupId}
|
|
85
|
-
onPress={onPress}
|
|
64
|
+
onPress={otherProps.onPress}
|
|
86
65
|
nextFocusUp={parentFocus?.nextFocusUp}
|
|
87
66
|
nextFocusDown={parentFocus?.nextFocusDown}
|
|
88
67
|
nextFocusLeft={getNextFocusLeft({ nextFocusLeft, parentFocus, isRTL })}
|
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { ImageStyle } from "react-native";
|
|
3
3
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
4
|
-
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
-
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
6
4
|
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
7
5
|
|
|
8
|
-
const { Logger } = getXray();
|
|
9
|
-
|
|
10
6
|
import {
|
|
11
7
|
recursiveCloneElementsWithState,
|
|
12
8
|
getFocusedButtonId,
|
|
13
9
|
} from "../../utils";
|
|
14
10
|
|
|
15
|
-
const logger = new Logger("plugin", "plugins/navigation-action");
|
|
16
|
-
|
|
17
11
|
type Props = Record<string, any> & {
|
|
18
12
|
style: ImageStyle;
|
|
19
13
|
};
|
|
20
14
|
|
|
21
15
|
const getFocusableId = (prefixId, suffixId) => `${prefixId}___${suffixId}`;
|
|
22
16
|
|
|
23
|
-
export function FocusableView({ style, children,
|
|
17
|
+
export function FocusableView({ style, children, ...otherProps }: Props) {
|
|
24
18
|
const {
|
|
25
19
|
groupId,
|
|
26
20
|
prefixId,
|
|
@@ -29,7 +23,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
29
23
|
focusedButtonId,
|
|
30
24
|
normalStyles,
|
|
31
25
|
focusedStyles,
|
|
32
|
-
pluginIdentifier,
|
|
33
26
|
preferredFocus,
|
|
34
27
|
} = otherProps;
|
|
35
28
|
|
|
@@ -41,20 +34,9 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
41
34
|
|
|
42
35
|
const additionalStyles = focused ? focusedStyles : normalStyles;
|
|
43
36
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const onPress = (event) => {
|
|
37
|
+
const handlePress = (event) => {
|
|
47
38
|
event?.preventDefault?.();
|
|
48
|
-
|
|
49
|
-
if (!actionContext) {
|
|
50
|
-
logger.warning(
|
|
51
|
-
`Cannot resolve action context for ${pluginIdentifier} - please make sure the plugin is installed and up to date`
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
actionContext?.invokeAction?.(item);
|
|
39
|
+
otherProps?.onPress?.(event);
|
|
58
40
|
};
|
|
59
41
|
|
|
60
42
|
const handleFocus = (focusable) => {
|
|
@@ -83,7 +65,7 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
83
65
|
style={styles}
|
|
84
66
|
onFocus={handleFocus}
|
|
85
67
|
onBlur={handleBlur}
|
|
86
|
-
onPress={
|
|
68
|
+
onPress={handlePress}
|
|
87
69
|
preferredFocus={preferredFocus}
|
|
88
70
|
skipFocusManagerRegistration={otherProps.skipFocusManagerRegistration}
|
|
89
71
|
isFocusable={otherProps.isFocusable}
|
|
@@ -24,7 +24,9 @@ const MemoizedImage = React.memo(QBImage, R.equals);
|
|
|
24
24
|
const isValidExternalSource = (source): boolean =>
|
|
25
25
|
source?.uri?.startsWith("http") ||
|
|
26
26
|
source?.uri?.startsWith("data:image") ||
|
|
27
|
-
source?.uri?.startsWith("file://")
|
|
27
|
+
source?.uri?.startsWith("file://") ||
|
|
28
|
+
source?.uri?.startsWith("asset:/") ||
|
|
29
|
+
source?.uri?.startsWith("node_modules_");
|
|
28
30
|
|
|
29
31
|
export default function Image({
|
|
30
32
|
style,
|
|
@@ -32,6 +34,7 @@ export default function Image({
|
|
|
32
34
|
placeholderImage,
|
|
33
35
|
entry,
|
|
34
36
|
withDimensions,
|
|
37
|
+
source: sourceProp,
|
|
35
38
|
...otherProps
|
|
36
39
|
}: Props) {
|
|
37
40
|
const [showDefault, setShowDefault] = React.useState(false);
|
|
@@ -48,7 +51,10 @@ export default function Image({
|
|
|
48
51
|
entry,
|
|
49
52
|
showDefault,
|
|
50
53
|
placeholderImage: placeholderImage || "",
|
|
51
|
-
otherProps
|
|
54
|
+
otherProps: {
|
|
55
|
+
source: sourceProp,
|
|
56
|
+
state: otherProps.state,
|
|
57
|
+
},
|
|
52
58
|
});
|
|
53
59
|
|
|
54
60
|
const onError = React.useCallback(() => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Image as RnImage, ImageStyle } from "react-native";
|
|
3
|
-
import { equals, omit } from "ramda";
|
|
4
3
|
|
|
5
4
|
import { useImageSource } from "./hooks";
|
|
5
|
+
import { equals } from "@applicaster/zapp-react-native-utils/utils";
|
|
6
6
|
|
|
7
7
|
type Source = {
|
|
8
8
|
uri: string;
|
|
@@ -25,11 +25,19 @@ function Image({
|
|
|
25
25
|
placeholderImage,
|
|
26
26
|
entry,
|
|
27
27
|
withDimensions,
|
|
28
|
+
source: sourceProp,
|
|
28
29
|
...otherProps
|
|
29
30
|
}: Props) {
|
|
30
31
|
const [error, setErrorState] = React.useState(null);
|
|
31
32
|
|
|
32
|
-
const source = useImageSource({
|
|
33
|
+
const source = useImageSource({
|
|
34
|
+
uri,
|
|
35
|
+
entry,
|
|
36
|
+
otherProps: {
|
|
37
|
+
source: sourceProp,
|
|
38
|
+
state: otherProps.state,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
33
41
|
|
|
34
42
|
React.useEffect(() => {
|
|
35
43
|
// reset error state on URI change as the error is referencing previous uri
|
|
@@ -49,7 +57,7 @@ function Image({
|
|
|
49
57
|
onError={React.useCallback(() => setErrorState(true), [])}
|
|
50
58
|
// as we have defaults as "" for placeholder image, we need to pass undefined to source to not throw warnings
|
|
51
59
|
source={_source?.uri ? _source : undefined}
|
|
52
|
-
{...
|
|
60
|
+
{...otherProps}
|
|
53
61
|
/>
|
|
54
62
|
);
|
|
55
63
|
}
|
|
@@ -23,9 +23,17 @@ function Image({
|
|
|
23
23
|
placeholderImage,
|
|
24
24
|
entry,
|
|
25
25
|
withDimensions,
|
|
26
|
+
source: sourceProp,
|
|
26
27
|
...otherProps
|
|
27
28
|
}: Props) {
|
|
28
|
-
const source = useImageSource({
|
|
29
|
+
const source = useImageSource({
|
|
30
|
+
uri,
|
|
31
|
+
entry,
|
|
32
|
+
otherProps: {
|
|
33
|
+
source: sourceProp,
|
|
34
|
+
state: otherProps.state,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
29
37
|
|
|
30
38
|
const updatedSource = source ? withDimensions(source) : { uri: "" };
|
|
31
39
|
|