react-native-popify 0.1.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.
@@ -0,0 +1,809 @@
1
+ import React, { useEffect, useRef, useCallback } from 'react';
2
+ import {
3
+ Modal,
4
+ View,
5
+ Text,
6
+ TouchableOpacity,
7
+ TouchableWithoutFeedback,
8
+ StyleSheet,
9
+ Animated,
10
+ Dimensions,
11
+ Platform,
12
+ } from 'react-native';
13
+
14
+ const { width: SCREEN_WIDTH } = Dimensions.get('window');
15
+
16
+ // ─── Type Configs ────────────────────────────────────────────
17
+ const TYPE_CONFIG = {
18
+ success: { color: '#00C853', bg: '#F1FBF4', icon: '✓' },
19
+ error: { color: '#FF3D71', bg: '#FFF2F2', icon: '✕' },
20
+ warning: { color: '#FFAA00', bg: '#FFFBF0', icon: '!' },
21
+ info: { color: '#3366FF', bg: '#F0F4FF', icon: 'i' },
22
+ };
23
+
24
+ const DARK_BG = {
25
+ success: '#152E1B',
26
+ error: '#2E1520',
27
+ warning: '#2E2815',
28
+ info: '#151E2E',
29
+ };
30
+
31
+ const PARTICLE_COUNT = 10;
32
+
33
+ const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
34
+
35
+ // ─── Animated Pressable Button ──────────────────────────────
36
+ const PressableButton = ({
37
+ onPress,
38
+ style,
39
+ textStyle,
40
+ label,
41
+ color,
42
+ outline,
43
+ }) => {
44
+ const scaleAnim = useRef(new Animated.Value(1)).current;
45
+ return (
46
+ <AnimatedTouchable
47
+ activeOpacity={0.85}
48
+ onPressIn={() =>
49
+ Animated.spring(scaleAnim, {
50
+ toValue: 0.93,
51
+ useNativeDriver: true,
52
+ speed: 50,
53
+ bounciness: 4,
54
+ }).start()
55
+ }
56
+ onPressOut={() =>
57
+ Animated.spring(scaleAnim, {
58
+ toValue: 1,
59
+ useNativeDriver: true,
60
+ speed: 20,
61
+ bounciness: 10,
62
+ }).start()
63
+ }
64
+ onPress={onPress}
65
+ style={[
66
+ btnStyles.base,
67
+ outline
68
+ ? [btnStyles.outline, { borderColor: color }]
69
+ : {
70
+ backgroundColor: color,
71
+ ...Platform.select({
72
+ ios: {
73
+ shadowColor: color,
74
+ shadowOffset: { width: 0, height: 4 },
75
+ shadowOpacity: 0.3,
76
+ shadowRadius: 8,
77
+ },
78
+ android: { elevation: 6 },
79
+ }),
80
+ },
81
+ { transform: [{ scale: scaleAnim }] },
82
+ style,
83
+ ]}
84
+ >
85
+ <Text style={[btnStyles.text, outline && { color: color }, textStyle]}>
86
+ {label}
87
+ </Text>
88
+ </AnimatedTouchable>
89
+ );
90
+ };
91
+
92
+ const btnStyles = StyleSheet.create({
93
+ base: {
94
+ paddingVertical: 14,
95
+ paddingHorizontal: 24,
96
+ borderRadius: 16,
97
+ alignItems: 'center',
98
+ justifyContent: 'center',
99
+ minWidth: 110,
100
+ },
101
+ text: {
102
+ color: '#FFF',
103
+ fontSize: 15,
104
+ fontWeight: '700',
105
+ letterSpacing: 0.3,
106
+ },
107
+ outline: {
108
+ backgroundColor: 'transparent',
109
+ borderWidth: 2,
110
+ },
111
+ });
112
+
113
+ // ─── PopifyAlert ────────────────────────────────────────────
114
+ const PopifyAlert = ({
115
+ visible = false,
116
+ type = 'info',
117
+ title,
118
+ message,
119
+ onClose,
120
+ buttons = [],
121
+ // Style overrides
122
+ titleStyle,
123
+ messageStyle,
124
+ containerStyle,
125
+ overlayStyle,
126
+ // Animation
127
+ animationType = 'spring', // 'spring' | 'slideUp' | 'fadeIn'
128
+ duration = 350,
129
+ // Features
130
+ autoClose = false,
131
+ autoCloseDuration = 3000,
132
+ showCloseButton = true,
133
+ showIcon = true,
134
+ showParticles = false,
135
+ customIcon,
136
+ // Layout
137
+ buttonLayout = 'auto', // 'horizontal' | 'vertical' | 'auto'
138
+ // Theme
139
+ theme = 'light', // 'light' | 'dark'
140
+ // Custom renders
141
+ renderHeader,
142
+ renderFooter,
143
+ }) => {
144
+ const config = TYPE_CONFIG[type] || TYPE_CONFIG.info;
145
+ const isDark = theme === 'dark';
146
+
147
+ // ── Animated values ──
148
+ const overlayAnim = useRef(new Animated.Value(0)).current;
149
+ const cardScale = useRef(new Animated.Value(0.6)).current;
150
+ const cardOpacity = useRef(new Animated.Value(0)).current;
151
+ const cardTranslateY = useRef(new Animated.Value(60)).current;
152
+ const iconScale = useRef(new Animated.Value(0)).current;
153
+ const iconRotate = useRef(new Animated.Value(0)).current;
154
+ const progressAnim = useRef(new Animated.Value(1)).current;
155
+ const contentOpacity = useRef(new Animated.Value(0)).current;
156
+ const contentTranslateY = useRef(new Animated.Value(15)).current;
157
+
158
+ // Per-button anims (max 6)
159
+ const btnAnims = useRef(
160
+ Array.from({ length: 6 }, () => new Animated.Value(0))
161
+ ).current;
162
+
163
+ // Particles
164
+ const particles = useRef(
165
+ Array.from({ length: PARTICLE_COUNT }, () => ({
166
+ x: new Animated.Value(0),
167
+ y: new Animated.Value(0),
168
+ opacity: new Animated.Value(0),
169
+ scale: new Animated.Value(0),
170
+ }))
171
+ ).current;
172
+
173
+ const autoCloseRef = useRef(null);
174
+
175
+ // ── Reset ──
176
+ const reset = useCallback(() => {
177
+ overlayAnim.setValue(0);
178
+ cardScale.setValue(0.6);
179
+ cardOpacity.setValue(0);
180
+ cardTranslateY.setValue(animationType === 'slideUp' ? 120 : 60);
181
+ iconScale.setValue(0);
182
+ iconRotate.setValue(0);
183
+ progressAnim.setValue(1);
184
+ contentOpacity.setValue(0);
185
+ contentTranslateY.setValue(15);
186
+ btnAnims.forEach((a) => a.setValue(0));
187
+ particles.forEach((p) => {
188
+ p.x.setValue(0);
189
+ p.y.setValue(0);
190
+ p.opacity.setValue(0);
191
+ p.scale.setValue(0);
192
+ });
193
+ }, [
194
+ animationType,
195
+ overlayAnim,
196
+ cardScale,
197
+ cardOpacity,
198
+ cardTranslateY,
199
+ iconScale,
200
+ iconRotate,
201
+ progressAnim,
202
+ contentOpacity,
203
+ contentTranslateY,
204
+ btnAnims,
205
+ particles,
206
+ ]);
207
+
208
+ // ── Animate In ──
209
+ const animateIn = useCallback(() => {
210
+ reset();
211
+
212
+ // Overlay
213
+ Animated.timing(overlayAnim, {
214
+ toValue: 1,
215
+ duration: 280,
216
+ useNativeDriver: true,
217
+ }).start();
218
+
219
+ // Card
220
+ const springCfg = { tension: 55, friction: 8, useNativeDriver: true };
221
+ const timingCfg = { duration, useNativeDriver: true };
222
+ const isSpring = animationType !== 'fadeIn';
223
+
224
+ const mkAnim = (anim, toValue) =>
225
+ isSpring
226
+ ? Animated.spring(anim, { toValue, ...springCfg })
227
+ : Animated.timing(anim, { toValue, ...timingCfg });
228
+
229
+ Animated.parallel([
230
+ mkAnim(cardScale, 1),
231
+ mkAnim(cardTranslateY, 0),
232
+ Animated.timing(cardOpacity, {
233
+ toValue: 1,
234
+ duration: 200,
235
+ useNativeDriver: true,
236
+ }),
237
+ ]).start();
238
+
239
+ // Content fade-in
240
+ setTimeout(() => {
241
+ Animated.parallel([
242
+ Animated.timing(contentOpacity, {
243
+ toValue: 1,
244
+ duration: 280,
245
+ useNativeDriver: true,
246
+ }),
247
+ Animated.spring(contentTranslateY, {
248
+ toValue: 0,
249
+ tension: 60,
250
+ friction: 10,
251
+ useNativeDriver: true,
252
+ }),
253
+ ]).start();
254
+ }, 150);
255
+
256
+ // Icon bounce
257
+ setTimeout(() => {
258
+ Animated.spring(iconScale, {
259
+ toValue: 1,
260
+ tension: 35,
261
+ friction: 5,
262
+ useNativeDriver: true,
263
+ }).start();
264
+
265
+ Animated.timing(iconRotate, {
266
+ toValue: 1,
267
+ duration: 500,
268
+ useNativeDriver: true,
269
+ }).start();
270
+ }, 250);
271
+
272
+ // Buttons stagger
273
+ setTimeout(() => {
274
+ Animated.stagger(
275
+ 70,
276
+ btnAnims.slice(0, buttons.length).map((anim) =>
277
+ Animated.spring(anim, {
278
+ toValue: 1,
279
+ tension: 55,
280
+ friction: 9,
281
+ useNativeDriver: true,
282
+ })
283
+ )
284
+ ).start();
285
+ }, 300);
286
+
287
+ // Particles burst
288
+ if (showParticles) {
289
+ setTimeout(() => {
290
+ const pAnims = particles.map((p, i) => {
291
+ const angle =
292
+ (i / PARTICLE_COUNT) * 2 * Math.PI + Math.random() * 0.3;
293
+ const radius = 45 + Math.random() * 35;
294
+ return Animated.parallel([
295
+ Animated.timing(p.x, {
296
+ toValue: Math.cos(angle) * radius,
297
+ duration: 650,
298
+ useNativeDriver: true,
299
+ }),
300
+ Animated.timing(p.y, {
301
+ toValue: Math.sin(angle) * radius,
302
+ duration: 650,
303
+ useNativeDriver: true,
304
+ }),
305
+ Animated.sequence([
306
+ Animated.timing(p.opacity, {
307
+ toValue: 1,
308
+ duration: 80,
309
+ useNativeDriver: true,
310
+ }),
311
+ Animated.timing(p.opacity, {
312
+ toValue: 0,
313
+ duration: 570,
314
+ useNativeDriver: true,
315
+ }),
316
+ ]),
317
+ Animated.sequence([
318
+ Animated.spring(p.scale, {
319
+ toValue: 1.8,
320
+ tension: 80,
321
+ friction: 5,
322
+ useNativeDriver: true,
323
+ }),
324
+ Animated.timing(p.scale, {
325
+ toValue: 0,
326
+ duration: 450,
327
+ useNativeDriver: true,
328
+ }),
329
+ ]),
330
+ ]);
331
+ });
332
+ Animated.stagger(35, pAnims).start();
333
+ }, 450);
334
+ }
335
+
336
+ // Auto close
337
+ if (autoClose && onClose) {
338
+ Animated.timing(progressAnim, {
339
+ toValue: 0,
340
+ duration: autoCloseDuration,
341
+ useNativeDriver: false,
342
+ }).start();
343
+ autoCloseRef.current = setTimeout(onClose, autoCloseDuration);
344
+ }
345
+ }, [
346
+ animationType,
347
+ autoClose,
348
+ autoCloseDuration,
349
+ showParticles,
350
+ buttons.length,
351
+ reset,
352
+ overlayAnim,
353
+ cardScale,
354
+ cardTranslateY,
355
+ cardOpacity,
356
+ contentOpacity,
357
+ contentTranslateY,
358
+ iconScale,
359
+ iconRotate,
360
+ btnAnims,
361
+ particles,
362
+ progressAnim,
363
+ onClose,
364
+ duration,
365
+ ]);
366
+
367
+ // ── Animate Out ──
368
+ const animateOut = useCallback(() => {
369
+ if (autoCloseRef.current) clearTimeout(autoCloseRef.current);
370
+
371
+ Animated.parallel([
372
+ Animated.timing(overlayAnim, {
373
+ toValue: 0,
374
+ duration: 180,
375
+ useNativeDriver: true,
376
+ }),
377
+ Animated.timing(cardScale, {
378
+ toValue: 0.6,
379
+ duration: 200,
380
+ useNativeDriver: true,
381
+ }),
382
+ Animated.timing(cardOpacity, {
383
+ toValue: 0,
384
+ duration: 180,
385
+ useNativeDriver: true,
386
+ }),
387
+ ]).start();
388
+ }, [overlayAnim, cardScale, cardOpacity]);
389
+
390
+ useEffect(() => {
391
+ if (visible) animateIn();
392
+ else animateOut();
393
+ return () => {
394
+ if (autoCloseRef.current) clearTimeout(autoCloseRef.current);
395
+ };
396
+ }, [visible, animateIn, animateOut]);
397
+
398
+ // ── Derived ──
399
+ const iconSpin = iconRotate.interpolate({
400
+ inputRange: [0, 1],
401
+ outputRange: ['0deg', '360deg'],
402
+ });
403
+
404
+ const layout =
405
+ buttonLayout === 'auto'
406
+ ? buttons.length <= 2
407
+ ? 'horizontal'
408
+ : 'vertical'
409
+ : buttonLayout;
410
+
411
+ const cardBg = isDark ? DARK_BG[type] || '#1A1A2E' : '#FFFFFF';
412
+ const titleColor = isDark ? '#F0F0F5' : '#1A1A2E';
413
+ const msgColor = isDark ? '#A0A0B0' : '#6B6B80';
414
+
415
+ const particleColors = [
416
+ config.color,
417
+ config.color + 'CC',
418
+ config.color + '99',
419
+ config.color + '66',
420
+ '#FFD700',
421
+ ];
422
+
423
+ return (
424
+ <Modal
425
+ transparent
426
+ visible={visible}
427
+ animationType="none"
428
+ statusBarTranslucent
429
+ >
430
+ <TouchableWithoutFeedback onPress={onClose}>
431
+ <Animated.View
432
+ style={[
433
+ styles.overlay,
434
+ { opacity: overlayAnim },
435
+ isDark && styles.overlayDark,
436
+ overlayStyle,
437
+ ]}
438
+ >
439
+ <TouchableWithoutFeedback>
440
+ <Animated.View
441
+ style={[
442
+ styles.card,
443
+ {
444
+ backgroundColor: cardBg,
445
+ opacity: cardOpacity,
446
+ transform: [
447
+ { scale: cardScale },
448
+ { translateY: cardTranslateY },
449
+ ],
450
+ },
451
+ isDark && styles.cardDark,
452
+ containerStyle,
453
+ ]}
454
+ >
455
+ {/* ── Header Banner ── */}
456
+ <View style={[styles.banner, { backgroundColor: config.color }]}>
457
+ <View style={styles.bannerShine} />
458
+ <View style={styles.bannerDots}>
459
+ {[0.15, 0.08, 0.12].map((op, i) => (
460
+ <View
461
+ key={i}
462
+ style={[
463
+ styles.bannerDot,
464
+ {
465
+ opacity: op,
466
+ width: 40 + i * 30,
467
+ height: 40 + i * 30,
468
+ borderRadius: 20 + i * 15,
469
+ left: -10 + i * 80,
470
+ top: -10 + i * 5,
471
+ },
472
+ ]}
473
+ />
474
+ ))}
475
+ </View>
476
+ {renderHeader && renderHeader()}
477
+ </View>
478
+
479
+ {/* ── Close Button ── */}
480
+ {showCloseButton && onClose && (
481
+ <TouchableOpacity
482
+ style={styles.closeBtn}
483
+ onPress={onClose}
484
+ hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
485
+ >
486
+ <Text style={styles.closeBtnText}>✕</Text>
487
+ </TouchableOpacity>
488
+ )}
489
+
490
+ {/* ── Icon ── */}
491
+ {showIcon && (
492
+ <View style={styles.iconWrap}>
493
+ <Animated.View
494
+ style={[
495
+ styles.iconCircleOuter,
496
+ {
497
+ backgroundColor: cardBg,
498
+ transform: [{ scale: iconScale }],
499
+ },
500
+ ]}
501
+ >
502
+ <Animated.View
503
+ style={[
504
+ styles.iconCircle,
505
+ {
506
+ backgroundColor: config.color,
507
+ transform: [{ rotate: iconSpin }],
508
+ ...Platform.select({
509
+ ios: {
510
+ shadowColor: config.color,
511
+ shadowOffset: { width: 0, height: 6 },
512
+ shadowOpacity: 0.45,
513
+ shadowRadius: 14,
514
+ },
515
+ android: { elevation: 14 },
516
+ }),
517
+ },
518
+ ]}
519
+ >
520
+ <View style={styles.iconRing}>
521
+ {customIcon || (
522
+ <Text style={styles.iconChar}>{config.icon}</Text>
523
+ )}
524
+ </View>
525
+ </Animated.View>
526
+ </Animated.View>
527
+
528
+ {/* Particles */}
529
+ {showParticles &&
530
+ particles.map((p, i) => (
531
+ <Animated.View
532
+ key={i}
533
+ style={[
534
+ styles.particle,
535
+ {
536
+ width: 7 + (i % 3) * 2,
537
+ height: 7 + (i % 3) * 2,
538
+ backgroundColor:
539
+ particleColors[i % particleColors.length],
540
+ opacity: p.opacity,
541
+ transform: [
542
+ { translateX: p.x },
543
+ { translateY: p.y },
544
+ { scale: p.scale },
545
+ ],
546
+ },
547
+ ]}
548
+ />
549
+ ))}
550
+ </View>
551
+ )}
552
+
553
+ {/* ── Content ── */}
554
+ <Animated.View
555
+ style={[
556
+ styles.content,
557
+ {
558
+ opacity: contentOpacity,
559
+ transform: [{ translateY: contentTranslateY }],
560
+ },
561
+ ]}
562
+ >
563
+ {title && (
564
+ <Text
565
+ style={[styles.title, { color: titleColor }, titleStyle]}
566
+ >
567
+ {title}
568
+ </Text>
569
+ )}
570
+ {message && (
571
+ <Text
572
+ style={[styles.message, { color: msgColor }, messageStyle]}
573
+ >
574
+ {message}
575
+ </Text>
576
+ )}
577
+ </Animated.View>
578
+
579
+ {/* ── Buttons ── */}
580
+ {buttons.length > 0 && (
581
+ <View
582
+ style={[
583
+ styles.btnWrap,
584
+ layout === 'horizontal' && styles.btnWrapRow,
585
+ ]}
586
+ >
587
+ {buttons.map((btn, idx) => {
588
+ const anim = btnAnims[idx];
589
+ return (
590
+ <Animated.View
591
+ key={idx}
592
+ style={[
593
+ layout === 'horizontal'
594
+ ? styles.btnItemHorizontal
595
+ : styles.btnItemVertical,
596
+ {
597
+ opacity: anim,
598
+ transform: [
599
+ {
600
+ translateY: anim.interpolate({
601
+ inputRange: [0, 1],
602
+ outputRange: [18, 0],
603
+ }),
604
+ },
605
+ ],
606
+ },
607
+ ]}
608
+ >
609
+ {/* <PressableButton
610
+ label={btn.label}
611
+ onPress={btn.onPress}
612
+ color={btn.color || config.color}
613
+ outline={btn.outline}
614
+ style={btn.style}
615
+ textStyle={btn.textStyle}
616
+ /> */}
617
+ </Animated.View>
618
+ );
619
+ })}
620
+ </View>
621
+ )}
622
+
623
+ {/* ── Auto-close Progress ── */}
624
+ {autoClose && (
625
+ <View style={styles.progressWrap}>
626
+ <Animated.View
627
+ style={[
628
+ styles.progressBar,
629
+ {
630
+ backgroundColor: config.color,
631
+ width: progressAnim.interpolate({
632
+ inputRange: [0, 1],
633
+ outputRange: ['0%', '100%'],
634
+ }),
635
+ },
636
+ ]}
637
+ />
638
+ </View>
639
+ )}
640
+
641
+ {/* ── Footer ── */}
642
+ {renderFooter && (
643
+ <View style={styles.footer}>{renderFooter()}</View>
644
+ )}
645
+ </Animated.View>
646
+ </TouchableWithoutFeedback>
647
+ </Animated.View>
648
+ </TouchableWithoutFeedback>
649
+ </Modal>
650
+ );
651
+ };
652
+
653
+ // ─── Styles ─────────────────────────────────────────────────
654
+ const styles = StyleSheet.create({
655
+ overlay: {
656
+ flex: 1,
657
+ justifyContent: 'center',
658
+ alignItems: 'center',
659
+ backgroundColor: 'rgba(15,15,35,0.55)',
660
+ padding: 20,
661
+ },
662
+ card: {
663
+ width: Math.min(SCREEN_WIDTH - 48, 380),
664
+ borderRadius: 28,
665
+ overflow: 'hidden',
666
+ ...Platform.select({
667
+ ios: {
668
+ shadowColor: '#000',
669
+ shadowOffset: { width: 0, height: 24 },
670
+ shadowOpacity: 0.2,
671
+ shadowRadius: 32,
672
+ },
673
+ android: { elevation: 28 },
674
+ }),
675
+ },
676
+ banner: {
677
+ height: 85,
678
+ overflow: 'hidden',
679
+ },
680
+ bannerShine: {
681
+ ...StyleSheet.absoluteFillObject,
682
+ backgroundColor: 'rgba(255,255,255,0.12)',
683
+ },
684
+ bannerDots: {
685
+ ...StyleSheet.absoluteFillObject,
686
+ },
687
+ bannerDot: {
688
+ position: 'absolute',
689
+ backgroundColor: '#FFF',
690
+ },
691
+ closeBtn: {
692
+ position: 'absolute',
693
+ top: 14,
694
+ right: 14,
695
+ width: 30,
696
+ height: 30,
697
+ borderRadius: 15,
698
+ backgroundColor: 'rgba(0,0,0,0.18)',
699
+ justifyContent: 'center',
700
+ alignItems: 'center',
701
+ zIndex: 10,
702
+ },
703
+ closeBtnText: {
704
+ color: '#FFF',
705
+ fontSize: 13,
706
+ fontWeight: '800',
707
+ },
708
+ iconWrap: {
709
+ alignItems: 'center',
710
+ justifyContent: 'center',
711
+ marginTop: -38,
712
+ height: 76,
713
+ zIndex: 5,
714
+ },
715
+ iconCircleOuter: {
716
+ width: 80,
717
+ height: 80,
718
+ borderRadius: 40,
719
+ justifyContent: 'center',
720
+ alignItems: 'center',
721
+ padding: 4,
722
+ },
723
+ iconCircle: {
724
+ width: 72,
725
+ height: 72,
726
+ borderRadius: 36,
727
+ justifyContent: 'center',
728
+ alignItems: 'center',
729
+ },
730
+ iconRing: {
731
+ width: 60,
732
+ height: 60,
733
+ borderRadius: 30,
734
+ borderWidth: 2.5,
735
+ borderColor: 'rgba(255,255,255,0.35)',
736
+ justifyContent: 'center',
737
+ alignItems: 'center',
738
+ },
739
+ iconChar: {
740
+ color: '#FFF',
741
+ fontSize: 28,
742
+ fontWeight: '900',
743
+ includeFontPadding: false,
744
+ textAlignVertical: 'center',
745
+ },
746
+ content: {
747
+ paddingHorizontal: 26,
748
+ paddingTop: 14,
749
+ paddingBottom: 6,
750
+ alignItems: 'center',
751
+ },
752
+ title: {
753
+ fontSize: 22,
754
+ fontWeight: '800',
755
+ textAlign: 'center',
756
+ marginBottom: 8,
757
+ letterSpacing: 0.2,
758
+ },
759
+ message: {
760
+ fontSize: 15,
761
+ textAlign: 'center',
762
+ lineHeight: 22,
763
+ letterSpacing: 0.15,
764
+ },
765
+ btnWrap: {
766
+ paddingHorizontal: 22,
767
+ paddingTop: 14,
768
+ paddingBottom: 22,
769
+ },
770
+ btnWrapRow: {
771
+ flexDirection: 'row',
772
+ justifyContent: 'center',
773
+ },
774
+ progressWrap: {
775
+ height: 4,
776
+ backgroundColor: 'rgba(0,0,0,0.06)',
777
+ borderBottomLeftRadius: 28,
778
+ borderBottomRightRadius: 28,
779
+ overflow: 'hidden',
780
+ },
781
+ progressBar: {
782
+ height: 4,
783
+ },
784
+ footer: {
785
+ paddingHorizontal: 24,
786
+ paddingBottom: 18,
787
+ },
788
+ overlayDark: {
789
+ backgroundColor: 'rgba(0,0,0,0.8)',
790
+ },
791
+ cardDark: {
792
+ borderWidth: 1,
793
+ borderColor: 'rgba(255,255,255,0.08)',
794
+ },
795
+ particle: {
796
+ position: 'absolute',
797
+ borderRadius: 5,
798
+ },
799
+ btnItemHorizontal: {
800
+ flex: 1,
801
+ marginHorizontal: 5,
802
+ },
803
+ btnItemVertical: {
804
+ width: '100%',
805
+ marginVertical: 5,
806
+ },
807
+ });
808
+
809
+ export default PopifyAlert;