@applicaster/zapp-react-native-ui-components 16.0.0-rc.33 → 16.0.0-rc.34

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 (22) hide show
  1. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  2. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  3. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  4. package/Components/MasterCell/DefaultComponents/PressableView.tsx +3 -3
  5. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +40 -2
  6. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  7. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  8. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  9. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -0
  10. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +1 -1
  11. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  12. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  13. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  14. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  15. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  16. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  17. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +368 -0
  18. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +424 -0
  19. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +728 -0
  20. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +709 -0
  21. package/Components/ModalComponent/AudioPlayer/Components/index.ts +7 -0
  22. package/package.json +5 -5
@@ -0,0 +1,728 @@
1
+ /**
2
+ * Modal Building Block — Header
3
+ *
4
+ * Single-file Expo Snack demo for the Header building block spec.
5
+ *
6
+ * Green (configurable): title, closeButton (enable + asset), actionButtons styling
7
+ * White (hardcoded): container, action button layout/enable/actions, close dimensions,
8
+ * action button padding/radius, action labels
9
+ */
10
+
11
+ import React, { useCallback, useState } from "react";
12
+ import { Image, Platform, StyleSheet, Text, View } from "react-native";
13
+
14
+ const styles = StyleSheet.create({
15
+ row: {
16
+ flexDirection: "row",
17
+ alignItems: "center",
18
+ },
19
+ centerAlignedRow: {
20
+ flexDirection: "row",
21
+ alignItems: "center",
22
+ minHeight: 44,
23
+ position: "relative",
24
+ },
25
+ measureLayer: {
26
+ position: "absolute",
27
+ opacity: 0,
28
+ left: 0,
29
+ top: 0,
30
+ flexDirection: "row",
31
+ },
32
+ sideSlotFixed: {
33
+ justifyContent: "center",
34
+ minHeight: 44,
35
+ },
36
+ centerSpacer: {
37
+ flex: 1,
38
+ },
39
+ absoluteTitleLayer: {
40
+ ...StyleSheet.absoluteFillObject,
41
+ alignItems: "center",
42
+ justifyContent: "center",
43
+ },
44
+ closeButton: {
45
+ alignItems: "center",
46
+ justifyContent: "center",
47
+ },
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
+ });
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Configuration (green / editable fields from spec)
75
+ // ---------------------------------------------------------------------------
76
+
77
+ export type HeaderActionType = "cancel" | "done" | "clear";
78
+
79
+ export type TextTransform =
80
+ | "default"
81
+ | "lowercase"
82
+ | "uppercase"
83
+ | "capitalize";
84
+
85
+ export type TitleAlignment = "left" | "center";
86
+
87
+ /** Selects hardcoded container + action layout (white fields). */
88
+ export type HeaderVariant =
89
+ | "singleLine"
90
+ | "headerWithActions"
91
+ | "headerWithActionsCancelOnly"
92
+ | "section";
93
+
94
+ export type TitleConfiguration = {
95
+ fontColor: string;
96
+ iosFontFamily: string;
97
+ androidFontFamily: string;
98
+ fontSize: number;
99
+ lineHeight: number;
100
+ iosLetterSpacing: number;
101
+ androidLetterSpacing: number;
102
+ textTransform: TextTransform;
103
+ alignment: TitleAlignment;
104
+ };
105
+
106
+ export type CloseButtonConfiguration = {
107
+ enabled: boolean;
108
+ asset?: string;
109
+ };
110
+
111
+ export type ActionButtonsStyleConfiguration = {
112
+ defaultBackgroundColor: string;
113
+ focusedBackgroundColor: string;
114
+ fontColor: string;
115
+ focusedFontColor: string;
116
+ iosFontFamily: string;
117
+ androidFontFamily: string;
118
+ fontSize: number;
119
+ lineHeight: number;
120
+ iosLetterSpacing: number;
121
+ androidLetterSpacing: number;
122
+ textTransform: TextTransform;
123
+ };
124
+
125
+ export type HeaderConfiguration = {
126
+ variant: HeaderVariant;
127
+ title: TitleConfiguration;
128
+ closeButton: CloseButtonConfiguration;
129
+ actionButtons: ActionButtonsStyleConfiguration;
130
+ };
131
+
132
+ export const DEFAULT_TITLE_CONFIGURATION: TitleConfiguration = {
133
+ fontColor: "#FFFFFF",
134
+ iosFontFamily: "SFProText-Bold",
135
+ androidFontFamily: "Roboto-Bold",
136
+ fontSize: 22,
137
+ lineHeight: 28,
138
+ iosLetterSpacing: -0.6,
139
+ androidLetterSpacing: 0,
140
+ textTransform: "default",
141
+ alignment: "left",
142
+ };
143
+
144
+ export const DEFAULT_CLOSE_BUTTON_CONFIGURATION: CloseButtonConfiguration = {
145
+ enabled: true,
146
+ };
147
+
148
+ export const DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION: ActionButtonsStyleConfiguration =
149
+ {
150
+ defaultBackgroundColor: "rgba(62, 62, 62, 1)",
151
+ focusedBackgroundColor: "rgba(78, 78, 78, 1)",
152
+ fontColor: "#FFFFFF",
153
+ focusedFontColor: "#FFFFFF",
154
+ iosFontFamily: "SFProText-Semibold",
155
+ androidFontFamily: "Roboto-Medium",
156
+ fontSize: 14,
157
+ lineHeight: 20,
158
+ iosLetterSpacing: 0,
159
+ androidLetterSpacing: 0,
160
+ textTransform: "default",
161
+ };
162
+
163
+ // ---------------------------------------------------------------------------
164
+ // Data (runtime / localization — not styling config)
165
+ // ---------------------------------------------------------------------------
166
+
167
+ export type HeaderData = {
168
+ title: string;
169
+ };
170
+
171
+ export type ItemProps = {
172
+ configuration: HeaderConfiguration;
173
+ data: HeaderData;
174
+ focusedCloseButton?: boolean;
175
+ focusedLeadingAction?: boolean;
176
+ focusedTrailingAction?: boolean;
177
+ };
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Hardcoded spec values (white fields)
181
+ // ---------------------------------------------------------------------------
182
+
183
+ type ContainerSpec = {
184
+ horizontalGutter: number;
185
+ paddingTop: number;
186
+ paddingRight: number;
187
+ paddingBottom: number;
188
+ paddingLeft: number;
189
+ };
190
+
191
+ type ActionSlotSpec = {
192
+ enabled: boolean;
193
+ actionType?: HeaderActionType;
194
+ };
195
+
196
+ type ActionButtonsBehaviorSpec = {
197
+ sectionEnabled: boolean;
198
+ leading: ActionSlotSpec;
199
+ trailing: ActionSlotSpec;
200
+ };
201
+
202
+ const CONTAINER_SPEC: Record<HeaderVariant, ContainerSpec> = {
203
+ singleLine: {
204
+ horizontalGutter: 16,
205
+ paddingTop: 0,
206
+ paddingRight: 14,
207
+ paddingBottom: 8,
208
+ paddingLeft: 20,
209
+ },
210
+ headerWithActions: {
211
+ horizontalGutter: 12,
212
+ paddingTop: 0,
213
+ paddingRight: 20,
214
+ paddingBottom: 8,
215
+ paddingLeft: 20,
216
+ },
217
+ headerWithActionsCancelOnly: {
218
+ horizontalGutter: 12,
219
+ paddingTop: 0,
220
+ paddingRight: 20,
221
+ paddingBottom: 8,
222
+ paddingLeft: 20,
223
+ },
224
+ section: {
225
+ horizontalGutter: 12,
226
+ paddingTop: 0,
227
+ paddingRight: 20,
228
+ paddingBottom: 0,
229
+ paddingLeft: 20,
230
+ },
231
+ };
232
+
233
+ const ACTION_BUTTONS_BEHAVIOR: Record<
234
+ HeaderVariant,
235
+ ActionButtonsBehaviorSpec
236
+ > = {
237
+ singleLine: {
238
+ sectionEnabled: false,
239
+ leading: { enabled: false },
240
+ trailing: { enabled: false },
241
+ },
242
+ headerWithActions: {
243
+ sectionEnabled: true,
244
+ leading: { enabled: true, actionType: "cancel" },
245
+ trailing: { enabled: true, actionType: "done" },
246
+ },
247
+ headerWithActionsCancelOnly: {
248
+ sectionEnabled: true,
249
+ leading: { enabled: true, actionType: "cancel" },
250
+ trailing: { enabled: false },
251
+ },
252
+ section: {
253
+ sectionEnabled: true,
254
+ leading: { enabled: false },
255
+ trailing: { enabled: true, actionType: "clear" },
256
+ },
257
+ };
258
+
259
+ const DEFAULT_CLOSE_BUTTON_ASSET =
260
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAAl0lEQVR42u2YQQ7AIAgE/f+naQ899dDYyiLT7DyAnRijwBjGGLNEnKCy4qJCNlVYKZ2aETeUshLhTGlZbUVh5UGkB8hlM4PKZDMCy2VXgrfJfhHYLvtGpI3sjFA72SextrKz0m1bUYwsTvhX97f9C4F8g5G/HLKPQHZqyF4YOW0g5znkxCytjV6koFZVqGVg5Yc0jDFmiQNENiwNUMoCZAAAAABJRU5ErkJggg==";
261
+
262
+ const CLOSE_BUTTON_SPEC = {
263
+ width: 44,
264
+ height: 44,
265
+ paddingTop: 0,
266
+ paddingRight: 0,
267
+ paddingBottom: 0,
268
+ paddingLeft: 0,
269
+ };
270
+
271
+ const ACTION_BUTTON_LAYOUT_SPEC = {
272
+ paddingTop: 6,
273
+ paddingRight: 10,
274
+ paddingBottom: 6,
275
+ paddingLeft: 10,
276
+ cornerRadius: 16,
277
+ };
278
+
279
+ const ACTION_LABELS: Record<HeaderActionType, string> = {
280
+ cancel: "Cancel",
281
+ done: "Done",
282
+ clear: "Clear",
283
+ };
284
+
285
+ function getTextTransformStyle(
286
+ textTransform: TextTransform
287
+ ): "none" | "lowercase" | "uppercase" | "capitalize" {
288
+ return textTransform === "default" ? "none" : textTransform;
289
+ }
290
+
291
+ function shouldShowActionSlot(slot: ActionSlotSpec, sectionEnabled: boolean) {
292
+ return sectionEnabled && slot.enabled && !!slot.actionType;
293
+ }
294
+
295
+ // ---------------------------------------------------------------------------
296
+ // Inner components
297
+ // ---------------------------------------------------------------------------
298
+
299
+ type HeaderContainerProps = {
300
+ variant: HeaderVariant;
301
+ children: React.ReactNode;
302
+ };
303
+
304
+ function HeaderContainer({ variant, children }: HeaderContainerProps) {
305
+ const container = CONTAINER_SPEC[variant];
306
+
307
+ return (
308
+ <View
309
+ style={{
310
+ paddingTop: container.paddingTop,
311
+ paddingRight: container.paddingRight,
312
+ paddingBottom: container.paddingBottom,
313
+ paddingLeft: container.paddingLeft,
314
+ }}
315
+ >
316
+ {children}
317
+ </View>
318
+ );
319
+ }
320
+
321
+ type HeaderTitleProps = {
322
+ title: string;
323
+ configuration: TitleConfiguration;
324
+ centered: boolean;
325
+ };
326
+
327
+ function HeaderTitle({ title, configuration, centered }: HeaderTitleProps) {
328
+ const letterSpacing =
329
+ Platform.OS === "ios"
330
+ ? configuration.iosLetterSpacing
331
+ : configuration.androidLetterSpacing;
332
+
333
+ return (
334
+ <Text
335
+ style={{
336
+ flex: centered ? undefined : 1,
337
+ color: configuration.fontColor,
338
+ fontSize: configuration.fontSize,
339
+ lineHeight: configuration.lineHeight,
340
+ letterSpacing,
341
+ fontFamily: Platform.select({
342
+ ios: configuration.iosFontFamily,
343
+ android: configuration.androidFontFamily,
344
+ default: configuration.androidFontFamily,
345
+ }),
346
+ textTransform: getTextTransformStyle(configuration.textTransform),
347
+ textAlign: centered ? "center" : configuration.alignment,
348
+ }}
349
+ numberOfLines={1}
350
+ >
351
+ {title}
352
+ </Text>
353
+ );
354
+ }
355
+
356
+ type HeaderCloseButtonProps = {
357
+ configuration: CloseButtonConfiguration;
358
+ focused?: boolean;
359
+ };
360
+
361
+ function HeaderCloseButton({
362
+ configuration,
363
+ focused = false,
364
+ }: HeaderCloseButtonProps) {
365
+ if (!configuration.enabled) {
366
+ return null;
367
+ }
368
+
369
+ const asset = configuration.asset ?? DEFAULT_CLOSE_BUTTON_ASSET;
370
+
371
+ return (
372
+ <View
373
+ style={[
374
+ styles.closeButton,
375
+ {
376
+ width: CLOSE_BUTTON_SPEC.width,
377
+ height: CLOSE_BUTTON_SPEC.height,
378
+ marginTop: CLOSE_BUTTON_SPEC.paddingTop,
379
+ marginRight: CLOSE_BUTTON_SPEC.paddingRight,
380
+ marginBottom: CLOSE_BUTTON_SPEC.paddingBottom,
381
+ marginLeft: CLOSE_BUTTON_SPEC.paddingLeft,
382
+ borderRadius: CLOSE_BUTTON_SPEC.width / 2,
383
+ borderWidth: 1,
384
+ borderColor: "#FFFFFF",
385
+ opacity: focused ? 0.7 : 1,
386
+ },
387
+ ]}
388
+ >
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>
398
+ );
399
+ }
400
+
401
+ type HeaderActionButtonProps = {
402
+ actionType: HeaderActionType;
403
+ configuration: ActionButtonsStyleConfiguration;
404
+ focused?: boolean;
405
+ };
406
+
407
+ function HeaderActionButton({
408
+ actionType,
409
+ configuration,
410
+ focused = false,
411
+ }: HeaderActionButtonProps) {
412
+ return (
413
+ <View
414
+ style={{
415
+ backgroundColor: focused
416
+ ? configuration.focusedBackgroundColor
417
+ : configuration.defaultBackgroundColor,
418
+ borderRadius: ACTION_BUTTON_LAYOUT_SPEC.cornerRadius,
419
+ paddingTop: ACTION_BUTTON_LAYOUT_SPEC.paddingTop,
420
+ paddingRight: ACTION_BUTTON_LAYOUT_SPEC.paddingRight,
421
+ paddingBottom: ACTION_BUTTON_LAYOUT_SPEC.paddingBottom,
422
+ paddingLeft: ACTION_BUTTON_LAYOUT_SPEC.paddingLeft,
423
+ }}
424
+ >
425
+ <Text
426
+ style={{
427
+ color: focused
428
+ ? configuration.focusedFontColor
429
+ : configuration.fontColor,
430
+ fontSize: configuration.fontSize,
431
+ lineHeight: configuration.lineHeight,
432
+ letterSpacing:
433
+ Platform.OS === "ios"
434
+ ? configuration.iosLetterSpacing
435
+ : configuration.androidLetterSpacing,
436
+ fontFamily: Platform.select({
437
+ ios: configuration.iosFontFamily,
438
+ android: configuration.androidFontFamily,
439
+ default: configuration.androidFontFamily,
440
+ }),
441
+ textTransform: getTextTransformStyle(configuration.textTransform),
442
+ }}
443
+ numberOfLines={1}
444
+ >
445
+ {ACTION_LABELS[actionType]}
446
+ </Text>
447
+ </View>
448
+ );
449
+ }
450
+
451
+ type CenterAlignedHeaderLayoutProps = {
452
+ horizontalGutter: number;
453
+ title: TitleConfiguration;
454
+ titleText: string;
455
+ leadingContent: React.ReactNode;
456
+ trailingContent: React.ReactNode;
457
+ };
458
+
459
+ function CenterAlignedHeaderLayout({
460
+ horizontalGutter,
461
+ title,
462
+ titleText,
463
+ leadingContent,
464
+ trailingContent,
465
+ }: CenterAlignedHeaderLayoutProps) {
466
+ const [sideWidths, setSideWidths] = useState({ leading: 0, trailing: 0 });
467
+ const sideSlotWidth = Math.max(sideWidths.leading, sideWidths.trailing);
468
+
469
+ const updateSideWidth = useCallback(
470
+ (side: "leading" | "trailing", width: number) => {
471
+ setSideWidths((previous) => {
472
+ if (previous[side] === width) {
473
+ return previous;
474
+ }
475
+
476
+ return { ...previous, [side]: width };
477
+ });
478
+ },
479
+ []
480
+ );
481
+
482
+ return (
483
+ <View style={styles.centerAlignedRow}>
484
+ <View style={styles.measureLayer} pointerEvents="none">
485
+ {leadingContent ? (
486
+ <View
487
+ onLayout={(event) =>
488
+ updateSideWidth("leading", event.nativeEvent.layout.width)
489
+ }
490
+ >
491
+ {leadingContent}
492
+ </View>
493
+ ) : null}
494
+ {trailingContent ? (
495
+ <View
496
+ onLayout={(event) =>
497
+ updateSideWidth("trailing", event.nativeEvent.layout.width)
498
+ }
499
+ >
500
+ {trailingContent}
501
+ </View>
502
+ ) : null}
503
+ </View>
504
+
505
+ <View
506
+ style={[
507
+ styles.sideSlotFixed,
508
+ { width: sideSlotWidth, alignItems: "flex-start" },
509
+ ]}
510
+ >
511
+ {leadingContent}
512
+ </View>
513
+ <View style={{ width: horizontalGutter }} />
514
+ <View style={styles.centerSpacer} />
515
+ <View style={{ width: horizontalGutter }} />
516
+ <View
517
+ style={[
518
+ styles.sideSlotFixed,
519
+ { width: sideSlotWidth, alignItems: "flex-end" },
520
+ ]}
521
+ >
522
+ {trailingContent}
523
+ </View>
524
+
525
+ <View
526
+ pointerEvents="none"
527
+ style={[
528
+ styles.absoluteTitleLayer,
529
+ { paddingHorizontal: sideSlotWidth + horizontalGutter },
530
+ ]}
531
+ >
532
+ <HeaderTitle title={titleText} configuration={title} centered />
533
+ </View>
534
+ </View>
535
+ );
536
+ }
537
+
538
+ // ---------------------------------------------------------------------------
539
+ // Item
540
+ // ---------------------------------------------------------------------------
541
+
542
+ export function Item({
543
+ configuration,
544
+ data,
545
+ focusedCloseButton = false,
546
+ focusedLeadingAction = false,
547
+ focusedTrailingAction = false,
548
+ }: ItemProps) {
549
+ const { variant, title, closeButton, actionButtons } = configuration;
550
+ const { title: titleText } = data;
551
+
552
+ const container = CONTAINER_SPEC[variant];
553
+ const behavior = ACTION_BUTTONS_BEHAVIOR[variant];
554
+ const isCenterAligned = title.alignment === "center";
555
+
556
+ const showLeadingAction = shouldShowActionSlot(
557
+ behavior.leading,
558
+ behavior.sectionEnabled
559
+ );
560
+
561
+ const showTrailingAction = shouldShowActionSlot(
562
+ behavior.trailing,
563
+ behavior.sectionEnabled
564
+ );
565
+
566
+ const leadingContent = showLeadingAction ? (
567
+ <HeaderActionButton
568
+ actionType={behavior.leading.actionType}
569
+ configuration={actionButtons}
570
+ focused={focusedLeadingAction}
571
+ />
572
+ ) : null;
573
+
574
+ const trailingContent = showTrailingAction ? (
575
+ <HeaderActionButton
576
+ actionType={behavior.trailing.actionType}
577
+ configuration={actionButtons}
578
+ focused={focusedTrailingAction}
579
+ />
580
+ ) : closeButton.enabled && !isCenterAligned ? (
581
+ <HeaderCloseButton
582
+ configuration={closeButton}
583
+ focused={focusedCloseButton}
584
+ />
585
+ ) : null;
586
+
587
+ if (isCenterAligned) {
588
+ return (
589
+ <HeaderContainer variant={variant}>
590
+ <CenterAlignedHeaderLayout
591
+ horizontalGutter={container.horizontalGutter}
592
+ title={title}
593
+ titleText={titleText}
594
+ leadingContent={leadingContent}
595
+ trailingContent={trailingContent}
596
+ />
597
+ </HeaderContainer>
598
+ );
599
+ }
600
+
601
+ return (
602
+ <HeaderContainer variant={variant}>
603
+ <View style={styles.row}>
604
+ {leadingContent ? (
605
+ <>
606
+ {leadingContent}
607
+ <View style={{ width: container.horizontalGutter }} />
608
+ </>
609
+ ) : null}
610
+ <HeaderTitle title={titleText} configuration={title} centered={false} />
611
+ {trailingContent ? (
612
+ <>
613
+ <View style={{ width: container.horizontalGutter }} />
614
+ {trailingContent}
615
+ </>
616
+ ) : null}
617
+ </View>
618
+ </HeaderContainer>
619
+ );
620
+ }
621
+
622
+ // ---------------------------------------------------------------------------
623
+ // Variant configurations (green defaults per PDF column)
624
+ // ---------------------------------------------------------------------------
625
+
626
+ /** Default / Variant 1 — Single Line Header */
627
+ export const VARIANT_SINGLE_LINE_HEADER: HeaderConfiguration = {
628
+ variant: "singleLine",
629
+ title: DEFAULT_TITLE_CONFIGURATION,
630
+ closeButton: DEFAULT_CLOSE_BUTTON_CONFIGURATION,
631
+ actionButtons: DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION,
632
+ };
633
+
634
+ /** Variant 2 — Header With Actions */
635
+ export const VARIANT_HEADER_WITH_ACTIONS: HeaderConfiguration = {
636
+ variant: "headerWithActions",
637
+ title: {
638
+ fontColor: "#FFFFFF",
639
+ iosFontFamily: "SFProText-Bold",
640
+ androidFontFamily: "Roboto-Bold",
641
+ fontSize: 22,
642
+ lineHeight: 28,
643
+ iosLetterSpacing: -0.6,
644
+ androidLetterSpacing: 0,
645
+ textTransform: "default",
646
+ alignment: "center",
647
+ },
648
+ closeButton: { enabled: false },
649
+ 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,
668
+ };
669
+
670
+ /** Variant 3 — Section Header */
671
+ export const VARIANT_SECTION_HEADER: HeaderConfiguration = {
672
+ variant: "section",
673
+ title: {
674
+ fontColor: "#FFFFFF",
675
+ iosFontFamily: "SFProText-Bold",
676
+ androidFontFamily: "Roboto-Bold",
677
+ fontSize: 13,
678
+ lineHeight: 32,
679
+ iosLetterSpacing: -0.1,
680
+ androidLetterSpacing: 0,
681
+ textTransform: "default",
682
+ alignment: "left",
683
+ },
684
+ closeButton: { enabled: false },
685
+ actionButtons: DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION,
686
+ };
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
+ }