@heyama/pay-react-native 0.0.0-stage → 1.0.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 (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -2
  3. package/assets/fonts/Figtree-Bold.ttf +0 -0
  4. package/assets/fonts/Figtree-ExtraBold.ttf +0 -0
  5. package/assets/fonts/Figtree-Medium.ttf +0 -0
  6. package/assets/fonts/Figtree-Regular.ttf +0 -0
  7. package/assets/fonts/Figtree-SemiBold.ttf +0 -0
  8. package/assets/fonts/OFL.txt +93 -0
  9. package/assets/logos/apple-pay.png +0 -0
  10. package/assets/logos/card.png +0 -0
  11. package/assets/logos/google-pay.png +0 -0
  12. package/assets/logos/mastercard.png +0 -0
  13. package/assets/logos/mixx.png +0 -0
  14. package/assets/logos/moov.png +0 -0
  15. package/assets/logos/mtn.png +0 -0
  16. package/assets/logos/orange.png +0 -0
  17. package/assets/logos/paypal.png +0 -0
  18. package/assets/logos/visa.png +0 -0
  19. package/assets/logos/wave.png +0 -0
  20. package/dist/checkout-core/hooks/use-checkout-controller.d.ts +9 -0
  21. package/dist/checkout-core/hooks/use-checkout-controller.js +319 -0
  22. package/dist/checkout-core/hooks/use-checkout-timers.d.ts +15 -0
  23. package/dist/checkout-core/hooks/use-checkout-timers.js +44 -0
  24. package/dist/checkout-core/hooks/use-phone-number.d.ts +7 -0
  25. package/dist/checkout-core/hooks/use-phone-number.js +82 -0
  26. package/dist/checkout-core/messages/en.d.ts +252 -0
  27. package/dist/checkout-core/messages/en.js +242 -0
  28. package/dist/checkout-core/messages/fr.d.ts +4 -0
  29. package/dist/checkout-core/messages/fr.js +244 -0
  30. package/dist/checkout-core/models/checkout.card.utils.d.ts +22 -0
  31. package/dist/checkout-core/models/checkout.card.utils.js +101 -0
  32. package/dist/checkout-core/models/checkout.constants.d.ts +56 -0
  33. package/dist/checkout-core/models/checkout.constants.js +142 -0
  34. package/dist/checkout-core/models/checkout.enum.d.ts +131 -0
  35. package/dist/checkout-core/models/checkout.enum.js +148 -0
  36. package/dist/checkout-core/models/checkout.interface.d.ts +417 -0
  37. package/dist/checkout-core/models/checkout.interface.js +1 -0
  38. package/dist/checkout-core/models/checkout.money.utils.d.ts +7 -0
  39. package/dist/checkout-core/models/checkout.money.utils.js +21 -0
  40. package/dist/checkout-core/models/checkout.phone.utils.d.ts +33 -0
  41. package/dist/checkout-core/models/checkout.phone.utils.js +159 -0
  42. package/dist/checkout-core/models/checkout.view.utils.d.ts +34 -0
  43. package/dist/checkout-core/models/checkout.view.utils.js +205 -0
  44. package/dist/checkout-core/models/domain.enum.d.ts +56 -0
  45. package/dist/checkout-core/models/domain.enum.js +62 -0
  46. package/dist/components/BrissotoPayProvider.d.ts +6 -0
  47. package/dist/components/BrissotoPayProvider.js +41 -0
  48. package/dist/components/PaymentSheetModal.d.ts +8 -0
  49. package/dist/components/PaymentSheetModal.js +72 -0
  50. package/dist/components/SheetBone.d.ts +3 -0
  51. package/dist/components/SheetBone.js +12 -0
  52. package/dist/components/SheetButton.d.ts +3 -0
  53. package/dist/components/SheetButton.js +33 -0
  54. package/dist/components/SheetCardFields.d.ts +8 -0
  55. package/dist/components/SheetCardFields.js +66 -0
  56. package/dist/components/SheetCodeField.d.ts +8 -0
  57. package/dist/components/SheetCodeField.js +95 -0
  58. package/dist/components/SheetContent.d.ts +3 -0
  59. package/dist/components/SheetContent.js +106 -0
  60. package/dist/components/SheetFailed.d.ts +3 -0
  61. package/dist/components/SheetFailed.js +43 -0
  62. package/dist/components/SheetForm.d.ts +7 -0
  63. package/dist/components/SheetForm.js +100 -0
  64. package/dist/components/SheetHeader.d.ts +6 -0
  65. package/dist/components/SheetHeader.js +69 -0
  66. package/dist/components/SheetIcon.d.ts +3 -0
  67. package/dist/components/SheetIcon.js +70 -0
  68. package/dist/components/SheetLayout.d.ts +8 -0
  69. package/dist/components/SheetLayout.js +35 -0
  70. package/dist/components/SheetLogo.d.ts +3 -0
  71. package/dist/components/SheetLogo.js +20 -0
  72. package/dist/components/SheetMessage.d.ts +3 -0
  73. package/dist/components/SheetMessage.js +15 -0
  74. package/dist/components/SheetMethodCard.d.ts +3 -0
  75. package/dist/components/SheetMethodCard.js +39 -0
  76. package/dist/components/SheetPhoneField.d.ts +3 -0
  77. package/dist/components/SheetPhoneField.js +56 -0
  78. package/dist/components/SheetRow.d.ts +3 -0
  79. package/dist/components/SheetRow.js +28 -0
  80. package/dist/components/SheetSimulator.d.ts +6 -0
  81. package/dist/components/SheetSimulator.js +39 -0
  82. package/dist/components/SheetSkeleton.d.ts +5 -0
  83. package/dist/components/SheetSkeleton.js +23 -0
  84. package/dist/components/SheetSuccess.d.ts +3 -0
  85. package/dist/components/SheetSuccess.js +51 -0
  86. package/dist/components/SheetSummary.d.ts +3 -0
  87. package/dist/components/SheetSummary.js +32 -0
  88. package/dist/components/SheetTerminal.d.ts +3 -0
  89. package/dist/components/SheetTerminal.js +21 -0
  90. package/dist/components/SheetText.d.ts +5 -0
  91. package/dist/components/SheetText.js +25 -0
  92. package/dist/components/SheetToast.d.ts +3 -0
  93. package/dist/components/SheetToast.js +24 -0
  94. package/dist/components/SheetWaiting.d.ts +7 -0
  95. package/dist/components/SheetWaiting.js +77 -0
  96. package/dist/hooks/use-native-platform.d.ts +10 -0
  97. package/dist/hooks/use-native-platform.js +47 -0
  98. package/dist/hooks/use-payment-sheet.d.ts +4 -0
  99. package/dist/hooks/use-payment-sheet.js +10 -0
  100. package/dist/hooks/use-sheet-motion.d.ts +7 -0
  101. package/dist/hooks/use-sheet-motion.js +62 -0
  102. package/dist/hooks/use-sheet-resize.d.ts +6 -0
  103. package/dist/hooks/use-sheet-resize.js +15 -0
  104. package/dist/hooks/use-sheet-theme.d.ts +4 -0
  105. package/dist/hooks/use-sheet-theme.js +12 -0
  106. package/dist/hooks/use-watched-payment.d.ts +6 -0
  107. package/dist/hooks/use-watched-payment.js +17 -0
  108. package/dist/index.d.ts +3 -0
  109. package/dist/index.js +2 -0
  110. package/dist/models/sheet.constants.d.ts +39 -0
  111. package/dist/models/sheet.constants.js +78 -0
  112. package/dist/models/sheet.enum.d.ts +16 -0
  113. package/dist/models/sheet.enum.js +18 -0
  114. package/dist/models/sheet.interface.d.ts +225 -0
  115. package/dist/models/sheet.interface.js +1 -0
  116. package/dist/models/sheet.utils.d.ts +43 -0
  117. package/dist/models/sheet.utils.js +132 -0
  118. package/dist/services/checkout.service.d.ts +7 -0
  119. package/dist/services/checkout.service.js +39 -0
  120. package/package.json +58 -4
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Image, Pressable, StyleSheet, View } from 'react-native';
3
+ import { useTranslations } from 'use-intl';
4
+ import { MEDIA_LINE } from "../checkout-core/models/checkout.constants.js";
5
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
6
+ import { FigtreeEnum } from "../models/sheet.enum.js";
7
+ import { SheetBone } from "./SheetBone.js";
8
+ import { SheetText } from "./SheetText.js";
9
+ /**
10
+ * The grabber, then the app being paid: its logo and name, the Test badge,
11
+ * and close. Dragged down, the sheet follows.
12
+ */
13
+ export function SheetHeader({ view, loading, onClose, dragHandlers, }) {
14
+ const t = useTranslations('checkout');
15
+ const { colors, brand } = useSheetTheme();
16
+ const app = view?.app;
17
+ return (_jsxs(View, { ...dragHandlers, children: [_jsx(View, { style: [styles.grabber, { backgroundColor: colors.line3 }] }), _jsxs(View, { style: styles.row, children: [loading ? (_jsxs(_Fragment, { children: [_jsx(SheetBone, { width: 34, height: 34, radius: 9 }), _jsx(View, { style: styles.name, children: _jsx(SheetBone, { width: 84, height: 14 }) })] })) : (app &&
18
+ (app.logoUrl ? (_jsx(Image, { source: { uri: app.logoUrl }, accessibilityIgnoresInvertColors: true, style: styles.logo })) : (_jsx(View, { testID: "app-initial", style: [styles.initial, { backgroundColor: brand }], children: _jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 15, color: "white", style: styles.centred, children: app.name.charAt(0) }) })))), !loading && (_jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 16, numberOfLines: 1, accessibilityRole: "header", style: [styles.name, styles.centred], children: app?.name ?? 'Brissoto Pay' })), !loading && view?.test && (_jsx(View, { style: [styles.badge, { backgroundColor: colors.warnSoft }], children: _jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 10.5, color: colors.warnInk, style: [styles.badgeText, styles.centred], children: t('test_badge') }) })), _jsxs(Pressable, { onPress: onClose, accessibilityRole: "button", accessibilityLabel: t('close'), hitSlop: 8, style: [styles.close, { backgroundColor: colors.fill }], children: [_jsx(View, { style: [styles.bar, styles.barUp, { backgroundColor: colors.ink2 }] }), _jsx(View, { style: [
19
+ styles.bar,
20
+ styles.barDown,
21
+ { backgroundColor: colors.ink2 },
22
+ ] })] })] })] }));
23
+ }
24
+ const styles = StyleSheet.create({
25
+ grabber: {
26
+ alignSelf: 'center',
27
+ width: 38,
28
+ height: 5,
29
+ borderRadius: 3,
30
+ marginTop: 7,
31
+ },
32
+ row: {
33
+ flexDirection: 'row',
34
+ alignItems: 'center',
35
+ gap: 10,
36
+ paddingHorizontal: 18,
37
+ paddingTop: 10,
38
+ },
39
+ logo: {
40
+ width: 34,
41
+ height: 34,
42
+ borderRadius: 9,
43
+ borderWidth: 1,
44
+ borderColor: MEDIA_LINE,
45
+ },
46
+ initial: {
47
+ width: 34,
48
+ height: 34,
49
+ borderRadius: 9,
50
+ alignItems: 'center',
51
+ justifyContent: 'center',
52
+ },
53
+ name: { flex: 1 },
54
+ // Android adds font padding above the ascender: without it, each text sits
55
+ // on the row's centre line like the logo, the badge and close.
56
+ centred: { includeFontPadding: false, textAlignVertical: 'center' },
57
+ badge: { borderRadius: 6, paddingHorizontal: 7, paddingVertical: 3 },
58
+ badgeText: { letterSpacing: 0.6, textTransform: 'uppercase' },
59
+ close: {
60
+ width: 30,
61
+ height: 30,
62
+ borderRadius: 15,
63
+ alignItems: 'center',
64
+ justifyContent: 'center',
65
+ },
66
+ bar: { position: 'absolute', width: 14, height: 2, borderRadius: 1 },
67
+ barUp: { transform: [{ rotate: '45deg' }] },
68
+ barDown: { transform: [{ rotate: '-45deg' }] },
69
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetIconProps } from '../models/sheet.interface.js';
2
+ /** A sign in a disc, drawn with views: the sheet ships no icon font or SVG. */
3
+ export declare function SheetIcon({ kind, color, background, size, }: SheetIconProps): import("react").JSX.Element;
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { SheetIconEnum } from "../models/sheet.enum.js";
4
+ /** A sign in a disc, drawn with views: the sheet ships no icon font or SVG. */
5
+ export function SheetIcon({ kind, color, background, size = 48, }) {
6
+ const scale = size / 48;
7
+ const ink = { backgroundColor: color };
8
+ const line = { borderColor: color };
9
+ let sign;
10
+ switch (kind) {
11
+ case SheetIconEnum.CHECK:
12
+ sign = _jsx(View, { style: [styles.check, line] });
13
+ break;
14
+ case SheetIconEnum.CROSS:
15
+ sign = (_jsxs(_Fragment, { children: [_jsx(View, { style: [styles.bar, styles.up, ink] }), _jsx(View, { style: [styles.bar, styles.down, ink] })] }));
16
+ break;
17
+ case SheetIconEnum.CLOCK:
18
+ sign = (_jsxs(View, { style: [styles.ring, line], children: [_jsx(View, { style: [styles.hour, ink] }), _jsx(View, { style: [styles.minute, ink] })] }));
19
+ break;
20
+ case SheetIconEnum.LOCK:
21
+ sign = (_jsxs(View, { style: styles.lock, children: [_jsx(View, { style: [styles.shackle, line] }), _jsx(View, { style: [styles.body, ink] })] }));
22
+ break;
23
+ default:
24
+ sign = (_jsx(View, { style: [styles.ring, line], children: _jsx(View, { style: [styles.slash, ink] }) }));
25
+ }
26
+ return (_jsx(View, { testID: `icon-${kind}`, style: [
27
+ styles.disc,
28
+ {
29
+ width: size,
30
+ height: size,
31
+ borderRadius: size / 2,
32
+ backgroundColor: background,
33
+ },
34
+ ], children: _jsx(View, { style: { transform: [{ scale }] }, children: sign }) }));
35
+ }
36
+ const styles = StyleSheet.create({
37
+ disc: { alignItems: 'center', justifyContent: 'center' },
38
+ check: {
39
+ width: 10,
40
+ height: 18,
41
+ marginTop: -4,
42
+ borderRightWidth: 3,
43
+ borderBottomWidth: 3,
44
+ transform: [{ rotate: '45deg' }],
45
+ },
46
+ bar: { position: 'absolute', width: 18, height: 2.6, borderRadius: 1.3 },
47
+ up: { transform: [{ rotate: '45deg' }] },
48
+ down: { transform: [{ rotate: '-45deg' }] },
49
+ ring: {
50
+ width: 20,
51
+ height: 20,
52
+ borderRadius: 10,
53
+ borderWidth: 2,
54
+ alignItems: 'center',
55
+ justifyContent: 'center',
56
+ },
57
+ slash: { width: 18, height: 2, transform: [{ rotate: '-45deg' }] },
58
+ hour: { position: 'absolute', width: 2, height: 6, top: 3, left: 7 },
59
+ minute: { position: 'absolute', width: 5, height: 2, top: 7, left: 7 },
60
+ lock: { alignItems: 'center' },
61
+ shackle: {
62
+ width: 12,
63
+ height: 10,
64
+ borderWidth: 2.4,
65
+ borderBottomWidth: 0,
66
+ borderTopLeftRadius: 6,
67
+ borderTopRightRadius: 6,
68
+ },
69
+ body: { width: 18, height: 12, borderRadius: 3 },
70
+ });
@@ -0,0 +1,8 @@
1
+ import type { SheetLayoutProps } from '../models/sheet.interface.js';
2
+ /**
3
+ * What scrolls, then the actions pinned at the bottom with "Payment secured
4
+ * by Brissoto Pay". What scrolls is as tall as it is, until the sheet runs
5
+ * out of room. The keyboard pushes the actions up: what scrolls follows to
6
+ * its end, where the number is.
7
+ */
8
+ export declare function SheetLayout({ children, foot, secure, }: SheetLayoutProps): import("react").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { Keyboard, ScrollView, StyleSheet, View } from 'react-native';
4
+ import { useTranslations } from 'use-intl';
5
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
6
+ import { FigtreeEnum } from "../models/sheet.enum.js";
7
+ import { SheetText } from "./SheetText.js";
8
+ /**
9
+ * What scrolls, then the actions pinned at the bottom with "Payment secured
10
+ * by Brissoto Pay". What scrolls is as tall as it is, until the sheet runs
11
+ * out of room. The keyboard pushes the actions up: what scrolls follows to
12
+ * its end, where the number is.
13
+ */
14
+ export function SheetLayout({ children, foot, secure = true, }) {
15
+ const t = useTranslations('checkout');
16
+ const { colors } = useSheetTheme();
17
+ const scroller = useRef(null);
18
+ useEffect(() => {
19
+ const shown = Keyboard.addListener('keyboardDidShow', () => scroller.current?.scrollToEnd());
20
+ return () => shown.remove();
21
+ }, []);
22
+ return (_jsxs(_Fragment, { children: [_jsx(ScrollView, { ref: scroller, style: styles.body, contentContainerStyle: styles.content, keyboardShouldPersistTaps: "handled", keyboardDismissMode: "interactive", children: children }), (foot || secure) && (_jsxs(View, { style: [styles.foot, { borderColor: colors.line }], children: [foot, secure && (_jsxs(SheetText, { size: 11.5, color: colors.muted, style: styles.secure, children: [t('secured_by'), ' ', _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 11.5, children: "Brissoto Pay" })] }))] }))] }));
23
+ }
24
+ const styles = StyleSheet.create({
25
+ body: { flexGrow: 0, flexShrink: 1, marginTop: 6 },
26
+ content: { paddingBottom: 16 },
27
+ foot: {
28
+ borderTopWidth: 1,
29
+ paddingHorizontal: 18,
30
+ paddingTop: 12,
31
+ paddingBottom: 28,
32
+ gap: 10,
33
+ },
34
+ secure: { textAlign: 'center' },
35
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetLogoProps } from '../models/sheet.interface.js';
2
+ /** A brand's square tile, the same as on the web checkout. */
3
+ export declare function SheetLogo({ logo, size, muted, }: SheetLogoProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Image, StyleSheet } from 'react-native';
3
+ import { LOGO_RADIUS_RATIO, LOGO_SIZE, MEDIA_LINE, } from "../checkout-core/models/checkout.constants.js";
4
+ import { PROVIDER_LOGOS } from "../models/sheet.constants.js";
5
+ /** A brand's square tile, the same as on the web checkout. */
6
+ export function SheetLogo({ logo, size = LOGO_SIZE.INLINE, muted = false, }) {
7
+ return (_jsx(Image, { source: PROVIDER_LOGOS[logo], accessibilityIgnoresInvertColors: true, style: [
8
+ styles.logo,
9
+ {
10
+ width: size,
11
+ height: size,
12
+ borderRadius: Math.round(size * LOGO_RADIUS_RATIO),
13
+ },
14
+ muted && styles.muted,
15
+ ] }));
16
+ }
17
+ const styles = StyleSheet.create({
18
+ logo: { borderWidth: 1, borderColor: MEDIA_LINE },
19
+ muted: { opacity: 0.45 },
20
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetMessageProps } from '../models/sheet.interface.js';
2
+ /** A dead end the payer cannot fix here: its sign, what happened and why. */
3
+ export declare function SheetMessage({ title, body, icon, danger, }: SheetMessageProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
4
+ import { FigtreeEnum, SheetIconEnum } from "../models/sheet.enum.js";
5
+ import { SheetIcon } from "./SheetIcon.js";
6
+ import { SheetText } from "./SheetText.js";
7
+ /** A dead end the payer cannot fix here: its sign, what happened and why. */
8
+ export function SheetMessage({ title, body, icon = SheetIconEnum.BAN, danger = false, }) {
9
+ const { colors } = useSheetTheme();
10
+ return (_jsxs(View, { style: styles.message, children: [_jsx(SheetIcon, { kind: icon, color: danger ? colors.danger : colors.ink2, background: danger ? colors.dangerSoft : colors.fill2 }), _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 22, accessibilityRole: "header", style: styles.title, children: title }), _jsx(SheetText, { size: 15, color: colors.inkStrong, children: body })] }));
11
+ }
12
+ const styles = StyleSheet.create({
13
+ message: { paddingHorizontal: 18, paddingTop: 28 },
14
+ title: { marginTop: 16, marginBottom: 8, letterSpacing: -0.4 },
15
+ });
@@ -0,0 +1,3 @@
1
+ import type { MethodCardProps } from '../models/sheet.interface.js';
2
+ /** One method of the grid: logo and radio dot, then its name and network, or why it is greyed. */
3
+ export declare function SheetMethodCard({ logo, name, sub, checked, muted, onSelect, }: MethodCardProps): import("react").JSX.Element;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, TouchableOpacity, View } from 'react-native';
3
+ import { LOGO_SIZE } from "../checkout-core/models/checkout.constants.js";
4
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
5
+ import { FigtreeEnum } from "../models/sheet.enum.js";
6
+ import { SheetLogo } from "./SheetLogo.js";
7
+ import { SheetText } from "./SheetText.js";
8
+ /** One method of the grid: logo and radio dot, then its name and network, or why it is greyed. */
9
+ export function SheetMethodCard({ logo, name, sub, checked, muted, onSelect, }) {
10
+ const { colors, brand } = useSheetTheme();
11
+ return (_jsxs(TouchableOpacity, { onPress: onSelect, disabled: muted, activeOpacity: 0.7, accessibilityRole: "radio", accessibilityLabel: `${name}, ${sub}`, accessibilityState: { checked, disabled: muted }, style: [
12
+ styles.card,
13
+ checked
14
+ ? { borderColor: brand, borderWidth: 2, padding: 11 }
15
+ : { borderColor: colors.line2 },
16
+ ], children: [_jsxs(View, { style: styles.top, children: [_jsx(SheetLogo, { logo: logo, size: LOGO_SIZE.ROW, muted: muted }), _jsx(View, { style: [
17
+ styles.radio,
18
+ checked
19
+ ? { borderColor: brand, borderWidth: 6 }
20
+ : { borderColor: muted ? colors.line2 : colors.line4 },
21
+ ] })] }), _jsx(SheetText, { weight: FigtreeEnum.BOLD, size: 15, numberOfLines: 1, color: muted ? colors.muted : colors.ink, children: name }), _jsx(SheetText, { size: 12.5, numberOfLines: 1, color: muted ? colors.warnInk : colors.muted, children: sub })] }));
22
+ }
23
+ const styles = StyleSheet.create({
24
+ card: {
25
+ flex: 1,
26
+ minWidth: 0,
27
+ borderWidth: 1,
28
+ borderRadius: 14,
29
+ padding: 12,
30
+ gap: 2,
31
+ },
32
+ top: {
33
+ flexDirection: 'row',
34
+ alignItems: 'flex-start',
35
+ justifyContent: 'space-between',
36
+ marginBottom: 10,
37
+ },
38
+ radio: { width: 20, height: 20, borderRadius: 10, borderWidth: 1.5 },
39
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetPhoneFieldProps } from '../models/sheet.interface.js';
2
+ /** The Mobile Money number on the phone keyboard: the country's dial code, the number grouped as typed, its check. */
3
+ export declare function SheetPhoneField({ core, phoneRef }: SheetPhoneFieldProps): import("react").JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, StyleSheet, View } from 'react-native';
3
+ import { useTranslations } from 'use-intl';
4
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
5
+ import { FigtreeEnum } from "../models/sheet.enum.js";
6
+ import { flagOf } from "../models/sheet.utils.js";
7
+ import { SheetLogo } from "./SheetLogo.js";
8
+ import { SheetInput, SheetText } from "./SheetText.js";
9
+ /** The Mobile Money number on the phone keyboard: the country's dial code, the number grouped as typed, its check. */
10
+ export function SheetPhoneField({ core, phoneRef }) {
11
+ const t = useTranslations('checkout');
12
+ const { colors, brand } = useSheetTheme();
13
+ const { ui, derived, phone, actions } = core;
14
+ const { error } = phone.status;
15
+ const { mismatch, selected } = derived;
16
+ return (_jsxs(View, { style: styles.section, children: [_jsx(SheetText, { weight: FigtreeEnum.BOLD, style: styles.label, children: t('phone_label') }), _jsxs(View, { testID: "phone-field", style: [
17
+ styles.field,
18
+ {
19
+ borderColor: error
20
+ ? colors.danger
21
+ : phone.focused
22
+ ? brand
23
+ : colors.line3,
24
+ },
25
+ ], children: [_jsxs(View, { style: [styles.dial, { borderColor: colors.line2 }], children: [_jsx(SheetText, { size: 16, children: flagOf(ui.country) }), _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 16, children: `+${phone.dial}` })] }), _jsx(SheetInput, { ref: phoneRef, accessibilityLabel: t('phone_label'), keyboardType: "phone-pad", textContentType: "telephoneNumber", autoComplete: "tel", returnKeyType: "go", placeholder: phone.placeholder, value: phone.value, onChangeText: (text) => void phone.type(text, text.length), onFocus: phone.onFocus, onBlur: phone.onBlur, onSubmitEditing: actions.pay, editable: !ui.submitting, style: styles.input })] }), _jsx(View, { accessibilityLiveRegion: "polite", style: styles.message, children: error ? (_jsx(SheetText, { weight: FigtreeEnum.MEDIUM, size: 13, color: colors.danger, children: t(`phone_errors.${error}`) })) : mismatch ? (_jsxs(View, { style: styles.mismatch, children: [_jsx(SheetLogo, { logo: mismatch.logos[0] }), _jsx(SheetText, { size: 13, color: colors.ink2, style: styles.grow, children: t('mismatch', { method: mismatch.name }) }), _jsx(Pressable, { onPress: actions.switchToDetected, accessibilityRole: "button", hitSlop: 6, style: [styles.switch, { borderColor: colors.line3 }], children: _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 13, children: t('switch_to') }) })] })) : (selected && (_jsx(SheetText, { size: 13, color: colors.muted, children: t('helper', { method: selected.name }) }))) })] }));
26
+ }
27
+ const styles = StyleSheet.create({
28
+ section: { marginTop: 18 },
29
+ label: { marginBottom: 8 },
30
+ field: {
31
+ flexDirection: 'row',
32
+ alignItems: 'center',
33
+ height: 50,
34
+ borderWidth: 1,
35
+ borderRadius: 13,
36
+ overflow: 'hidden',
37
+ },
38
+ dial: {
39
+ flexDirection: 'row',
40
+ alignItems: 'center',
41
+ gap: 6,
42
+ height: '100%',
43
+ paddingHorizontal: 12,
44
+ borderRightWidth: 1,
45
+ },
46
+ input: { flex: 1, height: '100%', paddingHorizontal: 12 },
47
+ message: { minHeight: 22, marginTop: 8 },
48
+ mismatch: { flexDirection: 'row', alignItems: 'center', gap: 8 },
49
+ grow: { flex: 1 },
50
+ switch: {
51
+ borderWidth: 1,
52
+ borderRadius: 8,
53
+ paddingHorizontal: 12,
54
+ paddingVertical: 6,
55
+ },
56
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetRowProps } from '../models/sheet.interface.js';
2
+ /** A label on the left, its value on the right, a hairline under them. */
3
+ export declare function SheetRow({ label, children, urgent }: SheetRowProps): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
4
+ import { FigtreeEnum } from "../models/sheet.enum.js";
5
+ import { SheetText } from "./SheetText.js";
6
+ /** A label on the left, its value on the right, a hairline under them. */
7
+ export function SheetRow({ label, children, urgent = false }) {
8
+ const { colors } = useSheetTheme();
9
+ return (_jsxs(View, { style: [styles.row, { borderColor: colors.line }], children: [_jsx(SheetText, { color: colors.muted, children: label }), _jsx(View, { style: styles.value, children: typeof children === 'string' ? (_jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, color: urgent ? colors.danger : colors.ink, style: styles.figures, children: children })) : (children) })] }));
10
+ }
11
+ const styles = StyleSheet.create({
12
+ row: {
13
+ flexDirection: 'row',
14
+ alignItems: 'center',
15
+ justifyContent: 'space-between',
16
+ gap: 12,
17
+ minHeight: 44,
18
+ paddingVertical: 11,
19
+ borderBottomWidth: 1,
20
+ },
21
+ value: {
22
+ flexShrink: 1,
23
+ flexDirection: 'row',
24
+ alignItems: 'center',
25
+ gap: 8,
26
+ },
27
+ figures: { fontVariant: ['tabular-nums'], textAlign: 'right' },
28
+ });
@@ -0,0 +1,6 @@
1
+ import type { SheetScreenProps } from '../models/sheet.interface.js';
2
+ /**
3
+ * On a test payment the Simulator took, the tester plays the payer's phone,
4
+ * or makes the provider misbehave. Never on a live payment.
5
+ */
6
+ export declare function SheetSimulator({ core }: SheetScreenProps): import("react").JSX.Element;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, TouchableOpacity, View } from 'react-native';
3
+ import { useTranslations } from 'use-intl';
4
+ import { FLOW_TRAITS, SIMULATOR_NO_ANSWER_SUFFIX, } from "../checkout-core/models/checkout.constants.js";
5
+ import { SimulatorOutcomeEnum } from "../checkout-core/models/checkout.enum.js";
6
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
7
+ import { FigtreeEnum } from "../models/sheet.enum.js";
8
+ import { SheetText } from "./SheetText.js";
9
+ /**
10
+ * On a test payment the Simulator took, the tester plays the payer's phone,
11
+ * or makes the provider misbehave. Never on a live payment.
12
+ */
13
+ export function SheetSimulator({ core }) {
14
+ const t = useTranslations('checkout');
15
+ const { colors } = useSheetTheme();
16
+ const traits = FLOW_TRAITS[core.view.attempt.flow];
17
+ const outcomes = Object.values(SimulatorOutcomeEnum).filter((outcome) => !traits.hiddenOutcomes.includes(outcome));
18
+ return (_jsxs(View, { accessibilityLabel: t('simulator.title'), style: [
19
+ styles.panel,
20
+ { backgroundColor: colors.warnSoft, borderColor: colors.warnLine },
21
+ ], children: [_jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 12, color: colors.warnInk2, style: styles.title, children: t('simulator.title') }), _jsxs(SheetText, { size: 13, color: colors.warnInk2, style: styles.body, children: [t('simulator.body'), traits.prompt &&
22
+ ` ${t('simulator.no_answer', { suffix: SIMULATOR_NO_ANSWER_SUFFIX })}`] }), _jsx(View, { style: styles.outcomes, children: outcomes.map((outcome) => (_jsx(TouchableOpacity, { onPress: () => core.actions.simulate(outcome), activeOpacity: 0.7, accessibilityRole: "button", style: [
23
+ styles.outcome,
24
+ { backgroundColor: colors.surface, borderColor: colors.warnLine },
25
+ ], children: _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 13, children: t(`simulator.${outcome}`) }) }, outcome))) })] }));
26
+ }
27
+ const styles = StyleSheet.create({
28
+ panel: { borderWidth: 1, borderRadius: 12, padding: 14, marginTop: 18 },
29
+ title: { letterSpacing: 0.7, textTransform: 'uppercase' },
30
+ body: { marginTop: 4, marginBottom: 12 },
31
+ outcomes: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 },
32
+ outcome: {
33
+ minHeight: 36,
34
+ justifyContent: 'center',
35
+ borderWidth: 1,
36
+ borderRadius: 8,
37
+ paddingHorizontal: 12,
38
+ },
39
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The summary while the payment loads, bone for bone: its label and the
3
+ * amount.
4
+ */
5
+ export declare function SheetSkeleton(): import("react").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { useTranslations } from 'use-intl';
4
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
5
+ import { SheetBone } from "./SheetBone.js";
6
+ /**
7
+ * The summary while the payment loads, bone for bone: its label and the
8
+ * amount.
9
+ */
10
+ export function SheetSkeleton() {
11
+ const t = useTranslations('checkout');
12
+ const { colors } = useSheetTheme();
13
+ return (_jsxs(View, { accessibilityRole: "progressbar", accessibilityLabel: t('loading'), style: [styles.summary, { borderColor: colors.line }], children: [_jsx(SheetBone, { width: 96, height: 13 }), _jsx(View, { style: styles.amount, children: _jsx(SheetBone, { width: 168, height: 34 }) })] }));
14
+ }
15
+ const styles = StyleSheet.create({
16
+ summary: {
17
+ paddingHorizontal: 18,
18
+ paddingVertical: 16,
19
+ borderBottomWidth: 1,
20
+ gap: 8,
21
+ },
22
+ amount: { paddingVertical: 2 },
23
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetClosingProps } from '../models/sheet.interface.js';
2
+ /** Paid: the check, the amount and the receipt; Done closes, as the countdown on it does by itself. */
3
+ export declare function SheetSuccess({ core, onClose }: SheetClosingProps): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { useFormatter, useTranslations } from 'use-intl';
4
+ import { CURRENCY_LABEL } from "../checkout-core/models/checkout.constants.js";
5
+ import { cardLabel } from "../checkout-core/models/checkout.card.utils.js";
6
+ import { formatAmount } from "../checkout-core/models/checkout.money.utils.js";
7
+ import { maskE164 } from "../checkout-core/models/checkout.phone.utils.js";
8
+ import { logoOf } from "../checkout-core/models/checkout.view.utils.js";
9
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
10
+ import { FigtreeEnum, SheetIconEnum } from "../models/sheet.enum.js";
11
+ import { SheetButton } from "./SheetButton.js";
12
+ import { SheetIcon } from "./SheetIcon.js";
13
+ import { SheetLayout } from "./SheetLayout.js";
14
+ import { SheetLogo } from "./SheetLogo.js";
15
+ import { SheetRow } from "./SheetRow.js";
16
+ import { SheetText } from "./SheetText.js";
17
+ /** Paid: the check, the amount and the receipt; Done closes, as the countdown on it does by itself. */
18
+ export function SheetSuccess({ core, onClose }) {
19
+ const t = useTranslations('checkout');
20
+ const format = useFormatter();
21
+ const { colors } = useSheetTheme();
22
+ const { view, returnLeft } = core;
23
+ // Only a status check pays a payment: there is always an attempt.
24
+ const attempt = view.attempt;
25
+ return (_jsx(SheetLayout, { foot: _jsx(SheetButton
26
+ // Nothing stops the countdown in the sheet: it always runs here.
27
+ , {
28
+ // Nothing stops the countdown in the sheet: it always runs here.
29
+ label: t('sheet.done_in', { seconds: returnLeft }), onPress: onClose }), children: _jsxs(View, { style: styles.success, children: [_jsx(View, { style: styles.icon, children: _jsx(SheetIcon, { kind: SheetIconEnum.CHECK, color: colors.surface, background: colors.successInk, size: 64 }) }), _jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 22, accessibilityRole: "header", style: [styles.center, styles.title], children: t('success_title') }), _jsxs(View, { style: styles.amountRow, children: [_jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 34, style: styles.amount, children: formatAmount(view.amount, view.currency) }), _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 16, color: colors.ink2, children: CURRENCY_LABEL[view.currency] })] }), _jsx(SheetText, { color: colors.muted, style: styles.center, children: t('paid_to', { app: view.app.name }) }), _jsxs(View, { style: [styles.rows, { borderColor: colors.line }], children: [_jsxs(SheetRow, { label: t('method'), children: [_jsx(SheetLogo, { logo: logoOf(attempt) }), _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, children: attempt.methodName })] }), attempt.phone && (_jsx(SheetRow, { label: t('number'), children: maskE164(attempt.phone) })), attempt.card && (_jsx(SheetRow, { label: t('card'), children: cardLabel(attempt.card) })), _jsx(SheetRow, { label: t('date'), children: format.dateTime(view.paidAt, {
30
+ day: 'numeric',
31
+ month: 'short',
32
+ year: 'numeric',
33
+ hour: '2-digit',
34
+ minute: '2-digit',
35
+ }) }), _jsx(SheetRow, { label: t('reference'), children: attempt.reference })] })] }) }));
36
+ }
37
+ const styles = StyleSheet.create({
38
+ success: { paddingHorizontal: 18, paddingTop: 30 },
39
+ icon: { alignItems: 'center' },
40
+ center: { textAlign: 'center' },
41
+ title: { marginTop: 18, letterSpacing: -0.4 },
42
+ amountRow: {
43
+ flexDirection: 'row',
44
+ alignItems: 'baseline',
45
+ justifyContent: 'center',
46
+ gap: 6,
47
+ marginTop: 6,
48
+ },
49
+ amount: { letterSpacing: -0.85, fontVariant: ['tabular-nums'] },
50
+ rows: { borderTopWidth: 1, marginTop: 22 },
51
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetSummaryProps } from '../models/sheet.interface.js';
2
+ /** The amount to pay. The order and its lines stay on the web checkout. */
3
+ export declare function SheetSummary({ view }: SheetSummaryProps): import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StyleSheet, View } from 'react-native';
3
+ import { useTranslations } from 'use-intl';
4
+ import { CURRENCY_LABEL } from "../checkout-core/models/checkout.constants.js";
5
+ import { formatAmount } from "../checkout-core/models/checkout.money.utils.js";
6
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
7
+ import { FigtreeEnum } from "../models/sheet.enum.js";
8
+ import { SheetText } from "./SheetText.js";
9
+ /** The amount to pay. The order and its lines stay on the web checkout. */
10
+ export function SheetSummary({ view }) {
11
+ const t = useTranslations('checkout');
12
+ const { colors } = useSheetTheme();
13
+ const { amount, currency } = view;
14
+ return (_jsxs(View, { style: [styles.summary, { borderColor: colors.line }], children: [_jsx(SheetText, { size: 13, color: colors.muted, children: t('total_label') }), _jsxs(View, { style: styles.amountRow, children: [_jsx(SheetText, { weight: FigtreeEnum.EXTRA_BOLD, size: 34, style: styles.amount, children: formatAmount(amount, currency) }), _jsx(SheetText, { weight: FigtreeEnum.SEMI_BOLD, size: 16, color: colors.ink2, children: CURRENCY_LABEL[currency] })] })] }));
15
+ }
16
+ const styles = StyleSheet.create({
17
+ summary: {
18
+ paddingHorizontal: 18,
19
+ paddingVertical: 16,
20
+ borderBottomWidth: 1,
21
+ },
22
+ amountRow: {
23
+ flexDirection: 'row',
24
+ alignItems: 'baseline',
25
+ gap: 6,
26
+ marginTop: 2,
27
+ },
28
+ amount: {
29
+ letterSpacing: -0.85,
30
+ fontVariant: ['tabular-nums'],
31
+ },
32
+ });
@@ -0,0 +1,3 @@
1
+ import type { SheetTerminalProps } from '../models/sheet.interface.js';
2
+ /** A payment that cannot be paid any more, said plainly, and the way back to the app. */
3
+ export declare function SheetTerminal({ core, kind, onClose }: SheetTerminalProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslations } from 'use-intl';
3
+ import { TerminalKindEnum } from "../checkout-core/models/checkout.enum.js";
4
+ import { SheetIconEnum } from "../models/sheet.enum.js";
5
+ import { SheetButton } from "./SheetButton.js";
6
+ import { SheetLayout } from "./SheetLayout.js";
7
+ import { SheetMessage } from "./SheetMessage.js";
8
+ const COPY_KEY = {
9
+ [TerminalKindEnum.EXPIRED]: 'expired',
10
+ [TerminalKindEnum.CANCELLED]: 'cancelled',
11
+ [TerminalKindEnum.LOCKED]: 'locked_screen',
12
+ [TerminalKindEnum.NO_METHOD]: 'no_method',
13
+ };
14
+ /** A payment that cannot be paid any more, said plainly, and the way back to the app. */
15
+ export function SheetTerminal({ core, kind, onClose }) {
16
+ const t = useTranslations('checkout');
17
+ const app = core.view.app.name;
18
+ const key = COPY_KEY[kind];
19
+ const locked = kind === TerminalKindEnum.LOCKED;
20
+ return (_jsx(SheetLayout, { foot: _jsx(SheetButton, { label: t('return_to', { app }), onPress: onClose }), children: _jsx(SheetMessage, { title: t(`${key}.title`), body: t(`${key}.body`, { app }), icon: locked ? SheetIconEnum.LOCK : SheetIconEnum.CLOCK, danger: locked }) }));
21
+ }
@@ -0,0 +1,5 @@
1
+ import type { SheetInputProps, SheetTextProps } from '../models/sheet.interface.js';
2
+ /** Every text of the sheet: Figtree, in the sheet's ink unless told otherwise. */
3
+ export declare function SheetText({ weight, size, color, style, ...props }: SheetTextProps): import("react").JSX.Element;
4
+ /** Every field of the sheet: Figtree at 16, so iOS never zooms, in the sheet's ink. */
5
+ export declare function SheetInput({ style, ...props }: SheetInputProps): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text, TextInput } from 'react-native';
3
+ import { useSheetTheme } from "../hooks/use-sheet-theme.js";
4
+ import { FigtreeEnum } from "../models/sheet.enum.js";
5
+ /** Every text of the sheet: Figtree, in the sheet's ink unless told otherwise. */
6
+ export function SheetText({ weight = FigtreeEnum.REGULAR, size = 14, color, style, ...props }) {
7
+ const { colors } = useSheetTheme();
8
+ return (_jsx(Text, { ...props, style: [
9
+ { fontFamily: weight, fontSize: size, color: color ?? colors.ink },
10
+ style,
11
+ ] }));
12
+ }
13
+ /** Every field of the sheet: Figtree at 16, so iOS never zooms, in the sheet's ink. */
14
+ export function SheetInput({ style, ...props }) {
15
+ const { colors } = useSheetTheme();
16
+ return (_jsx(TextInput, { placeholderTextColor: colors.faint, selectionColor: colors.ink, ...props, style: [
17
+ {
18
+ fontFamily: FigtreeEnum.MEDIUM,
19
+ fontSize: 16,
20
+ color: colors.ink,
21
+ fontVariant: ['tabular-nums'],
22
+ },
23
+ style,
24
+ ] }));
25
+ }
@@ -0,0 +1,3 @@
1
+ import type { SheetToastProps } from '../models/sheet.interface.js';
2
+ /** One short message at a time, over the top of the sheet. */
3
+ export declare function SheetToast({ message }: SheetToastProps): import("react").JSX.Element | null;