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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -0
  3. package/package.json +56 -0
  4. package/src/__tests__/components.test.tsx +368 -0
  5. package/src/__tests__/tokens.test.ts +47 -0
  6. package/src/components/badges/Badge.tsx +74 -0
  7. package/src/components/buttons/DestructiveButton.tsx +82 -0
  8. package/src/components/buttons/GhostButton.tsx +77 -0
  9. package/src/components/buttons/IconButton.tsx +61 -0
  10. package/src/components/buttons/PrimaryButton.tsx +82 -0
  11. package/src/components/buttons/SecondaryButton.tsx +84 -0
  12. package/src/components/cards/CardView.tsx +45 -0
  13. package/src/components/display/Avatar.tsx +53 -0
  14. package/src/components/display/Chip.tsx +64 -0
  15. package/src/components/display/CountBadge.tsx +58 -0
  16. package/src/components/display/DividerLine.tsx +29 -0
  17. package/src/components/display/LazyImageView.tsx +57 -0
  18. package/src/components/display/ListRow.tsx +76 -0
  19. package/src/components/display/SectionHeader.tsx +55 -0
  20. package/src/components/display/StarRatingView.tsx +70 -0
  21. package/src/components/feedback/EmptyStateView.tsx +93 -0
  22. package/src/components/feedback/ErrorStateView.tsx +75 -0
  23. package/src/components/feedback/LoadingView.tsx +57 -0
  24. package/src/components/feedback/ProgressBar.tsx +42 -0
  25. package/src/components/feedback/PullToRefresh.tsx +38 -0
  26. package/src/components/feedback/ShimmerView.tsx +47 -0
  27. package/src/components/feedback/ToastView.tsx +71 -0
  28. package/src/components/inputs/CheckboxInput.tsx +73 -0
  29. package/src/components/inputs/Dropdown.tsx +110 -0
  30. package/src/components/inputs/QuantityStepper.tsx +81 -0
  31. package/src/components/inputs/RadioButtonInput.tsx +73 -0
  32. package/src/components/inputs/SearchInput.tsx +107 -0
  33. package/src/components/inputs/SecureInput.tsx +105 -0
  34. package/src/components/inputs/SegmentedControl.tsx +70 -0
  35. package/src/components/inputs/SliderInput.tsx +131 -0
  36. package/src/components/inputs/TextInput.tsx +90 -0
  37. package/src/components/inputs/ToggleSwitch.tsx +50 -0
  38. package/src/components/layout/KeyboardAvoidingScrollView.tsx +40 -0
  39. package/src/components/navigation/AppBar.tsx +53 -0
  40. package/src/components/navigation/BackButton.tsx +63 -0
  41. package/src/components/navigation/BottomNavigationBar.tsx +65 -0
  42. package/src/components/navigation/PagerView.tsx +40 -0
  43. package/src/components/navigation/TabBar.tsx +68 -0
  44. package/src/components/navigation/TabBarItem.ts +8 -0
  45. package/src/components/overlay/BottomSheet.tsx +64 -0
  46. package/src/components/overlay/CollapsibleView.tsx +82 -0
  47. package/src/components/overlay/ModalDialog.tsx +47 -0
  48. package/src/index.ts +54 -0
  49. package/src/tokens/colors.ts +84 -0
  50. package/src/tokens/index.ts +4 -0
  51. package/src/tokens/radius.ts +8 -0
  52. package/src/tokens/spacing.ts +10 -0
  53. package/src/tokens/typography.ts +27 -0
  54. package/src/transitions/navigationTransitions.ts +66 -0
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import {
3
+ StyleProp,
4
+ StyleSheet,
5
+ Text,
6
+ useColorScheme,
7
+ View,
8
+ ViewStyle,
9
+ } from 'react-native';
10
+
11
+ import { getColors } from '../../tokens/colors';
12
+ import { radius } from '../../tokens/radius';
13
+ import { spacing } from '../../tokens/spacing';
14
+ import { fontSizes, fontWeights } from '../../tokens/typography';
15
+
16
+ export type BadgeVariant = 'primary' | 'success' | 'warning' | 'error';
17
+
18
+ export interface BadgeProps {
19
+ text: string;
20
+ variant?: BadgeVariant;
21
+ style?: StyleProp<ViewStyle>;
22
+ accessibilityLabel?: string;
23
+ }
24
+
25
+ export const Badge: React.FC<BadgeProps> = ({
26
+ text,
27
+ variant = 'primary',
28
+ style,
29
+ accessibilityLabel,
30
+ }) => {
31
+ const scheme = useColorScheme();
32
+ const colors = getColors(scheme);
33
+
34
+ const backgroundColor =
35
+ variant === 'primary'
36
+ ? colors.primary
37
+ : variant === 'success'
38
+ ? colors.success
39
+ : variant === 'warning'
40
+ ? colors.warning
41
+ : colors.error;
42
+
43
+ const textColor =
44
+ variant === 'primary'
45
+ ? colors.primaryText
46
+ : variant === 'success'
47
+ ? colors.successText
48
+ : variant === 'warning'
49
+ ? colors.warningText
50
+ : colors.errorText;
51
+
52
+ return (
53
+ <View
54
+ style={[styles.container, { backgroundColor }, style]}
55
+ accessibilityRole="text"
56
+ accessibilityLabel={accessibilityLabel ?? text}
57
+ >
58
+ <Text style={[styles.text, { color: textColor }]}>{text}</Text>
59
+ </View>
60
+ );
61
+ };
62
+
63
+ const styles = StyleSheet.create({
64
+ container: {
65
+ alignSelf: 'flex-start',
66
+ paddingHorizontal: spacing.sm,
67
+ paddingVertical: spacing.xs,
68
+ borderRadius: radius.full,
69
+ },
70
+ text: {
71
+ fontSize: fontSizes.xs,
72
+ fontWeight: fontWeights.semibold,
73
+ },
74
+ });
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ StyleProp,
5
+ StyleSheet,
6
+ Text,
7
+ TouchableOpacity,
8
+ useColorScheme,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+
12
+ import { getColors } from '../../tokens/colors';
13
+ import { radius } from '../../tokens/radius';
14
+ import { spacing } from '../../tokens/spacing';
15
+ import { fontSizes, fontWeights } from '../../tokens/typography';
16
+
17
+ export interface DestructiveButtonProps {
18
+ title: string;
19
+ onPress: () => void;
20
+ disabled?: boolean;
21
+ loading?: boolean;
22
+ style?: StyleProp<ViewStyle>;
23
+ accessibilityLabel?: string;
24
+ }
25
+
26
+ export const DestructiveButton: React.FC<DestructiveButtonProps> = ({
27
+ title,
28
+ onPress,
29
+ disabled = false,
30
+ loading = false,
31
+ style,
32
+ accessibilityLabel,
33
+ }) => {
34
+ const scheme = useColorScheme();
35
+ const colors = getColors(scheme);
36
+ const isDisabled = disabled || loading;
37
+
38
+ return (
39
+ <TouchableOpacity
40
+ onPress={onPress}
41
+ disabled={isDisabled}
42
+ accessibilityRole="button"
43
+ accessibilityLabel={accessibilityLabel ?? title}
44
+ accessibilityState={{ disabled: isDisabled, busy: loading }}
45
+ style={[
46
+ styles.base,
47
+ {
48
+ backgroundColor: isDisabled ? colors.disabled : colors.destructive,
49
+ },
50
+ style,
51
+ ]}
52
+ >
53
+ {loading ? (
54
+ <ActivityIndicator color={colors.destructiveText} />
55
+ ) : (
56
+ <Text
57
+ style={[
58
+ styles.text,
59
+ { color: isDisabled ? colors.disabledText : colors.destructiveText },
60
+ ]}
61
+ >
62
+ {title}
63
+ </Text>
64
+ )}
65
+ </TouchableOpacity>
66
+ );
67
+ };
68
+
69
+ const styles = StyleSheet.create({
70
+ base: {
71
+ minHeight: 44,
72
+ paddingHorizontal: spacing.md,
73
+ paddingVertical: spacing.sm,
74
+ borderRadius: radius.md,
75
+ alignItems: 'center',
76
+ justifyContent: 'center',
77
+ },
78
+ text: {
79
+ fontSize: fontSizes.md,
80
+ fontWeight: fontWeights.semibold,
81
+ },
82
+ });
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ StyleProp,
5
+ StyleSheet,
6
+ Text,
7
+ TouchableOpacity,
8
+ useColorScheme,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+
12
+ import { getColors } from '../../tokens/colors';
13
+ import { radius } from '../../tokens/radius';
14
+ import { spacing } from '../../tokens/spacing';
15
+ import { fontSizes, fontWeights } from '../../tokens/typography';
16
+
17
+ export interface GhostButtonProps {
18
+ title: string;
19
+ onPress: () => void;
20
+ disabled?: boolean;
21
+ loading?: boolean;
22
+ style?: StyleProp<ViewStyle>;
23
+ accessibilityLabel?: string;
24
+ }
25
+
26
+ export const GhostButton: React.FC<GhostButtonProps> = ({
27
+ title,
28
+ onPress,
29
+ disabled = false,
30
+ loading = false,
31
+ style,
32
+ accessibilityLabel,
33
+ }) => {
34
+ const scheme = useColorScheme();
35
+ const colors = getColors(scheme);
36
+ const isDisabled = disabled || loading;
37
+
38
+ return (
39
+ <TouchableOpacity
40
+ onPress={onPress}
41
+ disabled={isDisabled}
42
+ accessibilityRole="button"
43
+ accessibilityLabel={accessibilityLabel ?? title}
44
+ accessibilityState={{ disabled: isDisabled, busy: loading }}
45
+ style={[styles.base, style]}
46
+ >
47
+ {loading ? (
48
+ <ActivityIndicator color={colors.primary} />
49
+ ) : (
50
+ <Text
51
+ style={[
52
+ styles.text,
53
+ { color: isDisabled ? colors.disabledText : colors.primary },
54
+ ]}
55
+ >
56
+ {title}
57
+ </Text>
58
+ )}
59
+ </TouchableOpacity>
60
+ );
61
+ };
62
+
63
+ const styles = StyleSheet.create({
64
+ base: {
65
+ minHeight: 44,
66
+ paddingHorizontal: spacing.md,
67
+ paddingVertical: spacing.sm,
68
+ borderRadius: radius.md,
69
+ alignItems: 'center',
70
+ justifyContent: 'center',
71
+ backgroundColor: 'transparent',
72
+ },
73
+ text: {
74
+ fontSize: fontSizes.md,
75
+ fontWeight: fontWeights.semibold,
76
+ },
77
+ });
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import {
3
+ StyleProp,
4
+ StyleSheet,
5
+ TouchableOpacity,
6
+ useColorScheme,
7
+ ViewStyle,
8
+ } from 'react-native';
9
+
10
+ import { getColors } from '../../tokens/colors';
11
+ import { radius } from '../../tokens/radius';
12
+ import { spacing } from '../../tokens/spacing';
13
+
14
+ export interface IconButtonProps {
15
+ icon: React.ReactNode;
16
+ onPress: () => void;
17
+ disabled?: boolean;
18
+ style?: StyleProp<ViewStyle>;
19
+ accessibilityLabel: string;
20
+ }
21
+
22
+ export const IconButton: React.FC<IconButtonProps> = ({
23
+ icon,
24
+ onPress,
25
+ disabled = false,
26
+ style,
27
+ accessibilityLabel,
28
+ }) => {
29
+ const scheme = useColorScheme();
30
+ const colors = getColors(scheme);
31
+
32
+ return (
33
+ <TouchableOpacity
34
+ onPress={onPress}
35
+ disabled={disabled}
36
+ accessibilityRole="button"
37
+ accessibilityLabel={accessibilityLabel}
38
+ accessibilityState={{ disabled }}
39
+ style={[
40
+ styles.base,
41
+ {
42
+ backgroundColor: disabled ? colors.disabled : colors.surfaceAlt,
43
+ },
44
+ style,
45
+ ]}
46
+ >
47
+ {icon}
48
+ </TouchableOpacity>
49
+ );
50
+ };
51
+
52
+ const styles = StyleSheet.create({
53
+ base: {
54
+ width: 44,
55
+ height: 44,
56
+ borderRadius: radius.full,
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ padding: spacing.xs,
60
+ },
61
+ });
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ StyleProp,
5
+ StyleSheet,
6
+ Text,
7
+ TouchableOpacity,
8
+ useColorScheme,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+
12
+ import { getColors } from '../../tokens/colors';
13
+ import { radius } from '../../tokens/radius';
14
+ import { spacing } from '../../tokens/spacing';
15
+ import { fontSizes, fontWeights } from '../../tokens/typography';
16
+
17
+ export interface PrimaryButtonProps {
18
+ title: string;
19
+ onPress: () => void;
20
+ disabled?: boolean;
21
+ loading?: boolean;
22
+ style?: StyleProp<ViewStyle>;
23
+ accessibilityLabel?: string;
24
+ }
25
+
26
+ export const PrimaryButton: React.FC<PrimaryButtonProps> = ({
27
+ title,
28
+ onPress,
29
+ disabled = false,
30
+ loading = false,
31
+ style,
32
+ accessibilityLabel,
33
+ }) => {
34
+ const scheme = useColorScheme();
35
+ const colors = getColors(scheme);
36
+ const isDisabled = disabled || loading;
37
+
38
+ return (
39
+ <TouchableOpacity
40
+ onPress={onPress}
41
+ disabled={isDisabled}
42
+ accessibilityRole="button"
43
+ accessibilityLabel={accessibilityLabel ?? title}
44
+ accessibilityState={{ disabled: isDisabled, busy: loading }}
45
+ style={[
46
+ styles.base,
47
+ {
48
+ backgroundColor: isDisabled ? colors.disabled : colors.primary,
49
+ },
50
+ style,
51
+ ]}
52
+ >
53
+ {loading ? (
54
+ <ActivityIndicator color={colors.primaryText} />
55
+ ) : (
56
+ <Text
57
+ style={[
58
+ styles.text,
59
+ { color: isDisabled ? colors.disabledText : colors.primaryText },
60
+ ]}
61
+ >
62
+ {title}
63
+ </Text>
64
+ )}
65
+ </TouchableOpacity>
66
+ );
67
+ };
68
+
69
+ const styles = StyleSheet.create({
70
+ base: {
71
+ minHeight: 44,
72
+ paddingHorizontal: spacing.md,
73
+ paddingVertical: spacing.sm,
74
+ borderRadius: radius.md,
75
+ alignItems: 'center',
76
+ justifyContent: 'center',
77
+ },
78
+ text: {
79
+ fontSize: fontSizes.md,
80
+ fontWeight: fontWeights.semibold,
81
+ },
82
+ });
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ StyleProp,
5
+ StyleSheet,
6
+ Text,
7
+ TouchableOpacity,
8
+ useColorScheme,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+
12
+ import { getColors } from '../../tokens/colors';
13
+ import { radius } from '../../tokens/radius';
14
+ import { spacing } from '../../tokens/spacing';
15
+ import { fontSizes, fontWeights } from '../../tokens/typography';
16
+
17
+ export interface SecondaryButtonProps {
18
+ title: string;
19
+ onPress: () => void;
20
+ disabled?: boolean;
21
+ loading?: boolean;
22
+ style?: StyleProp<ViewStyle>;
23
+ accessibilityLabel?: string;
24
+ }
25
+
26
+ export const SecondaryButton: React.FC<SecondaryButtonProps> = ({
27
+ title,
28
+ onPress,
29
+ disabled = false,
30
+ loading = false,
31
+ style,
32
+ accessibilityLabel,
33
+ }) => {
34
+ const scheme = useColorScheme();
35
+ const colors = getColors(scheme);
36
+ const isDisabled = disabled || loading;
37
+
38
+ return (
39
+ <TouchableOpacity
40
+ onPress={onPress}
41
+ disabled={isDisabled}
42
+ accessibilityRole="button"
43
+ accessibilityLabel={accessibilityLabel ?? title}
44
+ accessibilityState={{ disabled: isDisabled, busy: loading }}
45
+ style={[
46
+ styles.base,
47
+ {
48
+ backgroundColor: isDisabled ? colors.disabled : colors.secondary,
49
+ borderColor: colors.border,
50
+ },
51
+ style,
52
+ ]}
53
+ >
54
+ {loading ? (
55
+ <ActivityIndicator color={colors.secondaryText} />
56
+ ) : (
57
+ <Text
58
+ style={[
59
+ styles.text,
60
+ { color: isDisabled ? colors.disabledText : colors.secondaryText },
61
+ ]}
62
+ >
63
+ {title}
64
+ </Text>
65
+ )}
66
+ </TouchableOpacity>
67
+ );
68
+ };
69
+
70
+ const styles = StyleSheet.create({
71
+ base: {
72
+ minHeight: 44,
73
+ paddingHorizontal: spacing.md,
74
+ paddingVertical: spacing.sm,
75
+ borderRadius: radius.md,
76
+ borderWidth: 1,
77
+ alignItems: 'center',
78
+ justifyContent: 'center',
79
+ },
80
+ text: {
81
+ fontSize: fontSizes.md,
82
+ fontWeight: fontWeights.semibold,
83
+ },
84
+ });
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, useColorScheme, View, ViewStyle } 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 CardViewProps {
9
+ children: React.ReactNode;
10
+ style?: StyleProp<ViewStyle>;
11
+ accessibilityLabel?: string;
12
+ }
13
+
14
+ export const CardView: React.FC<CardViewProps> = ({
15
+ children,
16
+ style,
17
+ accessibilityLabel,
18
+ }) => {
19
+ const scheme = useColorScheme();
20
+ const colors = getColors(scheme);
21
+
22
+ return (
23
+ <View
24
+ accessibilityLabel={accessibilityLabel}
25
+ style={[
26
+ styles.container,
27
+ {
28
+ backgroundColor: colors.surface,
29
+ borderColor: colors.border,
30
+ },
31
+ style,
32
+ ]}
33
+ >
34
+ {children}
35
+ </View>
36
+ );
37
+ };
38
+
39
+ const styles = StyleSheet.create({
40
+ container: {
41
+ borderRadius: radius.lg,
42
+ borderWidth: 1,
43
+ padding: spacing.md,
44
+ },
45
+ });
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, Text, useColorScheme, View, ViewStyle } from 'react-native';
3
+
4
+ import { getColors } from '../../tokens/colors';
5
+ import { fontSizes, fontWeights } from '../../tokens/typography';
6
+
7
+ export type AvatarSize = 'small' | 'medium' | 'large';
8
+
9
+ const DIMENSIONS: Record<AvatarSize, number> = {
10
+ small: 32,
11
+ medium: 44,
12
+ large: 64,
13
+ };
14
+
15
+ export interface AvatarProps {
16
+ initials: string;
17
+ size?: AvatarSize;
18
+ style?: StyleProp<ViewStyle>;
19
+ }
20
+
21
+ /** A circular avatar showing a fallback initials label. */
22
+ export const Avatar: React.FC<AvatarProps> = ({ initials, size = 'medium', style }) => {
23
+ const scheme = useColorScheme();
24
+ const colors = getColors(scheme);
25
+ const dimension = DIMENSIONS[size];
26
+
27
+ return (
28
+ <View
29
+ accessibilityLabel={initials}
30
+ style={[
31
+ styles.container,
32
+ {
33
+ width: dimension,
34
+ height: dimension,
35
+ borderRadius: dimension / 2,
36
+ backgroundColor: colors.primary,
37
+ },
38
+ style,
39
+ ]}
40
+ >
41
+ <Text style={{ color: colors.primaryText, fontSize: fontSizes.sm, fontWeight: fontWeights.semibold }}>
42
+ {initials}
43
+ </Text>
44
+ </View>
45
+ );
46
+ };
47
+
48
+ const styles = StyleSheet.create({
49
+ container: {
50
+ alignItems: 'center',
51
+ justifyContent: 'center',
52
+ },
53
+ });
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import {
3
+ StyleProp,
4
+ StyleSheet,
5
+ Text,
6
+ TouchableOpacity,
7
+ useColorScheme,
8
+ View,
9
+ ViewStyle,
10
+ } from 'react-native';
11
+
12
+ import { getColors } from '../../tokens/colors';
13
+ import { radius } from '../../tokens/radius';
14
+ import { spacing } from '../../tokens/spacing';
15
+ import { fontSizes } from '../../tokens/typography';
16
+
17
+ export interface ChipProps {
18
+ text: string;
19
+ onRemove?: () => void;
20
+ style?: StyleProp<ViewStyle>;
21
+ }
22
+
23
+ /** A compact tag/chip with an optional trailing remove button. */
24
+ export const Chip: React.FC<ChipProps> = ({ text, onRemove, style }) => {
25
+ const scheme = useColorScheme();
26
+ const colors = getColors(scheme);
27
+
28
+ return (
29
+ <View
30
+ style={[styles.container, { backgroundColor: colors.surfaceAlt }, style]}
31
+ accessibilityLabel={text}
32
+ >
33
+ <Text style={{ color: colors.textPrimary, fontSize: fontSizes.sm }}>{text}</Text>
34
+ {onRemove ? (
35
+ <TouchableOpacity
36
+ onPress={onRemove}
37
+ accessibilityRole="button"
38
+ accessibilityLabel={`Remove ${text}`}
39
+ style={styles.remove}
40
+ >
41
+ <Text style={{ color: colors.textSecondary }}>{'✕'}</Text>
42
+ </TouchableOpacity>
43
+ ) : null}
44
+ </View>
45
+ );
46
+ };
47
+
48
+ const styles = StyleSheet.create({
49
+ container: {
50
+ flexDirection: 'row',
51
+ alignItems: 'center',
52
+ alignSelf: 'flex-start',
53
+ paddingHorizontal: spacing.sm,
54
+ paddingVertical: spacing.xs,
55
+ borderRadius: radius.full,
56
+ },
57
+ remove: {
58
+ marginLeft: spacing.xs,
59
+ minWidth: 20,
60
+ minHeight: 20,
61
+ alignItems: 'center',
62
+ justifyContent: 'center',
63
+ },
64
+ });
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { StyleProp, StyleSheet, Text, View, ViewStyle, useColorScheme } from 'react-native';
3
+
4
+ import { getColors } from '../../tokens/colors';
5
+ import { radius } from '../../tokens/radius';
6
+ import { spacing } from '../../tokens/spacing';
7
+ import { fontSizes } from '../../tokens/typography';
8
+
9
+ export interface CountBadgeProps {
10
+ count?: number;
11
+ maxDisplayCount?: number;
12
+ style?: StyleProp<ViewStyle>;
13
+ }
14
+
15
+ /**
16
+ * A small numeric/dot badge meant to overlay an icon (e.g. a bell with an
17
+ * unread count). Distinct from this library's own `Badge`, which is a
18
+ * standalone labeled pill.
19
+ */
20
+ export const CountBadge: React.FC<CountBadgeProps> = ({ count, maxDisplayCount = 99, style }) => {
21
+ const scheme = useColorScheme();
22
+ const colors = getColors(scheme);
23
+
24
+ if (count != null && count > 0) {
25
+ const display = count > maxDisplayCount ? `${maxDisplayCount}+` : `${count}`;
26
+ return (
27
+ <View
28
+ style={[styles.numeric, { backgroundColor: colors.destructive }, style]}
29
+ accessibilityLabel={`${count} unread`}
30
+ >
31
+ <Text style={{ color: colors.destructiveText, fontSize: fontSizes.xs }}>{display}</Text>
32
+ </View>
33
+ );
34
+ }
35
+
36
+ return (
37
+ <View
38
+ style={[styles.dot, { backgroundColor: colors.destructive }, style]}
39
+ accessibilityLabel="New"
40
+ />
41
+ );
42
+ };
43
+
44
+ const styles = StyleSheet.create({
45
+ numeric: {
46
+ minWidth: 16,
47
+ height: 16,
48
+ borderRadius: radius.full,
49
+ alignItems: 'center',
50
+ justifyContent: 'center',
51
+ paddingHorizontal: spacing.xs,
52
+ },
53
+ dot: {
54
+ width: 10,
55
+ height: 10,
56
+ borderRadius: 5,
57
+ },
58
+ });