@applicaster/zapp-react-native-ui-components 15.1.12 → 15.1.13-alpha.1034774223

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 (36) hide show
  1. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +54 -0
  2. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +21 -0
  3. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  4. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +58 -0
  5. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -87
  6. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +57 -74
  7. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +142 -107
  8. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +653 -0
  9. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +410 -159
  10. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +89 -0
  11. package/Components/ModalComponent/AudioPlayer/Components/index.ts +8 -4
  12. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +230 -0
  13. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +332 -0
  14. package/Components/ModalComponent/BottomSheetModalContent.tsx +379 -61
  15. package/Components/ModalComponent/Button/index.tsx +5 -3
  16. package/Components/ModalComponent/Header/index.tsx +20 -7
  17. package/Components/ModalComponent/Header/utils.ts +1 -1
  18. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  19. package/Components/ModalComponent/SortableList.tsx +142 -0
  20. package/Components/ModalComponent/SortableList.web.tsx +102 -0
  21. package/Components/ModalComponent/TextInputContent.tsx +100 -0
  22. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +238 -0
  23. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +120 -0
  24. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  25. package/Components/ModalComponent/index.tsx +11 -0
  26. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +66 -0
  27. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  28. package/Components/ModalComponent/presets/index.ts +27 -0
  29. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  30. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  31. package/Components/ModalComponent/presets/types.ts +35 -0
  32. package/Components/ModalComponent/showTextInput.ts +43 -0
  33. package/Components/ModalComponent/utils.ts +82 -21
  34. package/Hooks/useEntryActionsExpander.ts +63 -0
  35. package/package.json +6 -5
  36. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -12,7 +12,20 @@
12
12
  */
13
13
 
14
14
  import React from "react";
15
- import { Image, Platform, StyleSheet, Text, View } from "react-native";
15
+ import {
16
+ Image,
17
+ Platform,
18
+ Pressable,
19
+ StyleSheet,
20
+ Text,
21
+ View,
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";
16
29
 
17
30
  const styles = StyleSheet.create({
18
31
  row: {
@@ -27,25 +40,6 @@ const styles = StyleSheet.create({
27
40
  flexDirection: "row",
28
41
  alignItems: "center",
29
42
  },
30
- demoScreen: {
31
- flex: 1,
32
- backgroundColor: "#1C1C1E",
33
- paddingTop: 64,
34
- paddingHorizontal: 0,
35
- gap: 16,
36
- },
37
- demoHeading: {
38
- color: "#FFFFFF",
39
- fontSize: 20,
40
- fontWeight: "600",
41
- marginBottom: 8,
42
- paddingHorizontal: 16,
43
- },
44
- demoLabel: {
45
- color: "#999999",
46
- fontSize: 13,
47
- paddingHorizontal: 16,
48
- },
49
43
  });
50
44
 
51
45
  // ---------------------------------------------------------------------------
@@ -62,6 +56,10 @@ export type BackgroundConfiguration = {
62
56
  defaultColor: string;
63
57
  focusedColor: string;
64
58
  cornerRadius: number;
59
+ paddingTop?: number;
60
+ paddingRight?: number;
61
+ paddingBottom?: number;
62
+ paddingLeft?: number;
65
63
  };
66
64
 
67
65
  export type ImageConfiguration = {
@@ -88,6 +86,20 @@ export type NowPlayingAssetConfiguration = {
88
86
  asset?: string;
89
87
  };
90
88
 
89
+ export type CheckboxAssetsConfiguration = {
90
+ /** Unchecked multiSelect control (item.checkbox_asset). */
91
+ asset?: string;
92
+ /** Checked multiSelect control (item.checked_checkbox_asset). */
93
+ checkedAsset?: string;
94
+ };
95
+
96
+ export type ActionButtonAssetsConfiguration = {
97
+ /** Leading remove control on Edit Order rows. */
98
+ removeAsset?: string;
99
+ /** Trailing drag handle on Edit Order / queue rows. */
100
+ dragAsset?: string;
101
+ };
102
+
91
103
  export type ItemConfiguration = {
92
104
  background: BackgroundConfiguration;
93
105
  image: ImageConfiguration;
@@ -95,52 +107,10 @@ export type ItemConfiguration = {
95
107
  nowPlayingLabel: LabelConfiguration;
96
108
  textLabel1: LabelConfiguration;
97
109
  textLabel2: LabelConfiguration;
110
+ checkboxAssets?: CheckboxAssetsConfiguration;
111
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
98
112
  };
99
113
 
100
- export interface Action {
101
- type: string;
102
- payload?: any;
103
- options?: any;
104
- }
105
-
106
- export interface Header {
107
- title: string;
108
- subtitle?: string;
109
- icon?: string;
110
- }
111
-
112
- export interface Content {
113
- title: string;
114
- stickyTitle?: boolean;
115
- items: MenuItem[];
116
- itemsUrl?: string;
117
- action?: Action;
118
- }
119
-
120
- export interface Menu {
121
- header?: Header;
122
- content: Content;
123
- closeButton?: boolean;
124
- }
125
-
126
- export interface PresentSubMenuAction extends Action {
127
- menu: Menu;
128
- }
129
-
130
- export interface SecondaryAction {
131
- action: Action | PresentSubMenuAction;
132
- icon?: string;
133
- }
134
-
135
- export interface MenuItem {
136
- title: string;
137
- summary?: string;
138
- icon?: string;
139
- isSelected?: boolean;
140
- action?: Action | PresentSubMenuAction;
141
- secondaryAction?: SecondaryAction;
142
- }
143
-
144
114
  export const DEFAULT_TEXT_LABEL_1_CONFIGURATION: LabelConfiguration = {
145
115
  fontColor: "#FFFFFF",
146
116
  focusedFontColor: "#FFFFFF",
@@ -191,14 +161,19 @@ export type ItemData = {
191
161
  leadingButton?: ItemButtonAction;
192
162
  /** Button 1 (right most) — hardcoded action decides icon. */
193
163
  trailingButton?: ItemButtonAction;
164
+ isSelected?: boolean;
194
165
  };
195
166
 
196
- export type ItemProps = {
167
+ export type AudioPlayerTrackItemProps = {
197
168
  configuration: ItemConfiguration;
198
169
  data: ItemData;
199
170
  focused?: boolean;
200
171
  focusedLeadingButton?: boolean;
201
172
  focusedTrailingButton?: boolean;
173
+ onPress?: () => void;
174
+ onLeadingButtonPress?: () => void;
175
+ onTrailingButtonPress?: () => void;
176
+ renderHandle?: (element: React.ReactElement) => React.ReactElement;
202
177
  };
203
178
 
204
179
  // ---------------------------------------------------------------------------
@@ -234,9 +209,9 @@ const CONTAINER_SPEC_LARGE_IMAGE: ContainerSpec = {
234
209
 
235
210
  const CONTAINER_SPEC_DEFAULT: ContainerSpec = {
236
211
  paddingTop: 8,
237
- paddingRight: 10,
212
+ paddingRight: 20,
238
213
  paddingBottom: 8,
239
- paddingLeft: 10,
214
+ paddingLeft: 20,
240
215
  horizontalGutter: 12,
241
216
  verticalGutter: 2,
242
217
  };
@@ -297,16 +272,6 @@ const DEFAULT_THUMB_ASSET =
297
272
  const DEFAULT_EQUALIZER_ASSET =
298
273
  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAI0lEQVR42mNgGNTgPxTQzwB0DVQ3gKCBowbQwYAhkBIHBAAA9TvPMeOFaLMAAAAASUVORK5CYII=";
299
274
 
300
- const BUTTON_ASSETS: Record<ItemButtonAction, string> = {
301
- playPause:
302
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAAdklEQVR42u3ZwRUAERAEUfkn3TaF5TFTrSuCf3CgjZGg6ast7G8YaAlcB8JAj8F1MRR2G63CUNglNAqsRvmB1TAfsBoXcMABV4MFKEci4IADNrkAed2H80R66SHqvUvglh/ktoZcL5H7MHKBx/5xYH+RsP90t5t2UpF6+22a+AAAAABJRU5ErkJggg==",
303
- drag: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAIklEQVR42mNgGAWjYGiC/2SCwWPBaByMxsFoHIyCUTDYAAAqhk7AVXdZJwAAAABJRU5ErkJggg==",
304
- multiSelect:
305
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAWklEQVR42t2VUQ4AIAhCvf+lrQsULJJZbH7GM1cS0Uk5dd0Qqcz4GJSCSs0pSCkAHNoWBfkTgIxpgGK+ujyEKABqRGyn9FO1A+yfrf+qsGxTSx5YEs2WyU9oAKBJKg8Kfq4dAAAAAElFTkSuQmCC",
306
- remove:
307
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAUklEQVR42mNgGPbgPx5AVYOJlSPJcGqrJU8DKXooCVui9FIacXj1UyNl4DWDLhYgc8nA+INpeFgwtCOZ5vmALjmZ5mURXUpTutQHdKnRRgEMAACwjWWprU1B+AAAAABJRU5ErkJggg==",
308
- };
309
-
310
275
  function getTextTransformStyle(
311
276
  textTransform: TextTransform
312
277
  ): "none" | "lowercase" | "uppercase" | "capitalize" {
@@ -324,32 +289,49 @@ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
324
289
  : CONTAINER_SPEC_DEFAULT;
325
290
  }
326
291
 
327
- function mapMenuItemToNowPlayingData(item: MenuItem): ItemData {
292
+ export function mapMenuItemToNowPlayingData(
293
+ item: MenuItem,
294
+ isPaused?: boolean
295
+ ): ItemData {
328
296
  return {
329
297
  imageUri: item.icon,
330
298
  textLabel1: item.title,
331
299
  textLabel2: item.summary ?? "",
332
300
  nowPlayingLabel: "Now Playing",
333
301
  trailingButton: "playPause",
302
+ isSelected: isPaused !== undefined ? !isPaused : item.isSelected,
334
303
  };
335
304
  }
336
305
 
337
- function mapMenuItemToQueuedData(item: MenuItem): ItemData {
306
+ export function mapMenuItemToQueuedData(item: MenuItem): ItemData {
338
307
  return {
339
308
  imageUri: item.icon,
340
309
  textLabel1: item.title,
341
310
  textLabel2: item.summary ?? "",
342
311
  leadingButton: "remove",
343
312
  trailingButton: "drag",
313
+ isSelected: item.isSelected,
344
314
  };
345
315
  }
346
316
 
347
- function mapMenuItemToPlaylistData(item: MenuItem): ItemData {
317
+ export function mapMenuItemToPlaylistData(
318
+ item: MenuItem,
319
+ behavior?: SelectionBehavior
320
+ ): ItemData {
321
+ const showSelectionWidget =
322
+ behavior?.selectMode !== "none" && item.trailingButton !== "none";
323
+
324
+ const summary =
325
+ typeof item.summary === "string" ? item.summary.trim() : item.summary;
326
+
348
327
  return {
349
328
  imageUri: item.icon,
350
329
  textLabel1: item.title,
351
- textLabel2: item.summary ?? "",
352
- trailingButton: "multiSelect",
330
+ textLabel2: summary || "",
331
+ trailingButton: showSelectionWidget
332
+ ? item.trailingButton || ("multiSelect" as ItemButtonAction)
333
+ : undefined,
334
+ isSelected: item.isSelected,
353
335
  };
354
336
  }
355
337
 
@@ -362,6 +344,7 @@ type ItemContainerProps = {
362
344
  background: BackgroundConfiguration;
363
345
  focused: boolean;
364
346
  children: React.ReactNode;
347
+ onPress?: () => void;
365
348
  };
366
349
 
367
350
  function ItemContainer({
@@ -369,23 +352,25 @@ function ItemContainer({
369
352
  background,
370
353
  focused,
371
354
  children,
355
+ onPress,
372
356
  }: ItemContainerProps) {
373
357
  const container = resolveContainerSpec(image);
374
358
  const bg = focused ? background.focusedColor : background.defaultColor;
375
359
 
376
360
  return (
377
- <View
378
- style={{
379
- backgroundColor: bg,
361
+ <Pressable
362
+ onPress={onPress}
363
+ style={({ pressed }) => ({
364
+ backgroundColor: pressed ? background.focusedColor : bg,
380
365
  borderRadius: background.cornerRadius,
381
- paddingTop: container.paddingTop,
382
- paddingRight: container.paddingRight,
383
- paddingBottom: container.paddingBottom,
384
- paddingLeft: container.paddingLeft,
385
- }}
366
+ paddingTop: background.paddingTop ?? container.paddingTop,
367
+ paddingRight: background.paddingRight ?? container.paddingRight,
368
+ paddingBottom: background.paddingBottom ?? container.paddingBottom,
369
+ paddingLeft: background.paddingLeft ?? container.paddingLeft,
370
+ })}
386
371
  >
387
372
  {children}
388
- </View>
373
+ </Pressable>
389
374
  );
390
375
  }
391
376
 
@@ -486,27 +471,259 @@ function NowPlayingRow({
486
471
  type ItemButtonProps = {
487
472
  action: ItemButtonAction;
488
473
  focused?: boolean;
474
+ onPress?: () => void;
475
+ isSelected?: boolean;
476
+ checkboxAssets?: CheckboxAssetsConfiguration;
477
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
489
478
  };
490
479
 
491
- function ItemButton({ action, focused = false }: ItemButtonProps) {
480
+ function ItemButton({
481
+ action,
482
+ focused = false,
483
+ onPress,
484
+ isSelected = false,
485
+ checkboxAssets,
486
+ actionButtonAssets,
487
+ }: ItemButtonProps) {
492
488
  const layout = BUTTON_LAYOUT_SPEC[action];
493
489
 
490
+ if (!layout) {
491
+ return null;
492
+ }
493
+
494
+ const renderIcon = () => {
495
+ switch (action) {
496
+ case "playPause": {
497
+ const iconSize = layout.assetWidth;
498
+ const iconRadius = iconSize / 2;
499
+
500
+ return (
501
+ <View
502
+ style={{
503
+ width: iconSize,
504
+ height: iconSize,
505
+ borderRadius: iconRadius,
506
+ backgroundColor: "#FFFFFF",
507
+ justifyContent: "center",
508
+ alignItems: "center",
509
+ }}
510
+ >
511
+ {isSelected ? (
512
+ <View
513
+ style={{
514
+ flexDirection: "row",
515
+ justifyContent: "space-between",
516
+ width: 10,
517
+ height: 16,
518
+ }}
519
+ >
520
+ <View
521
+ style={{
522
+ width: 3.5,
523
+ height: 16,
524
+ backgroundColor: "#1e1e1e",
525
+ borderRadius: 1,
526
+ }}
527
+ />
528
+ <View
529
+ style={{
530
+ width: 3.5,
531
+ height: 16,
532
+ backgroundColor: "#1e1e1e",
533
+ borderRadius: 1,
534
+ }}
535
+ />
536
+ </View>
537
+ ) : (
538
+ <View
539
+ style={{
540
+ width: 0,
541
+ height: 0,
542
+ backgroundColor: "transparent",
543
+ borderStyle: "solid",
544
+ borderLeftWidth: 14,
545
+ borderRightWidth: 0,
546
+ borderTopWidth: 8,
547
+ borderBottomWidth: 8,
548
+ borderLeftColor: "#1e1e1e",
549
+ borderRightColor: "transparent",
550
+ borderTopColor: "transparent",
551
+ borderBottomColor: "transparent",
552
+ marginLeft: 3,
553
+ }}
554
+ />
555
+ )}
556
+ </View>
557
+ );
558
+ }
559
+
560
+ case "drag": {
561
+ if (actionButtonAssets?.dragAsset) {
562
+ return (
563
+ <Image
564
+ source={{ uri: actionButtonAssets.dragAsset }}
565
+ style={{
566
+ width: layout.assetWidth,
567
+ height: layout.assetHeight,
568
+ }}
569
+ resizeMode="contain"
570
+ />
571
+ );
572
+ }
573
+
574
+ return (
575
+ <View
576
+ style={{
577
+ width: 20,
578
+ height: 12,
579
+ justifyContent: "space-between",
580
+ }}
581
+ >
582
+ <View
583
+ style={{
584
+ height: 2,
585
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
586
+ borderRadius: 1,
587
+ }}
588
+ />
589
+ <View
590
+ style={{
591
+ height: 2,
592
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
593
+ borderRadius: 1,
594
+ }}
595
+ />
596
+ <View
597
+ style={{
598
+ height: 2,
599
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
600
+ borderRadius: 1,
601
+ }}
602
+ />
603
+ </View>
604
+ );
605
+ }
606
+
607
+ case "remove": {
608
+ if (actionButtonAssets?.removeAsset) {
609
+ return (
610
+ <Image
611
+ source={{ uri: actionButtonAssets.removeAsset }}
612
+ style={{
613
+ width: layout.assetWidth,
614
+ height: layout.assetHeight,
615
+ }}
616
+ resizeMode="contain"
617
+ />
618
+ );
619
+ }
620
+
621
+ return (
622
+ <View
623
+ style={{
624
+ width: 24,
625
+ height: 24,
626
+ borderRadius: 12,
627
+ borderWidth: 1.5,
628
+ borderColor: "rgba(255, 255, 255, 0.7)",
629
+ justifyContent: "center",
630
+ alignItems: "center",
631
+ }}
632
+ >
633
+ <View
634
+ style={{
635
+ width: 10,
636
+ height: 1.5,
637
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
638
+ borderRadius: 1,
639
+ }}
640
+ />
641
+ </View>
642
+ );
643
+ }
644
+
645
+ case "multiSelect": {
646
+ const studioAsset = isSelected
647
+ ? checkboxAssets?.checkedAsset
648
+ : checkboxAssets?.asset;
649
+
650
+ if (studioAsset) {
651
+ return (
652
+ <Image
653
+ source={{ uri: studioAsset }}
654
+ style={{
655
+ width: layout.assetWidth,
656
+ height: layout.assetHeight,
657
+ }}
658
+ resizeMode="contain"
659
+ />
660
+ );
661
+ }
662
+
663
+ if (!isSelected) {
664
+ return (
665
+ <View
666
+ style={{
667
+ width: layout.assetWidth,
668
+ height: layout.assetHeight,
669
+ borderRadius: layout.assetWidth / 2,
670
+ borderWidth: 1.5,
671
+ borderColor: "rgba(255, 255, 255, 0.7)",
672
+ backgroundColor: "transparent",
673
+ }}
674
+ />
675
+ );
676
+ }
677
+
678
+ return (
679
+ <View
680
+ style={{
681
+ width: 24,
682
+ height: 24,
683
+ borderRadius: 12,
684
+ backgroundColor: "#FFFFFF",
685
+ justifyContent: "center",
686
+ alignItems: "center",
687
+ }}
688
+ >
689
+ <View
690
+ style={{
691
+ width: 6,
692
+ height: 10,
693
+ borderBottomWidth: 2,
694
+ borderRightWidth: 2,
695
+ borderColor: "#1e1e1e",
696
+ transform: [{ rotate: "45deg" }],
697
+ marginTop: -2,
698
+ }}
699
+ />
700
+ </View>
701
+ );
702
+ }
703
+
704
+ default:
705
+ return null;
706
+ }
707
+ };
708
+
494
709
  return (
495
- <View
496
- style={{
497
- paddingTop: layout.paddingTop,
498
- paddingRight: layout.paddingRight,
499
- paddingBottom: layout.paddingBottom,
500
- paddingLeft: layout.paddingLeft,
501
- opacity: focused ? 0.7 : 1,
710
+ <Pressable
711
+ onPress={(e: any) => {
712
+ e?.stopPropagation?.();
713
+ onPress?.();
502
714
  }}
715
+ style={({ pressed }) => [
716
+ {
717
+ paddingTop: layout.paddingTop,
718
+ paddingRight: layout.paddingRight,
719
+ paddingBottom: layout.paddingBottom,
720
+ paddingLeft: layout.paddingLeft,
721
+ opacity: pressed || focused ? 0.7 : 1,
722
+ },
723
+ ]}
503
724
  >
504
- <Image
505
- source={{ uri: BUTTON_ASSETS[action] }}
506
- style={{ width: layout.assetWidth, height: layout.assetHeight }}
507
- resizeMode="contain"
508
- />
509
- </View>
725
+ {renderIcon()}
726
+ </Pressable>
510
727
  );
511
728
  }
512
729
 
@@ -515,6 +732,12 @@ type ItemButtonsProps = {
515
732
  trailingButton?: ItemButtonAction;
516
733
  focusedLeadingButton?: boolean;
517
734
  focusedTrailingButton?: boolean;
735
+ onLeadingButtonPress?: () => void;
736
+ onTrailingButtonPress?: () => void;
737
+ isSelected?: boolean;
738
+ renderHandle?: (element: React.ReactElement) => React.ReactElement;
739
+ checkboxAssets?: CheckboxAssetsConfiguration;
740
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
518
741
  };
519
742
 
520
743
  function ItemButtons({
@@ -522,6 +745,12 @@ function ItemButtons({
522
745
  trailingButton,
523
746
  focusedLeadingButton = false,
524
747
  focusedTrailingButton = false,
748
+ onLeadingButtonPress,
749
+ onTrailingButtonPress,
750
+ isSelected = false,
751
+ renderHandle,
752
+ checkboxAssets,
753
+ actionButtonAssets,
525
754
  }: ItemButtonsProps) {
526
755
  if (!leadingButton && !trailingButton) {
527
756
  return null;
@@ -531,42 +760,102 @@ function ItemButtons({
531
760
  <View style={styles.buttonsRow}>
532
761
  {leadingButton ? (
533
762
  <>
534
- <ItemButton action={leadingButton} focused={focusedLeadingButton} />
763
+ <ItemButton
764
+ action={leadingButton}
765
+ focused={focusedLeadingButton}
766
+ onPress={onLeadingButtonPress}
767
+ isSelected={isSelected}
768
+ checkboxAssets={checkboxAssets}
769
+ actionButtonAssets={actionButtonAssets}
770
+ />
535
771
  <View style={{ width: BUTTONS_CONTAINER_HORIZONTAL_GUTTER }} />
536
772
  </>
537
773
  ) : null}
538
774
  {trailingButton ? (
539
- <ItemButton action={trailingButton} focused={focusedTrailingButton} />
775
+ trailingButton === "drag" ? (
776
+ renderHandle ? (
777
+ renderHandle(
778
+ <ItemButton
779
+ action={trailingButton}
780
+ focused={focusedTrailingButton}
781
+ onPress={onTrailingButtonPress}
782
+ isSelected={isSelected}
783
+ checkboxAssets={checkboxAssets}
784
+ actionButtonAssets={actionButtonAssets}
785
+ />
786
+ )
787
+ ) : (
788
+ <ItemButton
789
+ action={trailingButton}
790
+ focused={focusedTrailingButton}
791
+ onPress={onTrailingButtonPress}
792
+ isSelected={isSelected}
793
+ checkboxAssets={checkboxAssets}
794
+ actionButtonAssets={actionButtonAssets}
795
+ />
796
+ )
797
+ ) : (
798
+ <ItemButton
799
+ action={trailingButton}
800
+ focused={focusedTrailingButton}
801
+ onPress={onTrailingButtonPress}
802
+ isSelected={isSelected}
803
+ checkboxAssets={checkboxAssets}
804
+ actionButtonAssets={actionButtonAssets}
805
+ />
806
+ )
540
807
  ) : null}
541
808
  </View>
542
809
  );
543
810
  }
544
811
 
545
812
  // ---------------------------------------------------------------------------
546
- // Item
813
+ // AudioPlayerTrackItem
547
814
  // ---------------------------------------------------------------------------
548
815
 
549
- export function Item({
816
+ export function AudioPlayerTrackItem({
550
817
  configuration,
551
818
  data,
552
819
  focused = false,
553
820
  focusedLeadingButton = false,
554
821
  focusedTrailingButton = false,
555
- }: ItemProps) {
822
+ onPress,
823
+ onLeadingButtonPress,
824
+ onTrailingButtonPress,
825
+ renderHandle,
826
+ }: AudioPlayerTrackItemProps) {
827
+ const modalBlocksStyle = useModalBlocksStyle();
828
+ const showNowPlaying = !!data.nowPlayingLabel;
829
+
830
+ const itemBlock =
831
+ showNowPlaying && modalBlocksStyle?.nowPlayingItem
832
+ ? modalBlocksStyle.nowPlayingItem
833
+ : modalBlocksStyle?.item;
834
+
835
+ const effectiveConfig = React.useMemo(() => {
836
+ return mergeItemConfiguration(configuration, itemBlock);
837
+ }, [configuration, itemBlock]);
838
+
556
839
  const {
557
840
  background,
558
841
  image,
559
- nowPlayingAsset,
560
- nowPlayingLabel,
561
842
  textLabel1,
562
843
  textLabel2,
563
- } = configuration;
844
+ nowPlayingLabel,
845
+ nowPlayingAsset,
846
+ checkboxAssets,
847
+ actionButtonAssets,
848
+ } = effectiveConfig;
564
849
 
565
850
  const container = resolveContainerSpec(image);
566
- const showNowPlaying = !!data.nowPlayingLabel;
567
851
 
568
852
  return (
569
- <ItemContainer image={image} background={background} focused={focused}>
853
+ <ItemContainer
854
+ image={image}
855
+ background={background}
856
+ focused={focused}
857
+ onPress={onPress}
858
+ >
570
859
  <View style={styles.row}>
571
860
  <ItemThumbnail configuration={image} imageUri={data.imageUri} />
572
861
  <View style={{ width: container.horizontalGutter }} />
@@ -594,6 +883,12 @@ export function Item({
594
883
  trailingButton={data.trailingButton}
595
884
  focusedLeadingButton={focusedLeadingButton}
596
885
  focusedTrailingButton={focusedTrailingButton}
886
+ onLeadingButtonPress={onLeadingButtonPress}
887
+ onTrailingButtonPress={onTrailingButtonPress}
888
+ isSelected={data.isSelected}
889
+ renderHandle={renderHandle}
890
+ checkboxAssets={checkboxAssets}
891
+ actionButtonAssets={actionButtonAssets}
597
892
  />
598
893
  </View>
599
894
  </ItemContainer>
@@ -663,47 +958,3 @@ export const VARIANT_PLAYLIST_ITEM: ItemConfiguration = {
663
958
  textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
664
959
  textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
665
960
  };
666
-
667
- export default function App() {
668
- // Hardcoded core mock data objects matching user types framework
669
- const sharedMockItem: MenuItem = {
670
- title: "Content Title",
671
- summary: "Artist",
672
- icon: "https://picsum.photos/200",
673
- action: { type: "PLAY_AUDIO" },
674
- secondaryAction: { action: { type: "OPEN_CONTEXT_MENU" } },
675
- };
676
-
677
- const playlistMockItem: MenuItem = {
678
- title: "Playlist Name",
679
- summary: "12 Songs",
680
- icon: "https://picsum.photos/201",
681
- isSelected: true,
682
- action: { type: "NAVIGATE_PLAYLIST" },
683
- };
684
-
685
- return (
686
- <View style={styles.demoScreen}>
687
- <Text style={styles.demoHeading}>Modal Item Building Block</Text>
688
-
689
- <Text style={styles.demoLabel}>Now Playing</Text>
690
- <Item
691
- configuration={VARIANT_NOW_PLAYING}
692
- data={mapMenuItemToNowPlayingData(sharedMockItem)}
693
- />
694
-
695
- <Text style={styles.demoLabel}>Queued Item</Text>
696
- <Item
697
- configuration={VARIANT_QUEUED_ITEM}
698
- data={mapMenuItemToQueuedData(sharedMockItem)}
699
- focused
700
- />
701
-
702
- <Text style={styles.demoLabel}>Playlist Item</Text>
703
- <Item
704
- configuration={VARIANT_PLAYLIST_ITEM}
705
- data={mapMenuItemToPlaylistData(playlistMockItem)}
706
- />
707
- </View>
708
- );
709
- }