@sudobility/components-rn 1.0.41 → 1.0.42

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.
@@ -1,6 +1,24 @@
1
1
  import * as React from 'react';
2
2
  import { View, Text } from 'react-native';
3
3
  import { cn } from '../../lib/utils';
4
+ import { colors } from '@sudobility/design';
5
+
6
+ const alert = colors.component.alert;
7
+
8
+ // Split DS alert color strings into separate parts for RN
9
+ function splitAlertClasses(base: string, dark: string) {
10
+ const all = `${base} ${dark}`.split(' ');
11
+ return {
12
+ bg: all.filter(c => c.includes('bg-')).join(' '),
13
+ border: all.filter(c => c.includes('border-')).join(' '),
14
+ text: all.filter(c => c.includes('text-')).join(' '),
15
+ };
16
+ }
17
+
18
+ const dsInfo = splitAlertClasses(alert.info.base, alert.info.dark);
19
+ const dsSuccess = splitAlertClasses(alert.success.base, alert.success.dark);
20
+ const dsWarning = splitAlertClasses(alert.warning.base, alert.warning.dark);
21
+ const dsError = splitAlertClasses(alert.error.base, alert.error.dark);
4
22
 
5
23
  export interface InfoBoxProps {
6
24
  /** Content to display in the info box */
@@ -49,31 +67,31 @@ export const InfoBox: React.FC<InfoBoxProps> = ({
49
67
  bordered = true,
50
68
  className,
51
69
  }) => {
52
- // Color variant configurations
70
+ // Color variants derived from design system (colors.component.alert)
53
71
  const variantClasses = {
54
72
  info: {
55
- bg: 'bg-blue-50 dark:bg-blue-900/20',
56
- border: 'border-blue-200 dark:border-blue-800',
57
- title: 'text-blue-900 dark:text-blue-300',
58
- text: 'text-blue-800 dark:text-blue-400',
73
+ bg: dsInfo.bg,
74
+ border: dsInfo.border,
75
+ title: dsInfo.text,
76
+ text: dsInfo.text,
59
77
  },
60
78
  success: {
61
- bg: 'bg-green-50 dark:bg-green-900/20',
62
- border: 'border-green-200 dark:border-green-800',
63
- title: 'text-green-900 dark:text-green-300',
64
- text: 'text-green-800 dark:text-green-400',
79
+ bg: dsSuccess.bg,
80
+ border: dsSuccess.border,
81
+ title: dsSuccess.text,
82
+ text: dsSuccess.text,
65
83
  },
66
84
  warning: {
67
- bg: 'bg-yellow-50 dark:bg-yellow-900/20',
68
- border: 'border-yellow-200 dark:border-yellow-800',
69
- title: 'text-yellow-900 dark:text-yellow-300',
70
- text: 'text-yellow-800 dark:text-yellow-400',
85
+ bg: dsWarning.bg,
86
+ border: dsWarning.border,
87
+ title: dsWarning.text,
88
+ text: dsWarning.text,
71
89
  },
72
90
  danger: {
73
- bg: 'bg-red-50 dark:bg-red-900/20',
74
- border: 'border-red-200 dark:border-red-800',
75
- title: 'text-red-900 dark:text-red-300',
76
- text: 'text-red-800 dark:text-red-400',
91
+ bg: dsError.bg,
92
+ border: dsError.border,
93
+ title: dsError.text,
94
+ text: dsError.text,
77
95
  },
78
96
  neutral: {
79
97
  bg: 'bg-gray-50 dark:bg-gray-800',
@@ -8,6 +8,13 @@ import {
8
8
  import { cn } from '../../lib/utils';
9
9
  import { variants as v } from '@sudobility/design';
10
10
 
11
+ // RN input state classes aligned with design system (colors.component.input.default).
12
+ // CSS pseudo-class selectors (focus:, disabled:) don't apply in RN, so we
13
+ // apply these conditionally via component state instead.
14
+ const inputFocusClass = 'border-blue-500 dark:border-blue-400';
15
+ const inputErrorClass = 'border-red-500 dark:border-red-400';
16
+ const inputDisabledClass = 'opacity-50 bg-gray-100 dark:bg-gray-800';
17
+
11
18
  export interface InputProps extends Omit<TextInputProps, 'style'> {
12
19
  /** Additional class names for styling */
13
20
  className?: string;
@@ -54,9 +61,9 @@ export const Input = React.forwardRef<TextInput, InputProps>(
54
61
  ref={ref}
55
62
  className={cn(
56
63
  v.input.default(),
57
- isFocused && 'border-blue-500 dark:border-blue-400',
58
- error && 'border-red-500 dark:border-red-400',
59
- disabled && 'opacity-50 bg-gray-100 dark:bg-gray-800',
64
+ isFocused && inputFocusClass,
65
+ error && inputErrorClass,
66
+ disabled && inputDisabledClass,
60
67
  className
61
68
  )}
62
69
  editable={isEditable}
@@ -45,7 +45,9 @@ export const Label = React.forwardRef<Text, LabelProps>(
45
45
  {...props}
46
46
  >
47
47
  {children}
48
- {required && <Text className='text-red-500 ml-1'>*</Text>}
48
+ {required && (
49
+ <Text className='text-red-600 dark:text-red-400 ml-1'>*</Text>
50
+ )}
49
51
  </Text>
50
52
  );
51
53
  }
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { View, Text } from 'react-native';
3
3
  import { cn } from '../../lib/utils';
4
+ import { colors } from '@sudobility/design';
4
5
 
5
6
  export interface ProgressCircleProps {
6
7
  /** Progress value (0-100) */
@@ -48,18 +49,18 @@ export const ProgressCircle: React.FC<ProgressCircleProps> = ({
48
49
  label,
49
50
  variant = 'primary',
50
51
  color,
51
- trackColor = '#e5e7eb',
52
+ trackColor = colors.raw.neutral[200],
52
53
  className,
53
54
  }) => {
54
55
  // Clamp value between 0 and 100
55
56
  const progress = Math.min(100, Math.max(0, value));
56
57
 
57
- // Color variants
58
+ // Color variants from design system raw palette
58
59
  const variantColors = {
59
- primary: '#2563eb',
60
- success: '#16a34a',
61
- warning: '#ca8a04',
62
- danger: '#dc2626',
60
+ primary: colors.raw.blue[600],
61
+ success: colors.raw.green[600],
62
+ warning: colors.raw.amber[600],
63
+ danger: colors.raw.red[600],
63
64
  };
64
65
 
65
66
  const progressColor = color || variantColors[variant];
@@ -1,6 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { View, Text, Pressable } from 'react-native';
3
3
  import { cn } from '../../lib/utils';
4
+ import { designTokens } from '@sudobility/design';
5
+
6
+ const { typography } = designTokens;
4
7
 
5
8
  export interface QuickAction {
6
9
  /** Unique identifier */
@@ -53,6 +56,7 @@ export const QuickActions: React.FC<QuickActionsProps> = ({
53
56
  columns = 3,
54
57
  className,
55
58
  }) => {
59
+ // Variant styles aligned with DS button colors (colors.component.button)
56
60
  const variantStyles = {
57
61
  default: {
58
62
  bg: 'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700',
@@ -60,23 +64,23 @@ export const QuickActions: React.FC<QuickActionsProps> = ({
60
64
  text: 'text-gray-900 dark:text-white',
61
65
  },
62
66
  primary: {
63
- bg: 'bg-blue-500 border-blue-500',
64
- bgActive: 'active:bg-blue-600',
67
+ bg: 'bg-blue-600 border-blue-600',
68
+ bgActive: 'active:bg-blue-800',
65
69
  text: 'text-white',
66
70
  },
67
71
  success: {
68
- bg: 'bg-green-500 border-green-500',
69
- bgActive: 'active:bg-green-600',
72
+ bg: 'bg-green-600 border-green-600',
73
+ bgActive: 'active:bg-green-800',
70
74
  text: 'text-white',
71
75
  },
72
76
  warning: {
73
- bg: 'bg-yellow-500 border-yellow-500',
74
- bgActive: 'active:bg-yellow-600',
77
+ bg: 'bg-orange-600 border-orange-600',
78
+ bgActive: 'active:bg-orange-800',
75
79
  text: 'text-white',
76
80
  },
77
81
  danger: {
78
- bg: 'bg-red-500 border-red-500',
79
- bgActive: 'active:bg-red-600',
82
+ bg: 'bg-red-600 border-red-600',
83
+ bgActive: 'active:bg-red-800',
80
84
  text: 'text-white',
81
85
  },
82
86
  };
@@ -121,7 +125,7 @@ export const QuickActions: React.FC<QuickActionsProps> = ({
121
125
  accessibilityState={{ disabled: action.disabled }}
122
126
  >
123
127
  {action.icon && <View className='w-5 h-5'>{action.icon}</View>}
124
- <Text className={cn('font-medium', styles.text)}>
128
+ <Text className={cn(typography.weight.medium, styles.text)}>
125
129
  {action.label}
126
130
  </Text>
127
131
  </Pressable>
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { View, ActivityIndicator, Text, type ViewProps } from 'react-native';
3
3
  import { cn } from '../../lib/utils';
4
+ import { colors, textVariants } from '@sudobility/design';
4
5
 
5
6
  /**
6
7
  * Props for the Spinner loading indicator component.
@@ -25,12 +26,13 @@ const sizeMap = {
25
26
  extraLarge: 'large' as const,
26
27
  };
27
28
 
29
+ // Spinner colors from design system raw palette
28
30
  const colorMap = {
29
- default: '#2563eb', // blue-600
31
+ default: colors.raw.blue[600],
30
32
  white: '#ffffff',
31
- success: '#16a34a', // green-600
32
- warning: '#ea580c', // orange-600
33
- error: '#dc2626', // red-600
33
+ success: colors.raw.green[600],
34
+ warning: colors.raw.orange[600],
35
+ error: colors.raw.red[600],
34
36
  };
35
37
 
36
38
  /**
@@ -62,7 +64,7 @@ export const Spinner: React.FC<SpinnerProps> = ({
62
64
  >
63
65
  <ActivityIndicator size={activitySize} color={color} />
64
66
  {showText && (
65
- <Text className='mt-2 text-gray-600 dark:text-gray-400 text-sm'>
67
+ <Text className={cn(textVariants.body.sm(), 'mt-2')}>
66
68
  {loadingText}
67
69
  </Text>
68
70
  )}
@@ -1,6 +1,21 @@
1
1
  import * as React from 'react';
2
2
  import { Text as RNText } from 'react-native';
3
3
  import { cn } from '../../lib/utils';
4
+ import { designTokens } from '@sudobility/design';
5
+
6
+ const { typography } = designTokens;
7
+
8
+ // Semantic text colors aligned with the design system color architecture.
9
+ // The DS provides these as hex values via colors.semantic; here they are
10
+ // expressed as Tailwind classes for NativeWind consumption.
11
+ const colorClasses = {
12
+ default: 'text-gray-900 dark:text-gray-100',
13
+ muted: 'text-gray-600 dark:text-gray-400',
14
+ primary: 'text-blue-600 dark:text-blue-400',
15
+ success: 'text-green-600 dark:text-green-400',
16
+ warning: 'text-yellow-600 dark:text-yellow-400',
17
+ danger: 'text-red-600 dark:text-red-400',
18
+ } as const;
4
19
 
5
20
  export interface TextProps {
6
21
  /** Text content */
@@ -51,62 +66,14 @@ export const Text: React.FC<TextProps> = ({
51
66
  numberOfLines,
52
67
  className,
53
68
  }) => {
54
- // Size configurations
55
- const sizeClasses = {
56
- xs: 'text-xs',
57
- sm: 'text-sm',
58
- base: 'text-base',
59
- lg: 'text-lg',
60
- xl: 'text-xl',
61
- '2xl': 'text-2xl',
62
- '3xl': 'text-3xl',
63
- '4xl': 'text-4xl',
64
- };
65
-
66
- // Weight configurations
67
- const weightClasses = {
68
- light: 'font-light',
69
- normal: 'font-normal',
70
- medium: 'font-medium',
71
- semibold: 'font-semibold',
72
- bold: 'font-bold',
73
- };
74
-
75
- // Color configurations
76
- const colorClasses = {
77
- default: 'text-gray-900 dark:text-gray-100',
78
- muted: 'text-gray-600 dark:text-gray-400',
79
- primary: 'text-blue-600 dark:text-blue-400',
80
- success: 'text-green-600 dark:text-green-400',
81
- warning: 'text-yellow-600 dark:text-yellow-400',
82
- danger: 'text-red-600 dark:text-red-400',
83
- };
84
-
85
- // Alignment configurations
86
- const alignClasses = align
87
- ? {
88
- left: 'text-left',
89
- center: 'text-center',
90
- right: 'text-right',
91
- }[align]
92
- : '';
93
-
94
- // Transform configurations
95
- const transformClasses = {
96
- none: '',
97
- uppercase: 'uppercase',
98
- lowercase: 'lowercase',
99
- capitalize: 'capitalize',
100
- };
101
-
102
69
  return (
103
70
  <RNText
104
71
  className={cn(
105
- sizeClasses[size],
106
- weightClasses[weight],
72
+ typography.size[size],
73
+ typography.weight[weight],
107
74
  colorClasses[color],
108
- alignClasses,
109
- transformClasses[transform],
75
+ align ? typography.align[align] : '',
76
+ transform !== 'none' ? typography.transform[transform] : '',
110
77
  className
111
78
  )}
112
79
  numberOfLines={numberOfLines}
@@ -8,6 +8,35 @@ import React, {
8
8
  } from 'react';
9
9
  import { View, Text, Pressable, Animated, SafeAreaView } from 'react-native';
10
10
  import { cn } from '../../lib/utils';
11
+ import { colors, textVariants } from '@sudobility/design';
12
+
13
+ // Split DS alert colors for RN (Views don't cascade text color)
14
+ function splitAlertClasses(base: string, dark: string) {
15
+ const all = `${base} ${dark}`.split(' ');
16
+ return {
17
+ container: all
18
+ .filter(c => c.includes('bg-') || c.includes('border-'))
19
+ .join(' '),
20
+ icon: all.filter(c => c.includes('text-')).join(' '),
21
+ };
22
+ }
23
+
24
+ // Lazily derive alert colors so module-level access doesn't fail
25
+ // when Jest transforms ESM chunk imports.
26
+ let _alertColors: Record<string, ReturnType<typeof splitAlertClasses>> | null =
27
+ null;
28
+ function getAlertColors() {
29
+ if (!_alertColors) {
30
+ const alert = colors.component.alert;
31
+ _alertColors = {
32
+ success: splitAlertClasses(alert.success.base, alert.success.dark),
33
+ error: splitAlertClasses(alert.error.base, alert.error.dark),
34
+ warning: splitAlertClasses(alert.warning.base, alert.warning.dark),
35
+ info: splitAlertClasses(alert.info.base, alert.info.dark),
36
+ };
37
+ }
38
+ return _alertColors;
39
+ }
11
40
 
12
41
  /** Data structure representing a single toast notification. */
13
42
  export interface ToastMessage {
@@ -72,24 +101,25 @@ export const Toast: React.FC<ToastProps> = ({ toast, onRemove }) => {
72
101
  }).start();
73
102
  }, [slideAnim]);
74
103
 
75
- // Variant styles - background
104
+ const ac = getAlertColors();
105
+
106
+ // Variant background+border from design system (colors.component.alert)
76
107
  const variantBgClasses = {
77
108
  default: 'bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700',
78
- success:
79
- 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800',
80
- error: 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800',
81
- warning:
82
- 'bg-yellow-50 dark:bg-yellow-900/20 border-yellow-200 dark:border-yellow-800',
83
- info: 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800',
109
+ success: ac.success.container,
110
+ error: ac.error.container,
111
+ warning: ac.warning.container,
112
+ info: ac.info.container,
84
113
  };
85
114
 
86
- // Variant styles - icon color
115
+ // Variant icon colors from design system
116
+ const alert = colors.component.alert;
87
117
  const iconColorClasses = {
88
118
  default: 'text-gray-600 dark:text-gray-400',
89
- success: 'text-green-600 dark:text-green-400',
90
- error: 'text-red-600 dark:text-red-400',
91
- warning: 'text-yellow-600 dark:text-yellow-400',
92
- info: 'text-blue-600 dark:text-blue-400',
119
+ success: alert.success.icon,
120
+ error: alert.error.icon,
121
+ warning: alert.warning.icon,
122
+ info: alert.info.icon,
93
123
  };
94
124
 
95
125
  // Icon symbols
@@ -116,12 +146,17 @@ export const Toast: React.FC<ToastProps> = ({ toast, onRemove }) => {
116
146
 
117
147
  <View className='flex-1 min-w-0'>
118
148
  {title && (
119
- <Text className='font-semibold text-gray-900 dark:text-white'>
149
+ <Text
150
+ className={cn(
151
+ textVariants.label.default(),
152
+ 'text-gray-900 dark:text-white'
153
+ )}
154
+ >
120
155
  {title}
121
156
  </Text>
122
157
  )}
123
158
  {description && (
124
- <Text className='text-sm text-gray-600 dark:text-gray-300 mt-1'>
159
+ <Text className={cn(textVariants.body.sm(), 'mt-1')}>
125
160
  {description}
126
161
  </Text>
127
162
  )}