@sudobility/email-components-rn 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 (39) hide show
  1. package/dist/AbTestEmail.d.ts +12 -0
  2. package/dist/AbTestEmail.d.ts.map +1 -0
  3. package/dist/ContactCard.d.ts +14 -0
  4. package/dist/ContactCard.d.ts.map +1 -0
  5. package/dist/EmailAccountsList.d.ts +30 -0
  6. package/dist/EmailAccountsList.d.ts.map +1 -0
  7. package/dist/EmailAnalytics.d.ts +12 -0
  8. package/dist/EmailAnalytics.d.ts.map +1 -0
  9. package/dist/EmailCampaign.d.ts +12 -0
  10. package/dist/EmailCampaign.d.ts.map +1 -0
  11. package/dist/EmailInputGroup.d.ts +56 -0
  12. package/dist/EmailInputGroup.d.ts.map +1 -0
  13. package/dist/EmailTemplate.d.ts +13 -0
  14. package/dist/EmailTemplate.d.ts.map +1 -0
  15. package/dist/FreeEmailBanner.d.ts +24 -0
  16. package/dist/FreeEmailBanner.d.ts.map +1 -0
  17. package/dist/SubscriberList.d.ts +12 -0
  18. package/dist/SubscriberList.d.ts.map +1 -0
  19. package/dist/SubscriptionPlan.d.ts +13 -0
  20. package/dist/SubscriptionPlan.d.ts.map +1 -0
  21. package/dist/index.cjs.js +1545 -0
  22. package/dist/index.cjs.js.map +1 -0
  23. package/dist/index.d.ts +15 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.esm.js +1545 -0
  26. package/dist/index.esm.js.map +1 -0
  27. package/package.json +55 -0
  28. package/src/AbTestEmail.tsx +39 -0
  29. package/src/ContactCard.tsx +71 -0
  30. package/src/EmailAccountsList.tsx +209 -0
  31. package/src/EmailAnalytics.tsx +39 -0
  32. package/src/EmailCampaign.tsx +39 -0
  33. package/src/EmailInputGroup.tsx +204 -0
  34. package/src/EmailTemplate.tsx +46 -0
  35. package/src/FreeEmailBanner.tsx +167 -0
  36. package/src/SubscriberList.tsx +39 -0
  37. package/src/SubscriptionPlan.tsx +46 -0
  38. package/src/index.ts +27 -0
  39. package/src/nativewind.d.ts +23 -0
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { View, Text, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ export interface EmailAnalyticsProps extends ViewProps {
6
+ disabled?: boolean;
7
+ children?: React.ReactNode;
8
+ }
9
+
10
+ /**
11
+ * EmailAnalytics component for React Native
12
+ * Email analytics display container
13
+ */
14
+ export const EmailAnalytics: React.FC<EmailAnalyticsProps> = ({
15
+ className,
16
+ children,
17
+ disabled,
18
+ ...props
19
+ }) => {
20
+ return (
21
+ <View
22
+ className={cn(
23
+ 'p-4 rounded-lg border',
24
+ 'bg-white dark:bg-gray-900',
25
+ 'border-gray-200 dark:border-gray-700',
26
+ disabled && 'opacity-50',
27
+ className
28
+ )}
29
+ accessibilityLabel="Email Analytics"
30
+ {...props}
31
+ >
32
+ {children || (
33
+ <Text className="text-gray-900 dark:text-white">
34
+ EmailAnalytics Component
35
+ </Text>
36
+ )}
37
+ </View>
38
+ );
39
+ };
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { View, Text, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ export interface EmailCampaignProps extends ViewProps {
6
+ disabled?: boolean;
7
+ children?: React.ReactNode;
8
+ }
9
+
10
+ /**
11
+ * EmailCampaign component for React Native
12
+ * Email campaign display container
13
+ */
14
+ export const EmailCampaign: React.FC<EmailCampaignProps> = ({
15
+ className,
16
+ children,
17
+ disabled,
18
+ ...props
19
+ }) => {
20
+ return (
21
+ <View
22
+ className={cn(
23
+ 'p-4 rounded-lg border',
24
+ 'bg-white dark:bg-gray-900',
25
+ 'border-gray-200 dark:border-gray-700',
26
+ disabled && 'opacity-50',
27
+ className
28
+ )}
29
+ accessibilityLabel="Email Campaign"
30
+ {...props}
31
+ >
32
+ {children || (
33
+ <Text className="text-gray-900 dark:text-white">
34
+ EmailCampaign Component
35
+ </Text>
36
+ )}
37
+ </View>
38
+ );
39
+ };
@@ -0,0 +1,204 @@
1
+ import React from 'react';
2
+ import { View, Text, TextInput, Pressable, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ export interface EmailInputFieldProps {
6
+ label: string;
7
+ value: string;
8
+ onChangeText: (value: string) => void;
9
+ placeholder?: string;
10
+ required?: boolean;
11
+ error?: string;
12
+ className?: string;
13
+ }
14
+
15
+ export const EmailInputField: React.FC<EmailInputFieldProps> = ({
16
+ label,
17
+ value,
18
+ onChangeText,
19
+ placeholder,
20
+ required = false,
21
+ error,
22
+ className,
23
+ }) => {
24
+ return (
25
+ <View className={className}>
26
+ <Text className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
27
+ {label} {required && <Text className="text-red-500">*</Text>}
28
+ </Text>
29
+ <TextInput
30
+ value={value}
31
+ onChangeText={onChangeText}
32
+ placeholder={placeholder}
33
+ placeholderTextColor="#9CA3AF"
34
+ keyboardType="email-address"
35
+ autoCapitalize="none"
36
+ autoCorrect={false}
37
+ accessibilityLabel={label}
38
+ className={cn(
39
+ 'px-3 py-2 border rounded-md',
40
+ 'bg-white dark:bg-gray-800',
41
+ 'text-gray-900 dark:text-white',
42
+ error
43
+ ? 'border-red-300 dark:border-red-600'
44
+ : 'border-gray-300 dark:border-gray-600'
45
+ )}
46
+ />
47
+ {error && (
48
+ <Text className="mt-1 text-sm text-red-600 dark:text-red-400">
49
+ {error}
50
+ </Text>
51
+ )}
52
+ </View>
53
+ );
54
+ };
55
+
56
+ export interface CollapsibleEmailFieldProps extends EmailInputFieldProps {
57
+ isVisible: boolean;
58
+ onToggle: () => void;
59
+ showLabel?: string;
60
+ hideLabel?: string;
61
+ }
62
+
63
+ export const CollapsibleEmailField: React.FC<CollapsibleEmailFieldProps> = ({
64
+ isVisible,
65
+ onToggle,
66
+ showLabel,
67
+ hideLabel,
68
+ ...fieldProps
69
+ }) => {
70
+ const toggleLabel = isVisible ? hideLabel : showLabel;
71
+
72
+ return (
73
+ <View>
74
+ <Pressable
75
+ onPress={onToggle}
76
+ accessibilityRole="button"
77
+ className="flex-row items-center mb-2"
78
+ >
79
+ <Text className="text-sm text-blue-600 dark:text-blue-400">
80
+ {isVisible ? '▲' : '▼'} {toggleLabel}
81
+ </Text>
82
+ </Pressable>
83
+
84
+ {isVisible && <EmailInputField {...fieldProps} />}
85
+ </View>
86
+ );
87
+ };
88
+
89
+ export interface EmailInputGroupProps extends ViewProps {
90
+ to: string;
91
+ onToChange: (value: string) => void;
92
+ cc?: string;
93
+ onCcChange?: (value: string) => void;
94
+ bcc?: string;
95
+ onBccChange?: (value: string) => void;
96
+ showCc?: boolean;
97
+ showBcc?: boolean;
98
+ onToggleCc?: () => void;
99
+ onToggleBcc?: () => void;
100
+ errors?: {
101
+ to?: string;
102
+ cc?: string;
103
+ bcc?: string;
104
+ };
105
+ labels?: {
106
+ to?: string;
107
+ cc?: string;
108
+ bcc?: string;
109
+ addCc?: string;
110
+ removeCc?: string;
111
+ addBcc?: string;
112
+ removeBcc?: string;
113
+ };
114
+ placeholders?: {
115
+ to?: string;
116
+ cc?: string;
117
+ bcc?: string;
118
+ };
119
+ }
120
+
121
+ /**
122
+ * EmailInputGroup component for React Native
123
+ * Group of email input fields with collapsible CC/BCC
124
+ */
125
+ export const EmailInputGroup: React.FC<EmailInputGroupProps> = ({
126
+ to,
127
+ onToChange,
128
+ cc = '',
129
+ onCcChange,
130
+ bcc = '',
131
+ onBccChange,
132
+ showCc = false,
133
+ showBcc = false,
134
+ onToggleCc,
135
+ onToggleBcc,
136
+ errors = {},
137
+ className,
138
+ labels = {},
139
+ placeholders = {},
140
+ ...props
141
+ }) => {
142
+ const defaultLabels = {
143
+ to: 'To',
144
+ cc: 'CC',
145
+ bcc: 'BCC',
146
+ addCc: 'Add CC',
147
+ removeCc: 'Remove CC',
148
+ addBcc: 'Add BCC',
149
+ removeBcc: 'Remove BCC',
150
+ };
151
+
152
+ const defaultPlaceholders = {
153
+ to: 'recipient@example.com',
154
+ cc: 'cc@example.com',
155
+ bcc: 'bcc@example.com',
156
+ };
157
+
158
+ const finalLabels = { ...defaultLabels, ...labels };
159
+ const finalPlaceholders = { ...defaultPlaceholders, ...placeholders };
160
+
161
+ return (
162
+ <View className={cn('gap-4', className)} {...props}>
163
+ {/* To Field - Always visible */}
164
+ <EmailInputField
165
+ label={finalLabels.to}
166
+ value={to}
167
+ onChangeText={onToChange}
168
+ placeholder={finalPlaceholders.to}
169
+ required
170
+ error={errors.to}
171
+ />
172
+
173
+ {/* CC Field - Collapsible */}
174
+ {onToggleCc && onCcChange && (
175
+ <CollapsibleEmailField
176
+ label={finalLabels.cc}
177
+ value={cc}
178
+ onChangeText={onCcChange}
179
+ placeholder={finalPlaceholders.cc}
180
+ error={errors.cc}
181
+ isVisible={showCc}
182
+ onToggle={onToggleCc}
183
+ showLabel={finalLabels.addCc}
184
+ hideLabel={finalLabels.removeCc}
185
+ />
186
+ )}
187
+
188
+ {/* BCC Field - Collapsible */}
189
+ {onToggleBcc && onBccChange && (
190
+ <CollapsibleEmailField
191
+ label={finalLabels.bcc}
192
+ value={bcc}
193
+ onChangeText={onBccChange}
194
+ placeholder={finalPlaceholders.bcc}
195
+ error={errors.bcc}
196
+ isVisible={showBcc}
197
+ onToggle={onToggleBcc}
198
+ showLabel={finalLabels.addBcc}
199
+ hideLabel={finalLabels.removeBcc}
200
+ />
201
+ )}
202
+ </View>
203
+ );
204
+ };
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { Text, Pressable, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ export interface EmailTemplateProps extends ViewProps {
6
+ disabled?: boolean;
7
+ onPress?: () => void;
8
+ children?: React.ReactNode;
9
+ }
10
+
11
+ /**
12
+ * EmailTemplate component for React Native
13
+ * Email template display container
14
+ */
15
+ export const EmailTemplate: React.FC<EmailTemplateProps> = ({
16
+ className,
17
+ children,
18
+ disabled = false,
19
+ onPress,
20
+ ...props
21
+ }) => {
22
+ return (
23
+ <Pressable
24
+ onPress={disabled ? undefined : onPress}
25
+ disabled={disabled}
26
+ accessibilityRole="button"
27
+ accessibilityLabel="Email Template"
28
+ accessibilityState={{ disabled }}
29
+ className={cn(
30
+ 'p-4 rounded-lg border',
31
+ 'bg-white dark:bg-gray-900',
32
+ 'border-gray-200 dark:border-gray-700',
33
+ disabled && 'opacity-50',
34
+ 'active:bg-gray-50 dark:active:bg-gray-800',
35
+ className
36
+ )}
37
+ {...props}
38
+ >
39
+ {children || (
40
+ <Text className="text-gray-900 dark:text-white">
41
+ EmailTemplate Component
42
+ </Text>
43
+ )}
44
+ </Pressable>
45
+ );
46
+ };
@@ -0,0 +1,167 @@
1
+ import React from 'react';
2
+ import { View, Text, Pressable, Linking, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ type BannerVariant = 'default' | 'compact' | 'minimal' | 'vibrant';
6
+ type BannerSize = 'default' | 'compact' | 'large';
7
+
8
+ export interface FreeEmailBannerProps extends ViewProps {
9
+ variant?: BannerVariant;
10
+ size?: BannerSize;
11
+ message?: string;
12
+ ctaText?: string;
13
+ ctaLink?: string;
14
+ showBadge?: boolean;
15
+ badgeText?: string;
16
+ onDismiss?: () => void;
17
+ isDismissible?: boolean;
18
+ dismissAriaLabel?: string;
19
+ onCtaPress?: () => void;
20
+ }
21
+
22
+ const getBannerStyle = (variant: BannerVariant) => {
23
+ switch (variant) {
24
+ case 'compact':
25
+ return 'bg-green-50 dark:bg-green-900/10 border-green-200 dark:border-green-800';
26
+ case 'minimal':
27
+ return 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700';
28
+ case 'vibrant':
29
+ return 'bg-blue-600 border-blue-700';
30
+ default:
31
+ return 'bg-green-100 dark:bg-green-900/20 border-green-200 dark:border-green-700';
32
+ }
33
+ };
34
+
35
+ const getSizeStyle = (size: BannerSize) => {
36
+ switch (size) {
37
+ case 'compact':
38
+ return 'py-3';
39
+ case 'large':
40
+ return 'py-6';
41
+ default:
42
+ return 'py-4';
43
+ }
44
+ };
45
+
46
+ const getTextStyle = (variant: BannerVariant) => {
47
+ switch (variant) {
48
+ case 'compact':
49
+ return 'text-green-700 dark:text-green-300';
50
+ case 'minimal':
51
+ return 'text-gray-700 dark:text-gray-300';
52
+ case 'vibrant':
53
+ return 'text-white';
54
+ default:
55
+ return 'text-green-800 dark:text-green-200';
56
+ }
57
+ };
58
+
59
+ const getButtonStyle = (variant: BannerVariant) => {
60
+ switch (variant) {
61
+ case 'compact':
62
+ return 'bg-green-600 active:bg-green-700';
63
+ case 'minimal':
64
+ return 'bg-blue-600 active:bg-blue-700';
65
+ case 'vibrant':
66
+ return 'bg-white active:bg-gray-100';
67
+ default:
68
+ return 'bg-green-600 active:bg-green-700';
69
+ }
70
+ };
71
+
72
+ const getButtonTextStyle = (variant: BannerVariant) => {
73
+ if (variant === 'vibrant') {
74
+ return 'text-blue-600';
75
+ }
76
+ return 'text-white';
77
+ };
78
+
79
+ /**
80
+ * FreeEmailBanner component for React Native
81
+ * Promotional banner for free email signup
82
+ */
83
+ export const FreeEmailBanner: React.FC<FreeEmailBannerProps> = ({
84
+ className,
85
+ variant = 'default',
86
+ size = 'default',
87
+ message = 'Get Your Free Email Address - Start Using Web3 Email Today',
88
+ ctaText = 'Get Free Email',
89
+ ctaLink = '/connect',
90
+ showBadge = true,
91
+ badgeText = 'FREE',
92
+ onDismiss,
93
+ isDismissible = false,
94
+ dismissAriaLabel = 'Dismiss banner',
95
+ onCtaPress,
96
+ ...props
97
+ }) => {
98
+ const handleCtaPress = () => {
99
+ if (onCtaPress) {
100
+ onCtaPress();
101
+ } else if (ctaLink.startsWith('http')) {
102
+ Linking.openURL(ctaLink);
103
+ }
104
+ };
105
+
106
+ return (
107
+ <View
108
+ className={cn(
109
+ 'border-b px-4',
110
+ getBannerStyle(variant),
111
+ getSizeStyle(size),
112
+ className
113
+ )}
114
+ accessibilityLabel="Free Email Banner"
115
+ {...props}
116
+ >
117
+ <View className="items-center gap-4">
118
+ {isDismissible && onDismiss && (
119
+ <Pressable
120
+ onPress={onDismiss}
121
+ accessibilityRole="button"
122
+ accessibilityLabel={dismissAriaLabel}
123
+ className="absolute right-2 top-2 p-1 rounded-full"
124
+ >
125
+ <Text className="text-current opacity-60">✕</Text>
126
+ </Pressable>
127
+ )}
128
+
129
+ <View className="flex-row items-center justify-center flex-wrap gap-2">
130
+ {showBadge && (
131
+ <View
132
+ className={cn(
133
+ 'px-3 py-1 rounded-full',
134
+ variant === 'vibrant' ? 'bg-white' : 'bg-green-500'
135
+ )}
136
+ >
137
+ <Text
138
+ className={cn(
139
+ 'text-xs font-bold',
140
+ variant === 'vibrant' ? 'text-blue-600' : 'text-white'
141
+ )}
142
+ >
143
+ {badgeText}
144
+ </Text>
145
+ </View>
146
+ )}
147
+ <Text className={cn('font-semibold text-center', getTextStyle(variant))}>
148
+ {message}
149
+ </Text>
150
+ </View>
151
+
152
+ <Pressable
153
+ onPress={handleCtaPress}
154
+ accessibilityRole="button"
155
+ className={cn(
156
+ 'px-6 py-2 rounded-lg',
157
+ getButtonStyle(variant)
158
+ )}
159
+ >
160
+ <Text className={cn('font-medium', getButtonTextStyle(variant))}>
161
+ {ctaText}
162
+ </Text>
163
+ </Pressable>
164
+ </View>
165
+ </View>
166
+ );
167
+ };
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { View, Text, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ export interface SubscriberListProps extends ViewProps {
6
+ disabled?: boolean;
7
+ children?: React.ReactNode;
8
+ }
9
+
10
+ /**
11
+ * SubscriberList component for React Native
12
+ * Email subscriber list display container
13
+ */
14
+ export const SubscriberList: React.FC<SubscriberListProps> = ({
15
+ className,
16
+ children,
17
+ disabled,
18
+ ...props
19
+ }) => {
20
+ return (
21
+ <View
22
+ className={cn(
23
+ 'p-4 rounded-lg border',
24
+ 'bg-white dark:bg-gray-900',
25
+ 'border-gray-200 dark:border-gray-700',
26
+ disabled && 'opacity-50',
27
+ className
28
+ )}
29
+ accessibilityLabel="Subscriber List"
30
+ {...props}
31
+ >
32
+ {children || (
33
+ <Text className="text-gray-900 dark:text-white">
34
+ SubscriberList Component
35
+ </Text>
36
+ )}
37
+ </View>
38
+ );
39
+ };
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { Text, Pressable, type ViewProps } from 'react-native';
3
+ import { cn } from '@sudobility/components-rn';
4
+
5
+ export interface SubscriptionPlanProps extends ViewProps {
6
+ disabled?: boolean;
7
+ onPress?: () => void;
8
+ children?: React.ReactNode;
9
+ }
10
+
11
+ /**
12
+ * SubscriptionPlan component for React Native
13
+ * Subscription plan display container
14
+ */
15
+ export const SubscriptionPlan: React.FC<SubscriptionPlanProps> = ({
16
+ className,
17
+ children,
18
+ disabled = false,
19
+ onPress,
20
+ ...props
21
+ }) => {
22
+ return (
23
+ <Pressable
24
+ onPress={disabled ? undefined : onPress}
25
+ disabled={disabled}
26
+ accessibilityRole="button"
27
+ accessibilityLabel="Subscription Plan"
28
+ accessibilityState={{ disabled }}
29
+ className={cn(
30
+ 'p-4 rounded-lg border',
31
+ 'bg-white dark:bg-gray-900',
32
+ 'border-gray-200 dark:border-gray-700',
33
+ disabled && 'opacity-50',
34
+ 'active:bg-gray-50 dark:active:bg-gray-800',
35
+ className
36
+ )}
37
+ {...props}
38
+ >
39
+ {children || (
40
+ <Text className="text-gray-900 dark:text-white">
41
+ SubscriptionPlan Component
42
+ </Text>
43
+ )}
44
+ </Pressable>
45
+ );
46
+ };
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @sudobility/email-components-rn
3
+ * React Native Email components for Sudobility
4
+ */
5
+
6
+ export { AbTestEmail, type AbTestEmailProps } from './AbTestEmail';
7
+ export { ContactCard, type ContactCardProps } from './ContactCard';
8
+ export {
9
+ EmailAccountsList,
10
+ type EmailAccountsListProps,
11
+ type EmailAccount,
12
+ type WalletEmailGroup,
13
+ } from './EmailAccountsList';
14
+ export { EmailAnalytics, type EmailAnalyticsProps } from './EmailAnalytics';
15
+ export { EmailCampaign, type EmailCampaignProps } from './EmailCampaign';
16
+ export {
17
+ EmailInputGroup,
18
+ EmailInputField,
19
+ CollapsibleEmailField,
20
+ type EmailInputGroupProps,
21
+ type EmailInputFieldProps,
22
+ type CollapsibleEmailFieldProps,
23
+ } from './EmailInputGroup';
24
+ export { EmailTemplate, type EmailTemplateProps } from './EmailTemplate';
25
+ export { FreeEmailBanner, type FreeEmailBannerProps } from './FreeEmailBanner';
26
+ export { SubscriberList, type SubscriberListProps } from './SubscriberList';
27
+ export { SubscriptionPlan, type SubscriptionPlanProps } from './SubscriptionPlan';
@@ -0,0 +1,23 @@
1
+ // Type declarations for NativeWind className prop
2
+ import 'react-native';
3
+
4
+ declare module 'react-native' {
5
+ interface ViewProps {
6
+ className?: string;
7
+ }
8
+ interface TextProps {
9
+ className?: string;
10
+ }
11
+ interface ImageProps {
12
+ className?: string;
13
+ }
14
+ interface PressableProps {
15
+ className?: string;
16
+ }
17
+ interface TouchableOpacityProps {
18
+ className?: string;
19
+ }
20
+ interface ScrollViewProps {
21
+ className?: string;
22
+ }
23
+ }