syzygy-ui-rn 2.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.
- package/LICENSE +21 -0
- package/README.md +151 -0
- package/package.json +56 -0
- package/src/__tests__/components.test.tsx +368 -0
- package/src/__tests__/tokens.test.ts +47 -0
- package/src/components/badges/Badge.tsx +74 -0
- package/src/components/buttons/DestructiveButton.tsx +82 -0
- package/src/components/buttons/GhostButton.tsx +77 -0
- package/src/components/buttons/IconButton.tsx +61 -0
- package/src/components/buttons/PrimaryButton.tsx +82 -0
- package/src/components/buttons/SecondaryButton.tsx +84 -0
- package/src/components/cards/CardView.tsx +45 -0
- package/src/components/display/Avatar.tsx +53 -0
- package/src/components/display/Chip.tsx +64 -0
- package/src/components/display/CountBadge.tsx +58 -0
- package/src/components/display/DividerLine.tsx +29 -0
- package/src/components/display/LazyImageView.tsx +57 -0
- package/src/components/display/ListRow.tsx +76 -0
- package/src/components/display/SectionHeader.tsx +55 -0
- package/src/components/display/StarRatingView.tsx +70 -0
- package/src/components/feedback/EmptyStateView.tsx +93 -0
- package/src/components/feedback/ErrorStateView.tsx +75 -0
- package/src/components/feedback/LoadingView.tsx +57 -0
- package/src/components/feedback/ProgressBar.tsx +42 -0
- package/src/components/feedback/PullToRefresh.tsx +38 -0
- package/src/components/feedback/ShimmerView.tsx +47 -0
- package/src/components/feedback/ToastView.tsx +71 -0
- package/src/components/inputs/CheckboxInput.tsx +73 -0
- package/src/components/inputs/Dropdown.tsx +110 -0
- package/src/components/inputs/QuantityStepper.tsx +81 -0
- package/src/components/inputs/RadioButtonInput.tsx +73 -0
- package/src/components/inputs/SearchInput.tsx +107 -0
- package/src/components/inputs/SecureInput.tsx +105 -0
- package/src/components/inputs/SegmentedControl.tsx +70 -0
- package/src/components/inputs/SliderInput.tsx +131 -0
- package/src/components/inputs/TextInput.tsx +90 -0
- package/src/components/inputs/ToggleSwitch.tsx +50 -0
- package/src/components/layout/KeyboardAvoidingScrollView.tsx +40 -0
- package/src/components/navigation/AppBar.tsx +53 -0
- package/src/components/navigation/BackButton.tsx +63 -0
- package/src/components/navigation/BottomNavigationBar.tsx +65 -0
- package/src/components/navigation/PagerView.tsx +40 -0
- package/src/components/navigation/TabBar.tsx +68 -0
- package/src/components/navigation/TabBarItem.ts +8 -0
- package/src/components/overlay/BottomSheet.tsx +64 -0
- package/src/components/overlay/CollapsibleView.tsx +82 -0
- package/src/components/overlay/ModalDialog.tsx +47 -0
- package/src/index.ts +54 -0
- package/src/tokens/colors.ts +84 -0
- package/src/tokens/index.ts +4 -0
- package/src/tokens/radius.ts +8 -0
- package/src/tokens/spacing.ts +10 -0
- package/src/tokens/typography.ts +27 -0
- package/src/transitions/navigationTransitions.ts +66 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
LayoutAnimation,
|
|
4
|
+
Platform,
|
|
5
|
+
StyleProp,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Text,
|
|
8
|
+
TouchableOpacity,
|
|
9
|
+
UIManager,
|
|
10
|
+
View,
|
|
11
|
+
ViewStyle,
|
|
12
|
+
useColorScheme,
|
|
13
|
+
} from 'react-native';
|
|
14
|
+
|
|
15
|
+
import { getColors } from '../../tokens/colors';
|
|
16
|
+
import { spacing } from '../../tokens/spacing';
|
|
17
|
+
import { fontSizes, fontWeights } from '../../tokens/typography';
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
Platform.OS === 'android' &&
|
|
21
|
+
UIManager.setLayoutAnimationEnabledExperimental
|
|
22
|
+
) {
|
|
23
|
+
UIManager.setLayoutAnimationEnabledExperimental(true);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CollapsibleViewProps {
|
|
27
|
+
title: string;
|
|
28
|
+
initiallyExpanded?: boolean;
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
style?: StyleProp<ViewStyle>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** An expandable/collapsible section (accordion) with a tappable header. */
|
|
34
|
+
export const CollapsibleView: React.FC<CollapsibleViewProps> = ({
|
|
35
|
+
title,
|
|
36
|
+
initiallyExpanded = false,
|
|
37
|
+
children,
|
|
38
|
+
style,
|
|
39
|
+
}) => {
|
|
40
|
+
const scheme = useColorScheme();
|
|
41
|
+
const colors = getColors(scheme);
|
|
42
|
+
const [expanded, setExpanded] = useState(initiallyExpanded);
|
|
43
|
+
|
|
44
|
+
const toggle = () => {
|
|
45
|
+
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
46
|
+
setExpanded((prev) => !prev);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<View style={[styles.container, { backgroundColor: colors.surface }, style]}>
|
|
51
|
+
<TouchableOpacity
|
|
52
|
+
onPress={toggle}
|
|
53
|
+
accessibilityRole="button"
|
|
54
|
+
accessibilityLabel={`${title}, ${expanded ? 'expanded' : 'collapsed'}`}
|
|
55
|
+
style={styles.header}
|
|
56
|
+
>
|
|
57
|
+
<Text style={{ color: colors.textPrimary, fontSize: fontSizes.lg, fontWeight: fontWeights.semibold }}>
|
|
58
|
+
{title}
|
|
59
|
+
</Text>
|
|
60
|
+
<Text style={{ color: colors.textSecondary }}>{expanded ? '⌃' : '⌄'}</Text>
|
|
61
|
+
</TouchableOpacity>
|
|
62
|
+
{expanded ? <View style={styles.content}>{children}</View> : null}
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const styles = StyleSheet.create({
|
|
68
|
+
container: {
|
|
69
|
+
overflow: 'hidden',
|
|
70
|
+
},
|
|
71
|
+
header: {
|
|
72
|
+
minHeight: 44,
|
|
73
|
+
flexDirection: 'row',
|
|
74
|
+
alignItems: 'center',
|
|
75
|
+
justifyContent: 'space-between',
|
|
76
|
+
paddingHorizontal: spacing.md,
|
|
77
|
+
},
|
|
78
|
+
content: {
|
|
79
|
+
paddingHorizontal: spacing.md,
|
|
80
|
+
paddingBottom: spacing.md,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Modal, Pressable, StyleSheet, useColorScheme } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import { getColors } from '../../tokens/colors';
|
|
5
|
+
import { radius } from '../../tokens/radius';
|
|
6
|
+
import { spacing } from '../../tokens/spacing';
|
|
7
|
+
|
|
8
|
+
export interface ModalDialogProps {
|
|
9
|
+
visible: boolean;
|
|
10
|
+
onDismiss: () => void;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A centered dialog card over a dimmed scrim, wrapping React Native's core
|
|
16
|
+
* `Modal`. Named `ModalDialog` (not `Modal`) to avoid ambiguity with the
|
|
17
|
+
* `Modal` import from `react-native`.
|
|
18
|
+
*/
|
|
19
|
+
export const ModalDialog: React.FC<ModalDialogProps> = ({ visible, onDismiss, children }) => {
|
|
20
|
+
const scheme = useColorScheme();
|
|
21
|
+
const colors = getColors(scheme);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Modal visible={visible} transparent animationType="fade" onRequestClose={onDismiss}>
|
|
25
|
+
<Pressable style={[styles.backdrop, { backgroundColor: colors.overlay }]} onPress={onDismiss}>
|
|
26
|
+
<Pressable style={[styles.card, { backgroundColor: colors.surface }]}>
|
|
27
|
+
{children}
|
|
28
|
+
</Pressable>
|
|
29
|
+
</Pressable>
|
|
30
|
+
</Modal>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const styles = StyleSheet.create({
|
|
35
|
+
backdrop: {
|
|
36
|
+
flex: 1,
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
justifyContent: 'center',
|
|
39
|
+
padding: spacing.lg,
|
|
40
|
+
},
|
|
41
|
+
card: {
|
|
42
|
+
borderRadius: radius.lg,
|
|
43
|
+
padding: spacing.lg,
|
|
44
|
+
width: '100%',
|
|
45
|
+
maxWidth: 400,
|
|
46
|
+
},
|
|
47
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export * from './tokens';
|
|
2
|
+
|
|
3
|
+
export * from './components/buttons/PrimaryButton';
|
|
4
|
+
export * from './components/buttons/SecondaryButton';
|
|
5
|
+
export * from './components/buttons/DestructiveButton';
|
|
6
|
+
export * from './components/buttons/GhostButton';
|
|
7
|
+
export * from './components/buttons/IconButton';
|
|
8
|
+
|
|
9
|
+
export * from './components/inputs/TextInput';
|
|
10
|
+
export * from './components/inputs/SecureInput';
|
|
11
|
+
export * from './components/inputs/SearchInput';
|
|
12
|
+
export * from './components/inputs/ToggleSwitch';
|
|
13
|
+
export * from './components/inputs/CheckboxInput';
|
|
14
|
+
export * from './components/inputs/RadioButtonInput';
|
|
15
|
+
export * from './components/inputs/SliderInput';
|
|
16
|
+
export * from './components/inputs/Dropdown';
|
|
17
|
+
export * from './components/inputs/SegmentedControl';
|
|
18
|
+
export * from './components/inputs/QuantityStepper';
|
|
19
|
+
|
|
20
|
+
export * from './components/feedback/LoadingView';
|
|
21
|
+
export * from './components/feedback/EmptyStateView';
|
|
22
|
+
export * from './components/feedback/ToastView';
|
|
23
|
+
export * from './components/feedback/ShimmerView';
|
|
24
|
+
export * from './components/feedback/ProgressBar';
|
|
25
|
+
export * from './components/feedback/PullToRefresh';
|
|
26
|
+
export * from './components/feedback/ErrorStateView';
|
|
27
|
+
|
|
28
|
+
export * from './components/cards/CardView';
|
|
29
|
+
|
|
30
|
+
export * from './components/badges/Badge';
|
|
31
|
+
|
|
32
|
+
export * from './components/display/Avatar';
|
|
33
|
+
export * from './components/display/DividerLine';
|
|
34
|
+
export * from './components/display/Chip';
|
|
35
|
+
export * from './components/display/ListRow';
|
|
36
|
+
export * from './components/display/SectionHeader';
|
|
37
|
+
export * from './components/display/LazyImageView';
|
|
38
|
+
export * from './components/display/StarRatingView';
|
|
39
|
+
export * from './components/display/CountBadge';
|
|
40
|
+
|
|
41
|
+
export * from './components/overlay/ModalDialog';
|
|
42
|
+
export * from './components/overlay/BottomSheet';
|
|
43
|
+
export * from './components/overlay/CollapsibleView';
|
|
44
|
+
|
|
45
|
+
export * from './components/navigation/BackButton';
|
|
46
|
+
export * from './components/navigation/TabBarItem';
|
|
47
|
+
export * from './components/navigation/TabBar';
|
|
48
|
+
export * from './components/navigation/BottomNavigationBar';
|
|
49
|
+
export * from './components/navigation/AppBar';
|
|
50
|
+
export * from './components/navigation/PagerView';
|
|
51
|
+
|
|
52
|
+
export * from './components/layout/KeyboardAvoidingScrollView';
|
|
53
|
+
|
|
54
|
+
export * from './transitions/navigationTransitions';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type ColorScheme = 'light' | 'dark';
|
|
2
|
+
|
|
3
|
+
export interface ColorPalette {
|
|
4
|
+
background: string;
|
|
5
|
+
surface: string;
|
|
6
|
+
surfaceAlt: string;
|
|
7
|
+
border: string;
|
|
8
|
+
textPrimary: string;
|
|
9
|
+
textSecondary: string;
|
|
10
|
+
textInverse: string;
|
|
11
|
+
primary: string;
|
|
12
|
+
primaryText: string;
|
|
13
|
+
secondary: string;
|
|
14
|
+
secondaryText: string;
|
|
15
|
+
destructive: string;
|
|
16
|
+
destructiveText: string;
|
|
17
|
+
success: string;
|
|
18
|
+
successText: string;
|
|
19
|
+
warning: string;
|
|
20
|
+
warningText: string;
|
|
21
|
+
error: string;
|
|
22
|
+
errorText: string;
|
|
23
|
+
disabled: string;
|
|
24
|
+
disabledText: string;
|
|
25
|
+
overlay: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const lightColors: ColorPalette = {
|
|
29
|
+
background: '#FFFFFF',
|
|
30
|
+
surface: '#F5F5F7',
|
|
31
|
+
surfaceAlt: '#ECECEE',
|
|
32
|
+
border: '#D8D8DC',
|
|
33
|
+
textPrimary: '#1A1A1E',
|
|
34
|
+
textSecondary: '#6B6B72',
|
|
35
|
+
textInverse: '#FFFFFF',
|
|
36
|
+
primary: '#2F6FED',
|
|
37
|
+
primaryText: '#FFFFFF',
|
|
38
|
+
secondary: '#ECECEE',
|
|
39
|
+
secondaryText: '#1A1A1E',
|
|
40
|
+
destructive: '#E5484D',
|
|
41
|
+
destructiveText: '#FFFFFF',
|
|
42
|
+
success: '#1F9254',
|
|
43
|
+
successText: '#FFFFFF',
|
|
44
|
+
warning: '#B7791F',
|
|
45
|
+
warningText: '#FFFFFF',
|
|
46
|
+
error: '#E5484D',
|
|
47
|
+
errorText: '#FFFFFF',
|
|
48
|
+
disabled: '#D8D8DC',
|
|
49
|
+
disabledText: '#9A9AA1',
|
|
50
|
+
overlay: 'rgba(0,0,0,0.4)',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const darkColors: ColorPalette = {
|
|
54
|
+
background: '#101012',
|
|
55
|
+
surface: '#1C1C1F',
|
|
56
|
+
surfaceAlt: '#27272B',
|
|
57
|
+
border: '#3A3A3F',
|
|
58
|
+
textPrimary: '#F5F5F7',
|
|
59
|
+
textSecondary: '#A5A5AC',
|
|
60
|
+
textInverse: '#101012',
|
|
61
|
+
primary: '#5C8DF6',
|
|
62
|
+
primaryText: '#101012',
|
|
63
|
+
secondary: '#27272B',
|
|
64
|
+
secondaryText: '#F5F5F7',
|
|
65
|
+
destructive: '#F2555A',
|
|
66
|
+
destructiveText: '#101012',
|
|
67
|
+
success: '#3DD68C',
|
|
68
|
+
successText: '#101012',
|
|
69
|
+
warning: '#E3A008',
|
|
70
|
+
warningText: '#101012',
|
|
71
|
+
error: '#F2555A',
|
|
72
|
+
errorText: '#101012',
|
|
73
|
+
disabled: '#3A3A3F',
|
|
74
|
+
disabledText: '#6B6B72',
|
|
75
|
+
overlay: 'rgba(0,0,0,0.6)',
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const colors: Record<ColorScheme, ColorPalette> = {
|
|
79
|
+
light: lightColors,
|
|
80
|
+
dark: darkColors,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const getColors = (scheme: ColorScheme | 'unspecified' | null | undefined): ColorPalette =>
|
|
84
|
+
colors[scheme === 'dark' ? 'dark' : 'light'];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const fontSizes = {
|
|
2
|
+
xs: 12,
|
|
3
|
+
sm: 14,
|
|
4
|
+
md: 16,
|
|
5
|
+
lg: 18,
|
|
6
|
+
xl: 22,
|
|
7
|
+
xxl: 28,
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export const fontWeights = {
|
|
11
|
+
regular: '400' as const,
|
|
12
|
+
medium: '500' as const,
|
|
13
|
+
semibold: '600' as const,
|
|
14
|
+
bold: '700' as const,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const lineHeights = {
|
|
18
|
+
xs: 16,
|
|
19
|
+
sm: 20,
|
|
20
|
+
md: 22,
|
|
21
|
+
lg: 24,
|
|
22
|
+
xl: 28,
|
|
23
|
+
xxl: 34,
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
export type FontSizeToken = keyof typeof fontSizes;
|
|
27
|
+
export type LineHeightToken = keyof typeof lineHeights;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Animated } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reusable `Animated`-driven transition helpers for common
|
|
5
|
+
* navigation/presentation motion. Each returns an interpolated style object
|
|
6
|
+
* to spread onto an `Animated.View`, driven by an `Animated.Value` that the
|
|
7
|
+
* caller animates from 0 to 1 with `Animated.timing`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type HorizontalDirection = 'leftToRight' | 'rightToLeft';
|
|
11
|
+
|
|
12
|
+
/** Slides content in/out horizontally as `progress` animates 0 -> 1. */
|
|
13
|
+
export const slideTransition = (progress: Animated.Value, direction: HorizontalDirection, width: number) => {
|
|
14
|
+
const sign = direction === 'leftToRight' ? 1 : -1;
|
|
15
|
+
return {
|
|
16
|
+
opacity: progress,
|
|
17
|
+
transform: [
|
|
18
|
+
{
|
|
19
|
+
translateX: progress.interpolate({
|
|
20
|
+
inputRange: [0, 1],
|
|
21
|
+
outputRange: [sign * width, 0],
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** A plain cross-fade as `progress` animates 0 -> 1. */
|
|
29
|
+
export const crossFadeTransition = (progress: Animated.Value) => ({
|
|
30
|
+
opacity: progress,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export type VerticalDirection = 'topToBottom' | 'bottomToTop';
|
|
34
|
+
|
|
35
|
+
/** Slides content in/out vertically as `progress` animates 0 -> 1. */
|
|
36
|
+
export const slideVerticalTransition = (
|
|
37
|
+
progress: Animated.Value,
|
|
38
|
+
direction: VerticalDirection,
|
|
39
|
+
height: number
|
|
40
|
+
) => {
|
|
41
|
+
const sign = direction === 'topToBottom' ? -1 : 1;
|
|
42
|
+
return {
|
|
43
|
+
opacity: progress,
|
|
44
|
+
transform: [
|
|
45
|
+
{
|
|
46
|
+
translateY: progress.interpolate({
|
|
47
|
+
inputRange: [0, 1],
|
|
48
|
+
outputRange: [sign * height, 0],
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** A slide-up-and-fade transition suited to modal/sheet presentation. */
|
|
56
|
+
export const modalPresentationTransition = (progress: Animated.Value, height: number) => ({
|
|
57
|
+
opacity: progress,
|
|
58
|
+
transform: [
|
|
59
|
+
{
|
|
60
|
+
translateY: progress.interpolate({
|
|
61
|
+
inputRange: [0, 1],
|
|
62
|
+
outputRange: [height, 0],
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
});
|