related-ui-components 1.7.5 → 1.7.7

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 (27) hide show
  1. package/lib/commonjs/app.js +24 -31
  2. package/lib/commonjs/app.js.map +1 -1
  3. package/lib/commonjs/components/Card/Card.js +1 -1
  4. package/lib/commonjs/components/Popup/Popup.js +33 -32
  5. package/lib/commonjs/components/Popup/Popup.js.map +1 -1
  6. package/lib/commonjs/components/RedeemedVoucher/RedeemedVoucherSheet.js +445 -0
  7. package/lib/commonjs/components/RedeemedVoucher/RedeemedVoucherSheet.js.map +1 -0
  8. package/lib/module/app.js +22 -30
  9. package/lib/module/app.js.map +1 -1
  10. package/lib/module/components/Card/Card.js +1 -1
  11. package/lib/module/components/Popup/Popup.js +34 -33
  12. package/lib/module/components/Popup/Popup.js.map +1 -1
  13. package/lib/module/components/RedeemedVoucher/RedeemedVoucherSheet.js +438 -0
  14. package/lib/module/components/RedeemedVoucher/RedeemedVoucherSheet.js.map +1 -0
  15. package/lib/typescript/commonjs/app.d.ts.map +1 -1
  16. package/lib/typescript/commonjs/components/Popup/Popup.d.ts.map +1 -1
  17. package/lib/typescript/commonjs/components/RedeemedVoucher/RedeemedVoucherSheet.d.ts +58 -0
  18. package/lib/typescript/commonjs/components/RedeemedVoucher/RedeemedVoucherSheet.d.ts.map +1 -0
  19. package/lib/typescript/module/app.d.ts.map +1 -1
  20. package/lib/typescript/module/components/Popup/Popup.d.ts.map +1 -1
  21. package/lib/typescript/module/components/RedeemedVoucher/RedeemedVoucherSheet.d.ts +58 -0
  22. package/lib/typescript/module/components/RedeemedVoucher/RedeemedVoucherSheet.d.ts.map +1 -0
  23. package/package.json +4 -2
  24. package/src/app.tsx +19 -19
  25. package/src/components/Card/Card.tsx +1 -1
  26. package/src/components/Popup/Popup.tsx +53 -98
  27. package/src/components/RedeemedVoucher/RedeemedVoucherSheet.tsx +547 -0
@@ -0,0 +1,547 @@
1
+ import React, { useCallback, useMemo, useRef } from "react";
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ Image,
8
+ Pressable,
9
+ ImageSourcePropType,
10
+ StyleProp,
11
+ ViewStyle,
12
+ } from "react-native";
13
+ import BottomSheet, {
14
+ BottomSheetView,
15
+ BottomSheetProps,
16
+ BottomSheetBackdrop,
17
+ BottomSheetBackdropProps,
18
+ } from "@gorhom/bottom-sheet";
19
+ import QRCode from "react-native-qrcode-svg";
20
+ import { LinearGradient } from "expo-linear-gradient";
21
+ import * as Clipboard from "expo-clipboard";
22
+ import { ThemeType, useTheme } from "@/theme";
23
+ import { Ionicons } from "@expo/vector-icons";
24
+
25
+ type Props = {
26
+ visible: boolean;
27
+ onClose: () => void;
28
+ code: string;
29
+ amount?: number;
30
+ expiry?: string;
31
+ image?: ImageSourcePropType;
32
+
33
+ title?: string;
34
+ currency?: string;
35
+ codeReadyText?: string;
36
+ qrCodeLabel?: string;
37
+ barCodeLabel?: string;
38
+ myVouchersButtonText?: string;
39
+ onMyVouchersButtonPress?: () => void;
40
+ onCopyCode?: (copiedCode: string) => void;
41
+ showAppleWalletButton?: boolean;
42
+ appleWalletButtonText?: string;
43
+ onAppleWalletButtonPress?: () => void;
44
+ appleWalletIconSource?: ImageSourcePropType;
45
+ qrCodeSize?: number;
46
+ activeTabColors?: string[];
47
+ inactiveTabBackgroundColor?: string;
48
+ inactiveTabBorderColor?: string;
49
+ inactiveTabTextColor?: string;
50
+ primaryButtonColors?: string[];
51
+ allowBarcodeSwitch?: boolean;
52
+ renderBarcodeComponent?: (code: string) => React.ReactNode;
53
+ renderCopyIcon?: () => React.ReactNode;
54
+ amountIconComponent?: React.ReactNode;
55
+ expiryIconComponent?: React.ReactNode;
56
+ snapPoints?: string[];
57
+ sheetBackgroundStyle?: StyleProp<ViewStyle>;
58
+ sheetHandleStyle?: StyleProp<ViewStyle>;
59
+ backdropPressBehavior?: "close" | "none" | number;
60
+ bottomSheetOverrideProps?: Partial<
61
+ Omit<
62
+ BottomSheetProps,
63
+ | "children"
64
+ | "snapPoints"
65
+ | "index"
66
+ | "onClose"
67
+ | "enablePanDownToClose"
68
+ | "backgroundStyle"
69
+ | "handleIndicatorStyle"
70
+ | "backdropComponent"
71
+ >
72
+ >;
73
+ tabGradientStart?: { x: number; y: number };
74
+ tabGradientEnd?: { x: number; y: number };
75
+ primaryButtonGradientStart?: { x: number; y: number };
76
+ primaryButtonGradientEnd?: { x: number; y: number };
77
+ };
78
+
79
+ const RedeemedVoucherSheet: React.FC<Props> = (props) => {
80
+ const { theme } = useTheme();
81
+ const styles = useMemo(() => getStyles(theme), [theme]);
82
+
83
+ const defaultActiveTabColors = useMemo(
84
+ () =>
85
+ theme.primary
86
+ ? [theme.primary, theme.primaryVariant || theme.primary]
87
+ : ["#F97794", "#623AA2"],
88
+ [theme],
89
+ );
90
+
91
+ const defaultPrimaryButtonColors = useMemo(
92
+ () =>
93
+ defaultActiveTabColors,
94
+ [theme, defaultActiveTabColors],
95
+ );
96
+
97
+ const defaultInactiveTabBorderColor = useMemo(
98
+ () => theme.primary || defaultActiveTabColors[1] || "#623AA2",
99
+ [theme, defaultActiveTabColors],
100
+ );
101
+
102
+ const defaultInactiveTabTextColor = useMemo(
103
+ () => theme.primary || defaultActiveTabColors[1] || "#623AA2",
104
+ [theme, defaultActiveTabColors],
105
+ );
106
+
107
+
108
+ const {
109
+ visible,
110
+ onClose,
111
+ code,
112
+ amount,
113
+ expiry,
114
+ image, // Now directly from props
115
+ title = "Successfully Redeemed!",
116
+ currency = "SAR",
117
+ codeReadyText = "Your Code Is Ready!",
118
+ qrCodeLabel = "QR Code",
119
+ barCodeLabel = "Bar Code",
120
+ myVouchersButtonText = "My Vouchers",
121
+ onMyVouchersButtonPress,
122
+ onCopyCode,
123
+ showAppleWalletButton = true,
124
+ appleWalletButtonText = "Add to Apple Wallet",
125
+ onAppleWalletButtonPress,
126
+ appleWalletIconSource, // Now directly from props
127
+ qrCodeSize = 120,
128
+ activeTabColors = defaultActiveTabColors,
129
+ inactiveTabBackgroundColor = theme.surface || "#FFFFFF",
130
+ inactiveTabBorderColor: propInactiveTabBorderColor,
131
+ inactiveTabTextColor: propInactiveTabTextColor,
132
+ primaryButtonColors = defaultPrimaryButtonColors,
133
+ allowBarcodeSwitch = true,
134
+ renderBarcodeComponent,
135
+ renderCopyIcon,
136
+ amountIconComponent,
137
+ expiryIconComponent,
138
+ snapPoints: propSnapPoints,
139
+ sheetBackgroundStyle,
140
+ sheetHandleStyle,
141
+ backdropPressBehavior = "close",
142
+ bottomSheetOverrideProps,
143
+ tabGradientStart = { x: 0, y: 0.5 },
144
+ tabGradientEnd = { x: 1, y: 0.5 },
145
+ primaryButtonGradientStart = { x: 0, y: 0.5 },
146
+ primaryButtonGradientEnd = { x: 1, y: 0.5 },
147
+ } = props;
148
+
149
+
150
+ const finalInactiveTabBorderColor = propInactiveTabBorderColor || defaultInactiveTabBorderColor;
151
+ const finalInactiveTabTextColor = propInactiveTabTextColor || defaultInactiveTabTextColor;
152
+
153
+
154
+ const bottomSheetRef = useRef<BottomSheet>(null);
155
+ const finalSnapPoints = useMemo(
156
+ () => propSnapPoints || ["75%"],
157
+ [propSnapPoints],
158
+ );
159
+
160
+ const [tab, setTab] = React.useState<"qr" | "bar">("qr");
161
+
162
+ const handleCopyCode = async () => {
163
+ await Clipboard.setStringAsync(code);
164
+ if (onCopyCode) {
165
+ onCopyCode(code);
166
+ }
167
+ };
168
+
169
+ // Custom backdrop component
170
+ const renderBackdrop = useCallback(
171
+ (backdropProps: BottomSheetBackdropProps) => (
172
+ <BottomSheetBackdrop
173
+ {...backdropProps}
174
+ disappearsOnIndex={-1}
175
+ appearsOnIndex={0}
176
+ pressBehavior={backdropPressBehavior}
177
+ opacity={0.5}
178
+ style={[
179
+ backdropProps.style,
180
+ { backgroundColor: "rgba(0,0,0,0.5)" },
181
+ ]}
182
+ />
183
+ ),
184
+ [backdropPressBehavior, theme],
185
+ );
186
+ const iconSize = 24;
187
+ const defaultAmountIcon = (
188
+ <Ionicons style={styles.defaultIconStyle} name="cash-outline" size={iconSize} color={theme.onSurface} />
189
+ );
190
+
191
+ const defaultExpiryIcon = (
192
+ <Ionicons style={styles.defaultIconStyle} name="time-outline" size={iconSize} color={theme.onSurface} />
193
+ );
194
+
195
+ const defaultCopyIcon = (
196
+ <Ionicons
197
+ name="copy-outline"
198
+ size={iconSize}
199
+ color={finalInactiveTabTextColor}
200
+ style={styles.copyIconText}
201
+ />
202
+ );
203
+
204
+
205
+ return (
206
+ <BottomSheet
207
+ ref={bottomSheetRef}
208
+ index={visible ? 0 : -1}
209
+ snapPoints={finalSnapPoints}
210
+ enablePanDownToClose
211
+ onClose={onClose}
212
+ backgroundStyle={[styles.sheetBackgroundBase, sheetBackgroundStyle]}
213
+ handleIndicatorStyle={[styles.handleBase, sheetHandleStyle]}
214
+ backdropComponent={renderBackdrop}
215
+ {...bottomSheetOverrideProps}
216
+ >
217
+ <BottomSheetView style={styles.container}>
218
+ {image && (
219
+ <Image source={image} style={styles.giftImage} />
220
+ )}
221
+ <Text style={styles.title}>{title}</Text>
222
+
223
+ <View style={styles.infoRow}>
224
+ {amount && (
225
+ <View style={styles.infoItem}>
226
+ {amountIconComponent || defaultAmountIcon}
227
+ <Text style={styles.amountText}>
228
+ {amount} {currency}
229
+ </Text>
230
+ </View>
231
+ )}
232
+ {expiry && (
233
+ <View style={styles.infoItem}>
234
+ {expiryIconComponent || defaultExpiryIcon}
235
+ <Text style={styles.expiryText}>{expiry}</Text>
236
+ </View>
237
+ )}
238
+ </View>
239
+
240
+ <View style={styles.codeBox}>
241
+ <Text style={styles.codeReadyText}>{codeReadyText}</Text>
242
+ <View style={styles.qrContainer}>
243
+ {tab === "qr" ? (
244
+ <QRCode value={code} size={qrCodeSize} />
245
+ ) : renderBarcodeComponent ? (
246
+ renderBarcodeComponent(code)
247
+ ) : (
248
+ <View style={styles.barcodePlaceholder}>
249
+ <Text style={styles.barcodePlaceholderText}>Bar Code</Text>
250
+ </View>
251
+ )}
252
+ </View>
253
+
254
+ {allowBarcodeSwitch && (
255
+ <View style={styles.tabRow}>
256
+ <TouchableOpacity
257
+ style={[
258
+ styles.tabButton,
259
+ tab !== "qr" && {
260
+ borderColor: finalInactiveTabBorderColor,
261
+ borderWidth: 1.5,
262
+ },
263
+ ]}
264
+ onPress={() => setTab("qr")}
265
+ >
266
+ <LinearGradient
267
+ colors={
268
+ tab === "qr"
269
+ ? (activeTabColors as any)
270
+ : [inactiveTabBackgroundColor, inactiveTabBackgroundColor]
271
+ }
272
+ start={tabGradientStart}
273
+ end={tabGradientEnd}
274
+ style={styles.tabGradient}
275
+ >
276
+ <Text
277
+ style={[
278
+ styles.tabTextBase,
279
+ tab === "qr"
280
+ ? styles.tabTextActive
281
+ : { color: finalInactiveTabTextColor },
282
+ ]}
283
+ >
284
+ {qrCodeLabel}
285
+ </Text>
286
+ </LinearGradient>
287
+ </TouchableOpacity>
288
+
289
+ <TouchableOpacity
290
+ style={[
291
+ styles.tabButton,
292
+ tab !== "bar" && {
293
+ borderColor: finalInactiveTabBorderColor,
294
+ borderWidth: 1.5,
295
+ },
296
+ ]}
297
+ onPress={() => setTab("bar")}
298
+ >
299
+ <LinearGradient
300
+ colors={
301
+ tab === "bar"
302
+ ? (activeTabColors as any)
303
+ : [inactiveTabBackgroundColor, inactiveTabBackgroundColor]
304
+ }
305
+ start={{ x: 0, y: 0.5 }}
306
+ end={{ x: 1, y: 0.5 }}
307
+ style={styles.tabGradient}
308
+ >
309
+ <Text
310
+ style={[
311
+ styles.tabTextBase,
312
+ tab === "bar"
313
+ ? styles.tabTextActive
314
+ : { color: finalInactiveTabTextColor },
315
+ ]}
316
+ >
317
+ {barCodeLabel}
318
+ </Text>
319
+ </LinearGradient>
320
+ </TouchableOpacity>
321
+ </View>
322
+ )}
323
+
324
+ <View style={styles.codeDisplayRow}>
325
+ <Text style={styles.actualCodeText}>{code}</Text>
326
+ {onCopyCode && (
327
+ <TouchableOpacity
328
+ onPress={handleCopyCode}
329
+ style={styles.copyButton}
330
+ >
331
+ {renderCopyIcon ? renderCopyIcon() : defaultCopyIcon}
332
+ </TouchableOpacity>
333
+ )}
334
+ </View>
335
+
336
+ {showAppleWalletButton && appleWalletIconSource && (
337
+ <Pressable
338
+ style={styles.walletButton}
339
+ onPress={onAppleWalletButtonPress}
340
+ >
341
+ <Image source={appleWalletIconSource} style={styles.walletIcon} />
342
+ <Text style={styles.walletText}>{appleWalletButtonText}</Text>
343
+ </Pressable>
344
+ )}
345
+ </View>
346
+
347
+ {onMyVouchersButtonPress && (
348
+ <TouchableOpacity
349
+ style={styles.vouchersButton}
350
+ onPress={onMyVouchersButtonPress}
351
+ >
352
+ <LinearGradient
353
+ colors={primaryButtonColors as any}
354
+ start={primaryButtonGradientStart}
355
+ end={primaryButtonGradientEnd}
356
+ style={styles.vouchersGradient}
357
+ >
358
+ <Text style={styles.vouchersText}>{myVouchersButtonText}</Text>
359
+ </LinearGradient>
360
+ </TouchableOpacity>
361
+ )}
362
+ </BottomSheetView>
363
+ </BottomSheet>
364
+ );
365
+ };
366
+
367
+ const getStyles = (theme: ThemeType) =>
368
+ StyleSheet.create({
369
+ sheetBackgroundBase: {
370
+ backgroundColor: theme.surface || "#fff",
371
+ borderTopLeftRadius: 24,
372
+ borderTopRightRadius: 24,
373
+ },
374
+ handleBase: {
375
+ backgroundColor: theme.border || "#e0e0e0",
376
+ width: 40,
377
+ height: 5,
378
+ borderRadius: 2.5,
379
+ alignSelf: "center",
380
+ marginTop: 8,
381
+ },
382
+ container: {
383
+ flex: 1,
384
+ alignItems: "center",
385
+ paddingHorizontal: 20,
386
+ paddingTop: 8,
387
+ paddingBottom: 20,
388
+ },
389
+ giftImage: {
390
+ width: 64,
391
+ height: 64,
392
+ marginBottom: 10,
393
+ resizeMode: "contain",
394
+ },
395
+ title: {
396
+ fontSize: 20,
397
+ fontWeight: "600",
398
+ marginBottom: 16,
399
+ color: theme.onSurface || "#333333",
400
+ },
401
+ infoRow: {
402
+ flexDirection: "row",
403
+ justifyContent: "space-around",
404
+ width: "100%",
405
+ marginBottom: 20,
406
+ },
407
+ infoItem: {
408
+ flexDirection: "row",
409
+ alignItems: "center",
410
+ },
411
+ defaultIconStyle: {
412
+ marginRight: 8,
413
+ },
414
+ amountText: {
415
+ fontSize: 16,
416
+ color: theme.onSurface || "#333333",
417
+ fontWeight: "500",
418
+ },
419
+ expiryText: {
420
+ fontSize: 16,
421
+ color: theme.onSurface || "#333333",
422
+ fontWeight: "500",
423
+ },
424
+ codeBox: {
425
+ backgroundColor: theme.surface || "#f8f8f8",
426
+ borderRadius: 18,
427
+ padding: 20,
428
+ width: "100%",
429
+ alignItems: "center",
430
+ marginBottom: 20,
431
+ shadowColor: "#000",
432
+ shadowOpacity: 0.08,
433
+ shadowRadius: 12,
434
+ shadowOffset: { width: 0, height: 4 },
435
+ elevation: 3,
436
+ },
437
+ codeReadyText: {
438
+ fontSize: 15,
439
+ color: theme.onSurface || "#666666",
440
+ marginBottom: 12,
441
+ },
442
+ qrContainer: {
443
+ marginBottom: 18,
444
+ alignItems: "center",
445
+ justifyContent: "center",
446
+ height: 130,
447
+ },
448
+ barcodePlaceholder: {
449
+ width: 150,
450
+ height: 70,
451
+ backgroundColor: theme.disabled || "#eeeeee",
452
+ alignItems: "center",
453
+ justifyContent: "center",
454
+ borderRadius: 8,
455
+ },
456
+ barcodePlaceholderText: {
457
+ color: theme.disabled || "#aaaaaa",
458
+ fontSize: 14,
459
+ },
460
+ tabRow: {
461
+ flexDirection: "row",
462
+ marginBottom: 16,
463
+ width: "100%",
464
+ justifyContent: "center",
465
+ },
466
+ tabButton: {
467
+ flex: 1,
468
+ marginHorizontal: 5,
469
+ borderRadius: 10,
470
+ overflow: "hidden",
471
+ },
472
+ tabGradient: {
473
+ paddingVertical: 10,
474
+ alignItems: "center",
475
+ borderRadius: 10,
476
+ },
477
+ tabTextBase: {
478
+ fontSize: 15,
479
+ fontWeight: "600",
480
+ },
481
+ tabTextActive: {
482
+ color: theme.onPrimary || "#fff", // Color for text on active tab gradient
483
+ fontWeight: "700",
484
+ },
485
+ codeDisplayRow: {
486
+ flexDirection: "row",
487
+ alignItems: "center",
488
+ justifyContent: "center",
489
+ marginVertical: 12,
490
+ paddingHorizontal: 10,
491
+ paddingVertical: 6,
492
+ backgroundColor: theme.background || "#f0f0f0", // Subtle background
493
+ borderRadius: 8,
494
+ },
495
+ actualCodeText: {
496
+ fontSize: 16,
497
+ letterSpacing: 1.5,
498
+ color: theme.onBackground || "#444444",
499
+ fontWeight: "500",
500
+ marginRight: 10,
501
+ },
502
+ copyButton: {
503
+ padding: 6,
504
+ },
505
+ copyIconText: { // Base style for copy icon, color applied dynamically
506
+ fontSize: 20,
507
+ },
508
+ walletButton: {
509
+ flexDirection: "row",
510
+ alignItems: "center",
511
+ backgroundColor: "#000", // Apple Wallet button is typically black
512
+ borderRadius: 10,
513
+ paddingVertical: 10,
514
+ paddingHorizontal: 20,
515
+ marginTop: 10,
516
+ },
517
+ walletIcon: {
518
+ width: 30,
519
+ height: 20,
520
+ marginRight: 10,
521
+ resizeMode: "contain",
522
+ },
523
+ walletText: {
524
+ color: "#fff", // Text on black button
525
+ fontWeight: "600",
526
+ fontSize: 15,
527
+ },
528
+ vouchersButton: {
529
+ width: "100%",
530
+ // marginTop: "auto",
531
+ marginTop: 20,
532
+ borderRadius: 12,
533
+ overflow: "hidden",
534
+ },
535
+ vouchersGradient: {
536
+ paddingVertical: 16,
537
+ alignItems: "center",
538
+ borderRadius: 12,
539
+ },
540
+ vouchersText: {
541
+ color: theme.onPrimary || "#fff", // Text on primary button gradient
542
+ fontWeight: "700",
543
+ fontSize: 17,
544
+ },
545
+ });
546
+
547
+ export default RedeemedVoucherSheet;