@retray-dev/ui-kit 1.0.0 → 1.6.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.
- package/COMPONENTS.md +171 -66
- package/README.md +19 -20
- package/dist/index.d.mts +59 -5
- package/dist/index.d.ts +59 -5
- package/dist/index.js +475 -265
- package/dist/index.mjs +474 -266
- package/package.json +25 -8
- package/src/components/Accordion/Accordion.tsx +50 -38
- package/src/components/Alert/Alert.tsx +3 -1
- package/src/components/Avatar/Avatar.tsx +5 -1
- package/src/components/Badge/Badge.tsx +1 -1
- package/src/components/Button/Button.tsx +24 -8
- package/src/components/Card/Card.tsx +2 -8
- package/src/components/Checkbox/Checkbox.tsx +35 -7
- package/src/components/CurrencyInput/CurrencyInput.tsx +65 -0
- package/src/components/CurrencyInput/index.ts +2 -0
- package/src/components/EmptyState/EmptyState.tsx +1 -3
- package/src/components/Input/Input.tsx +18 -10
- package/src/components/Progress/Progress.tsx +3 -4
- package/src/components/RadioGroup/RadioGroup.tsx +72 -45
- package/src/components/Select/Select.tsx +117 -70
- package/src/components/Sheet/Sheet.tsx +9 -2
- package/src/components/Skeleton/Skeleton.tsx +36 -13
- package/src/components/Slider/Slider.tsx +5 -4
- package/src/components/Spinner/Spinner.tsx +1 -7
- package/src/components/Switch/Switch.tsx +5 -1
- package/src/components/Tabs/Tabs.tsx +82 -31
- package/src/components/Textarea/Textarea.tsx +29 -10
- package/src/components/Toast/Toast.tsx +69 -33
- package/src/components/Toggle/Toggle.tsx +32 -20
- package/src/index.ts +1 -0
- package/src/theme/colors.ts +4 -0
- package/src/theme/types.ts +2 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react'
|
|
2
2
|
import { View, TouchableOpacity, Text, Animated, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import * as Haptics from 'expo-haptics'
|
|
3
4
|
import { useTheme } from '../../theme'
|
|
4
5
|
|
|
5
6
|
export interface TabItem {
|
|
@@ -9,6 +10,10 @@ export interface TabItem {
|
|
|
9
10
|
|
|
10
11
|
export interface TabsProps {
|
|
11
12
|
tabs: TabItem[]
|
|
13
|
+
/**
|
|
14
|
+
* Controlled active tab value. When omitted the component manages state internally
|
|
15
|
+
* (uncontrolled), defaulting to the first tab.
|
|
16
|
+
*/
|
|
12
17
|
value?: string
|
|
13
18
|
onValueChange?: (value: string) => void
|
|
14
19
|
children?: React.ReactNode
|
|
@@ -22,6 +27,53 @@ export interface TabsContentProps {
|
|
|
22
27
|
style?: ViewStyle
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
function TabTrigger({
|
|
31
|
+
tab,
|
|
32
|
+
isActive,
|
|
33
|
+
onPress,
|
|
34
|
+
onLayout,
|
|
35
|
+
}: {
|
|
36
|
+
tab: TabItem
|
|
37
|
+
isActive: boolean
|
|
38
|
+
onPress: () => void
|
|
39
|
+
onLayout: (e: any) => void
|
|
40
|
+
}) {
|
|
41
|
+
const { colors } = useTheme()
|
|
42
|
+
const scale = useRef(new Animated.Value(1)).current
|
|
43
|
+
|
|
44
|
+
const handlePressIn = () => {
|
|
45
|
+
Animated.spring(scale, { toValue: 0.95, useNativeDriver: true, speed: 40, bounciness: 0 }).start()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const handlePressOut = () => {
|
|
49
|
+
Animated.spring(scale, { toValue: 1, useNativeDriver: true, speed: 40, bounciness: 4 }).start()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<TouchableOpacity
|
|
54
|
+
style={styles.trigger}
|
|
55
|
+
onPress={onPress}
|
|
56
|
+
onPressIn={handlePressIn}
|
|
57
|
+
onPressOut={handlePressOut}
|
|
58
|
+
onLayout={onLayout}
|
|
59
|
+
activeOpacity={1}
|
|
60
|
+
touchSoundDisabled={true}
|
|
61
|
+
>
|
|
62
|
+
<Animated.View style={{ transform: [{ scale }] }}>
|
|
63
|
+
<Text
|
|
64
|
+
style={[
|
|
65
|
+
styles.triggerLabel,
|
|
66
|
+
{ color: isActive ? colors.foreground : colors.mutedForeground },
|
|
67
|
+
isActive && styles.activeTriggerLabel,
|
|
68
|
+
]}
|
|
69
|
+
>
|
|
70
|
+
{tab.label}
|
|
71
|
+
</Text>
|
|
72
|
+
</Animated.View>
|
|
73
|
+
</TouchableOpacity>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
25
77
|
export function Tabs({ tabs, value, onValueChange, children, style }: TabsProps) {
|
|
26
78
|
const [internal, setInternal] = useState(tabs[0]?.value ?? '')
|
|
27
79
|
const { colors } = useTheme()
|
|
@@ -37,8 +89,18 @@ export function Tabs({ tabs, value, onValueChange, children, style }: TabsProps)
|
|
|
37
89
|
if (!layout) return
|
|
38
90
|
if (animate) {
|
|
39
91
|
Animated.parallel([
|
|
40
|
-
Animated.spring(pillX, {
|
|
41
|
-
|
|
92
|
+
Animated.spring(pillX, {
|
|
93
|
+
toValue: layout.x,
|
|
94
|
+
useNativeDriver: false,
|
|
95
|
+
speed: 20,
|
|
96
|
+
bounciness: 0,
|
|
97
|
+
}),
|
|
98
|
+
Animated.spring(pillWidth, {
|
|
99
|
+
toValue: layout.width,
|
|
100
|
+
useNativeDriver: false,
|
|
101
|
+
speed: 20,
|
|
102
|
+
bounciness: 0,
|
|
103
|
+
}),
|
|
42
104
|
]).start()
|
|
43
105
|
} else {
|
|
44
106
|
pillX.setValue(layout.x)
|
|
@@ -53,6 +115,7 @@ export function Tabs({ tabs, value, onValueChange, children, style }: TabsProps)
|
|
|
53
115
|
}, [active])
|
|
54
116
|
|
|
55
117
|
const handlePress = (v: string) => {
|
|
118
|
+
Haptics.selectionAsync()
|
|
56
119
|
if (!value) setInternal(v)
|
|
57
120
|
onValueChange?.(v)
|
|
58
121
|
}
|
|
@@ -79,35 +142,22 @@ export function Tabs({ tabs, value, onValueChange, children, style }: TabsProps)
|
|
|
79
142
|
},
|
|
80
143
|
]}
|
|
81
144
|
/>
|
|
82
|
-
{tabs.map((tab) =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
>
|
|
99
|
-
<Text
|
|
100
|
-
style={[
|
|
101
|
-
styles.triggerLabel,
|
|
102
|
-
{ color: isActive ? colors.foreground : colors.mutedForeground },
|
|
103
|
-
isActive && styles.activeTriggerLabel,
|
|
104
|
-
]}
|
|
105
|
-
>
|
|
106
|
-
{tab.label}
|
|
107
|
-
</Text>
|
|
108
|
-
</TouchableOpacity>
|
|
109
|
-
)
|
|
110
|
-
})}
|
|
145
|
+
{tabs.map((tab) => (
|
|
146
|
+
<TabTrigger
|
|
147
|
+
key={tab.value}
|
|
148
|
+
tab={tab}
|
|
149
|
+
isActive={tab.value === active}
|
|
150
|
+
onPress={() => handlePress(tab.value)}
|
|
151
|
+
onLayout={(e) => {
|
|
152
|
+
const { x, width } = e.nativeEvent.layout
|
|
153
|
+
tabLayouts.current[tab.value] = { x, width }
|
|
154
|
+
if (tab.value === active) {
|
|
155
|
+
animatePill(tab.value, false)
|
|
156
|
+
initialised.current = true
|
|
157
|
+
}
|
|
158
|
+
}}
|
|
159
|
+
/>
|
|
160
|
+
))}
|
|
111
161
|
</View>
|
|
112
162
|
{children}
|
|
113
163
|
</View>
|
|
@@ -133,6 +183,7 @@ const styles = StyleSheet.create({
|
|
|
133
183
|
paddingHorizontal: 12,
|
|
134
184
|
borderRadius: 6,
|
|
135
185
|
alignItems: 'center',
|
|
186
|
+
justifyContent: 'center',
|
|
136
187
|
zIndex: 1,
|
|
137
188
|
},
|
|
138
189
|
triggerLabel: {
|
|
@@ -1,23 +1,36 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
-
import { TextInput, View, Text, StyleSheet, TextInputProps } from 'react-native'
|
|
2
|
+
import { TextInput, View, Text, StyleSheet, TextInputProps, ViewStyle } from 'react-native'
|
|
3
3
|
import { useTheme } from '../../theme'
|
|
4
4
|
|
|
5
5
|
export interface TextareaProps extends TextInputProps {
|
|
6
6
|
label?: string
|
|
7
|
+
/** Red helper text below the textarea; also changes border to `destructive` color. Takes priority over `hint`. */
|
|
7
8
|
error?: string
|
|
9
|
+
/** Helper text shown below the textarea when there is no error. */
|
|
8
10
|
hint?: string
|
|
11
|
+
/** Number of visible text rows. Defaults to `4`. Controls `numberOfLines` and `minHeight`. */
|
|
9
12
|
rows?: number
|
|
13
|
+
/** Style for the outer container `View`. Use `style` (from `TextInputProps`) to style the `TextInput` itself. */
|
|
14
|
+
containerStyle?: ViewStyle
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
export function Textarea({
|
|
17
|
+
export function Textarea({
|
|
18
|
+
label,
|
|
19
|
+
error,
|
|
20
|
+
hint,
|
|
21
|
+
rows = 4,
|
|
22
|
+
containerStyle,
|
|
23
|
+
style,
|
|
24
|
+
onFocus,
|
|
25
|
+
onBlur,
|
|
26
|
+
...props
|
|
27
|
+
}: TextareaProps) {
|
|
13
28
|
const { colors } = useTheme()
|
|
14
29
|
const [focused, setFocused] = useState(false)
|
|
15
30
|
|
|
16
31
|
return (
|
|
17
|
-
<View style={styles.container}>
|
|
18
|
-
{label ?
|
|
19
|
-
<Text style={[styles.label, { color: colors.foreground }]}>{label}</Text>
|
|
20
|
-
) : null}
|
|
32
|
+
<View style={[styles.container, containerStyle]}>
|
|
33
|
+
{label ? <Text style={[styles.label, { color: colors.foreground }]} allowFontScaling={true}>{label}</Text> : null}
|
|
21
34
|
<TextInput
|
|
22
35
|
multiline
|
|
23
36
|
numberOfLines={rows}
|
|
@@ -32,17 +45,23 @@ export function Textarea({ label, error, hint, rows = 4, style, onFocus, onBlur,
|
|
|
32
45
|
},
|
|
33
46
|
style,
|
|
34
47
|
]}
|
|
35
|
-
onFocus={(e) => {
|
|
36
|
-
|
|
48
|
+
onFocus={(e) => {
|
|
49
|
+
setFocused(true)
|
|
50
|
+
onFocus?.(e)
|
|
51
|
+
}}
|
|
52
|
+
onBlur={(e) => {
|
|
53
|
+
setFocused(false)
|
|
54
|
+
onBlur?.(e)
|
|
55
|
+
}}
|
|
37
56
|
placeholderTextColor={colors.mutedForeground}
|
|
38
57
|
allowFontScaling={true}
|
|
39
58
|
{...props}
|
|
40
59
|
/>
|
|
41
60
|
{error ? (
|
|
42
|
-
<Text style={[styles.helperText, { color: colors.destructive }]}>{error}</Text>
|
|
61
|
+
<Text style={[styles.helperText, { color: colors.destructive }]} allowFontScaling={true}>{error}</Text>
|
|
43
62
|
) : null}
|
|
44
63
|
{!error && hint ? (
|
|
45
|
-
<Text style={[styles.helperText, { color: colors.mutedForeground }]}>{hint}</Text>
|
|
64
|
+
<Text style={[styles.helperText, { color: colors.mutedForeground }]} allowFontScaling={true}>{hint}</Text>
|
|
46
65
|
) : null}
|
|
47
66
|
</View>
|
|
48
67
|
)
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, useCallback, useEffect
|
|
2
|
-
import { View, Text, TouchableOpacity,
|
|
1
|
+
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withSpring,
|
|
7
|
+
withTiming,
|
|
8
|
+
Easing,
|
|
9
|
+
} from 'react-native-reanimated'
|
|
10
|
+
import { scheduleOnRN } from 'react-native-worklets'
|
|
11
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler'
|
|
3
12
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
4
13
|
import * as Haptics from 'expo-haptics'
|
|
5
14
|
import { useTheme } from '../../theme'
|
|
@@ -11,6 +20,7 @@ export interface ToastItem {
|
|
|
11
20
|
title?: string
|
|
12
21
|
description?: string
|
|
13
22
|
variant?: ToastVariant
|
|
23
|
+
/** Auto-dismiss delay in milliseconds. Defaults to `3000`. */
|
|
14
24
|
duration?: number
|
|
15
25
|
}
|
|
16
26
|
|
|
@@ -28,57 +38,83 @@ export function useToast() {
|
|
|
28
38
|
return useContext(ToastContext)
|
|
29
39
|
}
|
|
30
40
|
|
|
41
|
+
const SWIPE_THRESHOLD = 80
|
|
42
|
+
const VELOCITY_THRESHOLD = 800
|
|
43
|
+
|
|
31
44
|
function ToastNotification({ item, onDismiss }: { item: ToastItem; onDismiss: () => void }) {
|
|
32
45
|
const { colors } = useTheme()
|
|
33
|
-
const translateY =
|
|
34
|
-
const
|
|
46
|
+
const translateY = useSharedValue(-80)
|
|
47
|
+
const translateX = useSharedValue(0)
|
|
48
|
+
const opacity = useSharedValue(0)
|
|
35
49
|
|
|
36
50
|
useEffect(() => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Animated.timing(opacity, { toValue: 1, duration: 200, useNativeDriver: true }),
|
|
40
|
-
]).start()
|
|
51
|
+
translateY.value = withTiming(0, { duration: 120, easing: Easing.out(Easing.exp) })
|
|
52
|
+
opacity.value = withTiming(1, { duration: 100 })
|
|
41
53
|
|
|
42
54
|
const timer = setTimeout(() => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
translateY.value = withTiming(-80, { duration: 200 })
|
|
56
|
+
opacity.value = withTiming(0, { duration: 200 }, (done) => {
|
|
57
|
+
if (done) scheduleOnRN(onDismiss)
|
|
58
|
+
})
|
|
47
59
|
}, item.duration ?? 3000)
|
|
48
60
|
|
|
49
61
|
return () => clearTimeout(timer)
|
|
50
62
|
}, [])
|
|
51
63
|
|
|
64
|
+
const panGesture = Gesture.Pan()
|
|
65
|
+
.onUpdate((e) => {
|
|
66
|
+
translateX.value = e.translationX
|
|
67
|
+
})
|
|
68
|
+
.onEnd((e) => {
|
|
69
|
+
const shouldDismiss =
|
|
70
|
+
Math.abs(translateX.value) > SWIPE_THRESHOLD ||
|
|
71
|
+
Math.abs(e.velocityX) > VELOCITY_THRESHOLD
|
|
72
|
+
if (shouldDismiss) {
|
|
73
|
+
const direction = translateX.value > 0 ? 1 : -1
|
|
74
|
+
translateX.value = withTiming(direction * 500, { duration: 200 }, (done) => {
|
|
75
|
+
if (done) scheduleOnRN(onDismiss)
|
|
76
|
+
})
|
|
77
|
+
opacity.value = withTiming(0, { duration: 150 })
|
|
78
|
+
} else {
|
|
79
|
+
translateX.value = withSpring(0, { damping: 20, stiffness: 300 })
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
84
|
+
opacity: opacity.value,
|
|
85
|
+
transform: [{ translateY: translateY.value }, { translateX: translateX.value }],
|
|
86
|
+
}))
|
|
87
|
+
|
|
52
88
|
const bgColor = {
|
|
53
89
|
default: colors.foreground,
|
|
54
90
|
destructive: colors.destructive,
|
|
55
|
-
success:
|
|
91
|
+
success: colors.success,
|
|
56
92
|
}[item.variant ?? 'default']
|
|
57
93
|
|
|
58
94
|
const textColor = {
|
|
59
95
|
default: colors.background,
|
|
60
96
|
destructive: colors.destructiveForeground,
|
|
61
|
-
success:
|
|
97
|
+
success: colors.successForeground,
|
|
62
98
|
}[item.variant ?? 'default']
|
|
63
99
|
|
|
64
100
|
return (
|
|
65
|
-
<
|
|
66
|
-
style={[styles.toast, { backgroundColor: bgColor
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
</
|
|
81
|
-
</
|
|
101
|
+
<GestureDetector gesture={panGesture}>
|
|
102
|
+
<Animated.View style={[styles.toast, { backgroundColor: bgColor }, animatedStyle]}>
|
|
103
|
+
<View style={styles.toastContent}>
|
|
104
|
+
{item.title ? (
|
|
105
|
+
<Text style={[styles.toastTitle, { color: textColor }]}>{item.title}</Text>
|
|
106
|
+
) : null}
|
|
107
|
+
{item.description ? (
|
|
108
|
+
<Text style={[styles.toastDescription, { color: textColor, opacity: 0.85 }]}>
|
|
109
|
+
{item.description}
|
|
110
|
+
</Text>
|
|
111
|
+
) : null}
|
|
112
|
+
</View>
|
|
113
|
+
<TouchableOpacity onPress={onDismiss} style={styles.dismissButton} touchSoundDisabled={true}>
|
|
114
|
+
<Text style={[styles.dismissIcon, { color: textColor }]}>✕</Text>
|
|
115
|
+
</TouchableOpacity>
|
|
116
|
+
</Animated.View>
|
|
117
|
+
</GestureDetector>
|
|
82
118
|
)
|
|
83
119
|
}
|
|
84
120
|
|
|
@@ -155,8 +191,8 @@ const styles = StyleSheet.create({
|
|
|
155
191
|
fontSize: 13,
|
|
156
192
|
},
|
|
157
193
|
dismissButton: {
|
|
158
|
-
padding:
|
|
159
|
-
marginLeft:
|
|
194
|
+
padding: 12,
|
|
195
|
+
marginLeft: 4,
|
|
160
196
|
},
|
|
161
197
|
dismissIcon: {
|
|
162
198
|
fontSize: 12,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { TouchableOpacity, Text, StyleSheet, TouchableOpacityProps, ViewStyle } from 'react-native'
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
|
+
import { TouchableOpacity, Animated, Text, StyleSheet, TouchableOpacityProps, ViewStyle } from 'react-native'
|
|
3
3
|
import * as Haptics from 'expo-haptics'
|
|
4
4
|
import { useTheme } from '../../theme'
|
|
5
5
|
|
|
@@ -33,32 +33,44 @@ export function Toggle({
|
|
|
33
33
|
...props
|
|
34
34
|
}: ToggleProps) {
|
|
35
35
|
const { colors } = useTheme()
|
|
36
|
+
const scale = useRef(new Animated.Value(1)).current
|
|
37
|
+
|
|
38
|
+
const handlePressIn = () => {
|
|
39
|
+
if (disabled) return
|
|
40
|
+
Animated.spring(scale, { toValue: 0.95, useNativeDriver: true, speed: 40, bounciness: 0 }).start()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handlePressOut = () => {
|
|
44
|
+
Animated.spring(scale, { toValue: 1, useNativeDriver: true, speed: 40, bounciness: 4 }).start()
|
|
45
|
+
}
|
|
36
46
|
|
|
37
47
|
const containerStyle: ViewStyle = pressed
|
|
38
48
|
? { backgroundColor: colors.accent }
|
|
39
49
|
: variant === 'outline'
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
? { backgroundColor: 'transparent', borderWidth: 1, borderColor: colors.border }
|
|
51
|
+
: { backgroundColor: 'transparent' }
|
|
42
52
|
|
|
43
53
|
const textColor = pressed ? colors.accentForeground : colors.foreground
|
|
44
54
|
|
|
45
55
|
return (
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
styles.base,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
<Animated.View style={{ transform: [{ scale }] }}>
|
|
57
|
+
<TouchableOpacity
|
|
58
|
+
style={[styles.base, containerStyle, sizeStyles[size], disabled && styles.disabled, style]}
|
|
59
|
+
onPress={() => {
|
|
60
|
+
Haptics.selectionAsync()
|
|
61
|
+
onPressedChange?.(!pressed)
|
|
62
|
+
}}
|
|
63
|
+
onPressIn={handlePressIn}
|
|
64
|
+
onPressOut={handlePressOut}
|
|
65
|
+
disabled={disabled}
|
|
66
|
+
activeOpacity={1}
|
|
67
|
+
touchSoundDisabled={true}
|
|
68
|
+
{...props}
|
|
69
|
+
>
|
|
70
|
+
{icon}
|
|
71
|
+
{label ? <Text style={[styles.label, { color: textColor }]}>{label}</Text> : null}
|
|
72
|
+
</TouchableOpacity>
|
|
73
|
+
</Animated.View>
|
|
62
74
|
)
|
|
63
75
|
}
|
|
64
76
|
|
package/src/index.ts
CHANGED
package/src/theme/colors.ts
CHANGED
|
@@ -18,6 +18,8 @@ export const defaultLight: ThemeColors = {
|
|
|
18
18
|
border: '#e5e5e5',
|
|
19
19
|
input: '#e5e5e5',
|
|
20
20
|
ring: '#a3a3a3',
|
|
21
|
+
success: '#16a34a',
|
|
22
|
+
successForeground: '#ffffff',
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export const defaultDark: ThemeColors = {
|
|
@@ -38,4 +40,6 @@ export const defaultDark: ThemeColors = {
|
|
|
38
40
|
border: '#2a2a2a',
|
|
39
41
|
input: '#2a2a2a',
|
|
40
42
|
ring: '#d4d4d4',
|
|
43
|
+
success: '#22c55e',
|
|
44
|
+
successForeground: '#ffffff',
|
|
41
45
|
}
|