@butternutbox/pawprint-native 0.0.1

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 (105) hide show
  1. package/.turbo/turbo-build.log +30 -0
  2. package/COMPONENT_GUIDELINES.md +610 -0
  3. package/README.md +72 -0
  4. package/dist/ibm-plex-sans-condensed-400-normal-I2XLJNNB.woff2 +0 -0
  5. package/dist/ibm-plex-sans-condensed-500-normal-IEQBNVGX.woff2 +0 -0
  6. package/dist/ibm-plex-sans-condensed-600-normal-UX5ZU5T6.woff2 +0 -0
  7. package/dist/ibm-plex-sans-condensed-700-normal-4PFYFTSO.woff2 +0 -0
  8. package/dist/ida-narrow-500-normal-C6I2PK4T.woff2 +0 -0
  9. package/dist/ida-narrow-700-normal-UPHPRIN6.woff2 +0 -0
  10. package/dist/index.cjs +2686 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +780 -0
  13. package/dist/index.d.ts +780 -0
  14. package/dist/index.js +2617 -0
  15. package/dist/index.js.map +1 -0
  16. package/eslint.config.js +3 -0
  17. package/llms.txt +458 -0
  18. package/package.json +57 -0
  19. package/src/components/atoms/Avatar/Avatar.stories.tsx +125 -0
  20. package/src/components/atoms/Avatar/Avatar.tsx +159 -0
  21. package/src/components/atoms/Avatar/index.ts +7 -0
  22. package/src/components/atoms/Badge/Badge.stories.tsx +231 -0
  23. package/src/components/atoms/Badge/Badge.tsx +184 -0
  24. package/src/components/atoms/Badge/index.ts +2 -0
  25. package/src/components/atoms/Button/Button.stories.tsx +145 -0
  26. package/src/components/atoms/Button/Button.tsx +261 -0
  27. package/src/components/atoms/Button/index.ts +7 -0
  28. package/src/components/atoms/Hint/Hint.stories.tsx +84 -0
  29. package/src/components/atoms/Hint/Hint.tsx +59 -0
  30. package/src/components/atoms/Hint/index.ts +2 -0
  31. package/src/components/atoms/Icon/Icon.stories.tsx +200 -0
  32. package/src/components/atoms/Icon/Icon.tsx +112 -0
  33. package/src/components/atoms/Icon/index.ts +8 -0
  34. package/src/components/atoms/IconButton/IconButton.stories.tsx +162 -0
  35. package/src/components/atoms/IconButton/IconButton.tsx +227 -0
  36. package/src/components/atoms/IconButton/index.ts +7 -0
  37. package/src/components/atoms/Illustration/Illustration.stories.tsx +167 -0
  38. package/src/components/atoms/Illustration/Illustration.tsx +81 -0
  39. package/src/components/atoms/Illustration/index.ts +6 -0
  40. package/src/components/atoms/Input/Input.stories.tsx +142 -0
  41. package/src/components/atoms/Input/Input.tsx +110 -0
  42. package/src/components/atoms/Input/InputDescription.tsx +49 -0
  43. package/src/components/atoms/Input/InputError.tsx +39 -0
  44. package/src/components/atoms/Input/InputField.tsx +119 -0
  45. package/src/components/atoms/Input/InputLabel.tsx +61 -0
  46. package/src/components/atoms/Input/index.ts +10 -0
  47. package/src/components/atoms/Link/Link.stories.tsx +119 -0
  48. package/src/components/atoms/Link/Link.tsx +118 -0
  49. package/src/components/atoms/Link/index.ts +2 -0
  50. package/src/components/atoms/Logo/Logo.registry.ts +39 -0
  51. package/src/components/atoms/Logo/Logo.tsx +68 -0
  52. package/src/components/atoms/Logo/index.ts +4 -0
  53. package/src/components/atoms/Spinner/Spinner.stories.tsx +98 -0
  54. package/src/components/atoms/Spinner/Spinner.tsx +91 -0
  55. package/src/components/atoms/Spinner/index.ts +2 -0
  56. package/src/components/atoms/Switch/Switch.stories.tsx +120 -0
  57. package/src/components/atoms/Switch/Switch.tsx +196 -0
  58. package/src/components/atoms/Switch/index.ts +2 -0
  59. package/src/components/atoms/Tag/Tag.stories.tsx +89 -0
  60. package/src/components/atoms/Tag/Tag.tsx +122 -0
  61. package/src/components/atoms/Tag/index.ts +2 -0
  62. package/src/components/atoms/Typography/Typography.stories.tsx +315 -0
  63. package/src/components/atoms/Typography/Typography.tsx +284 -0
  64. package/src/components/atoms/Typography/index.ts +2 -0
  65. package/src/components/atoms/index.ts +14 -0
  66. package/src/components/index.ts +2 -0
  67. package/src/components/molecules/ButtonDock/ButtonDock.stories.tsx +95 -0
  68. package/src/components/molecules/ButtonDock/ButtonDock.tsx +148 -0
  69. package/src/components/molecules/ButtonDock/index.ts +2 -0
  70. package/src/components/molecules/ButtonGroup/ButtonGroup.stories.tsx +82 -0
  71. package/src/components/molecules/ButtonGroup/ButtonGroup.tsx +94 -0
  72. package/src/components/molecules/ButtonGroup/index.ts +2 -0
  73. package/src/components/molecules/Checkbox/Checkbox.stories.tsx +148 -0
  74. package/src/components/molecules/Checkbox/Checkbox.tsx +279 -0
  75. package/src/components/molecules/Checkbox/CheckboxGroup.tsx +53 -0
  76. package/src/components/molecules/Checkbox/index.ts +4 -0
  77. package/src/components/molecules/Radio/Radio.stories.tsx +182 -0
  78. package/src/components/molecules/Radio/Radio.tsx +249 -0
  79. package/src/components/molecules/Radio/RadioGroup.tsx +142 -0
  80. package/src/components/molecules/Radio/index.ts +4 -0
  81. package/src/components/molecules/SegmentedControl/SegmentedControl.stories.tsx +151 -0
  82. package/src/components/molecules/SegmentedControl/SegmentedControl.tsx +323 -0
  83. package/src/components/molecules/SegmentedControl/index.ts +5 -0
  84. package/src/components/molecules/Slider/Slider.stories.tsx +144 -0
  85. package/src/components/molecules/Slider/Slider.tsx +303 -0
  86. package/src/components/molecules/Slider/index.ts +2 -0
  87. package/src/components/molecules/index.ts +6 -0
  88. package/src/fonts/ibm-plex-sans-condensed-400-normal.woff2 +0 -0
  89. package/src/fonts/ibm-plex-sans-condensed-500-normal.woff2 +0 -0
  90. package/src/fonts/ibm-plex-sans-condensed-600-normal.woff2 +0 -0
  91. package/src/fonts/ibm-plex-sans-condensed-700-normal.woff2 +0 -0
  92. package/src/fonts/ida-narrow-500-normal.woff2 +0 -0
  93. package/src/fonts/ida-narrow-700-normal.woff2 +0 -0
  94. package/src/fonts/index.ts +49 -0
  95. package/src/index.ts +9 -0
  96. package/src/theme/PawprintProvider.tsx +26 -0
  97. package/src/theme/ThemeProvider.tsx +63 -0
  98. package/src/theme/index.ts +5 -0
  99. package/src/theme/theme.ts +3 -0
  100. package/src/theme/utils.ts +31 -0
  101. package/src/types/fonts.d.ts +4 -0
  102. package/src/types/index.ts +1 -0
  103. package/src/types/theme.ts +24 -0
  104. package/tsconfig.json +5 -0
  105. package/tsup.config.ts +11 -0
@@ -0,0 +1,159 @@
1
+ import React from "react"
2
+ import { View } from "react-native"
3
+ import * as AvatarPrimitive from "@rn-primitives/avatar"
4
+ import styled from "@emotion/native"
5
+ import { Typography } from "../Typography"
6
+
7
+ export type AvatarSize = "sm" | "md" | "lg"
8
+ export type AvatarBorder = "none" | "sm" | "md"
9
+ export type AvatarFallbackType = "string" | "image"
10
+
11
+ export interface AvatarProps {
12
+ src?: string
13
+ alt: string
14
+ size?: AvatarSize
15
+ border?: AvatarBorder
16
+ fallbackType?: AvatarFallbackType
17
+ fallbackString?: string
18
+ }
19
+
20
+ const SIZE_MAP = {
21
+ sm: "small",
22
+ md: "medium",
23
+ lg: "large"
24
+ } as const
25
+
26
+ const parseTokenValue = (value: string): number => parseFloat(value)
27
+
28
+ const StyledRoot = styled(AvatarPrimitive.Root)<{
29
+ size: AvatarSize
30
+ border: AvatarBorder
31
+ }>(({ theme, size, border }) => {
32
+ const { avatar } = theme.tokens.components
33
+ const { borderRadius } = theme.tokens.semantics.dimensions
34
+
35
+ const tokenSize = SIZE_MAP[size]
36
+ const avatarSize = parseTokenValue(avatar.size[tokenSize])
37
+
38
+ return {
39
+ display: "flex",
40
+ alignItems: "center",
41
+ justifyContent: "center",
42
+ overflow: "hidden",
43
+ borderRadius: parseTokenValue(borderRadius.round),
44
+ width: avatarSize,
45
+ height: avatarSize,
46
+ ...(border !== "none" && {
47
+ borderWidth: parseTokenValue(
48
+ avatar.borderWidth[border === "sm" ? "small" : "medium"]
49
+ ),
50
+ borderColor: avatar.colour.border.default,
51
+ borderStyle: "solid" as const
52
+ })
53
+ }
54
+ })
55
+
56
+ const StyledImage = styled(AvatarPrimitive.Image)({
57
+ width: "100%",
58
+ height: "100%",
59
+ resizeMode: "cover"
60
+ })
61
+
62
+ const StyledFallback = styled(AvatarPrimitive.Fallback)(({ theme }) => {
63
+ const { background, text } = theme.tokens.semantics.colour
64
+ return {
65
+ display: "flex",
66
+ alignItems: "center",
67
+ justifyContent: "center",
68
+ width: "100%",
69
+ height: "100%",
70
+ backgroundColor: background.container.disabled,
71
+ color: text.disabled
72
+ }
73
+ })
74
+
75
+ const AVATAR_TO_BODY_SIZE = {
76
+ sm: "xs",
77
+ md: "sm",
78
+ lg: "md"
79
+ } as const
80
+
81
+ /**
82
+ * Avatar component for displaying user profile pictures or fallback initials.
83
+ * Built on @rn-primitives/avatar with design system styling.
84
+ *
85
+ * Features:
86
+ * - Three size variants (sm, md, lg)
87
+ * - Optional borders (none, sm, md)
88
+ * - Automatic fallback handling when image fails to load
89
+ * - String fallback (user initials) or image fallback (default icon)
90
+ * - Accessible with required alt text
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * import { Avatar } from "@butternutbox/pawprint-native"
95
+ *
96
+ * // With image
97
+ * <Avatar src="https://example.com/photo.jpg" alt="User Name" size="md" />
98
+ *
99
+ * // With initials fallback
100
+ * <Avatar alt="John Doe" fallbackString="JD" size="sm" border="sm" />
101
+ *
102
+ * // With icon fallback
103
+ * <Avatar alt="User avatar" fallbackType="image" size="lg" />
104
+ * ```
105
+ *
106
+ * @param {string} [src] - Image source URL.
107
+ * @param {string} alt - Accessible label for the avatar.
108
+ * @param {"sm" | "md" | "lg"} [size="md"] - Size variant.
109
+ * @param {"none" | "sm" | "md"} [border="none"] - Border width variant.
110
+ * @param {"string" | "image"} [fallbackType] - Fallback type (auto-detected based on fallbackString if not specified).
111
+ * @param {string} [fallbackString] - Text to display as fallback (typically user initials).
112
+ */
113
+ const AvatarRoot = React.forwardRef<View, AvatarProps>(
114
+ (
115
+ {
116
+ src,
117
+ alt,
118
+ size = "md",
119
+ border = "none",
120
+ fallbackType,
121
+ fallbackString,
122
+ ...rest
123
+ },
124
+ ref
125
+ ) => {
126
+ // Auto-detect fallback type based on whether fallbackString is provided
127
+ const effectiveFallbackType =
128
+ fallbackType ?? (fallbackString ? "string" : "image")
129
+
130
+ const showStringFallback =
131
+ effectiveFallbackType === "string" && fallbackString
132
+
133
+ return (
134
+ <StyledRoot ref={ref} alt={alt} size={size} border={border} {...rest}>
135
+ {src && <StyledImage source={{ uri: src }} />}
136
+ <StyledFallback>
137
+ {showStringFallback ? (
138
+ <Typography
139
+ size={AVATAR_TO_BODY_SIZE[size]}
140
+ weight="bold"
141
+ color="disabled"
142
+ >
143
+ {fallbackString}
144
+ </Typography>
145
+ ) : (
146
+ // TODO: replace with the Icon when available
147
+ <Typography size={AVATAR_TO_BODY_SIZE[size]} color="disabled">
148
+ 👤
149
+ </Typography>
150
+ )}
151
+ </StyledFallback>
152
+ </StyledRoot>
153
+ )
154
+ }
155
+ )
156
+
157
+ AvatarRoot.displayName = "Avatar"
158
+
159
+ export const Avatar = AvatarRoot
@@ -0,0 +1,7 @@
1
+ export { Avatar } from "./Avatar"
2
+ export type {
3
+ AvatarProps,
4
+ AvatarSize,
5
+ AvatarBorder,
6
+ AvatarFallbackType
7
+ } from "./Avatar"
@@ -0,0 +1,231 @@
1
+ import React from "react"
2
+ import { View, StyleSheet, Text } from "react-native"
3
+ import { Badge } from "./Badge"
4
+ import type { BadgeProps } from "./Badge"
5
+
6
+ const PlaceholderIcon = ({
7
+ width = 24,
8
+ height = 24,
9
+ color = "#000"
10
+ }: {
11
+ width?: number
12
+ height?: number
13
+ color?: string
14
+ }) => (
15
+ <View
16
+ style={{
17
+ width,
18
+ height,
19
+ borderRadius: 4,
20
+ backgroundColor: color,
21
+ opacity: 0.3
22
+ }}
23
+ />
24
+ )
25
+
26
+ export default {
27
+ title: "Atoms/Badge",
28
+ component: Badge,
29
+ parameters: {
30
+ docs: {
31
+ description: {
32
+ component:
33
+ "Badge component for displaying labels with optional icons. Used to highlight status, categories, or notifications."
34
+ }
35
+ }
36
+ },
37
+ argTypes: {
38
+ variant: {
39
+ control: { type: "select" },
40
+ options: ["primary", "promo", "success", "warning", "error"],
41
+ description: "Visual style variant"
42
+ },
43
+ size: {
44
+ control: { type: "select" },
45
+ options: ["small", "medium", "large"],
46
+ description: "Size of the badge"
47
+ },
48
+ pinned: {
49
+ control: { type: "boolean" },
50
+ description: "If true, applies pinned styling (left border radius = 0)"
51
+ },
52
+ children: {
53
+ control: { type: "text" },
54
+ description: "Badge label text"
55
+ }
56
+ }
57
+ }
58
+
59
+ const variants = ["primary", "promo", "success", "warning", "error"] as const
60
+ const sizes = ["small", "medium", "large"] as const
61
+
62
+ export const Default = (args: BadgeProps) => <Badge {...args} />
63
+ Default.args = {
64
+ children: "Badge",
65
+ variant: "primary",
66
+ size: "medium"
67
+ }
68
+
69
+ export const AllVariants = () => (
70
+ <View style={styles.column}>
71
+ {variants.map((variant) => (
72
+ <View key={variant} style={styles.section}>
73
+ <Text style={styles.label}>{variant}</Text>
74
+ <View style={styles.row}>
75
+ {sizes.map((size) => (
76
+ <Badge key={size} variant={variant} size={size}>
77
+ {size}
78
+ </Badge>
79
+ ))}
80
+ </View>
81
+ </View>
82
+ ))}
83
+ </View>
84
+ )
85
+
86
+ export const WithIcons = () => (
87
+ <View style={styles.column}>
88
+ {variants.map((variant) => (
89
+ <View key={variant} style={styles.section}>
90
+ <Text style={styles.label}>{variant} with Icon</Text>
91
+ <View style={styles.row}>
92
+ {sizes.map((size) => (
93
+ <Badge
94
+ key={size}
95
+ variant={variant}
96
+ size={size}
97
+ icon={PlaceholderIcon}
98
+ >
99
+ {size}
100
+ </Badge>
101
+ ))}
102
+ </View>
103
+ </View>
104
+ ))}
105
+ </View>
106
+ )
107
+
108
+ export const Pinned = () => (
109
+ <View style={styles.column}>
110
+ <View style={styles.section}>
111
+ <Text style={styles.label}>Pinned Badges - Top Positioning</Text>
112
+ <View style={styles.row}>
113
+ {variants.map((variant) => (
114
+ <View key={variant} style={styles.pinnedContainer}>
115
+ <Badge
116
+ variant={variant}
117
+ size="medium"
118
+ pinned
119
+ top={16}
120
+ icon={PlaceholderIcon}
121
+ >
122
+ {variant}
123
+ </Badge>
124
+ </View>
125
+ ))}
126
+ </View>
127
+ </View>
128
+ <View style={styles.section}>
129
+ <Text style={styles.label}>Pinned Badges - Bottom Positioning</Text>
130
+ <View style={styles.row}>
131
+ {variants.map((variant) => (
132
+ <View key={variant} style={styles.pinnedContainer}>
133
+ <Badge
134
+ variant={variant}
135
+ size="medium"
136
+ pinned
137
+ bottom={16}
138
+ icon={PlaceholderIcon}
139
+ >
140
+ {variant}
141
+ </Badge>
142
+ </View>
143
+ ))}
144
+ </View>
145
+ </View>
146
+ </View>
147
+ )
148
+
149
+ export const UsageExamples = () => (
150
+ <View style={styles.column}>
151
+ <View style={styles.section}>
152
+ <Text style={styles.label}>Status indicators</Text>
153
+ <View style={styles.row}>
154
+ <Badge variant="success" size="small">
155
+ Active
156
+ </Badge>
157
+ <Badge variant="warning" size="small">
158
+ Pending
159
+ </Badge>
160
+ <Badge variant="error" size="small">
161
+ Expired
162
+ </Badge>
163
+ </View>
164
+ </View>
165
+ <View style={styles.section}>
166
+ <Text style={styles.label}>Category labels</Text>
167
+ <View style={styles.row}>
168
+ <Badge variant="primary" size="medium">
169
+ New
170
+ </Badge>
171
+ <Badge variant="promo" size="medium">
172
+ Offer
173
+ </Badge>
174
+ <Badge variant="success" size="medium">
175
+ Verified
176
+ </Badge>
177
+ </View>
178
+ </View>
179
+ <View style={styles.section}>
180
+ <Text style={styles.label}>Promotional tags</Text>
181
+ <View style={styles.row}>
182
+ <Badge variant="promo" size="large" icon={PlaceholderIcon}>
183
+ Premium
184
+ </Badge>
185
+ <Badge variant="primary" size="large">
186
+ New
187
+ </Badge>
188
+ <Badge variant="warning" size="large">
189
+ Sale
190
+ </Badge>
191
+ </View>
192
+ </View>
193
+ </View>
194
+ )
195
+
196
+ export const Playground = (args: BadgeProps) => <Badge {...args} />
197
+ Playground.args = {
198
+ variant: "primary",
199
+ size: "medium",
200
+ pinned: false,
201
+ children: "Badge"
202
+ }
203
+
204
+ const styles = StyleSheet.create({
205
+ column: {
206
+ flexDirection: "column",
207
+ gap: 24
208
+ },
209
+ section: {
210
+ flexDirection: "column",
211
+ gap: 8
212
+ },
213
+ row: {
214
+ flexDirection: "row",
215
+ gap: 12,
216
+ alignItems: "center"
217
+ },
218
+ label: {
219
+ fontSize: 13,
220
+ fontWeight: "600",
221
+ color: "#666"
222
+ },
223
+ pinnedContainer: {
224
+ position: "relative",
225
+ width: 160,
226
+ height: 100,
227
+ backgroundColor: "#f5f5f5",
228
+ borderRadius: 8,
229
+ overflow: "hidden"
230
+ }
231
+ })
@@ -0,0 +1,184 @@
1
+ import React from "react"
2
+ import { View, ViewProps } from "react-native"
3
+ import styled from "@emotion/native"
4
+ import { useTheme } from "@emotion/react"
5
+ import { Typography } from "../Typography"
6
+ import { Icon, type PawprintIcon } from "../Icon"
7
+
8
+ type BadgeVariant = "primary" | "promo" | "success" | "warning" | "error"
9
+ type BadgeSize = "small" | "medium" | "large"
10
+
11
+ type BaseBadgeProps = {
12
+ children: React.ReactNode
13
+ variant?: BadgeVariant
14
+ size?: BadgeSize
15
+ icon?: PawprintIcon
16
+ }
17
+
18
+ type PinnedBadgeProps = BaseBadgeProps & {
19
+ pinned: true
20
+ top?: number
21
+ bottom?: number
22
+ }
23
+
24
+ type UnpinnedBadgeProps = BaseBadgeProps & {
25
+ pinned?: false
26
+ top?: never
27
+ bottom?: never
28
+ }
29
+
30
+ type BadgeOwnProps = PinnedBadgeProps | UnpinnedBadgeProps
31
+
32
+ export type BadgeProps = BadgeOwnProps & Omit<ViewProps, keyof BadgeOwnProps>
33
+
34
+ const parseTokenValue = (value: string): number => parseFloat(value)
35
+
36
+ const StyledBadge = styled(View)<{
37
+ badgeVariant: BadgeVariant
38
+ badgeSize: BadgeSize
39
+ pinned: boolean
40
+ top?: number
41
+ bottom?: number
42
+ }>(({ theme, badgeVariant, badgeSize, pinned, top, bottom }) => {
43
+ const { sizing, spacing, colour, primary, pinnedBadge } =
44
+ theme.tokens.components.badges.badge
45
+
46
+ const backgroundColorMap = {
47
+ primary: colour.background.default,
48
+ promo: colour.background.promo,
49
+ success: colour.background.success,
50
+ warning: colour.background.warning,
51
+ error: colour.background.error
52
+ } as const
53
+
54
+ const borderRadiusStyles = pinned
55
+ ? {
56
+ borderTopLeftRadius: parseTokenValue(
57
+ pinnedBadge.borderRadius.leftRadius
58
+ ),
59
+ borderBottomLeftRadius: parseTokenValue(
60
+ pinnedBadge.borderRadius.leftRadius
61
+ ),
62
+ borderTopRightRadius: parseTokenValue(
63
+ pinnedBadge.borderRadius.rightRadius
64
+ ),
65
+ borderBottomRightRadius: parseTokenValue(
66
+ pinnedBadge.borderRadius.rightRadius
67
+ )
68
+ }
69
+ : {
70
+ borderRadius: parseTokenValue(primary.borderRadius.default)
71
+ }
72
+
73
+ const positionStyles = pinned
74
+ ? {
75
+ position: "absolute" as const,
76
+ left: 0,
77
+ ...(top !== undefined && { top }),
78
+ ...(bottom !== undefined && { bottom })
79
+ }
80
+ : {}
81
+
82
+ return {
83
+ flexDirection: "row" as const,
84
+ alignItems: "center" as const,
85
+ justifyContent: "center" as const,
86
+ height: parseTokenValue(sizing[badgeSize].height),
87
+ minWidth: parseTokenValue(sizing[badgeSize].minWidth),
88
+ paddingHorizontal: parseTokenValue(spacing[badgeSize].horizontalPadding),
89
+ gap: parseTokenValue(spacing[badgeSize].gap),
90
+ backgroundColor: backgroundColorMap[badgeVariant],
91
+ ...borderRadiusStyles,
92
+ ...positionStyles
93
+ }
94
+ })
95
+
96
+ /**
97
+ * Badge component for displaying labels with optional icons.
98
+ * Used to highlight status, categories, or notifications.
99
+ *
100
+ * @param {React.ReactNode} children - Badge label text.
101
+ * @param {"primary" | "promo" | "success" | "warning" | "error"} [variant="primary"] - Visual style variant.
102
+ * @param {"small" | "medium" | "large"} [size="medium"] - Size of the badge.
103
+ * @param {boolean} [pinned=false] - If true, applies pinned styling and positions absolutely to the left edge.
104
+ * @param {PawprintIcon} [icon] - Optional icon from @butternutbox/pawprint-icons.
105
+ * @param {number} [top] - Top position when pinned.
106
+ * @param {number} [bottom] - Bottom position when pinned.
107
+ *
108
+ * @example
109
+ * <Badge>New</Badge>
110
+ * <Badge variant="success" size="large" icon={CheckCircle}>Verified</Badge>
111
+ *
112
+ * @example
113
+ * <View style={{ position: "relative" }}>
114
+ * <Badge variant="promo" pinned top={16}>Special</Badge>
115
+ * </View>
116
+ */
117
+ export const Badge = React.forwardRef<View, BadgeProps>(
118
+ (
119
+ {
120
+ variant = "primary",
121
+ size = "medium",
122
+ pinned = false,
123
+ icon,
124
+ children,
125
+ top,
126
+ bottom,
127
+ ...rest
128
+ },
129
+ ref
130
+ ) => {
131
+ const theme = useTheme()
132
+ const { typography, colour } = theme.tokens.components.badges.badge
133
+
134
+ const iconSizeMap = {
135
+ small: "xs",
136
+ medium: "xs",
137
+ large: "sm"
138
+ } as const
139
+
140
+ const iconColourMap = {
141
+ primary: "primary",
142
+ promo: "promo",
143
+ success: "alt",
144
+ warning: "alt",
145
+ error: "alt"
146
+ } as const
147
+
148
+ const textColorMap = {
149
+ primary: colour.text.primary,
150
+ promo: colour.text.promo,
151
+ success: colour.text.default,
152
+ warning: colour.text.default,
153
+ error: colour.text.default
154
+ }
155
+
156
+ return (
157
+ <StyledBadge
158
+ ref={ref}
159
+ badgeVariant={variant}
160
+ badgeSize={size}
161
+ pinned={pinned}
162
+ top={top}
163
+ bottom={bottom}
164
+ {...rest}
165
+ >
166
+ {icon && (
167
+ <Icon
168
+ icon={icon}
169
+ size={iconSizeMap[size]}
170
+ colour={iconColourMap[variant]}
171
+ />
172
+ )}
173
+ <Typography
174
+ token={typography[size].default}
175
+ color={textColorMap[variant]}
176
+ >
177
+ {children}
178
+ </Typography>
179
+ </StyledBadge>
180
+ )
181
+ }
182
+ )
183
+
184
+ Badge.displayName = "Badge"
@@ -0,0 +1,2 @@
1
+ export { Badge } from "./Badge"
2
+ export type { BadgeProps } from "./Badge"