@applicaster/zapp-react-native-ui-components 16.0.0-rc.12 → 16.0.0-rc.120

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 (166) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/Cell/Cell.tsx +3 -6
  5. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  6. package/Components/CellRendererResolver/index.ts +1 -1
  7. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  8. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  9. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  10. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  11. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  12. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  15. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  16. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  17. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  18. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
  20. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  22. package/Components/GeneralContentScreen/index.ts +2 -0
  23. package/Components/Layout/FullWidthRow.tsx +38 -0
  24. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  25. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  26. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  27. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  28. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  29. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  30. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  31. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  32. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  35. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  36. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -6
  37. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  38. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  39. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  40. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  41. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  42. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  43. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  44. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  45. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  46. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +34 -21
  47. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  48. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  49. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  50. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  51. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  52. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  53. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  54. package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
  55. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  56. package/Components/MasterCell/dataAdapter.ts +15 -5
  57. package/Components/MasterCell/hooks/index.ts +2 -0
  58. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  59. package/Components/MasterCell/index.tsx +57 -8
  60. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  61. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  62. package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
  63. package/Components/MasterCell/utils/index.ts +103 -46
  64. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  65. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  66. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  67. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  68. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  69. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  70. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  71. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  72. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1028 -0
  73. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  74. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +164 -0
  75. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  76. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  77. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +433 -0
  78. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +563 -0
  79. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  80. package/Components/ModalComponent/BottomSheetModalContent.tsx +440 -50
  81. package/Components/ModalComponent/Button/index.tsx +5 -3
  82. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  83. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  84. package/Components/ModalComponent/Header/index.tsx +20 -7
  85. package/Components/ModalComponent/Header/utils.ts +1 -1
  86. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  87. package/Components/ModalComponent/SortableList.tsx +156 -0
  88. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  89. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  90. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  91. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  92. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  93. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  94. package/Components/ModalComponent/__tests__/showTextInput.test.ts +120 -0
  95. package/Components/ModalComponent/index.tsx +18 -9
  96. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  97. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  98. package/Components/ModalComponent/presets/index.ts +27 -0
  99. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  100. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  101. package/Components/ModalComponent/presets/types.ts +35 -0
  102. package/Components/ModalComponent/showTextInput.ts +54 -0
  103. package/Components/ModalComponent/utils.ts +82 -21
  104. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  105. package/Components/OfflineHandler/__tests__/index.test.tsx +186 -17
  106. package/Components/OfflineHandler/hooks.ts +111 -0
  107. package/Components/OfflineHandler/index.tsx +91 -75
  108. package/Components/OfflineHandler/utils/index.ts +1 -13
  109. package/Components/PlayerContainer/PlayerContainer.tsx +28 -11
  110. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  111. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  112. package/Components/PreloaderWrapper/index.tsx +1 -1
  113. package/Components/River/ComponentsMap/ComponentsMap.tsx +91 -62
  114. package/Components/River/River.tsx +4 -0
  115. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +142 -89
  116. package/Components/River/__tests__/componentsMap.test.js +8 -3
  117. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  118. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  119. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  120. package/Components/Screen/index.tsx +13 -4
  121. package/Components/Screen/navigationHandler.ts +8 -8
  122. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  123. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  124. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  125. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  126. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  127. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  128. package/Components/TopCutoffOverlay/index.tsx +2 -2
  129. package/Components/Transitioner/AnimationManager.js +1 -1
  130. package/Components/Transitioner/Scene.tsx +4 -3
  131. package/Components/Transitioner/Transitioner.tsx +98 -49
  132. package/Components/Transitioner/__tests__/transitioner.test.js +372 -3
  133. package/Components/VideoLive/LiveImageManager.ts +9 -3
  134. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  135. package/Components/VideoModal/PlayerWrapper.tsx +4 -1
  136. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  137. package/Components/VideoModal/__tests__/playerWrapperStyle.test.ts +58 -0
  138. package/Components/VideoModal/__tests__/playerWrapperUtils.test.ts +16 -0
  139. package/Components/VideoModal/playerWrapperStyle.ts +21 -3
  140. package/Components/VideoModal/playerWrapperUtils.ts +7 -1
  141. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  142. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  143. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  144. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  145. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  146. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  147. package/Decorators/ZappPipesDataConnector/__tests__/screenToolbox.test.ts +347 -0
  148. package/Decorators/ZappPipesDataConnector/__tests__/screenToolboxUnavailable.test.ts +53 -0
  149. package/Decorators/ZappPipesDataConnector/index.tsx +19 -4
  150. package/Decorators/ZappPipesDataConnector/screenToolbox.ts +384 -0
  151. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  152. package/Helpers/index.js +0 -39
  153. package/Hooks/useEntryActionsExpander.ts +63 -0
  154. package/assets/empty-Info.png +0 -0
  155. package/package.json +6 -5
  156. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  157. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  158. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  159. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  160. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  161. package/Components/Screen/utils.ts +0 -16
  162. package/Helpers/Analytics/index.js +0 -95
  163. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  164. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  165. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  166. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -0,0 +1,1028 @@
1
+ /**
2
+ * Modal Building Block — Item (Queue / Playlist)
3
+ *
4
+ * Single-file Expo Snack demo. Three item forms driven by configuration + data.
5
+ *
6
+ * Green (configurable): background, image, nowPlayingAsset, nowPlayingLabel,
7
+ * textLabel1, textLabel2
8
+ * White (hardcoded): container padding/gutters, nowPlaying container layout,
9
+ * button sizes/padding. Play/pause icons use plugin assets when set.
10
+ *
11
+ * Variants (VARIANT_*) are preset configuration bundles — not passed as a field.
12
+ */
13
+
14
+ import React from "react";
15
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
16
+ import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
17
+ import {
18
+ MenuItem,
19
+ SelectionBehavior,
20
+ } from "@applicaster/zapp-react-native-utils/modalState/types";
21
+ import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
22
+ import { mergeItemConfiguration } from "../utils/mergeStyles";
23
+
24
+ const styles = StyleSheet.create({
25
+ row: {
26
+ flexDirection: "row",
27
+ alignItems: "center",
28
+ },
29
+ contentColumn: {
30
+ flex: 1,
31
+ justifyContent: "center",
32
+ },
33
+ buttonsRow: {
34
+ flexDirection: "row",
35
+ alignItems: "center",
36
+ },
37
+ });
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Configuration (green fields)
41
+ // ---------------------------------------------------------------------------
42
+
43
+ export type TextTransform =
44
+ | "default"
45
+ | "lowercase"
46
+ | "uppercase"
47
+ | "capitalize";
48
+
49
+ export type BackgroundConfiguration = {
50
+ defaultColor: string;
51
+ focusedColor: string;
52
+ cornerRadius: number;
53
+ paddingTop?: number;
54
+ paddingRight?: number;
55
+ paddingBottom?: number;
56
+ paddingLeft?: number;
57
+ highlightInsetLeft?: number;
58
+ highlightInsetRight?: number;
59
+ };
60
+
61
+ export type ImageConfiguration = {
62
+ width: number;
63
+ height: number;
64
+ borderWidth: number;
65
+ borderColor: string;
66
+ cornerRadius: number;
67
+ };
68
+
69
+ export type LabelConfiguration = {
70
+ fontColor: string;
71
+ focusedFontColor?: string;
72
+ iosFontFamily: string;
73
+ androidFontFamily: string;
74
+ fontSize: number;
75
+ lineHeight: number;
76
+ iosLetterSpacing: number;
77
+ androidLetterSpacing: number;
78
+ textTransform: TextTransform;
79
+ };
80
+
81
+ export type NowPlayingAssetConfiguration = {
82
+ asset?: string;
83
+ };
84
+
85
+ export type CheckboxAssetsConfiguration = {
86
+ /** Unchecked multiSelect control (item.checkbox_asset). */
87
+ asset?: string;
88
+ /** Checked multiSelect control (item.checked_checkbox_asset). */
89
+ checkedAsset?: string;
90
+ };
91
+
92
+ export type ActionButtonAssetsConfiguration = {
93
+ /** Leading remove control on Edit Order rows. */
94
+ removeAsset?: string;
95
+ /** Trailing drag handle on Edit Order / queue rows. */
96
+ dragAsset?: string;
97
+ /** Shown on the Now Playing row while the track is paused. */
98
+ playAsset?: string;
99
+ /** Shown on the Now Playing row while the track is playing. */
100
+ pauseAsset?: string;
101
+ };
102
+
103
+ export type ItemConfiguration = {
104
+ background: BackgroundConfiguration;
105
+ image: ImageConfiguration;
106
+ nowPlayingAsset: NowPlayingAssetConfiguration;
107
+ nowPlayingLabel: LabelConfiguration;
108
+ textLabel1: LabelConfiguration;
109
+ textLabel2: LabelConfiguration;
110
+ checkboxAssets?: CheckboxAssetsConfiguration;
111
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
112
+ };
113
+
114
+ export const DEFAULT_TEXT_LABEL_1_CONFIGURATION: LabelConfiguration = {
115
+ fontColor: "#FFFFFF",
116
+ focusedFontColor: "#FFFFFF",
117
+ iosFontFamily: "SFProText-Semibold",
118
+ androidFontFamily: "Roboto-Medium",
119
+ fontSize: 15,
120
+ lineHeight: 18,
121
+ iosLetterSpacing: -0.2,
122
+ androidLetterSpacing: 0,
123
+ textTransform: "default",
124
+ };
125
+
126
+ export const DEFAULT_TEXT_LABEL_2_CONFIGURATION: LabelConfiguration = {
127
+ fontColor: "#999999",
128
+ iosFontFamily: "SFProText-Medium",
129
+ androidFontFamily: "Roboto-Medium",
130
+ fontSize: 13,
131
+ lineHeight: 16,
132
+ iosLetterSpacing: -0.1,
133
+ androidLetterSpacing: 0,
134
+ textTransform: "default",
135
+ };
136
+
137
+ export const DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION: LabelConfiguration = {
138
+ fontColor: "#FFFFFF",
139
+ iosFontFamily: "SFProText-Semibold",
140
+ androidFontFamily: "Roboto-Medium",
141
+ fontSize: 13,
142
+ lineHeight: 16,
143
+ iosLetterSpacing: 0,
144
+ androidLetterSpacing: 0,
145
+ textTransform: "default",
146
+ };
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Data
150
+ // ---------------------------------------------------------------------------
151
+
152
+ export type ItemButtonAction = "playPause" | "drag" | "multiSelect" | "remove";
153
+
154
+ export type ItemData = {
155
+ imageUri?: string;
156
+ textLabel1: string;
157
+ textLabel2: string;
158
+ /** When set, shows the Now Playing row (equalizer + label). */
159
+ nowPlayingLabel?: string;
160
+ /** Button 2 (left most) — hardcoded action decides icon. */
161
+ leadingButton?: ItemButtonAction;
162
+ /** Button 1 (right most) — hardcoded action decides icon. */
163
+ trailingButton?: ItemButtonAction;
164
+ isSelected?: boolean;
165
+ };
166
+
167
+ export type AudioPlayerTrackItemProps = {
168
+ configuration: ItemConfiguration;
169
+ data: ItemData;
170
+ focused?: boolean;
171
+ focusedLeadingButton?: boolean;
172
+ focusedTrailingButton?: boolean;
173
+ onPress?: () => void;
174
+ onLeadingButtonPress?: () => void;
175
+ onTrailingButtonPress?: () => void;
176
+ renderHandle?: (element: React.ReactElement) => React.ReactElement;
177
+ };
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Hardcoded (white fields)
181
+ // ---------------------------------------------------------------------------
182
+
183
+ type ContainerSpec = {
184
+ paddingTop: number;
185
+ paddingRight: number;
186
+ paddingBottom: number;
187
+ paddingLeft: number;
188
+ horizontalGutter: number;
189
+ verticalGutter: number;
190
+ };
191
+
192
+ type ButtonLayoutSpec = {
193
+ assetWidth: number;
194
+ assetHeight: number;
195
+ paddingTop: number;
196
+ paddingRight: number;
197
+ paddingBottom: number;
198
+ paddingLeft: number;
199
+ };
200
+
201
+ const CONTAINER_SPEC_LARGE_IMAGE: ContainerSpec = {
202
+ paddingTop: 8,
203
+ paddingRight: 20,
204
+ paddingBottom: 8,
205
+ paddingLeft: 20,
206
+ horizontalGutter: 12,
207
+ verticalGutter: 2,
208
+ };
209
+
210
+ const CONTAINER_SPEC_DEFAULT: ContainerSpec = {
211
+ paddingTop: 8,
212
+ paddingRight: 10,
213
+ paddingBottom: 8,
214
+ paddingLeft: 10,
215
+ horizontalGutter: 12,
216
+ verticalGutter: 2,
217
+ };
218
+
219
+ const BUTTONS_CONTAINER_HORIZONTAL_GUTTER = 4;
220
+
221
+ const NOW_PLAYING_CONTAINER_SPEC = {
222
+ horizontalGutter: 4,
223
+ marginTop: 0,
224
+ marginRight: 0,
225
+ marginBottom: 6,
226
+ marginLeft: 0,
227
+ };
228
+
229
+ const NOW_PLAYING_ASSET_SPEC = {
230
+ width: 16,
231
+ height: 16,
232
+ };
233
+
234
+ const BUTTON_LAYOUT_SPEC: Record<ItemButtonAction, ButtonLayoutSpec> = {
235
+ playPause: {
236
+ assetWidth: 44,
237
+ assetHeight: 44,
238
+ paddingTop: 0,
239
+ paddingRight: 0,
240
+ paddingBottom: 0,
241
+ paddingLeft: 0,
242
+ },
243
+ drag: {
244
+ assetWidth: 24,
245
+ assetHeight: 24,
246
+ paddingTop: 10,
247
+ paddingRight: 10,
248
+ paddingBottom: 10,
249
+ paddingLeft: 10,
250
+ },
251
+ multiSelect: {
252
+ assetWidth: 24,
253
+ assetHeight: 24,
254
+ paddingTop: 10,
255
+ paddingRight: 10,
256
+ paddingBottom: 10,
257
+ paddingLeft: 10,
258
+ },
259
+ remove: {
260
+ assetWidth: 24,
261
+ assetHeight: 24,
262
+ paddingTop: 10,
263
+ paddingRight: 10,
264
+ paddingBottom: 10,
265
+ paddingLeft: 10,
266
+ },
267
+ };
268
+
269
+ const DEFAULT_THUMB_ASSET =
270
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAQklEQVR42u3PMQ0AAAgDsEmcfzNggZekRwU0beezCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhcLQVXaABdK6k6AAAAAElFTkSuQmCC";
271
+
272
+ const DEFAULT_EQUALIZER_ASSET =
273
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAI0lEQVR42mNgGNTgPxTQzwB0DVQ3gKCBowbQwYAhkBIHBAAA9TvPMeOFaLMAAAAASUVORK5CYII=";
274
+
275
+ function getTextTransformStyle(
276
+ textTransform: TextTransform
277
+ ): "none" | "lowercase" | "uppercase" | "capitalize" {
278
+ return textTransform === "default" ? "none" : textTransform;
279
+ }
280
+
281
+ function getFontFamily(ios: string, android: string) {
282
+ return Platform.select({ ios, android, default: android });
283
+ }
284
+
285
+ /** Hardcoded: large image (72) uses wider horizontal container padding. */
286
+ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
287
+ return image.width >= 72
288
+ ? CONTAINER_SPEC_LARGE_IMAGE
289
+ : CONTAINER_SPEC_DEFAULT;
290
+ }
291
+
292
+ export function mapMenuItemToNowPlayingData(
293
+ item: MenuItem,
294
+ isPaused?: boolean,
295
+ nowPlayingLabel = "Now Playing"
296
+ ): ItemData {
297
+ return {
298
+ imageUri: item.icon,
299
+ textLabel1: item.title,
300
+ textLabel2: item.summary ?? "",
301
+ nowPlayingLabel,
302
+ trailingButton: "playPause",
303
+ isSelected: isPaused !== undefined ? !isPaused : item.isSelected,
304
+ };
305
+ }
306
+
307
+ export function mapMenuItemToQueuedData(item: MenuItem): ItemData {
308
+ return {
309
+ imageUri: item.icon,
310
+ textLabel1: item.title,
311
+ textLabel2: item.summary ?? "",
312
+ leadingButton: "remove",
313
+ trailingButton: "drag",
314
+ isSelected: item.isSelected,
315
+ };
316
+ }
317
+
318
+ export function mapMenuItemToPlaylistData(
319
+ item: MenuItem,
320
+ behavior?: SelectionBehavior
321
+ ): ItemData {
322
+ const showSelectionWidget =
323
+ behavior?.selectMode !== "none" && item.trailingButton !== "none";
324
+
325
+ const summary =
326
+ typeof item.summary === "string" ? item.summary.trim() : item.summary;
327
+
328
+ return {
329
+ imageUri: item.icon,
330
+ textLabel1: item.title,
331
+ textLabel2: summary || "",
332
+ trailingButton: showSelectionWidget
333
+ ? item.trailingButton || ("multiSelect" as ItemButtonAction)
334
+ : undefined,
335
+ isSelected: item.isSelected,
336
+ };
337
+ }
338
+
339
+ // ---------------------------------------------------------------------------
340
+ // Inner components
341
+ // ---------------------------------------------------------------------------
342
+
343
+ type ItemContainerProps = {
344
+ image: ImageConfiguration;
345
+ background: BackgroundConfiguration;
346
+ focused: boolean;
347
+ children: React.ReactNode;
348
+ onPress?: () => void;
349
+ };
350
+
351
+ function ItemContainer({
352
+ image,
353
+ background,
354
+ focused,
355
+ children,
356
+ onPress,
357
+ }: ItemContainerProps) {
358
+ const container = resolveContainerSpec(image);
359
+ const insetLeft = background.highlightInsetLeft ?? 0;
360
+ const insetRight = background.highlightInsetRight ?? 0;
361
+
362
+ return (
363
+ <Pressable onPress={onPress}>
364
+ {({ pressed }) => (
365
+ <View
366
+ style={{
367
+ marginLeft: insetLeft,
368
+ marginRight: insetRight,
369
+ backgroundColor:
370
+ pressed || focused
371
+ ? background.focusedColor
372
+ : background.defaultColor,
373
+ borderRadius: background.cornerRadius,
374
+ paddingTop: background.paddingTop ?? container.paddingTop,
375
+ paddingRight: background.paddingRight ?? container.paddingRight,
376
+ paddingBottom: background.paddingBottom ?? container.paddingBottom,
377
+ paddingLeft: background.paddingLeft ?? container.paddingLeft,
378
+ }}
379
+ >
380
+ {children}
381
+ </View>
382
+ )}
383
+ </Pressable>
384
+ );
385
+ }
386
+
387
+ type ItemThumbnailProps = {
388
+ configuration: ImageConfiguration;
389
+ imageUri?: string;
390
+ };
391
+
392
+ function ItemThumbnail({ configuration, imageUri }: ItemThumbnailProps) {
393
+ return (
394
+ <Image
395
+ source={{ uri: imageUri ?? DEFAULT_THUMB_ASSET }}
396
+ style={{
397
+ width: configuration.width,
398
+ height: configuration.height,
399
+ borderWidth: configuration.borderWidth,
400
+ borderColor: configuration.borderColor,
401
+ borderRadius: configuration.cornerRadius,
402
+ }}
403
+ resizeMode="cover"
404
+ />
405
+ );
406
+ }
407
+
408
+ type ItemLabelProps = {
409
+ text: string;
410
+ configuration: LabelConfiguration;
411
+ focused?: boolean;
412
+ };
413
+
414
+ function ItemLabel({ text, configuration, focused = false }: ItemLabelProps) {
415
+ if (!text) {
416
+ return null;
417
+ }
418
+
419
+ const color =
420
+ focused && configuration.focusedFontColor
421
+ ? configuration.focusedFontColor
422
+ : configuration.fontColor;
423
+
424
+ return (
425
+ <Text
426
+ style={{
427
+ color,
428
+ fontSize: configuration.fontSize,
429
+ lineHeight: configuration.lineHeight,
430
+ letterSpacing:
431
+ Platform.OS === "ios"
432
+ ? configuration.iosLetterSpacing
433
+ : configuration.androidLetterSpacing,
434
+ fontFamily: getFontFamily(
435
+ configuration.iosFontFamily,
436
+ configuration.androidFontFamily
437
+ ),
438
+ textTransform: getTextTransformStyle(configuration.textTransform),
439
+ }}
440
+ numberOfLines={1}
441
+ >
442
+ {text}
443
+ </Text>
444
+ );
445
+ }
446
+
447
+ type NowPlayingRowProps = {
448
+ assetConfiguration: NowPlayingAssetConfiguration;
449
+ labelConfiguration: LabelConfiguration;
450
+ label: string;
451
+ };
452
+
453
+ function NowPlayingRow({
454
+ assetConfiguration,
455
+ labelConfiguration,
456
+ label,
457
+ }: NowPlayingRowProps) {
458
+ const asset = assetConfiguration.asset ?? DEFAULT_EQUALIZER_ASSET;
459
+
460
+ return (
461
+ <View
462
+ style={{
463
+ flexDirection: "row",
464
+ alignItems: "center",
465
+ marginTop: NOW_PLAYING_CONTAINER_SPEC.marginTop,
466
+ marginRight: NOW_PLAYING_CONTAINER_SPEC.marginRight,
467
+ marginBottom: NOW_PLAYING_CONTAINER_SPEC.marginBottom,
468
+ marginLeft: NOW_PLAYING_CONTAINER_SPEC.marginLeft,
469
+ }}
470
+ >
471
+ <Image
472
+ source={{ uri: asset }}
473
+ style={{
474
+ width: NOW_PLAYING_ASSET_SPEC.width,
475
+ height: NOW_PLAYING_ASSET_SPEC.height,
476
+ }}
477
+ resizeMode="contain"
478
+ />
479
+ <View style={{ width: NOW_PLAYING_CONTAINER_SPEC.horizontalGutter }} />
480
+ <ItemLabel text={label} configuration={labelConfiguration} />
481
+ </View>
482
+ );
483
+ }
484
+
485
+ type ItemButtonProps = {
486
+ action: ItemButtonAction;
487
+ focused?: boolean;
488
+ onPress?: () => void;
489
+ isSelected?: boolean;
490
+ checkboxAssets?: CheckboxAssetsConfiguration;
491
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
492
+ };
493
+
494
+ function ItemButton({
495
+ action,
496
+ focused = false,
497
+ onPress,
498
+ isSelected = false,
499
+ checkboxAssets,
500
+ actionButtonAssets,
501
+ }: ItemButtonProps) {
502
+ const layout = BUTTON_LAYOUT_SPEC[action];
503
+
504
+ if (!layout) {
505
+ return null;
506
+ }
507
+
508
+ const renderIcon = () => {
509
+ switch (action) {
510
+ case "playPause": {
511
+ const playbackAsset = isSelected
512
+ ? actionButtonAssets?.pauseAsset
513
+ : actionButtonAssets?.playAsset;
514
+
515
+ if (playbackAsset) {
516
+ return (
517
+ <Image
518
+ source={{ uri: playbackAsset }}
519
+ style={{
520
+ width: layout.assetWidth,
521
+ height: layout.assetHeight,
522
+ }}
523
+ resizeMode="contain"
524
+ />
525
+ );
526
+ }
527
+
528
+ const iconSize = layout.assetWidth;
529
+ const iconRadius = iconSize / 2;
530
+
531
+ return (
532
+ <View
533
+ style={{
534
+ width: iconSize,
535
+ height: iconSize,
536
+ borderRadius: iconRadius,
537
+ backgroundColor: "#FFFFFF",
538
+ justifyContent: "center",
539
+ alignItems: "center",
540
+ }}
541
+ >
542
+ {isSelected ? (
543
+ <View
544
+ style={{
545
+ flexDirection: "row",
546
+ justifyContent: "space-between",
547
+ width: 10,
548
+ height: 16,
549
+ }}
550
+ >
551
+ <View
552
+ style={{
553
+ width: 3.5,
554
+ height: 16,
555
+ backgroundColor: "#1e1e1e",
556
+ borderRadius: 1,
557
+ }}
558
+ />
559
+ <View
560
+ style={{
561
+ width: 3.5,
562
+ height: 16,
563
+ backgroundColor: "#1e1e1e",
564
+ borderRadius: 1,
565
+ }}
566
+ />
567
+ </View>
568
+ ) : (
569
+ <View
570
+ style={{
571
+ width: 0,
572
+ height: 0,
573
+ backgroundColor: "transparent",
574
+ borderStyle: "solid",
575
+ borderLeftWidth: 14,
576
+ borderRightWidth: 0,
577
+ borderTopWidth: 8,
578
+ borderBottomWidth: 8,
579
+ borderLeftColor: "#1e1e1e",
580
+ borderRightColor: "transparent",
581
+ borderTopColor: "transparent",
582
+ borderBottomColor: "transparent",
583
+ marginLeft: 3,
584
+ }}
585
+ />
586
+ )}
587
+ </View>
588
+ );
589
+ }
590
+
591
+ case "drag": {
592
+ if (actionButtonAssets?.dragAsset) {
593
+ return (
594
+ <Image
595
+ source={{ uri: actionButtonAssets.dragAsset }}
596
+ style={{
597
+ width: layout.assetWidth,
598
+ height: layout.assetHeight,
599
+ }}
600
+ resizeMode="contain"
601
+ />
602
+ );
603
+ }
604
+
605
+ return (
606
+ <View
607
+ style={{
608
+ width: 20,
609
+ height: 12,
610
+ justifyContent: "space-between",
611
+ }}
612
+ >
613
+ <View
614
+ style={{
615
+ height: 2,
616
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
617
+ borderRadius: 1,
618
+ }}
619
+ />
620
+ <View
621
+ style={{
622
+ height: 2,
623
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
624
+ borderRadius: 1,
625
+ }}
626
+ />
627
+ <View
628
+ style={{
629
+ height: 2,
630
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
631
+ borderRadius: 1,
632
+ }}
633
+ />
634
+ </View>
635
+ );
636
+ }
637
+
638
+ case "remove": {
639
+ if (actionButtonAssets?.removeAsset) {
640
+ return (
641
+ <Image
642
+ source={{ uri: actionButtonAssets.removeAsset }}
643
+ style={{
644
+ width: layout.assetWidth,
645
+ height: layout.assetHeight,
646
+ }}
647
+ resizeMode="contain"
648
+ />
649
+ );
650
+ }
651
+
652
+ return (
653
+ <View
654
+ style={{
655
+ width: 24,
656
+ height: 24,
657
+ borderRadius: 12,
658
+ borderWidth: 1.5,
659
+ borderColor: "rgba(255, 255, 255, 0.7)",
660
+ justifyContent: "center",
661
+ alignItems: "center",
662
+ }}
663
+ >
664
+ <View
665
+ style={{
666
+ width: 10,
667
+ height: 1.5,
668
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
669
+ borderRadius: 1,
670
+ }}
671
+ />
672
+ </View>
673
+ );
674
+ }
675
+
676
+ case "multiSelect": {
677
+ const studioAsset = isSelected
678
+ ? checkboxAssets?.checkedAsset
679
+ : checkboxAssets?.asset;
680
+
681
+ if (studioAsset) {
682
+ return (
683
+ <Image
684
+ source={{ uri: studioAsset }}
685
+ style={{
686
+ width: layout.assetWidth,
687
+ height: layout.assetHeight,
688
+ }}
689
+ resizeMode="contain"
690
+ />
691
+ );
692
+ }
693
+
694
+ if (!isSelected) {
695
+ return (
696
+ <View
697
+ style={{
698
+ width: layout.assetWidth,
699
+ height: layout.assetHeight,
700
+ borderRadius: layout.assetWidth / 2,
701
+ borderWidth: 1.5,
702
+ borderColor: "rgba(255, 255, 255, 0.7)",
703
+ backgroundColor: "transparent",
704
+ }}
705
+ />
706
+ );
707
+ }
708
+
709
+ return (
710
+ <View
711
+ style={{
712
+ width: 24,
713
+ height: 24,
714
+ borderRadius: 12,
715
+ backgroundColor: "#FFFFFF",
716
+ justifyContent: "center",
717
+ alignItems: "center",
718
+ }}
719
+ >
720
+ <View
721
+ style={{
722
+ width: 6,
723
+ height: 10,
724
+ borderBottomWidth: 2,
725
+ borderRightWidth: 2,
726
+ borderColor: "#1e1e1e",
727
+ transform: [{ rotate: "45deg" }],
728
+ marginTop: -2,
729
+ }}
730
+ />
731
+ </View>
732
+ );
733
+ }
734
+
735
+ default:
736
+ return null;
737
+ }
738
+ };
739
+
740
+ const buttonPadding = {
741
+ paddingTop: layout.paddingTop,
742
+ paddingRight: layout.paddingRight,
743
+ paddingBottom: layout.paddingBottom,
744
+ paddingLeft: layout.paddingLeft,
745
+ };
746
+
747
+ // A nested Pressable with no handler swallows the parent row press.
748
+ if (!onPress) {
749
+ return (
750
+ <View pointerEvents="none" style={buttonPadding}>
751
+ {renderIcon()}
752
+ </View>
753
+ );
754
+ }
755
+
756
+ return (
757
+ <Pressable
758
+ onPress={(e: any) => {
759
+ e?.stopPropagation?.();
760
+ onPress();
761
+ }}
762
+ style={({ pressed }) => [
763
+ buttonPadding,
764
+ {
765
+ opacity: pressed || focused ? 0.7 : 1,
766
+ },
767
+ ]}
768
+ >
769
+ {renderIcon()}
770
+ </Pressable>
771
+ );
772
+ }
773
+
774
+ type ItemButtonsProps = {
775
+ leadingButton?: ItemButtonAction;
776
+ trailingButton?: ItemButtonAction;
777
+ focusedLeadingButton?: boolean;
778
+ focusedTrailingButton?: boolean;
779
+ onLeadingButtonPress?: () => void;
780
+ onTrailingButtonPress?: () => void;
781
+ isSelected?: boolean;
782
+ renderHandle?: (element: React.ReactElement) => React.ReactElement;
783
+ checkboxAssets?: CheckboxAssetsConfiguration;
784
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
785
+ };
786
+
787
+ function ItemButtons({
788
+ leadingButton,
789
+ trailingButton,
790
+ focusedLeadingButton = false,
791
+ focusedTrailingButton = false,
792
+ onLeadingButtonPress,
793
+ onTrailingButtonPress,
794
+ isSelected = false,
795
+ renderHandle,
796
+ checkboxAssets,
797
+ actionButtonAssets,
798
+ }: ItemButtonsProps) {
799
+ if (!leadingButton && !trailingButton) {
800
+ return null;
801
+ }
802
+
803
+ return (
804
+ <View style={styles.buttonsRow}>
805
+ {leadingButton ? (
806
+ <>
807
+ <ItemButton
808
+ action={leadingButton}
809
+ focused={focusedLeadingButton}
810
+ onPress={onLeadingButtonPress}
811
+ isSelected={isSelected}
812
+ checkboxAssets={checkboxAssets}
813
+ actionButtonAssets={actionButtonAssets}
814
+ />
815
+ <View style={{ width: BUTTONS_CONTAINER_HORIZONTAL_GUTTER }} />
816
+ </>
817
+ ) : null}
818
+ {trailingButton ? (
819
+ trailingButton === "drag" ? (
820
+ renderHandle ? (
821
+ renderHandle(
822
+ <ItemButton
823
+ action={trailingButton}
824
+ focused={focusedTrailingButton}
825
+ onPress={onTrailingButtonPress}
826
+ isSelected={isSelected}
827
+ checkboxAssets={checkboxAssets}
828
+ actionButtonAssets={actionButtonAssets}
829
+ />
830
+ )
831
+ ) : (
832
+ <ItemButton
833
+ action={trailingButton}
834
+ focused={focusedTrailingButton}
835
+ onPress={onTrailingButtonPress}
836
+ isSelected={isSelected}
837
+ checkboxAssets={checkboxAssets}
838
+ actionButtonAssets={actionButtonAssets}
839
+ />
840
+ )
841
+ ) : (
842
+ <ItemButton
843
+ action={trailingButton}
844
+ focused={focusedTrailingButton}
845
+ onPress={onTrailingButtonPress}
846
+ isSelected={isSelected}
847
+ checkboxAssets={checkboxAssets}
848
+ actionButtonAssets={actionButtonAssets}
849
+ />
850
+ )
851
+ ) : null}
852
+ </View>
853
+ );
854
+ }
855
+
856
+ // ---------------------------------------------------------------------------
857
+ // AudioPlayerTrackItem
858
+ // ---------------------------------------------------------------------------
859
+
860
+ export function AudioPlayerTrackItem({
861
+ configuration,
862
+ data,
863
+ focused = false,
864
+ focusedLeadingButton = false,
865
+ focusedTrailingButton = false,
866
+ onPress,
867
+ onLeadingButtonPress,
868
+ onTrailingButtonPress,
869
+ renderHandle,
870
+ }: AudioPlayerTrackItemProps) {
871
+ const modalBlocksStyle = useModalBlocksStyle();
872
+ const showNowPlaying = !!data.nowPlayingLabel;
873
+
874
+ const itemBlock =
875
+ showNowPlaying && modalBlocksStyle?.nowPlayingItem
876
+ ? modalBlocksStyle.nowPlayingItem
877
+ : modalBlocksStyle?.item;
878
+
879
+ const effectiveConfig = React.useMemo(() => {
880
+ return mergeItemConfiguration(configuration, itemBlock);
881
+ }, [configuration, itemBlock]);
882
+
883
+ const {
884
+ background,
885
+ image,
886
+ textLabel1,
887
+ textLabel2,
888
+ nowPlayingLabel,
889
+ nowPlayingAsset,
890
+ checkboxAssets,
891
+ actionButtonAssets,
892
+ } = effectiveConfig;
893
+
894
+ const container = resolveContainerSpec(image);
895
+
896
+ const handleTrailingButtonPress =
897
+ onTrailingButtonPress ??
898
+ (data.trailingButton === "multiSelect" ? onPress : undefined);
899
+
900
+ return (
901
+ <ItemContainer
902
+ image={image}
903
+ background={background}
904
+ focused={focused}
905
+ onPress={onPress}
906
+ >
907
+ <View style={styles.row}>
908
+ <ItemThumbnail configuration={image} imageUri={data.imageUri} />
909
+ <View style={{ width: container.horizontalGutter }} />
910
+ <View style={styles.contentColumn}>
911
+ {showNowPlaying ? (
912
+ <NowPlayingRow
913
+ assetConfiguration={nowPlayingAsset}
914
+ labelConfiguration={nowPlayingLabel}
915
+ label={data.nowPlayingLabel}
916
+ />
917
+ ) : null}
918
+ {showNowPlaying && (data.textLabel1 || data.textLabel2) ? (
919
+ <View style={{ height: container.verticalGutter }} />
920
+ ) : null}
921
+ {data.textLabel1 ? (
922
+ <ItemLabel
923
+ text={data.textLabel1}
924
+ configuration={textLabel1}
925
+ focused={focused}
926
+ />
927
+ ) : null}
928
+ {data.textLabel1 && data.textLabel2 ? (
929
+ <View style={{ height: container.verticalGutter }} />
930
+ ) : null}
931
+ {data.textLabel2 ? (
932
+ <ItemLabel text={data.textLabel2} configuration={textLabel2} />
933
+ ) : null}
934
+ </View>
935
+ <ItemButtons
936
+ leadingButton={data.leadingButton}
937
+ trailingButton={data.trailingButton}
938
+ focusedLeadingButton={focusedLeadingButton}
939
+ focusedTrailingButton={focusedTrailingButton}
940
+ onLeadingButtonPress={onLeadingButtonPress}
941
+ onTrailingButtonPress={handleTrailingButtonPress}
942
+ isSelected={data.isSelected}
943
+ renderHandle={renderHandle}
944
+ checkboxAssets={checkboxAssets}
945
+ actionButtonAssets={actionButtonAssets}
946
+ />
947
+ </View>
948
+ </ItemContainer>
949
+ );
950
+ }
951
+
952
+ // ---------------------------------------------------------------------------
953
+ // Variant presets — collections of configuration keys (not a variant field)
954
+ // ---------------------------------------------------------------------------
955
+
956
+ /** Variant 1 — Now Playing */
957
+ export const VARIANT_NOW_PLAYING: ItemConfiguration = {
958
+ background: {
959
+ defaultColor: "transparent",
960
+ focusedColor: "transparent",
961
+ cornerRadius: 0,
962
+ paddingTop: 8,
963
+ paddingRight: 20,
964
+ paddingBottom: 8,
965
+ paddingLeft: 20,
966
+ },
967
+ image: {
968
+ width: 72,
969
+ height: 72,
970
+ borderWidth: 1,
971
+ borderColor: "rgba(255, 255, 255, 0.1)",
972
+ cornerRadius: 8,
973
+ },
974
+ nowPlayingAsset: {},
975
+ nowPlayingLabel: DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION,
976
+ textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
977
+ textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
978
+ };
979
+
980
+ /** Default / Variant 2 — Queued Item */
981
+ export const VARIANT_QUEUED_ITEM: ItemConfiguration = {
982
+ background: {
983
+ defaultColor: "transparent",
984
+ focusedColor: "rgba(62, 62, 62, 1)",
985
+ cornerRadius: 12,
986
+ paddingTop: 8,
987
+ paddingRight: 10,
988
+ paddingBottom: 8,
989
+ paddingLeft: 10,
990
+ highlightInsetLeft: 10,
991
+ highlightInsetRight: 10,
992
+ },
993
+ image: {
994
+ width: 48,
995
+ height: 48,
996
+ borderWidth: 1,
997
+ borderColor: "rgba(255, 255, 255, 0.1)",
998
+ cornerRadius: 6,
999
+ },
1000
+ nowPlayingAsset: {},
1001
+ nowPlayingLabel: DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION,
1002
+ textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
1003
+ textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
1004
+ };
1005
+
1006
+ /** Variant 3 — Playlist Item */
1007
+ export const VARIANT_PLAYLIST_ITEM: ItemConfiguration = {
1008
+ background: {
1009
+ defaultColor: "transparent",
1010
+ focusedColor: "rgba(62, 62, 62, 1)",
1011
+ cornerRadius: 12,
1012
+ paddingTop: 8,
1013
+ paddingRight: 10,
1014
+ paddingBottom: 8,
1015
+ paddingLeft: 10,
1016
+ },
1017
+ image: {
1018
+ width: 48,
1019
+ height: 48,
1020
+ borderWidth: 1,
1021
+ borderColor: "rgba(255, 255, 255, 0.1)",
1022
+ cornerRadius: 6,
1023
+ },
1024
+ nowPlayingAsset: {},
1025
+ nowPlayingLabel: DEFAULT_NOW_PLAYING_LABEL_CONFIGURATION,
1026
+ textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
1027
+ textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
1028
+ };