@applicaster/zapp-react-native-ui-components 16.0.0-rc.8 → 16.0.0-rc.81
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/tv/__tests__/audioPlayer.test.js +19 -0
- package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
- package/Components/AudioPlayer/tv/index.tsx +5 -1
- package/Components/Cell/Cell.tsx +3 -6
- package/Components/Cell/TvOSCellComponent.tsx +0 -1
- package/Components/CellRendererResolver/index.ts +2 -2
- package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
- package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
- package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
- package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
- package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
- package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
- package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
- package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
- package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
- package/Components/GeneralContentScreen/index.ts +2 -0
- package/Components/Layout/FullWidthRow.tsx +38 -0
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +16 -5
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
- package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
- package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
- package/Components/MasterCell/dataAdapter.ts +15 -5
- package/Components/MasterCell/hooks/index.ts +2 -0
- package/Components/MasterCell/hooks/usePressedState.ts +4 -0
- package/Components/MasterCell/index.tsx +30 -5
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
- package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
- package/Components/MasterCell/utils/index.ts +85 -46
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +54 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +21 -0
- package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +58 -0
- package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +314 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +407 -0
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +763 -0
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +653 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +960 -0
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +89 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +11 -0
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +230 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +332 -0
- package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +383 -42
- package/Components/ModalComponent/Button/index.tsx +5 -3
- package/Components/ModalComponent/Header/index.tsx +20 -7
- package/Components/ModalComponent/Header/utils.ts +1 -1
- package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
- package/Components/ModalComponent/SortableList.tsx +142 -0
- package/Components/ModalComponent/SortableList.web.tsx +34 -0
- package/Components/ModalComponent/TextInputContent.tsx +100 -0
- package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +238 -0
- package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +120 -0
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
- package/Components/ModalComponent/index.tsx +11 -0
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +66 -0
- package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
- package/Components/ModalComponent/presets/index.ts +27 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
- package/Components/ModalComponent/presets/standardCell.tsx +41 -0
- package/Components/ModalComponent/presets/types.ts +35 -0
- package/Components/ModalComponent/showTextInput.ts +43 -0
- package/Components/ModalComponent/utils.ts +82 -21
- package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
- package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
- package/Components/OfflineHandler/hooks.ts +111 -0
- package/Components/OfflineHandler/index.tsx +51 -73
- package/Components/OfflineHandler/utils/index.ts +1 -13
- package/Components/PlayerContainer/PlayerContainer.tsx +25 -17
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
- package/Components/PreloaderWrapper/index.tsx +1 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +2 -2
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +3 -15
- package/Components/Screen/index.tsx +8 -4
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
- package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +20 -3
- package/Components/TopCutoffOverlay/index.tsx +2 -2
- package/Components/Transitioner/Transitioner.tsx +28 -17
- package/Components/VideoLive/LiveImageManager.ts +1 -1
- package/Components/VideoModal/utils.ts +6 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
- package/Contexts/CachedDimensionsContext/index.ts +17 -2
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/index.js +0 -39
- package/Hooks/useEntryActionsExpander.ts +63 -0
- package/package.json +6 -5
- package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
- package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
- package/Components/Screen/utils.ts +0 -16
- package/Helpers/Analytics/index.js +0 -95
- /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
- /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
- /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
- /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, View } from "react-native";
|
|
3
|
+
import {
|
|
4
|
+
AudioPlayerTrackItem,
|
|
5
|
+
VARIANT_NOW_PLAYING,
|
|
6
|
+
mapMenuItemToNowPlayingData,
|
|
7
|
+
} from "./Item";
|
|
8
|
+
import { AudioPlayerHeader, VARIANT_SECTION_HEADER } from "./Header";
|
|
9
|
+
import type { MenuItem } from "@applicaster/zapp-react-native-utils/modalState/types";
|
|
10
|
+
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
|
|
11
|
+
import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
|
|
12
|
+
|
|
13
|
+
const styles = StyleSheet.create({
|
|
14
|
+
divider: {
|
|
15
|
+
height: StyleSheet.hairlineWidth,
|
|
16
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
|
17
|
+
marginHorizontal: 20,
|
|
18
|
+
marginVertical: 8,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
type NowPlayingHeaderSectionProps = {
|
|
23
|
+
onClear?: () => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function NowPlayingHeaderSection({
|
|
27
|
+
onClear,
|
|
28
|
+
}: NowPlayingHeaderSectionProps) {
|
|
29
|
+
const player = usePlayer();
|
|
30
|
+
const playerState = usePlayerState("queue-now-playing-section");
|
|
31
|
+
|
|
32
|
+
const renderNowPlayingTrack = () => {
|
|
33
|
+
if (!player?.entry) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const mediaGroup = player.entry.media_group;
|
|
38
|
+
|
|
39
|
+
const imageGroup = Array.isArray(mediaGroup)
|
|
40
|
+
? mediaGroup.find((g: any) => g.type === "image")
|
|
41
|
+
: null;
|
|
42
|
+
|
|
43
|
+
const firstImage = Array.isArray(imageGroup?.media_item)
|
|
44
|
+
? imageGroup.media_item[0]?.src
|
|
45
|
+
: imageGroup?.media_item?.src;
|
|
46
|
+
|
|
47
|
+
const nowPlayingItem: MenuItem = {
|
|
48
|
+
id: String(player.entry.id ?? "now_playing"),
|
|
49
|
+
title: (player.entry.title as string) || String(player.entry.id),
|
|
50
|
+
summary: (player.entry.summary as string) ?? "",
|
|
51
|
+
icon: firstImage,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const data = mapMenuItemToNowPlayingData(
|
|
55
|
+
nowPlayingItem,
|
|
56
|
+
playerState?.isPaused
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const handlePlayPause = () => {
|
|
60
|
+
if (playerState?.isPaused) {
|
|
61
|
+
player?.play();
|
|
62
|
+
} else {
|
|
63
|
+
player?.pause();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<View>
|
|
69
|
+
<AudioPlayerTrackItem
|
|
70
|
+
configuration={VARIANT_NOW_PLAYING}
|
|
71
|
+
data={data}
|
|
72
|
+
onTrailingButtonPress={handlePlayPause}
|
|
73
|
+
/>
|
|
74
|
+
<View style={styles.divider} />
|
|
75
|
+
</View>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<View>
|
|
81
|
+
{renderNowPlayingTrack()}
|
|
82
|
+
<AudioPlayerHeader
|
|
83
|
+
configuration={VARIANT_SECTION_HEADER}
|
|
84
|
+
data={{ title: "Next Up" }}
|
|
85
|
+
onClear={onClear}
|
|
86
|
+
/>
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AudioPlayerAction } from "./Action";
|
|
2
|
+
|
|
3
|
+
export { AudioPlayerButton, VARIANT_WITH_ICON } from "./Button";
|
|
4
|
+
|
|
5
|
+
export { AudioPlayerHeader } from "./Header";
|
|
6
|
+
|
|
7
|
+
export { AudioPlayerTrackItem } from "./Item";
|
|
8
|
+
|
|
9
|
+
export { NowPlayingHeaderSection } from "./NowPlayingHeaderSection";
|
|
10
|
+
|
|
11
|
+
export { AudioPlayerInput } from "./Input";
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mergeItemConfiguration,
|
|
3
|
+
mergeButtonConfiguration,
|
|
4
|
+
} from "../mergeStyles";
|
|
5
|
+
|
|
6
|
+
const base = {
|
|
7
|
+
background: {
|
|
8
|
+
defaultColor: "transparent",
|
|
9
|
+
focusedColor: "rgba(62, 62, 62, 1)",
|
|
10
|
+
cornerRadius: 12,
|
|
11
|
+
},
|
|
12
|
+
image: {
|
|
13
|
+
width: 48,
|
|
14
|
+
height: 48,
|
|
15
|
+
borderWidth: 1,
|
|
16
|
+
borderColor: "rgba(255, 255, 255, 0.1)",
|
|
17
|
+
cornerRadius: 6,
|
|
18
|
+
},
|
|
19
|
+
nowPlayingAsset: {},
|
|
20
|
+
nowPlayingLabel: { fontColor: "#fff", fontSize: 12 },
|
|
21
|
+
textLabel1: { fontColor: "#fff", fontSize: 15 },
|
|
22
|
+
textLabel2: { fontColor: "#999", fontSize: 13 },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe("mergeItemConfiguration background", () => {
|
|
26
|
+
it("applies Studio focus background to focusedColor", () => {
|
|
27
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
28
|
+
background: {
|
|
29
|
+
default: { backgroundColor: "transparent" },
|
|
30
|
+
focused: { backgroundColor: "#FF3B30" },
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(merged.background.defaultColor).toBe("transparent");
|
|
35
|
+
expect(merged.background.focusedColor).toBe("#FF3B30");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("accepts focus alias bucket from parser output", () => {
|
|
39
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
40
|
+
background: {
|
|
41
|
+
default: { backgroundColor: "transparent" },
|
|
42
|
+
focus: { backgroundColor: "#00FF66" },
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(merged.background.focusedColor).toBe("#00FF66");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("merges text label lineHeight from Studio keys", () => {
|
|
50
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
51
|
+
textLabel1: {
|
|
52
|
+
default: { fontSize: 28, lineHeight: 40 },
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(merged.textLabel1.fontSize).toBe(28);
|
|
57
|
+
expect(merged.textLabel1.lineHeight).toBe(40);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("merges background padding from Studio keys", () => {
|
|
61
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
62
|
+
background: {
|
|
63
|
+
default: {
|
|
64
|
+
backgroundColor: "#2E7D32",
|
|
65
|
+
paddingTop: 32,
|
|
66
|
+
paddingBottom: 32,
|
|
67
|
+
paddingLeft: 40,
|
|
68
|
+
paddingRight: 40,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(merged.background.paddingTop).toBe(32);
|
|
74
|
+
expect(merged.background.paddingBottom).toBe(32);
|
|
75
|
+
expect(merged.background.paddingLeft).toBe(40);
|
|
76
|
+
expect(merged.background.paddingRight).toBe(40);
|
|
77
|
+
// Default-only override must not steal the base focused highlight color.
|
|
78
|
+
expect(merged.background.focusedColor).toBe("rgba(62, 62, 62, 1)");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("preserves base focusedColor when only default background is set", () => {
|
|
82
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
83
|
+
background: {
|
|
84
|
+
default: { backgroundColor: "#2E7D32" },
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(merged.background.defaultColor).toBe("#2E7D32");
|
|
89
|
+
expect(merged.background.focusedColor).toBe("rgba(62, 62, 62, 1)");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("applies Studio checkbox assets onto checkboxAssets", () => {
|
|
93
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
94
|
+
checkbox: {
|
|
95
|
+
default: {
|
|
96
|
+
asset: "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=CB",
|
|
97
|
+
checkedAsset: "https://placehold.co/24x24/FFD60A/000000/png?text=CK",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(merged.checkboxAssets?.asset).toBe(
|
|
103
|
+
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=CB"
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(merged.checkboxAssets?.checkedAsset).toBe(
|
|
107
|
+
"https://placehold.co/24x24/FFD60A/000000/png?text=CK"
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("applies Studio reorder/remove assets onto actionButtonAssets", () => {
|
|
112
|
+
const merged = mergeItemConfiguration(base as any, {
|
|
113
|
+
actionButtons: {
|
|
114
|
+
default: {
|
|
115
|
+
removeAsset: "https://placehold.co/24x24/00FF00/000000/png?text=RM",
|
|
116
|
+
dragAsset: "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(merged.actionButtonAssets?.removeAsset).toBe(
|
|
122
|
+
"https://placehold.co/24x24/00FF00/000000/png?text=RM"
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect(merged.actionButtonAssets?.dragAsset).toBe(
|
|
126
|
+
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH"
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("mergeButtonConfiguration background", () => {
|
|
132
|
+
const buttonBase = {
|
|
133
|
+
background: {
|
|
134
|
+
defaultColor: "#FFFFFF",
|
|
135
|
+
focusedColor: "#E0E0E0",
|
|
136
|
+
inactiveColor: "#CCCCCC",
|
|
137
|
+
cornerRadius: 12,
|
|
138
|
+
paddingTop: 12,
|
|
139
|
+
paddingRight: 12,
|
|
140
|
+
paddingBottom: 12,
|
|
141
|
+
paddingLeft: 12,
|
|
142
|
+
horizontalGutter: 8,
|
|
143
|
+
},
|
|
144
|
+
title: {
|
|
145
|
+
fontColor: "#000000",
|
|
146
|
+
focusedFontColor: "#000000",
|
|
147
|
+
inactiveFontColor: "#666666",
|
|
148
|
+
iosFontFamily: "SFProText-Semibold",
|
|
149
|
+
androidFontFamily: "Roboto-Medium",
|
|
150
|
+
fontSize: 15,
|
|
151
|
+
lineHeight: 24,
|
|
152
|
+
iosLetterSpacing: 0,
|
|
153
|
+
androidLetterSpacing: 0,
|
|
154
|
+
textTransform: "default" as const,
|
|
155
|
+
},
|
|
156
|
+
icons: {
|
|
157
|
+
width: 24,
|
|
158
|
+
height: 24,
|
|
159
|
+
leadingIcon: { enabled: true },
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
it("applies Studio button focus background to focusedColor", () => {
|
|
164
|
+
const merged = mergeButtonConfiguration(buttonBase as any, {
|
|
165
|
+
background: {
|
|
166
|
+
default: { backgroundColor: "#FFFFFF" },
|
|
167
|
+
focused: { backgroundColor: "#00FF00" },
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(merged.background.defaultColor).toBe("#FFFFFF");
|
|
172
|
+
expect(merged.background.focusedColor).toBe("#00FF00");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("applies Studio button title lineHeight", () => {
|
|
176
|
+
const merged = mergeButtonConfiguration(buttonBase as any, {
|
|
177
|
+
title: {
|
|
178
|
+
default: { fontColor: "#000000", fontSize: 15, lineHeight: 40 },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(merged.title.lineHeight).toBe(40);
|
|
183
|
+
expect(merged.title.fontSize).toBe(15);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("applies Studio button background padding", () => {
|
|
187
|
+
const merged = mergeButtonConfiguration(buttonBase as any, {
|
|
188
|
+
background: {
|
|
189
|
+
default: {
|
|
190
|
+
backgroundColor: "#FFFFFF",
|
|
191
|
+
paddingTop: 32,
|
|
192
|
+
paddingRight: 20,
|
|
193
|
+
paddingBottom: 8,
|
|
194
|
+
paddingLeft: 4,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(merged.background.paddingTop).toBe(32);
|
|
200
|
+
expect(merged.background.paddingRight).toBe(20);
|
|
201
|
+
expect(merged.background.paddingBottom).toBe(8);
|
|
202
|
+
expect(merged.background.paddingLeft).toBe(4);
|
|
203
|
+
expect(merged.background.focusedColor).toBe("#E0E0E0");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("preserves base button focusedColor when only default background is set", () => {
|
|
207
|
+
const merged = mergeButtonConfiguration(buttonBase as any, {
|
|
208
|
+
background: {
|
|
209
|
+
default: { backgroundColor: "#ABCDEF" },
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
expect(merged.background.defaultColor).toBe("#ABCDEF");
|
|
214
|
+
expect(merged.background.focusedColor).toBe("#E0E0E0");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("applies Studio create-new leading icon asset", () => {
|
|
218
|
+
const merged = mergeButtonConfiguration(buttonBase as any, {
|
|
219
|
+
leadingIcon: {
|
|
220
|
+
default: {
|
|
221
|
+
asset: "https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(merged.icons.leadingIcon.asset).toBe(
|
|
227
|
+
"https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN"
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BackgroundConfiguration as ItemBackgroundConfiguration,
|
|
3
|
+
ImageConfiguration as ItemImageConfiguration,
|
|
4
|
+
LabelConfiguration as ItemLabelConfiguration,
|
|
5
|
+
NowPlayingAssetConfiguration as ItemNowPlayingAssetConfiguration,
|
|
6
|
+
CheckboxAssetsConfiguration as ItemCheckboxAssetsConfiguration,
|
|
7
|
+
ActionButtonAssetsConfiguration as ItemActionButtonAssetsConfiguration,
|
|
8
|
+
ItemConfiguration,
|
|
9
|
+
} from "../Components/Item";
|
|
10
|
+
import {
|
|
11
|
+
TitleConfiguration as HeaderTitleConfiguration,
|
|
12
|
+
HeaderConfiguration,
|
|
13
|
+
} from "../Components/Header";
|
|
14
|
+
import {
|
|
15
|
+
BackgroundConfiguration as ButtonBackgroundConfiguration,
|
|
16
|
+
TitleConfiguration as ButtonTitleConfiguration,
|
|
17
|
+
ButtonConfiguration,
|
|
18
|
+
} from "../Components/Button";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Item Merging Helpers
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
function mergeItemBackground(
|
|
25
|
+
base: ItemBackgroundConfiguration,
|
|
26
|
+
defaultOverride?: Record<string, any>,
|
|
27
|
+
focusedOverride?: Record<string, any>
|
|
28
|
+
): ItemBackgroundConfiguration {
|
|
29
|
+
if (!defaultOverride && !focusedOverride) return base;
|
|
30
|
+
|
|
31
|
+
// Only take focused color from the focused/pressed bucket (or legacy
|
|
32
|
+
// mis-parsed focus* aliases). Never fall back to default background —
|
|
33
|
+
// that would overwrite base.focusedColor whenever Studio only sets default.
|
|
34
|
+
const focusedColor =
|
|
35
|
+
focusedOverride?.backgroundColor ??
|
|
36
|
+
defaultOverride?.focusColor ??
|
|
37
|
+
defaultOverride?.focusBackgroundColor;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...base,
|
|
41
|
+
...(defaultOverride?.backgroundColor
|
|
42
|
+
? { defaultColor: defaultOverride.backgroundColor }
|
|
43
|
+
: {}),
|
|
44
|
+
...(focusedColor != null && focusedColor !== "" ? { focusedColor } : {}),
|
|
45
|
+
...(defaultOverride?.borderRadius !== undefined
|
|
46
|
+
? { cornerRadius: defaultOverride.borderRadius }
|
|
47
|
+
: {}),
|
|
48
|
+
...(defaultOverride?.paddingTop !== undefined
|
|
49
|
+
? { paddingTop: defaultOverride.paddingTop }
|
|
50
|
+
: {}),
|
|
51
|
+
...(defaultOverride?.paddingRight !== undefined
|
|
52
|
+
? { paddingRight: defaultOverride.paddingRight }
|
|
53
|
+
: {}),
|
|
54
|
+
...(defaultOverride?.paddingBottom !== undefined
|
|
55
|
+
? { paddingBottom: defaultOverride.paddingBottom }
|
|
56
|
+
: {}),
|
|
57
|
+
...(defaultOverride?.paddingLeft !== undefined
|
|
58
|
+
? { paddingLeft: defaultOverride.paddingLeft }
|
|
59
|
+
: {}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function mergeItemImage(
|
|
64
|
+
base: ItemImageConfiguration,
|
|
65
|
+
override?: Record<string, any>
|
|
66
|
+
): ItemImageConfiguration {
|
|
67
|
+
if (!override) return base;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
...base,
|
|
71
|
+
...(override.width !== undefined ? { width: override.width } : {}),
|
|
72
|
+
...(override.height !== undefined ? { height: override.height } : {}),
|
|
73
|
+
...(override.borderRadius !== undefined
|
|
74
|
+
? { cornerRadius: override.borderRadius }
|
|
75
|
+
: {}),
|
|
76
|
+
...(override.borderWidth !== undefined
|
|
77
|
+
? { borderWidth: override.borderWidth }
|
|
78
|
+
: {}),
|
|
79
|
+
...(override.borderColor ? { borderColor: override.borderColor } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function mergeItemLabel(
|
|
84
|
+
base: ItemLabelConfiguration,
|
|
85
|
+
override?: Record<string, any>
|
|
86
|
+
): ItemLabelConfiguration {
|
|
87
|
+
if (!override) return base;
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...base,
|
|
91
|
+
...(override.fontColor ? { fontColor: override.fontColor } : {}),
|
|
92
|
+
...(override.fontSize !== undefined ? { fontSize: override.fontSize } : {}),
|
|
93
|
+
...(override.lineHeight !== undefined
|
|
94
|
+
? { lineHeight: override.lineHeight }
|
|
95
|
+
: {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function mergeItemNowPlayingAsset(
|
|
100
|
+
base: ItemNowPlayingAssetConfiguration,
|
|
101
|
+
override?: Record<string, any>
|
|
102
|
+
): ItemNowPlayingAssetConfiguration {
|
|
103
|
+
if (!override) return base;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
...base,
|
|
107
|
+
...(override.asset ? { asset: override.asset } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function mergeItemCheckboxAssets(
|
|
112
|
+
base: ItemCheckboxAssetsConfiguration,
|
|
113
|
+
override?: Record<string, any>
|
|
114
|
+
): ItemCheckboxAssetsConfiguration {
|
|
115
|
+
if (!override) return base;
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
...base,
|
|
119
|
+
...(override.asset ? { asset: override.asset } : {}),
|
|
120
|
+
...(override.checkedAsset ? { checkedAsset: override.checkedAsset } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function mergeItemActionButtonAssets(
|
|
125
|
+
base: ItemActionButtonAssetsConfiguration,
|
|
126
|
+
override?: Record<string, any>
|
|
127
|
+
): ItemActionButtonAssetsConfiguration {
|
|
128
|
+
if (!override) return base;
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
...base,
|
|
132
|
+
...(override.removeAsset ? { removeAsset: override.removeAsset } : {}),
|
|
133
|
+
...(override.dragAsset ? { dragAsset: override.dragAsset } : {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function mergeItemConfiguration(
|
|
138
|
+
base: ItemConfiguration,
|
|
139
|
+
itemBlock?: Record<string, any>
|
|
140
|
+
): ItemConfiguration {
|
|
141
|
+
if (!itemBlock) return base;
|
|
142
|
+
|
|
143
|
+
const bgDefault = itemBlock.background?.default || itemBlock.background;
|
|
144
|
+
|
|
145
|
+
const bgFocused =
|
|
146
|
+
itemBlock.background?.focused ||
|
|
147
|
+
itemBlock.background?.focus ||
|
|
148
|
+
itemBlock.background?.pressed;
|
|
149
|
+
|
|
150
|
+
const img = itemBlock.image?.default || itemBlock.image;
|
|
151
|
+
const label1 = itemBlock.textLabel1?.default || itemBlock.textLabel1;
|
|
152
|
+
const label2 = itemBlock.textLabel2?.default || itemBlock.textLabel2;
|
|
153
|
+
|
|
154
|
+
const npLabel =
|
|
155
|
+
itemBlock.nowPlayingLabel?.default || itemBlock.nowPlayingLabel;
|
|
156
|
+
|
|
157
|
+
const npAsset =
|
|
158
|
+
itemBlock.nowPlayingAsset?.default || itemBlock.nowPlayingAsset;
|
|
159
|
+
|
|
160
|
+
const checkbox = itemBlock.checkbox?.default || itemBlock.checkbox;
|
|
161
|
+
|
|
162
|
+
const actionButtons =
|
|
163
|
+
itemBlock.actionButtons?.default || itemBlock.actionButtons;
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
...base,
|
|
167
|
+
background: mergeItemBackground(base.background, bgDefault, bgFocused),
|
|
168
|
+
image: mergeItemImage(base.image, img),
|
|
169
|
+
textLabel1: mergeItemLabel(base.textLabel1, label1),
|
|
170
|
+
textLabel2: mergeItemLabel(base.textLabel2, label2),
|
|
171
|
+
nowPlayingLabel: mergeItemLabel(base.nowPlayingLabel, npLabel),
|
|
172
|
+
nowPlayingAsset: mergeItemNowPlayingAsset(base.nowPlayingAsset, npAsset),
|
|
173
|
+
checkboxAssets: mergeItemCheckboxAssets(
|
|
174
|
+
base.checkboxAssets || {},
|
|
175
|
+
checkbox
|
|
176
|
+
),
|
|
177
|
+
actionButtonAssets: mergeItemActionButtonAssets(
|
|
178
|
+
base.actionButtonAssets || {},
|
|
179
|
+
actionButtons
|
|
180
|
+
),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Header Merging Helpers
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
function mergeHeaderTitle(
|
|
189
|
+
base: HeaderTitleConfiguration,
|
|
190
|
+
override?: Record<string, any>
|
|
191
|
+
): HeaderTitleConfiguration {
|
|
192
|
+
if (!override) return base;
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
...base,
|
|
196
|
+
...(override.fontColor ? { fontColor: override.fontColor } : {}),
|
|
197
|
+
...(override.fontSize !== undefined ? { fontSize: override.fontSize } : {}),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function mergeHeaderConfiguration(
|
|
202
|
+
base: HeaderConfiguration,
|
|
203
|
+
headerBlock?: Record<string, any>
|
|
204
|
+
): HeaderConfiguration & { backgroundColor?: string } {
|
|
205
|
+
if (!headerBlock) return base;
|
|
206
|
+
|
|
207
|
+
const bgOverride = headerBlock.container?.default?.backgroundColor;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
...base,
|
|
211
|
+
title: mergeHeaderTitle(base.title, headerBlock.title?.default),
|
|
212
|
+
...(bgOverride ? { backgroundColor: bgOverride } : {}),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// Button Merging Helpers
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
function mergeButtonBackground(
|
|
221
|
+
base: ButtonBackgroundConfiguration,
|
|
222
|
+
defaultOverride?: Record<string, any>,
|
|
223
|
+
focusedOverride?: Record<string, any>
|
|
224
|
+
): ButtonBackgroundConfiguration {
|
|
225
|
+
if (!defaultOverride && !focusedOverride) return base;
|
|
226
|
+
|
|
227
|
+
// Same as mergeItemBackground: do not use default background as focusedColor.
|
|
228
|
+
const focusedColor =
|
|
229
|
+
focusedOverride?.backgroundColor ??
|
|
230
|
+
defaultOverride?.focusColor ??
|
|
231
|
+
defaultOverride?.focusBackgroundColor;
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
...base,
|
|
235
|
+
...(defaultOverride?.backgroundColor
|
|
236
|
+
? { defaultColor: defaultOverride.backgroundColor }
|
|
237
|
+
: {}),
|
|
238
|
+
...(focusedColor != null && focusedColor !== "" ? { focusedColor } : {}),
|
|
239
|
+
...(defaultOverride?.borderRadius !== undefined
|
|
240
|
+
? { cornerRadius: defaultOverride.borderRadius }
|
|
241
|
+
: {}),
|
|
242
|
+
...(defaultOverride?.paddingTop !== undefined
|
|
243
|
+
? { paddingTop: defaultOverride.paddingTop }
|
|
244
|
+
: {}),
|
|
245
|
+
...(defaultOverride?.paddingRight !== undefined
|
|
246
|
+
? { paddingRight: defaultOverride.paddingRight }
|
|
247
|
+
: {}),
|
|
248
|
+
...(defaultOverride?.paddingBottom !== undefined
|
|
249
|
+
? { paddingBottom: defaultOverride.paddingBottom }
|
|
250
|
+
: {}),
|
|
251
|
+
...(defaultOverride?.paddingLeft !== undefined
|
|
252
|
+
? { paddingLeft: defaultOverride.paddingLeft }
|
|
253
|
+
: {}),
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function mergeButtonTitle(
|
|
258
|
+
base: ButtonTitleConfiguration,
|
|
259
|
+
override?: Record<string, any>
|
|
260
|
+
): ButtonTitleConfiguration {
|
|
261
|
+
if (!override) return base;
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
...base,
|
|
265
|
+
...(override.fontColor ? { fontColor: override.fontColor } : {}),
|
|
266
|
+
...(override.fontSize !== undefined ? { fontSize: override.fontSize } : {}),
|
|
267
|
+
...(override.lineHeight !== undefined
|
|
268
|
+
? { lineHeight: override.lineHeight }
|
|
269
|
+
: {}),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function mergeButtonIcons(
|
|
274
|
+
base: ButtonConfiguration["icons"],
|
|
275
|
+
iconsOverride?: Record<string, any>,
|
|
276
|
+
leadingOverride?: Record<string, any>
|
|
277
|
+
): ButtonConfiguration["icons"] {
|
|
278
|
+
if (!iconsOverride && !leadingOverride) return base;
|
|
279
|
+
|
|
280
|
+
const iconsDefault = iconsOverride?.default || iconsOverride;
|
|
281
|
+
const leadingDefault = leadingOverride?.default || leadingOverride;
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
...base,
|
|
285
|
+
...(iconsDefault?.width !== undefined ? { width: iconsDefault.width } : {}),
|
|
286
|
+
...(iconsDefault?.height !== undefined
|
|
287
|
+
? { height: iconsDefault.height }
|
|
288
|
+
: {}),
|
|
289
|
+
leadingIcon: {
|
|
290
|
+
...base.leadingIcon,
|
|
291
|
+
...(leadingDefault?.enabled !== undefined
|
|
292
|
+
? { enabled: leadingDefault.enabled }
|
|
293
|
+
: {}),
|
|
294
|
+
...(leadingDefault?.actionType
|
|
295
|
+
? { actionType: leadingDefault.actionType }
|
|
296
|
+
: {}),
|
|
297
|
+
...(leadingDefault?.asset ? { asset: leadingDefault.asset } : {}),
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function mergeButtonConfiguration(
|
|
303
|
+
base: ButtonConfiguration,
|
|
304
|
+
buttonBlock?: Record<string, any>,
|
|
305
|
+
actionBlock?: Record<string, any>
|
|
306
|
+
): ButtonConfiguration {
|
|
307
|
+
if (!buttonBlock && !actionBlock) return base;
|
|
308
|
+
|
|
309
|
+
const bgDefault =
|
|
310
|
+
buttonBlock?.background?.default || actionBlock?.background?.default;
|
|
311
|
+
|
|
312
|
+
const bgFocused =
|
|
313
|
+
buttonBlock?.background?.focused ||
|
|
314
|
+
buttonBlock?.background?.focus ||
|
|
315
|
+
actionBlock?.background?.focused ||
|
|
316
|
+
actionBlock?.background?.focus ||
|
|
317
|
+
buttonBlock?.background?.pressed ||
|
|
318
|
+
actionBlock?.background?.pressed;
|
|
319
|
+
|
|
320
|
+
const titleOverride =
|
|
321
|
+
buttonBlock?.title?.default || actionBlock?.title?.default;
|
|
322
|
+
|
|
323
|
+
const iconsOverride = buttonBlock?.icons || actionBlock?.icons;
|
|
324
|
+
const leadingOverride = buttonBlock?.leadingIcon || actionBlock?.leadingIcon;
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
...base,
|
|
328
|
+
background: mergeButtonBackground(base.background, bgDefault, bgFocused),
|
|
329
|
+
title: mergeButtonTitle(base.title, titleOverride),
|
|
330
|
+
icons: mergeButtonIcons(base.icons, iconsOverride, leadingOverride),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PanGestureHandler } from "react-native-gesture-handler";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import { useBottomSheet } from "@applicaster/zapp-react-native-utils/bottomSheet";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Turns its children into a drag zone for the enclosing DraggableBottomSheet:
|
|
8
|
+
* dragging here moves/dismisses the sheet (used for the modal header bar).
|
|
9
|
+
* Outside a sheet (no context) it renders children unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export const BottomSheetDragArea = ({
|
|
12
|
+
children,
|
|
13
|
+
}: {
|
|
14
|
+
children: React.ReactElement;
|
|
15
|
+
}) => {
|
|
16
|
+
const sheet = useBottomSheet();
|
|
17
|
+
|
|
18
|
+
// Outside a sheet (no context) render children unchanged.
|
|
19
|
+
if (!sheet) return children;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<PanGestureHandler
|
|
23
|
+
simultaneousHandlers={[sheet.masterDrawerRef]}
|
|
24
|
+
shouldCancelWhenOutside={false}
|
|
25
|
+
onGestureEvent={sheet.onDragGestureEvent}
|
|
26
|
+
onHandlerStateChange={sheet.onDragStateChange}
|
|
27
|
+
>
|
|
28
|
+
{/* Old-API PanGestureHandler needs a host-component child; children may
|
|
29
|
+
be a function component (e.g. ModalHeader), so wrap in a View. */}
|
|
30
|
+
<View>{children}</View>
|
|
31
|
+
</PanGestureHandler>
|
|
32
|
+
);
|
|
33
|
+
};
|