@applicaster/zapp-react-native-ui-components 16.0.0-rc.56 → 16.0.0-rc.58
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/ModalComponent/AudioPlayer/Components/Action.tsx +0 -18
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +17 -24
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +25 -73
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +0 -18
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +75 -79
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +89 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +3 -1
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +199 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +382 -61
- 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/TextInputContent.tsx +100 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +238 -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 +76 -20
- package/package.json +6 -5
- /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
|
@@ -25,24 +25,6 @@ const styles = StyleSheet.create({
|
|
|
25
25
|
flexDirection: "row",
|
|
26
26
|
alignItems: "center",
|
|
27
27
|
},
|
|
28
|
-
demoScreen: {
|
|
29
|
-
flex: 1,
|
|
30
|
-
backgroundColor: "#1C1C1E",
|
|
31
|
-
paddingTop: 64,
|
|
32
|
-
paddingHorizontal: 16,
|
|
33
|
-
gap: 12,
|
|
34
|
-
},
|
|
35
|
-
demoHeading: {
|
|
36
|
-
color: "#FFFFFF",
|
|
37
|
-
fontSize: 20,
|
|
38
|
-
fontWeight: "600",
|
|
39
|
-
marginBottom: 8,
|
|
40
|
-
},
|
|
41
|
-
demoLabel: {
|
|
42
|
-
color: "#999999",
|
|
43
|
-
fontSize: 13,
|
|
44
|
-
marginTop: 8,
|
|
45
|
-
},
|
|
46
28
|
});
|
|
47
29
|
|
|
48
30
|
// ---------------------------------------------------------------------------
|
|
@@ -32,24 +32,6 @@ const styles = StyleSheet.create({
|
|
|
32
32
|
title: {
|
|
33
33
|
textAlign: "center",
|
|
34
34
|
},
|
|
35
|
-
demoScreen: {
|
|
36
|
-
flex: 1,
|
|
37
|
-
backgroundColor: "#1C1C1E",
|
|
38
|
-
paddingTop: 64,
|
|
39
|
-
paddingHorizontal: 24,
|
|
40
|
-
gap: 12,
|
|
41
|
-
},
|
|
42
|
-
demoHeading: {
|
|
43
|
-
color: "#FFFFFF",
|
|
44
|
-
fontSize: 20,
|
|
45
|
-
fontWeight: "600",
|
|
46
|
-
marginBottom: 8,
|
|
47
|
-
},
|
|
48
|
-
demoLabel: {
|
|
49
|
-
color: "#999999",
|
|
50
|
-
fontSize: 13,
|
|
51
|
-
marginTop: 8,
|
|
52
|
-
},
|
|
53
35
|
});
|
|
54
36
|
|
|
55
37
|
// ---------------------------------------------------------------------------
|
|
@@ -310,6 +292,9 @@ function ButtonLeadingIcon({
|
|
|
310
292
|
// AudioPlayerButton — single building block driven by configuration + data
|
|
311
293
|
// ---------------------------------------------------------------------------
|
|
312
294
|
|
|
295
|
+
import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
|
|
296
|
+
import { mergeButtonConfiguration } from "../utils/mergeStyles";
|
|
297
|
+
|
|
313
298
|
export function AudioPlayerButton({
|
|
314
299
|
configuration,
|
|
315
300
|
data,
|
|
@@ -317,11 +302,19 @@ export function AudioPlayerButton({
|
|
|
317
302
|
focused = false,
|
|
318
303
|
onPress,
|
|
319
304
|
}: AudioPlayerButtonProps) {
|
|
305
|
+
const modalBlocksStyle = useModalBlocksStyle();
|
|
306
|
+
const buttonBlock = modalBlocksStyle?.button;
|
|
307
|
+
const actionBlock = modalBlocksStyle?.action;
|
|
308
|
+
|
|
309
|
+
const effectiveConfig = React.useMemo(() => {
|
|
310
|
+
return mergeButtonConfiguration(configuration, buttonBlock, actionBlock);
|
|
311
|
+
}, [configuration, buttonBlock, actionBlock]);
|
|
312
|
+
|
|
320
313
|
const {
|
|
321
|
-
background:
|
|
322
|
-
title:
|
|
314
|
+
background: effectiveBgConfig,
|
|
315
|
+
title: effectiveTitleConfig,
|
|
323
316
|
icons: iconsConfiguration,
|
|
324
|
-
} =
|
|
317
|
+
} = effectiveConfig;
|
|
325
318
|
|
|
326
319
|
const { title } = data;
|
|
327
320
|
const isDisabled = disabled ?? data.disabled ?? false;
|
|
@@ -333,7 +326,7 @@ export function AudioPlayerButton({
|
|
|
333
326
|
<ButtonContainer
|
|
334
327
|
focused={focused}
|
|
335
328
|
disabled={isDisabled}
|
|
336
|
-
configuration={
|
|
329
|
+
configuration={effectiveBgConfig}
|
|
337
330
|
onPress={onPress}
|
|
338
331
|
>
|
|
339
332
|
<View style={styles.contentRow}>
|
|
@@ -343,14 +336,14 @@ export function AudioPlayerButton({
|
|
|
343
336
|
actionType={leadingIcon.actionType}
|
|
344
337
|
configuration={iconsConfiguration}
|
|
345
338
|
/>
|
|
346
|
-
<View style={{ width:
|
|
339
|
+
<View style={{ width: effectiveBgConfig.horizontalGutter }} />
|
|
347
340
|
</>
|
|
348
341
|
) : null}
|
|
349
342
|
<ButtonTitle
|
|
350
343
|
title={title}
|
|
351
344
|
focused={focused}
|
|
352
345
|
disabled={isDisabled}
|
|
353
|
-
configuration={
|
|
346
|
+
configuration={effectiveTitleConfig}
|
|
354
347
|
/>
|
|
355
348
|
</View>
|
|
356
349
|
</ButtonContainer>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import React, { useCallback, useState } from "react";
|
|
12
12
|
import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
|
|
13
|
+
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
13
14
|
|
|
14
15
|
const styles = StyleSheet.create({
|
|
15
16
|
row: {
|
|
@@ -45,29 +46,6 @@ const styles = StyleSheet.create({
|
|
|
45
46
|
alignItems: "center",
|
|
46
47
|
justifyContent: "center",
|
|
47
48
|
},
|
|
48
|
-
demoScreen: {
|
|
49
|
-
flex: 1,
|
|
50
|
-
backgroundColor: "#1C1C1E",
|
|
51
|
-
paddingTop: 64,
|
|
52
|
-
paddingHorizontal: 0,
|
|
53
|
-
gap: 24,
|
|
54
|
-
},
|
|
55
|
-
demoBlock: {
|
|
56
|
-
gap: 8,
|
|
57
|
-
paddingHorizontal: 0,
|
|
58
|
-
},
|
|
59
|
-
demoHeading: {
|
|
60
|
-
color: "#FFFFFF",
|
|
61
|
-
fontSize: 20,
|
|
62
|
-
fontWeight: "600",
|
|
63
|
-
marginBottom: 8,
|
|
64
|
-
paddingHorizontal: 24,
|
|
65
|
-
},
|
|
66
|
-
demoLabel: {
|
|
67
|
-
color: "#999999",
|
|
68
|
-
fontSize: 13,
|
|
69
|
-
paddingHorizontal: 24,
|
|
70
|
-
},
|
|
71
49
|
});
|
|
72
50
|
|
|
73
51
|
// ---------------------------------------------------------------------------
|
|
@@ -123,7 +101,7 @@ export type ActionButtonsStyleConfiguration = {
|
|
|
123
101
|
};
|
|
124
102
|
|
|
125
103
|
export type HeaderConfiguration = {
|
|
126
|
-
paddingTop
|
|
104
|
+
paddingTop?: number;
|
|
127
105
|
variant: HeaderVariant;
|
|
128
106
|
title: TitleConfiguration;
|
|
129
107
|
closeButton: CloseButtonConfiguration;
|
|
@@ -305,6 +283,8 @@ type HeaderContainerProps = {
|
|
|
305
283
|
children: React.ReactNode;
|
|
306
284
|
};
|
|
307
285
|
|
|
286
|
+
const TRANSPARENT = "transparent";
|
|
287
|
+
|
|
308
288
|
function HeaderContainer({
|
|
309
289
|
variant,
|
|
310
290
|
configuration,
|
|
@@ -317,6 +297,8 @@ function HeaderContainer({
|
|
|
317
297
|
? configuration.paddingTop
|
|
318
298
|
: container.paddingTop;
|
|
319
299
|
|
|
300
|
+
const backgroundColor = (configuration as any).backgroundColor || TRANSPARENT;
|
|
301
|
+
|
|
320
302
|
return (
|
|
321
303
|
<View
|
|
322
304
|
style={{
|
|
@@ -324,6 +306,7 @@ function HeaderContainer({
|
|
|
324
306
|
paddingRight: container.paddingRight,
|
|
325
307
|
paddingBottom: container.paddingBottom,
|
|
326
308
|
paddingLeft: container.paddingLeft,
|
|
309
|
+
backgroundColor: backgroundColor,
|
|
327
310
|
}}
|
|
328
311
|
>
|
|
329
312
|
{children}
|
|
@@ -338,17 +321,18 @@ type HeaderTitleProps = {
|
|
|
338
321
|
};
|
|
339
322
|
|
|
340
323
|
function HeaderTitle({ title, configuration, centered }: HeaderTitleProps) {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
324
|
+
const { fontColor, fontSize } = configuration;
|
|
325
|
+
|
|
326
|
+
const letterSpacing = isApplePlatform()
|
|
327
|
+
? configuration.iosLetterSpacing
|
|
328
|
+
: configuration.androidLetterSpacing;
|
|
345
329
|
|
|
346
330
|
return (
|
|
347
331
|
<Text
|
|
348
332
|
style={{
|
|
349
333
|
flex: centered ? undefined : 1,
|
|
350
|
-
color:
|
|
351
|
-
fontSize:
|
|
334
|
+
color: fontColor,
|
|
335
|
+
fontSize: fontSize,
|
|
352
336
|
lineHeight: configuration.lineHeight,
|
|
353
337
|
letterSpacing,
|
|
354
338
|
fontFamily: Platform.select({
|
|
@@ -610,6 +594,9 @@ function CenterAlignedHeaderLayout({
|
|
|
610
594
|
// AudioPlayerHeader
|
|
611
595
|
// ---------------------------------------------------------------------------
|
|
612
596
|
|
|
597
|
+
import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
|
|
598
|
+
import { mergeHeaderConfiguration } from "../utils/mergeStyles";
|
|
599
|
+
|
|
613
600
|
export function AudioPlayerHeader({
|
|
614
601
|
configuration,
|
|
615
602
|
data,
|
|
@@ -622,7 +609,14 @@ export function AudioPlayerHeader({
|
|
|
622
609
|
onDone,
|
|
623
610
|
onClear,
|
|
624
611
|
}: AudioPlayerHeaderProps) {
|
|
625
|
-
const
|
|
612
|
+
const modalBlocksStyle = useModalBlocksStyle();
|
|
613
|
+
const headerBlock = modalBlocksStyle?.header;
|
|
614
|
+
|
|
615
|
+
const effectiveConfig = React.useMemo(() => {
|
|
616
|
+
return mergeHeaderConfiguration(configuration, headerBlock);
|
|
617
|
+
}, [configuration, headerBlock]);
|
|
618
|
+
|
|
619
|
+
const { variant, title, closeButton, actionButtons } = effectiveConfig;
|
|
626
620
|
const { title: titleText } = data;
|
|
627
621
|
|
|
628
622
|
const container = CONTAINER_SPEC[variant];
|
|
@@ -767,45 +761,3 @@ export const VARIANT_SECTION_HEADER: HeaderConfiguration = {
|
|
|
767
761
|
closeButton: { enabled: false },
|
|
768
762
|
actionButtons: DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION,
|
|
769
763
|
};
|
|
770
|
-
|
|
771
|
-
export default function App() {
|
|
772
|
-
return (
|
|
773
|
-
<View style={styles.demoScreen}>
|
|
774
|
-
<Text style={styles.demoHeading}>Modal Header Building Block</Text>
|
|
775
|
-
|
|
776
|
-
<View style={styles.demoBlock}>
|
|
777
|
-
<Text style={styles.demoLabel}>Single Line Header</Text>
|
|
778
|
-
<AudioPlayerHeader
|
|
779
|
-
configuration={VARIANT_SINGLE_LINE_HEADER}
|
|
780
|
-
data={{ title: "Playback Speed" }}
|
|
781
|
-
/>
|
|
782
|
-
</View>
|
|
783
|
-
|
|
784
|
-
<View style={styles.demoBlock}>
|
|
785
|
-
<Text style={styles.demoLabel}>Header With Actions</Text>
|
|
786
|
-
<AudioPlayerHeader
|
|
787
|
-
configuration={VARIANT_HEADER_WITH_ACTIONS}
|
|
788
|
-
data={{ title: "Playback Speed" }}
|
|
789
|
-
/>
|
|
790
|
-
</View>
|
|
791
|
-
|
|
792
|
-
<View style={styles.demoBlock}>
|
|
793
|
-
<Text style={styles.demoLabel}>
|
|
794
|
-
Header With Actions — single side action
|
|
795
|
-
</Text>
|
|
796
|
-
<AudioPlayerHeader
|
|
797
|
-
configuration={VARIANT_HEADER_WITH_CANCEL_ONLY}
|
|
798
|
-
data={{ title: "Playback Speed" }}
|
|
799
|
-
/>
|
|
800
|
-
</View>
|
|
801
|
-
|
|
802
|
-
<View style={styles.demoBlock}>
|
|
803
|
-
<Text style={styles.demoLabel}>Section Header</Text>
|
|
804
|
-
<AudioPlayerHeader
|
|
805
|
-
configuration={VARIANT_SECTION_HEADER}
|
|
806
|
-
data={{ title: "Next in Queue" }}
|
|
807
|
-
/>
|
|
808
|
-
</View>
|
|
809
|
-
</View>
|
|
810
|
-
);
|
|
811
|
-
}
|
|
@@ -37,24 +37,6 @@ const styles = StyleSheet.create({
|
|
|
37
37
|
underline: {
|
|
38
38
|
alignSelf: "stretch",
|
|
39
39
|
},
|
|
40
|
-
demoScreen: {
|
|
41
|
-
flex: 1,
|
|
42
|
-
backgroundColor: "#1C1C1E",
|
|
43
|
-
paddingTop: 64,
|
|
44
|
-
paddingHorizontal: 16,
|
|
45
|
-
gap: 24,
|
|
46
|
-
},
|
|
47
|
-
demoHeading: {
|
|
48
|
-
color: "#FFFFFF",
|
|
49
|
-
fontSize: 20,
|
|
50
|
-
fontWeight: "600",
|
|
51
|
-
marginBottom: 8,
|
|
52
|
-
},
|
|
53
|
-
demoLabel: {
|
|
54
|
-
color: "#999999",
|
|
55
|
-
fontSize: 13,
|
|
56
|
-
marginTop: 8,
|
|
57
|
-
},
|
|
58
40
|
});
|
|
59
41
|
|
|
60
42
|
// ---------------------------------------------------------------------------
|
|
@@ -20,6 +20,12 @@ import {
|
|
|
20
20
|
Text,
|
|
21
21
|
View,
|
|
22
22
|
} from "react-native";
|
|
23
|
+
import {
|
|
24
|
+
MenuItem,
|
|
25
|
+
SelectionBehavior,
|
|
26
|
+
} from "@applicaster/zapp-react-native-utils/modalState/types";
|
|
27
|
+
import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
|
|
28
|
+
import { mergeItemConfiguration } from "../utils/mergeStyles";
|
|
23
29
|
|
|
24
30
|
const styles = StyleSheet.create({
|
|
25
31
|
row: {
|
|
@@ -34,25 +40,6 @@ const styles = StyleSheet.create({
|
|
|
34
40
|
flexDirection: "row",
|
|
35
41
|
alignItems: "center",
|
|
36
42
|
},
|
|
37
|
-
demoScreen: {
|
|
38
|
-
flex: 1,
|
|
39
|
-
backgroundColor: "#1C1C1E",
|
|
40
|
-
paddingTop: 64,
|
|
41
|
-
paddingHorizontal: 0,
|
|
42
|
-
gap: 16,
|
|
43
|
-
},
|
|
44
|
-
demoHeading: {
|
|
45
|
-
color: "#FFFFFF",
|
|
46
|
-
fontSize: 20,
|
|
47
|
-
fontWeight: "600",
|
|
48
|
-
marginBottom: 8,
|
|
49
|
-
paddingHorizontal: 16,
|
|
50
|
-
},
|
|
51
|
-
demoLabel: {
|
|
52
|
-
color: "#999999",
|
|
53
|
-
fontSize: 13,
|
|
54
|
-
paddingHorizontal: 16,
|
|
55
|
-
},
|
|
56
43
|
});
|
|
57
44
|
|
|
58
45
|
// ---------------------------------------------------------------------------
|
|
@@ -104,51 +91,6 @@ export type ItemConfiguration = {
|
|
|
104
91
|
textLabel2: LabelConfiguration;
|
|
105
92
|
};
|
|
106
93
|
|
|
107
|
-
export interface Action {
|
|
108
|
-
type: string;
|
|
109
|
-
payload?: any;
|
|
110
|
-
options?: any;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export interface Header {
|
|
114
|
-
title: string;
|
|
115
|
-
subtitle?: string;
|
|
116
|
-
icon?: string;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export interface Content {
|
|
120
|
-
title: string;
|
|
121
|
-
stickyTitle?: boolean;
|
|
122
|
-
items: MenuItem[];
|
|
123
|
-
itemsUrl?: string;
|
|
124
|
-
action?: Action;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export interface Menu {
|
|
128
|
-
header?: Header;
|
|
129
|
-
content: Content;
|
|
130
|
-
closeButton?: boolean;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface PresentSubMenuAction extends Action {
|
|
134
|
-
menu: Menu;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export interface SecondaryAction {
|
|
138
|
-
action: Action | PresentSubMenuAction;
|
|
139
|
-
icon?: string;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface MenuItem {
|
|
143
|
-
trailingButton?: ItemButtonAction;
|
|
144
|
-
title: string;
|
|
145
|
-
summary?: string;
|
|
146
|
-
icon?: string;
|
|
147
|
-
isSelected?: boolean;
|
|
148
|
-
action?: Action | PresentSubMenuAction;
|
|
149
|
-
secondaryAction?: SecondaryAction;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
94
|
export const DEFAULT_TEXT_LABEL_1_CONFIGURATION: LabelConfiguration = {
|
|
153
95
|
fontColor: "#FFFFFF",
|
|
154
96
|
focusedFontColor: "#FFFFFF",
|
|
@@ -211,6 +153,7 @@ export type AudioPlayerTrackItemProps = {
|
|
|
211
153
|
onPress?: () => void;
|
|
212
154
|
onLeadingButtonPress?: () => void;
|
|
213
155
|
onTrailingButtonPress?: () => void;
|
|
156
|
+
renderHandle?: (element: React.ReactElement) => React.ReactElement;
|
|
214
157
|
};
|
|
215
158
|
|
|
216
159
|
// ---------------------------------------------------------------------------
|
|
@@ -326,14 +269,17 @@ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
|
|
|
326
269
|
: CONTAINER_SPEC_DEFAULT;
|
|
327
270
|
}
|
|
328
271
|
|
|
329
|
-
export function mapMenuItemToNowPlayingData(
|
|
272
|
+
export function mapMenuItemToNowPlayingData(
|
|
273
|
+
item: MenuItem,
|
|
274
|
+
isPaused?: boolean
|
|
275
|
+
): ItemData {
|
|
330
276
|
return {
|
|
331
277
|
imageUri: item.icon,
|
|
332
278
|
textLabel1: item.title,
|
|
333
279
|
textLabel2: item.summary ?? "",
|
|
334
280
|
nowPlayingLabel: "Now Playing",
|
|
335
281
|
trailingButton: "playPause",
|
|
336
|
-
isSelected: item.isSelected,
|
|
282
|
+
isSelected: isPaused !== undefined ? !isPaused : item.isSelected,
|
|
337
283
|
};
|
|
338
284
|
}
|
|
339
285
|
|
|
@@ -348,12 +294,20 @@ export function mapMenuItemToQueuedData(item: MenuItem): ItemData {
|
|
|
348
294
|
};
|
|
349
295
|
}
|
|
350
296
|
|
|
351
|
-
export function mapMenuItemToPlaylistData(
|
|
297
|
+
export function mapMenuItemToPlaylistData(
|
|
298
|
+
item: MenuItem,
|
|
299
|
+
behavior?: SelectionBehavior
|
|
300
|
+
): ItemData {
|
|
301
|
+
const showSelectionWidget =
|
|
302
|
+
behavior?.selectMode !== "none" && item.trailingButton !== "none";
|
|
303
|
+
|
|
352
304
|
return {
|
|
353
305
|
imageUri: item.icon,
|
|
354
306
|
textLabel1: item.title,
|
|
355
307
|
textLabel2: item.summary ?? "",
|
|
356
|
-
trailingButton:
|
|
308
|
+
trailingButton: showSelectionWidget
|
|
309
|
+
? item.trailingButton || ("multiSelect" as ItemButtonAction)
|
|
310
|
+
: undefined,
|
|
357
311
|
isSelected: item.isSelected,
|
|
358
312
|
};
|
|
359
313
|
}
|
|
@@ -506,6 +460,10 @@ function ItemButton({
|
|
|
506
460
|
}: ItemButtonProps) {
|
|
507
461
|
const layout = BUTTON_LAYOUT_SPEC[action];
|
|
508
462
|
|
|
463
|
+
if (!layout) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
|
|
509
467
|
const renderIcon = () => {
|
|
510
468
|
switch (action) {
|
|
511
469
|
case "playPause": {
|
|
@@ -680,7 +638,10 @@ function ItemButton({
|
|
|
680
638
|
|
|
681
639
|
return (
|
|
682
640
|
<Pressable
|
|
683
|
-
onPress={
|
|
641
|
+
onPress={(e: any) => {
|
|
642
|
+
e?.stopPropagation?.();
|
|
643
|
+
onPress?.();
|
|
644
|
+
}}
|
|
684
645
|
style={({ pressed }) => [
|
|
685
646
|
{
|
|
686
647
|
paddingTop: layout.paddingTop,
|
|
@@ -704,6 +665,7 @@ type ItemButtonsProps = {
|
|
|
704
665
|
onLeadingButtonPress?: () => void;
|
|
705
666
|
onTrailingButtonPress?: () => void;
|
|
706
667
|
isSelected?: boolean;
|
|
668
|
+
renderHandle?: (element: React.ReactElement) => React.ReactElement;
|
|
707
669
|
};
|
|
708
670
|
|
|
709
671
|
function ItemButtons({
|
|
@@ -714,6 +676,7 @@ function ItemButtons({
|
|
|
714
676
|
onLeadingButtonPress,
|
|
715
677
|
onTrailingButtonPress,
|
|
716
678
|
isSelected = false,
|
|
679
|
+
renderHandle,
|
|
717
680
|
}: ItemButtonsProps) {
|
|
718
681
|
if (!leadingButton && !trailingButton) {
|
|
719
682
|
return null;
|
|
@@ -733,12 +696,32 @@ function ItemButtons({
|
|
|
733
696
|
</>
|
|
734
697
|
) : null}
|
|
735
698
|
{trailingButton ? (
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
699
|
+
trailingButton === "drag" ? (
|
|
700
|
+
renderHandle ? (
|
|
701
|
+
renderHandle(
|
|
702
|
+
<ItemButton
|
|
703
|
+
action={trailingButton}
|
|
704
|
+
focused={focusedTrailingButton}
|
|
705
|
+
onPress={onTrailingButtonPress}
|
|
706
|
+
isSelected={isSelected}
|
|
707
|
+
/>
|
|
708
|
+
)
|
|
709
|
+
) : (
|
|
710
|
+
<ItemButton
|
|
711
|
+
action={trailingButton}
|
|
712
|
+
focused={focusedTrailingButton}
|
|
713
|
+
onPress={onTrailingButtonPress}
|
|
714
|
+
isSelected={isSelected}
|
|
715
|
+
/>
|
|
716
|
+
)
|
|
717
|
+
) : (
|
|
718
|
+
<ItemButton
|
|
719
|
+
action={trailingButton}
|
|
720
|
+
focused={focusedTrailingButton}
|
|
721
|
+
onPress={onTrailingButtonPress}
|
|
722
|
+
isSelected={isSelected}
|
|
723
|
+
/>
|
|
724
|
+
)
|
|
742
725
|
) : null}
|
|
743
726
|
</View>
|
|
744
727
|
);
|
|
@@ -757,18 +740,30 @@ export function AudioPlayerTrackItem({
|
|
|
757
740
|
onPress,
|
|
758
741
|
onLeadingButtonPress,
|
|
759
742
|
onTrailingButtonPress,
|
|
743
|
+
renderHandle,
|
|
760
744
|
}: AudioPlayerTrackItemProps) {
|
|
745
|
+
const modalBlocksStyle = useModalBlocksStyle();
|
|
746
|
+
const showNowPlaying = !!data.nowPlayingLabel;
|
|
747
|
+
|
|
748
|
+
const itemBlock =
|
|
749
|
+
showNowPlaying && modalBlocksStyle?.nowPlayingItem
|
|
750
|
+
? modalBlocksStyle.nowPlayingItem
|
|
751
|
+
: modalBlocksStyle?.item;
|
|
752
|
+
|
|
753
|
+
const effectiveConfig = React.useMemo(() => {
|
|
754
|
+
return mergeItemConfiguration(configuration, itemBlock);
|
|
755
|
+
}, [configuration, itemBlock]);
|
|
756
|
+
|
|
761
757
|
const {
|
|
762
758
|
background,
|
|
763
759
|
image,
|
|
764
|
-
nowPlayingAsset,
|
|
765
|
-
nowPlayingLabel,
|
|
766
760
|
textLabel1,
|
|
767
761
|
textLabel2,
|
|
768
|
-
|
|
762
|
+
nowPlayingLabel,
|
|
763
|
+
nowPlayingAsset,
|
|
764
|
+
} = effectiveConfig;
|
|
769
765
|
|
|
770
766
|
const container = resolveContainerSpec(image);
|
|
771
|
-
const showNowPlaying = !!data.nowPlayingLabel;
|
|
772
767
|
|
|
773
768
|
return (
|
|
774
769
|
<ItemContainer
|
|
@@ -807,6 +802,7 @@ export function AudioPlayerTrackItem({
|
|
|
807
802
|
onLeadingButtonPress={onLeadingButtonPress}
|
|
808
803
|
onTrailingButtonPress={onTrailingButtonPress}
|
|
809
804
|
isSelected={data.isSelected}
|
|
805
|
+
renderHandle={renderHandle}
|
|
810
806
|
/>
|
|
811
807
|
</View>
|
|
812
808
|
</ItemContainer>
|
|
@@ -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
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export { AudioPlayerAction } from "./Action";
|
|
2
2
|
|
|
3
|
-
export { AudioPlayerButton } from "./Button";
|
|
3
|
+
export { AudioPlayerButton, VARIANT_WITH_ICON } from "./Button";
|
|
4
4
|
|
|
5
5
|
export { AudioPlayerHeader } from "./Header";
|
|
6
6
|
|
|
7
7
|
export { AudioPlayerTrackItem } from "./Item";
|
|
8
8
|
|
|
9
|
+
export { NowPlayingHeaderSection } from "./NowPlayingHeaderSection";
|
|
10
|
+
|
|
9
11
|
export { AudioPlayerInput } from "./Input";
|