@applicaster/zapp-react-native-ui-components 15.1.14 → 15.1.15-rc.0

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 (46) hide show
  1. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
  2. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
  3. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  4. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  5. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  6. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  7. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  8. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +58 -97
  9. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +142 -142
  10. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +628 -0
  11. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +448 -175
  12. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +94 -0
  13. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  14. package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
  15. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +248 -0
  16. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +386 -0
  17. package/Components/ModalComponent/BottomSheetModalContent.tsx +425 -66
  18. package/Components/ModalComponent/Button/index.tsx +5 -3
  19. package/Components/ModalComponent/Header/index.tsx +20 -7
  20. package/Components/ModalComponent/Header/utils.ts +1 -1
  21. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  22. package/Components/ModalComponent/SortableList.tsx +157 -0
  23. package/Components/ModalComponent/SortableList.web.tsx +102 -0
  24. package/Components/ModalComponent/TextInputContent.tsx +112 -0
  25. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +255 -0
  26. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +156 -0
  27. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  28. package/Components/ModalComponent/index.tsx +18 -9
  29. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  30. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  31. package/Components/ModalComponent/presets/index.ts +27 -0
  32. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  33. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  34. package/Components/ModalComponent/presets/types.ts +35 -0
  35. package/Components/ModalComponent/showTextInput.ts +43 -0
  36. package/Components/ModalComponent/utils.ts +82 -21
  37. package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -60
  38. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +139 -75
  39. package/Components/River/__tests__/componentsMap.test.js +8 -3
  40. package/Components/Screen/navigationHandler.ts +8 -8
  41. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  42. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  43. package/Hooks/useEntryActionsExpander.ts +63 -0
  44. package/package.json +6 -5
  45. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -368
  46. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -9,7 +9,8 @@
9
9
  */
10
10
 
11
11
  import React, { useCallback, useState } from "react";
12
- import { Image, Platform, StyleSheet, Text, View } from "react-native";
12
+ import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
13
+ import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
13
14
 
14
15
  const styles = StyleSheet.create({
15
16
  row: {
@@ -45,29 +46,6 @@ const styles = StyleSheet.create({
45
46
  alignItems: "center",
46
47
  justifyContent: "center",
47
48
  },
48
- demoScreen: {
49
- flex: 1,
50
- backgroundColor: "#1C1C1E",
51
- paddingTop: 64,
52
- paddingHorizontal: 0,
53
- gap: 24,
54
- },
55
- demoBlock: {
56
- gap: 8,
57
- paddingHorizontal: 0,
58
- },
59
- demoHeading: {
60
- color: "#FFFFFF",
61
- fontSize: 20,
62
- fontWeight: "600",
63
- marginBottom: 8,
64
- paddingHorizontal: 24,
65
- },
66
- demoLabel: {
67
- color: "#999999",
68
- fontSize: 13,
69
- paddingHorizontal: 24,
70
- },
71
49
  });
72
50
 
73
51
  // ---------------------------------------------------------------------------
@@ -85,11 +63,7 @@ export type TextTransform =
85
63
  export type TitleAlignment = "left" | "center";
86
64
 
87
65
  /** Selects hardcoded container + action layout (white fields). */
88
- export type HeaderVariant =
89
- | "singleLine"
90
- | "headerWithActions"
91
- | "headerWithActionsCancelOnly"
92
- | "section";
66
+ export type HeaderVariant = "singleLine" | "headerWithActions" | "section";
93
67
 
94
68
  export type TitleConfiguration = {
95
69
  fontColor: string;
@@ -123,6 +97,7 @@ export type ActionButtonsStyleConfiguration = {
123
97
  };
124
98
 
125
99
  export type HeaderConfiguration = {
100
+ paddingTop?: number;
126
101
  variant: HeaderVariant;
127
102
  title: TitleConfiguration;
128
103
  closeButton: CloseButtonConfiguration;
@@ -168,12 +143,17 @@ export type HeaderData = {
168
143
  title: string;
169
144
  };
170
145
 
171
- export type ItemProps = {
146
+ export type AudioPlayerHeaderProps = {
172
147
  configuration: HeaderConfiguration;
173
148
  data: HeaderData;
174
149
  focusedCloseButton?: boolean;
175
150
  focusedLeadingAction?: boolean;
176
151
  focusedTrailingAction?: boolean;
152
+ onClose?: () => void;
153
+ onBack?: () => void;
154
+ onCancel?: () => void;
155
+ onDone?: () => void;
156
+ onClear?: () => void;
177
157
  };
178
158
 
179
159
  // ---------------------------------------------------------------------------
@@ -202,19 +182,12 @@ type ActionButtonsBehaviorSpec = {
202
182
  const CONTAINER_SPEC: Record<HeaderVariant, ContainerSpec> = {
203
183
  singleLine: {
204
184
  horizontalGutter: 16,
205
- paddingTop: 0,
206
- paddingRight: 14,
207
- paddingBottom: 8,
208
- paddingLeft: 20,
209
- },
210
- headerWithActions: {
211
- horizontalGutter: 12,
212
- paddingTop: 0,
185
+ paddingTop: 24,
213
186
  paddingRight: 20,
214
- paddingBottom: 8,
187
+ paddingBottom: 12,
215
188
  paddingLeft: 20,
216
189
  },
217
- headerWithActionsCancelOnly: {
190
+ headerWithActions: {
218
191
  horizontalGutter: 12,
219
192
  paddingTop: 0,
220
193
  paddingRight: 20,
@@ -244,11 +217,6 @@ const ACTION_BUTTONS_BEHAVIOR: Record<
244
217
  leading: { enabled: true, actionType: "cancel" },
245
218
  trailing: { enabled: true, actionType: "done" },
246
219
  },
247
- headerWithActionsCancelOnly: {
248
- sectionEnabled: true,
249
- leading: { enabled: true, actionType: "cancel" },
250
- trailing: { enabled: false },
251
- },
252
220
  section: {
253
221
  sectionEnabled: true,
254
222
  leading: { enabled: false },
@@ -256,9 +224,6 @@ const ACTION_BUTTONS_BEHAVIOR: Record<
256
224
  },
257
225
  };
258
226
 
259
- const DEFAULT_CLOSE_BUTTON_ASSET =
260
- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAAl0lEQVR42u2YQQ7AIAgE/f+naQ899dDYyiLT7DyAnRijwBjGGLNEnKCy4qJCNlVYKZ2aETeUshLhTGlZbUVh5UGkB8hlM4PKZDMCy2VXgrfJfhHYLvtGpI3sjFA72SextrKz0m1bUYwsTvhX97f9C4F8g5G/HLKPQHZqyF4YOW0g5znkxCytjV6koFZVqGVg5Yc0jDFmiQNENiwNUMoCZAAAAABJRU5ErkJggg==";
261
-
262
227
  const CLOSE_BUTTON_SPEC = {
263
228
  width: 44,
264
229
  height: 44,
@@ -298,19 +263,34 @@ function shouldShowActionSlot(slot: ActionSlotSpec, sectionEnabled: boolean) {
298
263
 
299
264
  type HeaderContainerProps = {
300
265
  variant: HeaderVariant;
266
+ configuration: HeaderConfiguration;
301
267
  children: React.ReactNode;
302
268
  };
303
269
 
304
- function HeaderContainer({ variant, children }: HeaderContainerProps) {
270
+ const TRANSPARENT = "transparent";
271
+
272
+ function HeaderContainer({
273
+ variant,
274
+ configuration,
275
+ children,
276
+ }: HeaderContainerProps) {
305
277
  const container = CONTAINER_SPEC[variant];
306
278
 
279
+ const paddingTop =
280
+ configuration.paddingTop !== undefined
281
+ ? configuration.paddingTop
282
+ : container.paddingTop;
283
+
284
+ const backgroundColor = (configuration as any).backgroundColor || TRANSPARENT;
285
+
307
286
  return (
308
287
  <View
309
288
  style={{
310
- paddingTop: container.paddingTop,
289
+ paddingTop: paddingTop,
311
290
  paddingRight: container.paddingRight,
312
291
  paddingBottom: container.paddingBottom,
313
292
  paddingLeft: container.paddingLeft,
293
+ backgroundColor: backgroundColor,
314
294
  }}
315
295
  >
316
296
  {children}
@@ -325,17 +305,18 @@ type HeaderTitleProps = {
325
305
  };
326
306
 
327
307
  function HeaderTitle({ title, configuration, centered }: HeaderTitleProps) {
328
- const letterSpacing =
329
- Platform.OS === "ios"
330
- ? configuration.iosLetterSpacing
331
- : configuration.androidLetterSpacing;
308
+ const { fontColor, fontSize } = configuration;
309
+
310
+ const letterSpacing = isApplePlatform()
311
+ ? configuration.iosLetterSpacing
312
+ : configuration.androidLetterSpacing;
332
313
 
333
314
  return (
334
315
  <Text
335
316
  style={{
336
317
  flex: centered ? undefined : 1,
337
- color: configuration.fontColor,
338
- fontSize: configuration.fontSize,
318
+ color: fontColor,
319
+ fontSize: fontSize,
339
320
  lineHeight: configuration.lineHeight,
340
321
  letterSpacing,
341
322
  fontFamily: Platform.select({
@@ -356,21 +337,80 @@ function HeaderTitle({ title, configuration, centered }: HeaderTitleProps) {
356
337
  type HeaderCloseButtonProps = {
357
338
  configuration: CloseButtonConfiguration;
358
339
  focused?: boolean;
340
+ onPress?: () => void;
359
341
  };
360
342
 
361
343
  function HeaderCloseButton({
362
344
  configuration,
363
345
  focused = false,
346
+ onPress,
364
347
  }: HeaderCloseButtonProps) {
365
348
  if (!configuration.enabled) {
366
349
  return null;
367
350
  }
368
351
 
369
- const asset = configuration.asset ?? DEFAULT_CLOSE_BUTTON_ASSET;
352
+ const buttonSize = 32;
370
353
 
371
354
  return (
372
- <View
373
- style={[
355
+ <Pressable
356
+ onPress={onPress}
357
+ style={({ pressed }) => [
358
+ styles.closeButton,
359
+ {
360
+ width: buttonSize,
361
+ height: buttonSize,
362
+ borderRadius: buttonSize / 2,
363
+ borderWidth: 1.5,
364
+ borderColor: "rgba(255, 255, 255, 0.4)",
365
+ opacity: pressed || focused ? 0.7 : 1,
366
+ justifyContent: "center",
367
+ alignItems: "center",
368
+ },
369
+ ]}
370
+ >
371
+ <View
372
+ style={{
373
+ width: 14,
374
+ height: 14,
375
+ justifyContent: "center",
376
+ alignItems: "center",
377
+ }}
378
+ >
379
+ <View
380
+ style={{
381
+ position: "absolute",
382
+ width: 14,
383
+ height: 1.5,
384
+ backgroundColor: "#FFFFFF",
385
+ transform: [{ rotate: "45deg" }],
386
+ borderRadius: 1,
387
+ }}
388
+ />
389
+ <View
390
+ style={{
391
+ position: "absolute",
392
+ width: 14,
393
+ height: 1.5,
394
+ backgroundColor: "#FFFFFF",
395
+ transform: [{ rotate: "-45deg" }],
396
+ borderRadius: 1,
397
+ }}
398
+ />
399
+ </View>
400
+ </Pressable>
401
+ );
402
+ }
403
+
404
+ type HeaderBackButtonProps = {
405
+ focused?: boolean;
406
+ onPress?: () => void;
407
+ };
408
+
409
+ function HeaderBackButton({ focused = false, onPress }: HeaderBackButtonProps) {
410
+ return (
411
+ <Pressable
412
+ onPress={onPress}
413
+ style={({ pressed }) => [
374
414
  styles.closeButton,
375
415
  {
376
416
  width: CLOSE_BUTTON_SPEC.width,
@@ -382,19 +422,14 @@ function HeaderCloseButton({
382
422
  borderRadius: CLOSE_BUTTON_SPEC.width / 2,
383
423
  borderWidth: 1,
384
424
  borderColor: "#FFFFFF",
385
- opacity: focused ? 0.7 : 1,
425
+ opacity: pressed || focused ? 0.7 : 1,
386
426
  },
387
427
  ]}
388
428
  >
389
- <Image
390
- source={{ uri: asset }}
391
- style={{
392
- width: CLOSE_BUTTON_SPEC.width * 0.55,
393
- height: CLOSE_BUTTON_SPEC.height * 0.55,
394
- }}
395
- resizeMode="contain"
396
- />
397
- </View>
429
+ <Text style={{ color: "#FFFFFF", fontSize: 20, fontWeight: "bold" }}>
430
+ ‹
431
+ </Text>
432
+ </Pressable>
398
433
  );
399
434
  }
400
435
 
@@ -402,25 +437,29 @@ type HeaderActionButtonProps = {
402
437
  actionType: HeaderActionType;
403
438
  configuration: ActionButtonsStyleConfiguration;
404
439
  focused?: boolean;
440
+ onPress?: () => void;
405
441
  };
406
442
 
407
443
  function HeaderActionButton({
408
444
  actionType,
409
445
  configuration,
410
446
  focused = false,
447
+ onPress,
411
448
  }: HeaderActionButtonProps) {
412
449
  return (
413
- <View
414
- style={{
415
- backgroundColor: focused
416
- ? configuration.focusedBackgroundColor
417
- : configuration.defaultBackgroundColor,
450
+ <Pressable
451
+ onPress={onPress}
452
+ style={({ pressed }) => ({
453
+ backgroundColor:
454
+ pressed || focused
455
+ ? configuration.focusedBackgroundColor
456
+ : configuration.defaultBackgroundColor,
418
457
  borderRadius: ACTION_BUTTON_LAYOUT_SPEC.cornerRadius,
419
458
  paddingTop: ACTION_BUTTON_LAYOUT_SPEC.paddingTop,
420
459
  paddingRight: ACTION_BUTTON_LAYOUT_SPEC.paddingRight,
421
460
  paddingBottom: ACTION_BUTTON_LAYOUT_SPEC.paddingBottom,
422
461
  paddingLeft: ACTION_BUTTON_LAYOUT_SPEC.paddingLeft,
423
- }}
462
+ })}
424
463
  >
425
464
  <Text
426
465
  style={{
@@ -444,7 +483,7 @@ function HeaderActionButton({
444
483
  >
445
484
  {ACTION_LABELS[actionType]}
446
485
  </Text>
447
- </View>
486
+ </Pressable>
448
487
  );
449
488
  }
450
489
 
@@ -536,17 +575,32 @@ function CenterAlignedHeaderLayout({
536
575
  }
537
576
 
538
577
  // ---------------------------------------------------------------------------
539
- // Item
578
+ // AudioPlayerHeader
540
579
  // ---------------------------------------------------------------------------
541
580
 
542
- export function Item({
581
+ import { useModalBlocksStyle } from "../../ModalBlocksStyleContext";
582
+ import { mergeHeaderConfiguration } from "../utils/mergeStyles";
583
+
584
+ export function AudioPlayerHeader({
543
585
  configuration,
544
586
  data,
545
587
  focusedCloseButton = false,
546
588
  focusedLeadingAction = false,
547
589
  focusedTrailingAction = false,
548
- }: ItemProps) {
549
- const { variant, title, closeButton, actionButtons } = configuration;
590
+ onClose,
591
+ onBack,
592
+ onCancel,
593
+ onDone,
594
+ onClear,
595
+ }: AudioPlayerHeaderProps) {
596
+ const modalBlocksStyle = useModalBlocksStyle();
597
+ const headerBlock = modalBlocksStyle?.header;
598
+
599
+ const effectiveConfig = React.useMemo(() => {
600
+ return mergeHeaderConfiguration(configuration, headerBlock);
601
+ }, [configuration, headerBlock]);
602
+
603
+ const { variant, title, closeButton, actionButtons } = effectiveConfig;
550
604
  const { title: titleText } = data;
551
605
 
552
606
  const container = CONTAINER_SPEC[variant];
@@ -568,7 +622,10 @@ export function Item({
568
622
  actionType={behavior.leading.actionType}
569
623
  configuration={actionButtons}
570
624
  focused={focusedLeadingAction}
625
+ onPress={onCancel}
571
626
  />
627
+ ) : onBack ? (
628
+ <HeaderBackButton focused={focusedCloseButton} onPress={onBack} />
572
629
  ) : null;
573
630
 
574
631
  const trailingContent = showTrailingAction ? (
@@ -576,17 +633,19 @@ export function Item({
576
633
  actionType={behavior.trailing.actionType}
577
634
  configuration={actionButtons}
578
635
  focused={focusedTrailingAction}
636
+ onPress={behavior.trailing.actionType === "done" ? onDone : onClear}
579
637
  />
580
638
  ) : closeButton.enabled && !isCenterAligned ? (
581
639
  <HeaderCloseButton
582
640
  configuration={closeButton}
583
641
  focused={focusedCloseButton}
642
+ onPress={onClose}
584
643
  />
585
644
  ) : null;
586
645
 
587
646
  if (isCenterAligned) {
588
647
  return (
589
- <HeaderContainer variant={variant}>
648
+ <HeaderContainer variant={variant} configuration={configuration}>
590
649
  <CenterAlignedHeaderLayout
591
650
  horizontalGutter={container.horizontalGutter}
592
651
  title={title}
@@ -599,7 +658,7 @@ export function Item({
599
658
  }
600
659
 
601
660
  return (
602
- <HeaderContainer variant={variant}>
661
+ <HeaderContainer variant={variant} configuration={configuration}>
603
662
  <View style={styles.row}>
604
663
  {leadingContent ? (
605
664
  <>
@@ -647,24 +706,7 @@ export const VARIANT_HEADER_WITH_ACTIONS: HeaderConfiguration = {
647
706
  },
648
707
  closeButton: { enabled: false },
649
708
  actionButtons: DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION,
650
- };
651
-
652
- /** Variant 2 — single side action (layout rule demo) */
653
- export const VARIANT_HEADER_WITH_CANCEL_ONLY: HeaderConfiguration = {
654
- variant: "headerWithActionsCancelOnly",
655
- title: {
656
- fontColor: "#FFFFFF",
657
- iosFontFamily: "SFProText-Bold",
658
- androidFontFamily: "Roboto-Bold",
659
- fontSize: 22,
660
- lineHeight: 28,
661
- iosLetterSpacing: -0.6,
662
- androidLetterSpacing: 0,
663
- textTransform: "default",
664
- alignment: "center",
665
- },
666
- closeButton: { enabled: false },
667
- actionButtons: DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION,
709
+ paddingTop: 16,
668
710
  };
669
711
 
670
712
  /** Variant 3 — Section Header */
@@ -684,45 +726,3 @@ export const VARIANT_SECTION_HEADER: HeaderConfiguration = {
684
726
  closeButton: { enabled: false },
685
727
  actionButtons: DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION,
686
728
  };
687
-
688
- export default function App() {
689
- return (
690
- <View style={styles.demoScreen}>
691
- <Text style={styles.demoHeading}>Modal Header Building Block</Text>
692
-
693
- <View style={styles.demoBlock}>
694
- <Text style={styles.demoLabel}>Single Line Header</Text>
695
- <Item
696
- configuration={VARIANT_SINGLE_LINE_HEADER}
697
- data={{ title: "Playback Speed" }}
698
- />
699
- </View>
700
-
701
- <View style={styles.demoBlock}>
702
- <Text style={styles.demoLabel}>Header With Actions</Text>
703
- <Item
704
- configuration={VARIANT_HEADER_WITH_ACTIONS}
705
- data={{ title: "Playback Speed" }}
706
- />
707
- </View>
708
-
709
- <View style={styles.demoBlock}>
710
- <Text style={styles.demoLabel}>
711
- Header With Actions — single side action
712
- </Text>
713
- <Item
714
- configuration={VARIANT_HEADER_WITH_CANCEL_ONLY}
715
- data={{ title: "Playback Speed" }}
716
- />
717
- </View>
718
-
719
- <View style={styles.demoBlock}>
720
- <Text style={styles.demoLabel}>Section Header</Text>
721
- <Item
722
- configuration={VARIANT_SECTION_HEADER}
723
- data={{ title: "Next in Queue" }}
724
- />
725
- </View>
726
- </View>
727
- );
728
- }