@applicaster/zapp-react-native-ui-components 16.0.0-rc.57 → 16.0.0-rc.59

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.
Files changed (29) hide show
  1. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -18
  2. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +17 -24
  3. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +25 -73
  4. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +0 -18
  5. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +75 -79
  6. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +89 -0
  7. package/Components/ModalComponent/AudioPlayer/Components/index.ts +3 -1
  8. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +199 -0
  9. package/Components/ModalComponent/BottomSheetModalContent.tsx +382 -61
  10. package/Components/ModalComponent/Button/index.tsx +5 -3
  11. package/Components/ModalComponent/Header/index.tsx +20 -7
  12. package/Components/ModalComponent/Header/utils.ts +1 -1
  13. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  14. package/Components/ModalComponent/SortableList.tsx +142 -0
  15. package/Components/ModalComponent/TextInputContent.tsx +100 -0
  16. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +238 -0
  17. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +120 -0
  18. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  19. package/Components/ModalComponent/index.tsx +11 -0
  20. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +66 -0
  21. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  22. package/Components/ModalComponent/presets/index.ts +27 -0
  23. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  24. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  25. package/Components/ModalComponent/presets/types.ts +35 -0
  26. package/Components/ModalComponent/showTextInput.ts +43 -0
  27. package/Components/ModalComponent/utils.ts +76 -20
  28. package/package.json +6 -5
  29. /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: backgroundConfiguration,
322
- title: titleConfiguration,
314
+ background: effectiveBgConfig,
315
+ title: effectiveTitleConfig,
323
316
  icons: iconsConfiguration,
324
- } = configuration;
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={backgroundConfiguration}
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: backgroundConfiguration.horizontalGutter }} />
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={titleConfiguration}
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: undefined;
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 letterSpacing =
342
- Platform.OS === "ios"
343
- ? configuration.iosLetterSpacing
344
- : configuration.androidLetterSpacing;
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: configuration.fontColor,
351
- fontSize: configuration.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 { variant, title, closeButton, actionButtons } = configuration;
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(item: MenuItem): ItemData {
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(item: MenuItem): ItemData {
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: item.trailingButton || ("multiSelect" as ItemButtonAction),
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={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
- <ItemButton
737
- action={trailingButton}
738
- focused={focusedTrailingButton}
739
- onPress={onTrailingButtonPress}
740
- isSelected={isSelected}
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
- } = configuration;
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";