@applicaster/zapp-react-native-ui-components 15.1.4 → 16.0.0-alpha.1042942411

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 (202) hide show
  1. package/Components/BackgroundImage/index.ts +1 -8
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/FocusableWrapper.tsx +3 -0
  4. package/Components/Cell/TvOSCellComponent.tsx +6 -3
  5. package/Components/CellRendererResolver/index.ts +1 -1
  6. package/Components/Focusable/Focusable.tsx +4 -2
  7. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  8. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  9. package/Components/FocusableGroup/FocusableTvOS.tsx +30 -1
  10. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
  11. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  12. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  15. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
  16. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  17. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  18. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -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/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  24. package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
  25. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  26. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  27. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  28. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  29. package/Components/Layout/TV/index.tsx +3 -4
  30. package/Components/Layout/TV/index.web.tsx +3 -4
  31. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  32. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +27 -48
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +213 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  39. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  40. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  41. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  42. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +198 -0
  43. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  44. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  45. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  46. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +217 -46
  47. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +50 -119
  48. package/Components/MasterCell/DefaultComponents/Button.tsx +50 -58
  49. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  50. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.android.tv.tsx +58 -0
  51. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  52. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  53. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  54. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  55. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/__snapshots__/index.test.tsx.snap +18 -0
  56. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/index.test.tsx +90 -0
  57. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +13 -14
  58. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  59. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  60. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  61. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
  62. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  63. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  64. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  65. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  66. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
  67. package/Components/MasterCell/DefaultComponents/PressableView.tsx +54 -0
  68. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +46 -50
  69. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +47 -39
  70. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  71. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +19 -6
  72. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +15 -0
  73. package/Components/MasterCell/DefaultComponents/Text/index.tsx +45 -15
  74. package/Components/MasterCell/DefaultComponents/UIKitAdapters/Badge.tsx +49 -0
  75. package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +122 -0
  76. package/Components/MasterCell/DefaultComponents/__tests__/Button.test.tsx +186 -0
  77. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  78. package/Components/MasterCell/DefaultComponents/__tests__/DynamicBadge.test.tsx +156 -0
  79. package/Components/MasterCell/DefaultComponents/__tests__/__snapshots__/ActionButton.test.tsx.snap +60 -0
  80. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +196 -67
  81. package/Components/MasterCell/DefaultComponents/index.ts +13 -3
  82. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +189 -0
  83. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  84. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +33 -0
  85. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +159 -0
  86. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +18 -0
  87. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  88. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +39 -0
  89. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +402 -0
  90. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  91. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +354 -0
  92. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  93. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +129 -0
  94. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +123 -0
  95. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  96. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  97. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  98. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  99. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  100. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  101. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  102. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
  103. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  104. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  105. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
  106. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  107. package/Components/MasterCell/README.md +4 -0
  108. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  109. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  110. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  111. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  112. package/Components/MasterCell/dataAdapter.ts +4 -1
  113. package/Components/MasterCell/elementMapper.tsx +54 -7
  114. package/Components/MasterCell/hooks/index.ts +2 -0
  115. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  116. package/Components/MasterCell/index.tsx +20 -4
  117. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  118. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  119. package/Components/MasterCell/utils/index.ts +82 -16
  120. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -69
  121. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +25 -47
  122. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +122 -39
  123. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +671 -0
  124. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +257 -88
  125. package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
  126. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  127. package/Components/ModalComponent/BottomSheetModalContent.tsx +17 -10
  128. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  129. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  130. package/Components/OfflineHandler/__tests__/index.test.tsx +101 -32
  131. package/Components/OfflineHandler/hooks.ts +123 -0
  132. package/Components/OfflineHandler/index.tsx +51 -73
  133. package/Components/OfflineHandler/utils/index.ts +1 -13
  134. package/Components/PlayerContainer/PlayerContainer.tsx +6 -5
  135. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +2 -4
  136. package/Components/PreloaderWrapper/index.tsx +1 -1
  137. package/Components/River/ComponentsMap/ComponentsMap.tsx +8 -21
  138. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  139. package/Components/River/RefreshControl.tsx +19 -105
  140. package/Components/River/River.tsx +9 -82
  141. package/Components/River/TV/River.tsx +31 -14
  142. package/Components/River/TV/index.tsx +8 -4
  143. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  144. package/Components/River/TV/utils/index.ts +4 -0
  145. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  146. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +4 -0
  147. package/Components/River/__tests__/componentsMap.test.js +38 -0
  148. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  149. package/Components/River/hooks/index.ts +1 -0
  150. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  151. package/Components/Screen/TV/index.web.tsx +4 -2
  152. package/Components/Screen/__tests__/Screen.test.tsx +65 -42
  153. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  154. package/Components/Screen/hooks.ts +2 -3
  155. package/Components/Screen/index.tsx +2 -3
  156. package/Components/Screen/orientationHandler.ts +3 -3
  157. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  158. package/Components/ScreenResolver/index.tsx +9 -5
  159. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  160. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  161. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  162. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +1 -1
  163. package/Components/Tabs/TabContent.tsx +7 -4
  164. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  165. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  166. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  167. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  168. package/Components/TopCutoffOverlay/index.tsx +55 -0
  169. package/Components/Transitioner/index.js +3 -3
  170. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  171. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  172. package/Components/VideoModal/utils.ts +12 -10
  173. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  174. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  175. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  176. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  177. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  178. package/Contexts/ScreenContext/index.tsx +25 -18
  179. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  180. package/Decorators/Analytics/index.tsx +6 -5
  181. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  182. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  183. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  184. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  185. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  186. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  187. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  188. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  189. package/Helpers/DataSourceHelper/index.ts +19 -0
  190. package/package.json +5 -5
  191. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  192. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  193. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  194. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  195. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  196. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -67
  197. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  198. package/Components/River/TV/withTVEventHandler.tsx +0 -36
  199. package/Helpers/DataSourceHelper/index.js +0 -19
  200. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  201. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  202. /package/Components/BackgroundImage/{BackgroundImage.tv.web.tsx → BackgroundImage.tsx} +0 -0
@@ -12,7 +12,14 @@
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";
16
23
 
17
24
  const styles = StyleSheet.create({
18
25
  row: {
@@ -133,6 +140,7 @@ export interface SecondaryAction {
133
140
  }
134
141
 
135
142
  export interface MenuItem {
143
+ trailingButton?: ItemButtonAction;
136
144
  title: string;
137
145
  summary?: string;
138
146
  icon?: string;
@@ -191,14 +199,18 @@ export type ItemData = {
191
199
  leadingButton?: ItemButtonAction;
192
200
  /** Button 1 (right most) — hardcoded action decides icon. */
193
201
  trailingButton?: ItemButtonAction;
202
+ isSelected?: boolean;
194
203
  };
195
204
 
196
- export type ItemProps = {
205
+ export type AudioPlayerTrackItemProps = {
197
206
  configuration: ItemConfiguration;
198
207
  data: ItemData;
199
208
  focused?: boolean;
200
209
  focusedLeadingButton?: boolean;
201
210
  focusedTrailingButton?: boolean;
211
+ onPress?: () => void;
212
+ onLeadingButtonPress?: () => void;
213
+ onTrailingButtonPress?: () => void;
202
214
  };
203
215
 
204
216
  // ---------------------------------------------------------------------------
@@ -234,9 +246,9 @@ const CONTAINER_SPEC_LARGE_IMAGE: ContainerSpec = {
234
246
 
235
247
  const CONTAINER_SPEC_DEFAULT: ContainerSpec = {
236
248
  paddingTop: 8,
237
- paddingRight: 10,
249
+ paddingRight: 20,
238
250
  paddingBottom: 8,
239
- paddingLeft: 10,
251
+ paddingLeft: 20,
240
252
  horizontalGutter: 12,
241
253
  verticalGutter: 2,
242
254
  };
@@ -297,16 +309,6 @@ const DEFAULT_THUMB_ASSET =
297
309
  const DEFAULT_EQUALIZER_ASSET =
298
310
  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAI0lEQVR42mNgGNTgPxTQzwB0DVQ3gKCBowbQwYAhkBIHBAAA9TvPMeOFaLMAAAAASUVORK5CYII=";
299
311
 
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
312
  function getTextTransformStyle(
311
313
  textTransform: TextTransform
312
314
  ): "none" | "lowercase" | "uppercase" | "capitalize" {
@@ -324,32 +326,35 @@ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
324
326
  : CONTAINER_SPEC_DEFAULT;
325
327
  }
326
328
 
327
- function mapMenuItemToNowPlayingData(item: MenuItem): ItemData {
329
+ export function mapMenuItemToNowPlayingData(item: MenuItem): ItemData {
328
330
  return {
329
331
  imageUri: item.icon,
330
332
  textLabel1: item.title,
331
333
  textLabel2: item.summary ?? "",
332
334
  nowPlayingLabel: "Now Playing",
333
335
  trailingButton: "playPause",
336
+ isSelected: item.isSelected,
334
337
  };
335
338
  }
336
339
 
337
- function mapMenuItemToQueuedData(item: MenuItem): ItemData {
340
+ export function mapMenuItemToQueuedData(item: MenuItem): ItemData {
338
341
  return {
339
342
  imageUri: item.icon,
340
343
  textLabel1: item.title,
341
344
  textLabel2: item.summary ?? "",
342
345
  leadingButton: "remove",
343
346
  trailingButton: "drag",
347
+ isSelected: item.isSelected,
344
348
  };
345
349
  }
346
350
 
347
- function mapMenuItemToPlaylistData(item: MenuItem): ItemData {
351
+ export function mapMenuItemToPlaylistData(item: MenuItem): ItemData {
348
352
  return {
349
353
  imageUri: item.icon,
350
354
  textLabel1: item.title,
351
355
  textLabel2: item.summary ?? "",
352
- trailingButton: "multiSelect",
356
+ trailingButton: item.trailingButton || ("multiSelect" as ItemButtonAction),
357
+ isSelected: item.isSelected,
353
358
  };
354
359
  }
355
360
 
@@ -362,6 +367,7 @@ type ItemContainerProps = {
362
367
  background: BackgroundConfiguration;
363
368
  focused: boolean;
364
369
  children: React.ReactNode;
370
+ onPress?: () => void;
365
371
  };
366
372
 
367
373
  function ItemContainer({
@@ -369,23 +375,25 @@ function ItemContainer({
369
375
  background,
370
376
  focused,
371
377
  children,
378
+ onPress,
372
379
  }: ItemContainerProps) {
373
380
  const container = resolveContainerSpec(image);
374
381
  const bg = focused ? background.focusedColor : background.defaultColor;
375
382
 
376
383
  return (
377
- <View
378
- style={{
379
- backgroundColor: bg,
384
+ <Pressable
385
+ onPress={onPress}
386
+ style={({ pressed }) => ({
387
+ backgroundColor: pressed ? background.focusedColor : bg,
380
388
  borderRadius: background.cornerRadius,
381
389
  paddingTop: container.paddingTop,
382
390
  paddingRight: container.paddingRight,
383
391
  paddingBottom: container.paddingBottom,
384
392
  paddingLeft: container.paddingLeft,
385
- }}
393
+ })}
386
394
  >
387
395
  {children}
388
- </View>
396
+ </Pressable>
389
397
  );
390
398
  }
391
399
 
@@ -486,27 +494,205 @@ function NowPlayingRow({
486
494
  type ItemButtonProps = {
487
495
  action: ItemButtonAction;
488
496
  focused?: boolean;
497
+ onPress?: () => void;
498
+ isSelected?: boolean;
489
499
  };
490
500
 
491
- function ItemButton({ action, focused = false }: ItemButtonProps) {
501
+ function ItemButton({
502
+ action,
503
+ focused = false,
504
+ onPress,
505
+ isSelected = false,
506
+ }: ItemButtonProps) {
492
507
  const layout = BUTTON_LAYOUT_SPEC[action];
493
508
 
509
+ const renderIcon = () => {
510
+ switch (action) {
511
+ case "playPause": {
512
+ const iconSize = layout.assetWidth;
513
+ const iconRadius = iconSize / 2;
514
+
515
+ return (
516
+ <View
517
+ style={{
518
+ width: iconSize,
519
+ height: iconSize,
520
+ borderRadius: iconRadius,
521
+ backgroundColor: "#FFFFFF",
522
+ justifyContent: "center",
523
+ alignItems: "center",
524
+ }}
525
+ >
526
+ {isSelected ? (
527
+ <View
528
+ style={{
529
+ flexDirection: "row",
530
+ justifyContent: "space-between",
531
+ width: 10,
532
+ height: 16,
533
+ }}
534
+ >
535
+ <View
536
+ style={{
537
+ width: 3.5,
538
+ height: 16,
539
+ backgroundColor: "#1e1e1e",
540
+ borderRadius: 1,
541
+ }}
542
+ />
543
+ <View
544
+ style={{
545
+ width: 3.5,
546
+ height: 16,
547
+ backgroundColor: "#1e1e1e",
548
+ borderRadius: 1,
549
+ }}
550
+ />
551
+ </View>
552
+ ) : (
553
+ <View
554
+ style={{
555
+ width: 0,
556
+ height: 0,
557
+ backgroundColor: "transparent",
558
+ borderStyle: "solid",
559
+ borderLeftWidth: 14,
560
+ borderRightWidth: 0,
561
+ borderTopWidth: 8,
562
+ borderBottomWidth: 8,
563
+ borderLeftColor: "#1e1e1e",
564
+ borderRightColor: "transparent",
565
+ borderTopColor: "transparent",
566
+ borderBottomColor: "transparent",
567
+ marginLeft: 3,
568
+ }}
569
+ />
570
+ )}
571
+ </View>
572
+ );
573
+ }
574
+
575
+ case "drag": {
576
+ return (
577
+ <View
578
+ style={{
579
+ width: 20,
580
+ height: 12,
581
+ justifyContent: "space-between",
582
+ }}
583
+ >
584
+ <View
585
+ style={{
586
+ height: 2,
587
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
588
+ borderRadius: 1,
589
+ }}
590
+ />
591
+ <View
592
+ style={{
593
+ height: 2,
594
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
595
+ borderRadius: 1,
596
+ }}
597
+ />
598
+ <View
599
+ style={{
600
+ height: 2,
601
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
602
+ borderRadius: 1,
603
+ }}
604
+ />
605
+ </View>
606
+ );
607
+ }
608
+
609
+ case "remove": {
610
+ return (
611
+ <View
612
+ style={{
613
+ width: 24,
614
+ height: 24,
615
+ borderRadius: 12,
616
+ borderWidth: 1.5,
617
+ borderColor: "rgba(255, 255, 255, 0.7)",
618
+ justifyContent: "center",
619
+ alignItems: "center",
620
+ }}
621
+ >
622
+ <View
623
+ style={{
624
+ width: 10,
625
+ height: 1.5,
626
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
627
+ borderRadius: 1,
628
+ }}
629
+ />
630
+ </View>
631
+ );
632
+ }
633
+
634
+ case "multiSelect": {
635
+ if (!isSelected) {
636
+ return (
637
+ <View
638
+ style={{
639
+ width: layout.assetWidth,
640
+ height: layout.assetHeight,
641
+ borderRadius: layout.assetWidth / 2,
642
+ borderWidth: 1.5,
643
+ borderColor: "rgba(255, 255, 255, 0.7)",
644
+ backgroundColor: "transparent",
645
+ }}
646
+ />
647
+ );
648
+ }
649
+
650
+ return (
651
+ <View
652
+ style={{
653
+ width: 24,
654
+ height: 24,
655
+ borderRadius: 12,
656
+ backgroundColor: "#FFFFFF",
657
+ justifyContent: "center",
658
+ alignItems: "center",
659
+ }}
660
+ >
661
+ <View
662
+ style={{
663
+ width: 6,
664
+ height: 10,
665
+ borderBottomWidth: 2,
666
+ borderRightWidth: 2,
667
+ borderColor: "#1e1e1e",
668
+ transform: [{ rotate: "45deg" }],
669
+ marginTop: -2,
670
+ }}
671
+ />
672
+ </View>
673
+ );
674
+ }
675
+
676
+ default:
677
+ return null;
678
+ }
679
+ };
680
+
494
681
  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,
502
- }}
682
+ <Pressable
683
+ onPress={onPress}
684
+ style={({ pressed }) => [
685
+ {
686
+ paddingTop: layout.paddingTop,
687
+ paddingRight: layout.paddingRight,
688
+ paddingBottom: layout.paddingBottom,
689
+ paddingLeft: layout.paddingLeft,
690
+ opacity: pressed || focused ? 0.7 : 1,
691
+ },
692
+ ]}
503
693
  >
504
- <Image
505
- source={{ uri: BUTTON_ASSETS[action] }}
506
- style={{ width: layout.assetWidth, height: layout.assetHeight }}
507
- resizeMode="contain"
508
- />
509
- </View>
694
+ {renderIcon()}
695
+ </Pressable>
510
696
  );
511
697
  }
512
698
 
@@ -515,6 +701,9 @@ type ItemButtonsProps = {
515
701
  trailingButton?: ItemButtonAction;
516
702
  focusedLeadingButton?: boolean;
517
703
  focusedTrailingButton?: boolean;
704
+ onLeadingButtonPress?: () => void;
705
+ onTrailingButtonPress?: () => void;
706
+ isSelected?: boolean;
518
707
  };
519
708
 
520
709
  function ItemButtons({
@@ -522,6 +711,9 @@ function ItemButtons({
522
711
  trailingButton,
523
712
  focusedLeadingButton = false,
524
713
  focusedTrailingButton = false,
714
+ onLeadingButtonPress,
715
+ onTrailingButtonPress,
716
+ isSelected = false,
525
717
  }: ItemButtonsProps) {
526
718
  if (!leadingButton && !trailingButton) {
527
719
  return null;
@@ -531,28 +723,41 @@ function ItemButtons({
531
723
  <View style={styles.buttonsRow}>
532
724
  {leadingButton ? (
533
725
  <>
534
- <ItemButton action={leadingButton} focused={focusedLeadingButton} />
726
+ <ItemButton
727
+ action={leadingButton}
728
+ focused={focusedLeadingButton}
729
+ onPress={onLeadingButtonPress}
730
+ isSelected={isSelected}
731
+ />
535
732
  <View style={{ width: BUTTONS_CONTAINER_HORIZONTAL_GUTTER }} />
536
733
  </>
537
734
  ) : null}
538
735
  {trailingButton ? (
539
- <ItemButton action={trailingButton} focused={focusedTrailingButton} />
736
+ <ItemButton
737
+ action={trailingButton}
738
+ focused={focusedTrailingButton}
739
+ onPress={onTrailingButtonPress}
740
+ isSelected={isSelected}
741
+ />
540
742
  ) : null}
541
743
  </View>
542
744
  );
543
745
  }
544
746
 
545
747
  // ---------------------------------------------------------------------------
546
- // Item
748
+ // AudioPlayerTrackItem
547
749
  // ---------------------------------------------------------------------------
548
750
 
549
- export function Item({
751
+ export function AudioPlayerTrackItem({
550
752
  configuration,
551
753
  data,
552
754
  focused = false,
553
755
  focusedLeadingButton = false,
554
756
  focusedTrailingButton = false,
555
- }: ItemProps) {
757
+ onPress,
758
+ onLeadingButtonPress,
759
+ onTrailingButtonPress,
760
+ }: AudioPlayerTrackItemProps) {
556
761
  const {
557
762
  background,
558
763
  image,
@@ -566,7 +771,12 @@ export function Item({
566
771
  const showNowPlaying = !!data.nowPlayingLabel;
567
772
 
568
773
  return (
569
- <ItemContainer image={image} background={background} focused={focused}>
774
+ <ItemContainer
775
+ image={image}
776
+ background={background}
777
+ focused={focused}
778
+ onPress={onPress}
779
+ >
570
780
  <View style={styles.row}>
571
781
  <ItemThumbnail configuration={image} imageUri={data.imageUri} />
572
782
  <View style={{ width: container.horizontalGutter }} />
@@ -594,6 +804,9 @@ export function Item({
594
804
  trailingButton={data.trailingButton}
595
805
  focusedLeadingButton={focusedLeadingButton}
596
806
  focusedTrailingButton={focusedTrailingButton}
807
+ onLeadingButtonPress={onLeadingButtonPress}
808
+ onTrailingButtonPress={onTrailingButtonPress}
809
+ isSelected={data.isSelected}
597
810
  />
598
811
  </View>
599
812
  </ItemContainer>
@@ -663,47 +876,3 @@ export const VARIANT_PLAYLIST_ITEM: ItemConfiguration = {
663
876
  textLabel1: DEFAULT_TEXT_LABEL_1_CONFIGURATION,
664
877
  textLabel2: DEFAULT_TEXT_LABEL_2_CONFIGURATION,
665
878
  };
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
- }
@@ -1,7 +1,9 @@
1
- export { Item as AudioPlayerItem } from "./Item";
1
+ export { AudioPlayerAction } from "./Action";
2
2
 
3
- export { Item as AudioPlayerHeader } from "./Header";
3
+ export { AudioPlayerButton } from "./Button";
4
4
 
5
- export { Item as AudioPlayerButton } from "./Button";
5
+ export { AudioPlayerHeader } from "./Header";
6
6
 
7
- export { Item as AudioPlayerAction } from "./Action";
7
+ export { AudioPlayerTrackItem } from "./Item";
8
+
9
+ export { AudioPlayerInput } from "./Input";
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import { PanGestureHandler } from "react-native-gesture-handler";
3
+ import { View } from "react-native";
4
+ import { useBottomSheet } from "@applicaster/zapp-react-native-utils/bottomSheet";
5
+
6
+ /**
7
+ * Turns its children into a drag zone for the enclosing DraggableBottomSheet:
8
+ * dragging here moves/dismisses the sheet (used for the modal header bar).
9
+ * Outside a sheet (no context) it renders children unchanged.
10
+ */
11
+ export const BottomSheetDragArea = ({
12
+ children,
13
+ }: {
14
+ children: React.ReactElement;
15
+ }) => {
16
+ const sheet = useBottomSheet();
17
+
18
+ // Outside a sheet (no context) render children unchanged.
19
+ if (!sheet) return children;
20
+
21
+ return (
22
+ <PanGestureHandler
23
+ simultaneousHandlers={[sheet.masterDrawerRef]}
24
+ shouldCancelWhenOutside={false}
25
+ onGestureEvent={sheet.onDragGestureEvent}
26
+ onHandlerStateChange={sheet.onDragStateChange}
27
+ >
28
+ {/* Old-API PanGestureHandler needs a host-component child; children may
29
+ be a function component (e.g. ModalHeader), so wrap in a View. */}
30
+ <View>{children}</View>
31
+ </PanGestureHandler>
32
+ );
33
+ };
@@ -6,11 +6,11 @@ import { ItemProps } from "./Button/Item";
6
6
  import { ItemLabelProps } from "./Button/ItemLabel";
7
7
  import { getItemIconProps, getItemLabelProps, getItemProps } from "./utils";
8
8
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
9
-
10
9
  import type { PluginConfiguration } from "./";
11
10
 
12
11
  import { ModalHeader as DefaultModalHeader } from "./Header";
13
12
  import { useSafeAreaInsets } from "react-native-safe-area-context";
13
+ import { BottomSheetDragArea } from "./BottomSheetDragArea";
14
14
 
15
15
  type ModalComponentProps = {
16
16
  items: any[];
@@ -55,8 +55,7 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
55
55
  dismiss,
56
56
  summary,
57
57
  title,
58
- headerComponent: ModalHeader = DefaultModalHeader,
59
-
58
+ headerComponent = DefaultModalHeader,
60
59
  scrollViewContent = null,
61
60
 
62
61
  buttonComponent: ButtonComponent = Button,
@@ -72,6 +71,8 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
72
71
  const paddingTop = Number(theme.modal_bottom_sheet_padding_top);
73
72
  const paddingBottom = Number(theme.modal_bottom_sheet_padding_bottom);
74
73
 
74
+ const Header = headerComponent;
75
+
75
76
  useEffect(() => {
76
77
  if (currentRoute !== route.current) {
77
78
  props.dismiss();
@@ -107,13 +108,20 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
107
108
  paddingTop: paddingTop,
108
109
  }}
109
110
  >
110
- <ModalHeader
111
- dismiss={dismiss}
112
- configuration={theme}
113
- summary={summary}
114
- title={title}
115
- />
111
+ {Header ? (
112
+ <BottomSheetDragArea>
113
+ <Header
114
+ dismiss={dismiss}
115
+ configuration={theme}
116
+ summary={summary}
117
+ title={title}
118
+ />
119
+ </BottomSheetDragArea>
120
+ ) : null}
116
121
  <ScrollView
122
+ overScrollMode="never"
123
+ bounces={false}
124
+ showsVerticalScrollIndicator={false}
117
125
  contentContainerStyle={{
118
126
  paddingBottom: paddingBottom + bottomInset,
119
127
  }}
@@ -123,7 +131,6 @@ export function BottomSheetModalContent(props: ModalComponentProps) {
123
131
  scrollViewContent.renderItem(item, index)
124
132
  )
125
133
  : null}
126
-
127
134
  {!scrollViewContent &&
128
135
  items.map((item, index) =>
129
136
  item ? (
@@ -0,0 +1,74 @@
1
+ import React from "react";
2
+ import { Text } from "react-native";
3
+ import { render, screen } from "@testing-library/react-native";
4
+ import { BottomSheetDragArea } from "../BottomSheetDragArea";
5
+ import { BottomSheetContext } from "@applicaster/zapp-react-native-utils/bottomSheet";
6
+
7
+ // Reveal PanGestureHandler and capture its props.
8
+ jest.mock("react-native-gesture-handler", () => {
9
+ const { View } = require("react-native");
10
+ const actual = jest.requireActual("react-native-gesture-handler");
11
+
12
+ return {
13
+ __esModule: true,
14
+ ...actual,
15
+ PanGestureHandler: ({
16
+ children,
17
+ onGestureEvent,
18
+ onHandlerStateChange,
19
+ simultaneousHandlers,
20
+ }: any) => (
21
+ <View
22
+ testID="drag-pan"
23
+ onGestureEvent={onGestureEvent}
24
+ onHandlerStateChange={onHandlerStateChange}
25
+ simultaneousHandlers={simultaneousHandlers}
26
+ >
27
+ {children}
28
+ </View>
29
+ ),
30
+ };
31
+ });
32
+
33
+ const makeValue = () => ({
34
+ scrollRef: React.createRef<any>(),
35
+ masterDrawerRef: React.createRef<any>(),
36
+ drawerRef: React.createRef<any>(),
37
+ onScroll: jest.fn(),
38
+ setBodyDragEnabled: jest.fn(),
39
+ onDragGestureEvent: jest.fn(),
40
+ onDragStateChange: jest.fn(),
41
+ });
42
+
43
+ describe("BottomSheetDragArea", () => {
44
+ it("wraps children in a sheet-wired PanGestureHandler when inside a sheet", () => {
45
+ const value = makeValue();
46
+
47
+ render(
48
+ <BottomSheetContext.Provider value={value}>
49
+ <BottomSheetDragArea>
50
+ <Text testID="child">hi</Text>
51
+ </BottomSheetDragArea>
52
+ </BottomSheetContext.Provider>
53
+ );
54
+
55
+ const pan = screen.getByTestId("drag-pan");
56
+ expect(pan.props.onGestureEvent).toBe(value.onDragGestureEvent);
57
+ expect(pan.props.onHandlerStateChange).toBe(value.onDragStateChange);
58
+
59
+ expect(pan.props.simultaneousHandlers).toEqual([value.masterDrawerRef]);
60
+
61
+ expect(screen.getByTestId("child")).toBeTruthy();
62
+ });
63
+
64
+ it("renders children bare when used outside a sheet", () => {
65
+ render(
66
+ <BottomSheetDragArea>
67
+ <Text testID="child">hi</Text>
68
+ </BottomSheetDragArea>
69
+ );
70
+
71
+ expect(screen.queryByTestId("drag-pan")).toBeNull();
72
+ expect(screen.getByTestId("child")).toBeTruthy();
73
+ });
74
+ });