@applicaster/zapp-react-native-ui-components 12.0.0-rc.10 → 12.0.0-rc.100
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/AudioPlayer/AudioPlayer.tsx +9 -1
- package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
- package/Components/AudioPlayer/helpers.tsx +1 -0
- package/Components/BaseFocusable/index.tsx +3 -3
- package/Components/Cell/Cell.tsx +32 -11
- package/Components/Cell/CellWithFocusable.tsx +40 -19
- package/Components/Cell/TvOSCellComponent.tsx +5 -9
- package/Components/ErrorScreen/index.tsx +1 -1
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableCell/index.tsx +7 -7
- package/Components/FocusableGroup/index.tsx +11 -2
- package/Components/FocusableList/FocusableChild.ts +24 -14
- package/Components/FocusableList/FocusableItem.tsx +1 -1
- package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
- package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
- package/Components/FocusableList/index.tsx +26 -25
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
- package/Components/HookRenderer/index.tsx +1 -6
- package/Components/Image/Image.android.tsx +3 -26
- package/Components/Image/Image.tsx +18 -2
- package/Components/Image/utils/index.ts +39 -0
- package/Components/Layout/TV/ScreenContainer.tsx +79 -5
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
- package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
- package/Components/Layout/TV/index.tsx +15 -84
- package/Components/Layout/TV/index.web.tsx +33 -0
- package/Components/Layout/index.tsx +15 -11
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -113
- package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +21 -8
- package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +37 -2
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +6 -10
- package/Components/MasterCell/dataAdapter.ts +21 -7
- package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
- package/Components/MasterCell/index.tsx +3 -1
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +33 -35
- package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
- package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
- package/Components/ModalComponent/Button/Item.tsx +82 -0
- package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
- package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
- package/Components/ModalComponent/Button/assets.ts +4 -0
- package/Components/ModalComponent/Button/index.tsx +111 -0
- package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
- package/Components/ModalComponent/Header/Title.tsx +70 -0
- package/Components/ModalComponent/Header/assets.ts +4 -0
- package/Components/ModalComponent/Header/index.tsx +71 -0
- package/Components/ModalComponent/Header/utils.ts +79 -0
- package/Components/ModalComponent/index.tsx +38 -0
- package/Components/ModalComponent/utils.ts +126 -0
- package/Components/Navigator/StackNavigator.tsx +57 -0
- package/Components/Navigator/index.ts +1 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +66 -31
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
- package/Components/PlayerImageBackground/index.tsx +34 -8
- package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
- package/Components/River/RefreshControl.tsx +55 -1
- package/Components/River/River.tsx +8 -5
- package/Components/River/TV/River.tsx +2 -25
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
- package/Components/River/__tests__/componentsMap.test.js +7 -0
- package/Components/River/useScreenConfiguration.ts +2 -2
- package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
- package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
- package/Components/RouteManager/index.tsx +4 -2
- package/Components/Screen/TV/hooks/index.ts +3 -0
- package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
- package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
- package/Components/Screen/TV/index.web.tsx +165 -0
- package/Components/Screen/__tests__/Screen.test.tsx +91 -74
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
- package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
- package/Components/Screen/index.tsx +110 -54
- package/Components/Screen/navigationHandler.ts +5 -82
- package/Components/ScreenResolver/index.tsx +10 -13
- package/Components/Tabs/TV/styles.ts +1 -1
- package/Components/TrackedView/index.tsx +6 -3
- package/Components/Transitioner/Scene.tsx +32 -26
- package/Components/Transitioner/Transitioner.tsx +21 -28
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
- package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
- package/Components/VideoLive/LiveImageManager.ts +70 -14
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +132 -95
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +21 -29
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +66 -0
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +355 -0
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +161 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +456 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
- package/Components/VideoModal/ModalAnimation/index.ts +15 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
- package/Components/VideoModal/PlayerDetails.tsx +6 -3
- package/Components/VideoModal/PlayerWrapper.tsx +81 -49
- package/Components/VideoModal/VideoModal.tsx +23 -40
- package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +0 -2
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +12 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +266 -166
- package/Components/VideoModal/hooks/useModalSize.ts +10 -12
- package/Components/VideoModal/utils.ts +8 -5
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
- package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
- package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
- package/Components/Viewport/ViewportTracker/index.tsx +1 -0
- package/Components/Viewport/utils.js +1 -1
- package/Components/ZappUIComponent/index.tsx +22 -12
- package/Components/index.js +2 -0
- package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
- package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
- package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
- package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
- package/Contexts/FocusableGroupContext/index.ts +7 -0
- package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
- package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
- package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
- package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
- package/Contexts/ScreenContext/index.tsx +95 -29
- package/Contexts/ScreenDataContext/index.tsx +20 -0
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
- package/Decorators/RiverResolver/index.tsx +0 -4
- package/Decorators/index.tsx +0 -4
- package/events/index.ts +1 -0
- package/package.json +5 -6
- package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
- package/Components/VideoModal/hooks/usePosition.ts +0 -50
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
- /package/Contexts/{index.js → index.ts} +0 -0
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
2
|
+
import RN from "react-native";
|
|
3
3
|
import { Provider } from "react-redux";
|
|
4
4
|
import TestRenderer from "react-test-renderer";
|
|
5
|
-
import
|
|
6
|
-
import CustomText from "../Text";
|
|
5
|
+
import configureStoreFn from "redux-mock-store";
|
|
7
6
|
|
|
8
|
-
const
|
|
7
|
+
const mockUseIsRTL = jest.fn(() => true);
|
|
8
|
+
const mockGetIsRTL = jest.fn(() => true);
|
|
9
|
+
const mockUseLocalizedStrings = jest.fn(() => ({}));
|
|
10
|
+
const mockGetUILanguage = jest.fn(() => "he-HE");
|
|
11
|
+
|
|
12
|
+
jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
|
|
13
|
+
useIsRTL: mockUseIsRTL,
|
|
14
|
+
getIsRTL: mockGetIsRTL,
|
|
15
|
+
useLocalizedStrings: mockUseLocalizedStrings,
|
|
16
|
+
getUILanguage: mockGetUILanguage,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const CustomText = require("../Text").default;
|
|
20
|
+
const mockStore = configureStoreFn();
|
|
9
21
|
|
|
10
22
|
const defaultProps = {
|
|
11
23
|
entry: {},
|
|
@@ -22,7 +34,7 @@ const getRenderedText = (label: string, store: any) => {
|
|
|
22
34
|
</Provider>
|
|
23
35
|
);
|
|
24
36
|
|
|
25
|
-
return testRenderer.root.findByType(Text).props.children;
|
|
37
|
+
return testRenderer.root.findByType(RN.Text).props.children;
|
|
26
38
|
};
|
|
27
39
|
|
|
28
40
|
describe("RTL app: Hebrew text contains english word", () => {
|
|
@@ -34,7 +46,7 @@ describe("RTL app: Hebrew text contains english word", () => {
|
|
|
34
46
|
remoteConfigurations: { localizations: { he: {} } },
|
|
35
47
|
appData: {
|
|
36
48
|
languageCode: "he",
|
|
37
|
-
countryCode: "
|
|
49
|
+
countryCode: "IL",
|
|
38
50
|
},
|
|
39
51
|
});
|
|
40
52
|
|
|
@@ -47,18 +59,22 @@ describe("RTL app: Hebrew text contains english word", () => {
|
|
|
47
59
|
|
|
48
60
|
it("Hebrew text contains not first english word", () => {
|
|
49
61
|
const renderedText = getRenderedText(textWithNotFirstEnglishWord, store);
|
|
50
|
-
|
|
51
62
|
expect(renderedText).toEqual(textWithNotFirstEnglishWord);
|
|
52
63
|
});
|
|
53
64
|
|
|
54
65
|
it("Hebrew text doesn't contain english word", () => {
|
|
55
66
|
const renderedText = getRenderedText(textWithoutEnglishWord, store);
|
|
56
|
-
|
|
57
67
|
expect(renderedText).toEqual(textWithoutEnglishWord);
|
|
58
68
|
});
|
|
59
69
|
});
|
|
60
70
|
|
|
61
71
|
describe("LTR app: English text", () => {
|
|
72
|
+
beforeAll(() => {
|
|
73
|
+
mockUseIsRTL.mockReturnValue(false);
|
|
74
|
+
mockGetIsRTL.mockReturnValue(false);
|
|
75
|
+
mockGetUILanguage.mockReturnValue("en-US");
|
|
76
|
+
});
|
|
77
|
+
|
|
62
78
|
const englishText = "Test sentence";
|
|
63
79
|
|
|
64
80
|
const store = mockStore({
|
|
@@ -71,7 +87,6 @@ describe("LTR app: English text", () => {
|
|
|
71
87
|
|
|
72
88
|
it("English text", () => {
|
|
73
89
|
const renderedText = getRenderedText(englishText, store);
|
|
74
|
-
|
|
75
90
|
expect(renderedText).toEqual(englishText);
|
|
76
91
|
});
|
|
77
92
|
});
|
|
@@ -61,7 +61,10 @@ export function ButtonContainerView({
|
|
|
61
61
|
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
62
62
|
{insertBetween(
|
|
63
63
|
(index) => (
|
|
64
|
-
<HorizontalSeparator
|
|
64
|
+
<HorizontalSeparator
|
|
65
|
+
key={`separator_${index}`}
|
|
66
|
+
width={horizontalGutter}
|
|
67
|
+
/>
|
|
65
68
|
),
|
|
66
69
|
cloneElementsWithIds(buttonIds, filteredChildren)
|
|
67
70
|
)}
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
ContainerProps,
|
|
8
8
|
ContainerChildren,
|
|
9
9
|
} from "./types";
|
|
10
|
+
import {
|
|
11
|
+
useIsRTL,
|
|
12
|
+
applyRTLStylesIfNeeded,
|
|
13
|
+
} from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
10
14
|
|
|
11
15
|
const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
|
|
12
16
|
<View style={{ width }} />
|
|
@@ -17,6 +21,7 @@ export function ButtonContainerView({
|
|
|
17
21
|
children,
|
|
18
22
|
...otherProps
|
|
19
23
|
}: ContainerProps) {
|
|
24
|
+
const isRTL = useIsRTL();
|
|
20
25
|
const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
|
|
21
26
|
|
|
22
27
|
const filteredChildren = useFilterChildren<ContainerChildren>(children);
|
|
@@ -26,10 +31,13 @@ export function ButtonContainerView({
|
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
return (
|
|
29
|
-
<View style={style}>
|
|
34
|
+
<View style={applyRTLStylesIfNeeded(style, isRTL)}>
|
|
30
35
|
{insertBetween(
|
|
31
36
|
(index) => (
|
|
32
|
-
<HorizontalSeparator
|
|
37
|
+
<HorizontalSeparator
|
|
38
|
+
key={`separator_${index}`}
|
|
39
|
+
width={horizontalGutter}
|
|
40
|
+
/>
|
|
33
41
|
),
|
|
34
42
|
filteredChildren
|
|
35
43
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { path
|
|
1
|
+
import { path } from "ramda";
|
|
2
2
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
3
3
|
import {
|
|
4
|
+
getColorFromData,
|
|
4
5
|
getKeyForLabel,
|
|
5
6
|
mapSelfAlignment,
|
|
6
7
|
} from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
@@ -48,15 +49,10 @@ export const CollapsibleTextContainer = ({
|
|
|
48
49
|
]),
|
|
49
50
|
{
|
|
50
51
|
func: (data) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return pathOr(value, value.split("."), data);
|
|
56
|
-
} else {
|
|
57
|
-
// return default value
|
|
58
|
-
return additionalStyles.backgroundColor;
|
|
59
|
-
}
|
|
52
|
+
return getColorFromData({
|
|
53
|
+
data,
|
|
54
|
+
valueFromLayout: additionalStyles.backgroundColor,
|
|
55
|
+
});
|
|
60
56
|
},
|
|
61
57
|
propName: "backgroundColor",
|
|
62
58
|
},
|
|
@@ -37,14 +37,28 @@ function retrieveData(entry, func, args) {
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
export function configInflater(
|
|
40
|
-
entry,
|
|
41
|
-
{
|
|
40
|
+
entry: any,
|
|
41
|
+
{
|
|
42
|
+
type,
|
|
43
|
+
style,
|
|
44
|
+
additionalProps = {},
|
|
45
|
+
data = [],
|
|
46
|
+
elements,
|
|
47
|
+
}: {
|
|
48
|
+
type: any;
|
|
49
|
+
style: any;
|
|
50
|
+
additionalProps?: Record<string, any>;
|
|
51
|
+
data?: Array<{ propName: string; func: Function; args: any[] }>;
|
|
52
|
+
elements?: any[];
|
|
53
|
+
}
|
|
42
54
|
) {
|
|
43
|
-
const props =
|
|
44
|
-
(acc, curr) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
const props = data.reduce(
|
|
56
|
+
(acc, curr) => {
|
|
57
|
+
acc[curr.propName] = retrieveData(entry, curr.func, curr.args);
|
|
58
|
+
|
|
59
|
+
return acc;
|
|
60
|
+
},
|
|
61
|
+
{ ...additionalProps }
|
|
48
62
|
);
|
|
49
63
|
|
|
50
64
|
let adjustedElements;
|
|
@@ -52,6 +52,8 @@ export function masterCellBuilder({
|
|
|
52
52
|
const { View } = componentsMap;
|
|
53
53
|
const elementsBuilder = dataAdapter(cellConfiguration);
|
|
54
54
|
|
|
55
|
+
// This is very expensive operation
|
|
56
|
+
// Keep calls to minimum
|
|
55
57
|
const hasFocusableInside = hasFocusableInsideBuilder(elementsBuilder);
|
|
56
58
|
|
|
57
59
|
/**
|
|
@@ -84,7 +86,7 @@ export function masterCellBuilder({
|
|
|
84
86
|
entry: item,
|
|
85
87
|
state: getEntryState(state, entryIsSelected),
|
|
86
88
|
}),
|
|
87
|
-
[state, item, entryIsSelected]
|
|
89
|
+
[state, item?.id, entryIsSelected] // Assuming that item won't mutate
|
|
88
90
|
);
|
|
89
91
|
|
|
90
92
|
const wrapperRef = React.useRef(null);
|
|
@@ -9,4 +9,13 @@ describe("insertBetween", () => {
|
|
|
9
9
|
|
|
10
10
|
expect(result).toEqual([1, "|", 2, "|", 3]);
|
|
11
11
|
});
|
|
12
|
+
|
|
13
|
+
it("insertBetween", () => {
|
|
14
|
+
const separator = (index) => `separator:${index}`;
|
|
15
|
+
const items = [1, 2, 3];
|
|
16
|
+
|
|
17
|
+
const result = insertBetween(separator, items);
|
|
18
|
+
|
|
19
|
+
expect(result).toEqual([1, "separator:0", 2, "separator:1", 3]);
|
|
20
|
+
});
|
|
12
21
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useMemo } from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
|
+
import validateColor from "validate-color";
|
|
3
4
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
4
5
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
6
|
|
|
@@ -9,6 +10,7 @@ import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerM
|
|
|
9
10
|
import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
|
|
10
11
|
import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
|
|
11
12
|
import { getCellState } from "../../Cell/utils";
|
|
13
|
+
import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
12
14
|
|
|
13
15
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
14
16
|
if (R.isNil(element)) {
|
|
@@ -47,44 +49,29 @@ export const hasElementsSpecificViewType = (viewType) => (elements) => {
|
|
|
47
49
|
return R.any(hasElementSpecificViewType(viewType))(elements);
|
|
48
50
|
};
|
|
49
51
|
|
|
50
|
-
const isColorOrEmpty = (value) =>
|
|
51
|
-
value === null ||
|
|
52
|
-
value === undefined ||
|
|
53
|
-
value === "" ||
|
|
54
|
-
value.startsWith("#") ||
|
|
55
|
-
value.startsWith("rgba") ||
|
|
56
|
-
value === "transparent";
|
|
57
|
-
|
|
58
52
|
function resolveColorForProp(entry, style, colorProp) {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
const colorFromProp = style[colorProp];
|
|
54
|
+
const nestedEntryValue = R.path(colorFromProp.split("."), entry);
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
nestedEntryValue === undefined &&
|
|
58
|
+
!validateColor(colorFromProp.replace(".00", "")) // https://github.com/dreamyguy/validate-color/issues/44
|
|
59
|
+
) {
|
|
60
|
+
logWarning(colorFromProp, style, entry, nestedEntryValue);
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (nestedEntryValue === null || typeof nestedEntryValue !== "object") {
|
|
70
|
-
logWarning(colorValue, style, entry, nestedEntryValue);
|
|
71
|
-
|
|
72
|
-
return style;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
nestedEntryValue = nestedEntryValue[part];
|
|
76
|
-
}
|
|
63
|
+
const colorValue = getColorFromData({
|
|
64
|
+
data: entry,
|
|
65
|
+
valueFromLayout: colorFromProp,
|
|
66
|
+
});
|
|
77
67
|
|
|
78
|
-
if (
|
|
79
|
-
logWarning(
|
|
68
|
+
if (!colorValue) {
|
|
69
|
+
logWarning(colorProp, style, entry, colorValue);
|
|
80
70
|
|
|
81
71
|
return style;
|
|
82
72
|
}
|
|
83
73
|
|
|
84
|
-
|
|
85
|
-
style[colorProp] = nestedEntryValue;
|
|
86
|
-
|
|
87
|
-
return style;
|
|
74
|
+
return { ...style, [colorProp]: colorValue };
|
|
88
75
|
}
|
|
89
76
|
|
|
90
77
|
export function resolveColor(entry, style) {
|
|
@@ -125,13 +112,24 @@ export const useFilterChildren = <
|
|
|
125
112
|
|
|
126
113
|
const filteredChildren = children.filter((child) => {
|
|
127
114
|
const item = child.props.item;
|
|
128
|
-
const
|
|
115
|
+
const actionIdentifier = child.props.pluginIdentifier;
|
|
116
|
+
const action = actions.actions[actionIdentifier];
|
|
117
|
+
|
|
129
118
|
// context value of specific plugin
|
|
130
|
-
|
|
119
|
+
if (action?.module && action.module.context) {
|
|
120
|
+
const currentValue = action.module.context._currentValue;
|
|
131
121
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
122
|
+
return currentValue?.isActionAvailable
|
|
123
|
+
? currentValue.isActionAvailable(item)
|
|
124
|
+
: true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
masterCellLogger.error({
|
|
128
|
+
message: `Action plugin for ${actionIdentifier} could not be found, check the configuration of your action button`,
|
|
129
|
+
data: { item, action: child.props.pluginIdentifier },
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return false;
|
|
135
133
|
});
|
|
136
134
|
|
|
137
135
|
return filteredChildren;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, StyleSheet } from "react-native";
|
|
3
|
+
import { MeasurementPortalContext } from "./MeasurementsPortal";
|
|
3
4
|
|
|
4
5
|
type Props = {
|
|
5
6
|
Component: React.ElementType;
|
|
@@ -19,13 +20,16 @@ const _MeasurementsPortal = (
|
|
|
19
20
|
{ Component, onLayout, componentProps, containerStyle }: Props,
|
|
20
21
|
ref
|
|
21
22
|
) => (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
// hack for cell to determine if cell is in measurement mode
|
|
24
|
+
<MeasurementPortalContext.Provider value={{}}>
|
|
25
|
+
<View
|
|
26
|
+
onLayout={onLayout}
|
|
27
|
+
style={[styles.container, containerStyle]}
|
|
28
|
+
ref={ref}
|
|
29
|
+
>
|
|
30
|
+
<Component {...componentProps} />
|
|
31
|
+
</View>
|
|
32
|
+
</MeasurementPortalContext.Provider>
|
|
29
33
|
);
|
|
30
34
|
|
|
31
35
|
export const MeasurementsPortal = React.forwardRef(_MeasurementsPortal);
|
|
@@ -35,7 +35,16 @@ const MeasurementsPortal = ({ Component, onLayout, componentProps }: Props) => {
|
|
|
35
35
|
);
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
type MeasurementPortalContextType = {
|
|
39
|
+
measureComponent?: (
|
|
40
|
+
comp: React.ElementType,
|
|
41
|
+
props: Record<any, any>
|
|
42
|
+
) => Promise<any>;
|
|
43
|
+
measuringInProgress?: boolean;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const MeasurementPortalContext =
|
|
47
|
+
React.createContext<null | MeasurementPortalContextType>(null);
|
|
39
48
|
|
|
40
49
|
const MeasurementsPortalContextProvider = ({ children }) => {
|
|
41
50
|
const Component = React.useRef(View);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { View, LayoutChangeEvent, ScrollView } from "react-native";
|
|
9
|
+
import { Button } from "./Button";
|
|
10
|
+
import { ItemIconProps } from "./Button/ItemIcon";
|
|
11
|
+
import { ItemProps } from "./Button/Item";
|
|
12
|
+
import { ItemLabelProps } from "./Button/ItemLabel";
|
|
13
|
+
import {
|
|
14
|
+
defaultItemIconProps,
|
|
15
|
+
defaultItemLabelProps,
|
|
16
|
+
defaultItemProps,
|
|
17
|
+
} from "./utils";
|
|
18
|
+
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
19
|
+
|
|
20
|
+
import { ModalHeader } from "./Header";
|
|
21
|
+
|
|
22
|
+
type ModalComponentProps = {
|
|
23
|
+
items: any[];
|
|
24
|
+
current_selection?: any;
|
|
25
|
+
onPress: (item: any) => void;
|
|
26
|
+
width: number;
|
|
27
|
+
currentRoute: boolean;
|
|
28
|
+
summary?: string;
|
|
29
|
+
title?: string;
|
|
30
|
+
maxHeight?: number;
|
|
31
|
+
dismiss: () => void;
|
|
32
|
+
buttonComponent?: React.ComponentType;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export function BottomSheetModalContent(props: ModalComponentProps) {
|
|
36
|
+
const {
|
|
37
|
+
items,
|
|
38
|
+
currentRoute,
|
|
39
|
+
maxHeight,
|
|
40
|
+
current_selection = null,
|
|
41
|
+
onPress,
|
|
42
|
+
dismiss,
|
|
43
|
+
summary,
|
|
44
|
+
title,
|
|
45
|
+
buttonComponent: ButtonComponent = Button,
|
|
46
|
+
} = props;
|
|
47
|
+
|
|
48
|
+
const [headerHeight, setHeaderHeight] = useState(0);
|
|
49
|
+
const route = useRef(currentRoute);
|
|
50
|
+
const theme = useTheme<BaseThemePropertiesMobile>();
|
|
51
|
+
|
|
52
|
+
const maxContentHeight = maxHeight
|
|
53
|
+
? maxHeight - headerHeight - Number(theme.modal_bottom_sheet_padding_top)
|
|
54
|
+
: undefined;
|
|
55
|
+
|
|
56
|
+
const onHeaderLayout = useCallback((event: LayoutChangeEvent) => {
|
|
57
|
+
setHeaderHeight(event.nativeEvent.layout.height);
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (currentRoute !== route.current) {
|
|
62
|
+
props.dismiss();
|
|
63
|
+
}
|
|
64
|
+
}, [currentRoute]);
|
|
65
|
+
|
|
66
|
+
const iconBaseProps = useMemo<ItemIconProps>(() => {
|
|
67
|
+
return defaultItemIconProps(theme);
|
|
68
|
+
}, [theme]);
|
|
69
|
+
|
|
70
|
+
const itemBaseProps = useMemo<ItemProps>(() => {
|
|
71
|
+
return defaultItemProps(theme, props.width);
|
|
72
|
+
}, [theme, props.width]);
|
|
73
|
+
|
|
74
|
+
const labelBaseProps = useMemo<ItemLabelProps>(() => {
|
|
75
|
+
return defaultItemLabelProps(theme, props.width);
|
|
76
|
+
}, [theme, props.width]);
|
|
77
|
+
|
|
78
|
+
const handlePress = (item: any) => {
|
|
79
|
+
onPress(item);
|
|
80
|
+
dismiss();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<View
|
|
85
|
+
style={{
|
|
86
|
+
paddingTop: Number(theme.modal_bottom_sheet_padding_top),
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<ModalHeader
|
|
90
|
+
width={props.width}
|
|
91
|
+
dismiss={dismiss}
|
|
92
|
+
configuration={theme}
|
|
93
|
+
onLayout={onHeaderLayout}
|
|
94
|
+
summary={summary}
|
|
95
|
+
title={title}
|
|
96
|
+
/>
|
|
97
|
+
<ScrollView
|
|
98
|
+
bounces={false}
|
|
99
|
+
style={{ maxHeight: maxContentHeight }}
|
|
100
|
+
contentContainerStyle={{
|
|
101
|
+
paddingBottom: Number(theme.modal_bottom_sheet_padding_bottom),
|
|
102
|
+
paddingTop: Number(theme.modal_bottom_sheet_padding_top),
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
{items.map((item, index) => (
|
|
106
|
+
<ButtonComponent
|
|
107
|
+
key={index}
|
|
108
|
+
width={props.width}
|
|
109
|
+
configuration={theme}
|
|
110
|
+
selectedItem={current_selection}
|
|
111
|
+
item={item}
|
|
112
|
+
onPress={handlePress}
|
|
113
|
+
iconBaseProps={iconBaseProps}
|
|
114
|
+
itemBaseProps={itemBaseProps}
|
|
115
|
+
labelBaseProps={labelBaseProps}
|
|
116
|
+
/>
|
|
117
|
+
))}
|
|
118
|
+
</ScrollView>
|
|
119
|
+
</View>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React, { ReactChild } from "react";
|
|
2
|
+
import { View, StyleSheet } from "react-native";
|
|
3
|
+
|
|
4
|
+
export type ItemProps = {
|
|
5
|
+
backgroundColor: string;
|
|
6
|
+
focusedBackgroundColor: string;
|
|
7
|
+
selectedBackgroundColor?: string;
|
|
8
|
+
focusectedBackgroundColor?: string;
|
|
9
|
+
borderRadius: number;
|
|
10
|
+
marginBottom: number;
|
|
11
|
+
marginLeft: number;
|
|
12
|
+
marginRight: number;
|
|
13
|
+
marginTop: number;
|
|
14
|
+
paddingTop: number;
|
|
15
|
+
paddingBottom: number;
|
|
16
|
+
paddingRight: number;
|
|
17
|
+
paddingLeft: number;
|
|
18
|
+
focused?: boolean;
|
|
19
|
+
selected?: boolean;
|
|
20
|
+
children?: ReactChild[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const styles = StyleSheet.create({
|
|
24
|
+
container: {
|
|
25
|
+
flexDirection: "row",
|
|
26
|
+
justifyContent: "space-between",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
function getBackgroundColor({
|
|
31
|
+
selected,
|
|
32
|
+
focused,
|
|
33
|
+
backgroundColor,
|
|
34
|
+
focusedBackgroundColor,
|
|
35
|
+
selectedBackgroundColor,
|
|
36
|
+
focusectedBackgroundColor,
|
|
37
|
+
}) {
|
|
38
|
+
switch (true) {
|
|
39
|
+
case selected && focused:
|
|
40
|
+
return focusectedBackgroundColor;
|
|
41
|
+
case selected && !focused:
|
|
42
|
+
return selectedBackgroundColor;
|
|
43
|
+
case !selected && focused:
|
|
44
|
+
return focusedBackgroundColor;
|
|
45
|
+
default:
|
|
46
|
+
return backgroundColor;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function Item(props: ItemProps) {
|
|
51
|
+
const {
|
|
52
|
+
backgroundColor,
|
|
53
|
+
focusedBackgroundColor,
|
|
54
|
+
focusectedBackgroundColor,
|
|
55
|
+
selectedBackgroundColor,
|
|
56
|
+
children,
|
|
57
|
+
focused,
|
|
58
|
+
selected,
|
|
59
|
+
...itemStyles
|
|
60
|
+
} = props;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<View
|
|
64
|
+
style={[
|
|
65
|
+
styles.container,
|
|
66
|
+
itemStyles,
|
|
67
|
+
{
|
|
68
|
+
backgroundColor: getBackgroundColor({
|
|
69
|
+
selected,
|
|
70
|
+
focused,
|
|
71
|
+
focusectedBackgroundColor,
|
|
72
|
+
focusedBackgroundColor,
|
|
73
|
+
selectedBackgroundColor,
|
|
74
|
+
backgroundColor,
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
]}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { QBImage } from "@applicaster/zapp-react-native-ui-components/Components";
|
|
4
|
+
|
|
5
|
+
export type ItemIconProps = {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
asset?: string | string[] | CellActionAssetComponent;
|
|
9
|
+
flavour?: "flavour_1" | "flavour_2";
|
|
10
|
+
marginTop: number;
|
|
11
|
+
marginBottom: number;
|
|
12
|
+
marginLeft: number;
|
|
13
|
+
marginRight: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function isImageAsset(asset) {
|
|
17
|
+
return typeof asset === "string" || Array.isArray(asset);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getImageAsset(asset, flavour) {
|
|
21
|
+
if (typeof asset === "string") return asset;
|
|
22
|
+
|
|
23
|
+
if (flavour) {
|
|
24
|
+
const flavourIndex = Number(flavour.replace("flavour_", ""));
|
|
25
|
+
if (flavourIndex > -1) return asset?.[flavourIndex - 1];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return asset?.[0];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function ItemIcon(props: ItemIconProps) {
|
|
32
|
+
const { width, height, asset, flavour, ...styles } = props;
|
|
33
|
+
|
|
34
|
+
if (isImageAsset(asset)) {
|
|
35
|
+
const imageAsset = getImageAsset(asset, flavour);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<View style={styles}>
|
|
39
|
+
<QBImage
|
|
40
|
+
fadeDuration={0}
|
|
41
|
+
source={{ uri: imageAsset }}
|
|
42
|
+
style={{ width, height }}
|
|
43
|
+
/>
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const AssetComponent = asset as CellActionAssetComponent;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<View style={styles}>
|
|
52
|
+
<AssetComponent
|
|
53
|
+
width={width}
|
|
54
|
+
height={height}
|
|
55
|
+
style={styles}
|
|
56
|
+
flavour={flavour}
|
|
57
|
+
/>
|
|
58
|
+
</View>
|
|
59
|
+
);
|
|
60
|
+
}
|