@butternutbox/pawprint-native 0.3.2 → 0.4.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 (31) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +14 -0
  3. package/dist/index.cjs +1012 -173
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.cts +252 -5
  6. package/dist/index.d.ts +252 -5
  7. package/dist/index.js +1007 -173
  8. package/dist/index.js.map +1 -1
  9. package/package.json +2 -1
  10. package/src/components/atoms/Hint/Hint.tsx +1 -2
  11. package/src/components/atoms/Input/InputField.tsx +7 -1
  12. package/src/components/molecules/Animated/Animated.tsx +12 -3
  13. package/src/components/molecules/Countdown/Countdown.stories.tsx +218 -0
  14. package/src/components/molecules/Countdown/Countdown.tsx +315 -0
  15. package/src/components/molecules/Countdown/index.ts +2 -0
  16. package/src/components/molecules/NumberField/NumberFieldInput.tsx +14 -24
  17. package/src/components/molecules/ProductDisplayCard/ProductDisplayCard.stories.tsx +275 -0
  18. package/src/components/molecules/ProductDisplayCard/ProductDisplayCard.test.tsx +198 -0
  19. package/src/components/molecules/ProductDisplayCard/ProductDisplayCard.tsx +292 -0
  20. package/src/components/molecules/ProductDisplayCard/index.ts +5 -0
  21. package/src/components/molecules/ProductListingCard/Badge.tsx +65 -0
  22. package/src/components/molecules/ProductListingCard/Grid.tsx +59 -0
  23. package/src/components/molecules/ProductListingCard/ProductListingCard.stories.tsx +209 -0
  24. package/src/components/molecules/ProductListingCard/ProductListingCard.tsx +235 -0
  25. package/src/components/molecules/ProductListingCard/index.ts +2 -0
  26. package/src/components/molecules/TabNavigation/TabNavigation.stories.tsx +183 -0
  27. package/src/components/molecules/TabNavigation/TabNavigation.tsx +354 -0
  28. package/src/components/molecules/TabNavigation/index.ts +7 -0
  29. package/src/components/molecules/index.ts +4 -0
  30. package/src/utils/index.ts +1 -0
  31. package/src/utils/token.ts +1 -0
@@ -0,0 +1,235 @@
1
+ import React from "react"
2
+ import {
3
+ View,
4
+ Image,
5
+ ImageSourcePropType,
6
+ Pressable,
7
+ PressableProps
8
+ } from "react-native"
9
+ import styled from "@emotion/native"
10
+ import { Typography } from "../../atoms/Typography"
11
+ import { Badge } from "./Badge"
12
+ import { Grid } from "./Grid"
13
+
14
+ type ProductListingCardOwnProps = {
15
+ image?: ImageSourcePropType
16
+ imageAlt?: string
17
+ badge?: React.ReactNode
18
+ category?: string
19
+ title?: string
20
+ size?: string
21
+ price?: string
22
+ wasPrice?: string
23
+ showBadge?: boolean
24
+ showCategory?: boolean
25
+ showTitle?: boolean
26
+ showSize?: boolean
27
+ showPrice?: boolean
28
+ onPress?: () => void
29
+ }
30
+
31
+ export type ProductListingCardProps = ProductListingCardOwnProps &
32
+ Omit<PressableProps, keyof ProductListingCardOwnProps>
33
+
34
+ const parseTokenValue = (value: string | number): number => {
35
+ if (typeof value === "number") return value
36
+ return parseFloat(value)
37
+ }
38
+
39
+ const StyledRoot = styled(Pressable)(({ theme }) => {
40
+ const spacing = theme.tokens.semantics.dimensions.spacing
41
+
42
+ return {
43
+ display: "flex",
44
+ flexDirection: "column",
45
+ gap: parseTokenValue(spacing.md),
46
+ alignItems: "flex-start",
47
+ width: "100%"
48
+ }
49
+ })
50
+
51
+ const ImageContainer = styled(View)({
52
+ position: "relative",
53
+ width: "100%",
54
+ aspectRatio: 1,
55
+ overflow: "hidden"
56
+ })
57
+
58
+ const StyledImage = styled(Image)({
59
+ width: "100%",
60
+ height: "100%",
61
+ resizeMode: "cover"
62
+ })
63
+
64
+ const BadgeContainer = styled(View)(({ theme }) => {
65
+ const spacing = theme.tokens.semantics.dimensions.spacing
66
+ const { sizing, borderRadius } = theme.tokens.semantics.dimensions
67
+ const { colour } = theme.tokens.semantics
68
+
69
+ return {
70
+ position: "absolute",
71
+ top: parseTokenValue(spacing.md),
72
+ left: 0,
73
+ display: "flex",
74
+ flexDirection: "row",
75
+ gap: parseTokenValue(spacing["2xs"]),
76
+ alignItems: "center",
77
+ justifyContent: "center",
78
+ height: parseTokenValue(sizing["2xl"]),
79
+ paddingRight: parseTokenValue(spacing.sm),
80
+ backgroundColor: colour.background.container.alt,
81
+ borderTopRightRadius: parseTokenValue(borderRadius.sm),
82
+ borderBottomRightRadius: parseTokenValue(borderRadius.sm)
83
+ }
84
+ })
85
+
86
+ const ContentContainer = styled(View)(({ theme }) => {
87
+ const spacing = theme.tokens.semantics.dimensions.spacing
88
+
89
+ return {
90
+ display: "flex",
91
+ flexDirection: "column",
92
+ gap: parseTokenValue(spacing.md),
93
+ alignItems: "flex-start",
94
+ width: "100%"
95
+ }
96
+ })
97
+
98
+ const DetailsContainer = styled(View)({
99
+ display: "flex",
100
+ flexDirection: "column",
101
+ alignItems: "flex-start",
102
+ width: "100%"
103
+ })
104
+
105
+ const CategoryWrapper = styled(View)(({ theme }) => {
106
+ const spacing = theme.tokens.semantics.dimensions.spacing
107
+
108
+ return {
109
+ marginBottom: parseTokenValue(spacing["3xs"])
110
+ }
111
+ })
112
+
113
+ const TitleContainer = styled(View)(({ theme }) => {
114
+ const spacing = theme.tokens.semantics.dimensions.spacing
115
+
116
+ return {
117
+ display: "flex",
118
+ flexDirection: "row",
119
+ width: "100%",
120
+ marginBottom: parseTokenValue(spacing.xs)
121
+ }
122
+ })
123
+
124
+ const SizeWrapper = styled(View)(({ theme }) => {
125
+ const spacing = theme.tokens.semantics.dimensions.spacing
126
+
127
+ return {
128
+ marginBottom: parseTokenValue(spacing.md)
129
+ }
130
+ })
131
+
132
+ const PricingContainer = styled(View)(({ theme }) => {
133
+ const spacing = theme.tokens.semantics.dimensions.spacing
134
+
135
+ return {
136
+ display: "flex",
137
+ flexDirection: "row",
138
+ gap: Math.round(parseFloat(spacing["2xs"] as string)),
139
+ alignItems: "center",
140
+ width: "100%"
141
+ }
142
+ })
143
+
144
+ const _ProductListingCard = React.forwardRef<View, ProductListingCardProps>(
145
+ (
146
+ {
147
+ image,
148
+ imageAlt,
149
+ badge,
150
+ category,
151
+ title,
152
+ size,
153
+ price,
154
+ wasPrice,
155
+ showBadge = true,
156
+ showCategory = true,
157
+ showTitle = true,
158
+ showSize = true,
159
+ showPrice = true,
160
+ onPress,
161
+ ...rest
162
+ },
163
+ ref
164
+ ) => {
165
+ return (
166
+ <StyledRoot ref={ref} onPress={onPress} {...rest}>
167
+ <ImageContainer>
168
+ {image && (
169
+ <View style={{ flex: 1, width: "100%", height: "100%" }}>
170
+ <StyledImage
171
+ source={image}
172
+ accessibilityLabel={imageAlt}
173
+ accessible
174
+ />
175
+ </View>
176
+ )}
177
+ {showBadge && badge && <BadgeContainer>{badge}</BadgeContainer>}
178
+ </ImageContainer>
179
+
180
+ <ContentContainer>
181
+ <DetailsContainer>
182
+ {showCategory && category && (
183
+ <CategoryWrapper>
184
+ <Typography variant="body" size="sm" color="secondary">
185
+ {category}
186
+ </Typography>
187
+ </CategoryWrapper>
188
+ )}
189
+ {showTitle && (
190
+ <TitleContainer>
191
+ <Typography variant="heading" size="xs">
192
+ {title}
193
+ </Typography>
194
+ </TitleContainer>
195
+ )}
196
+ {showSize && size && (
197
+ <SizeWrapper>
198
+ <Typography variant="body" size="md" color="secondary">
199
+ {size}
200
+ </Typography>
201
+ </SizeWrapper>
202
+ )}
203
+ </DetailsContainer>
204
+
205
+ {showPrice && price && (
206
+ <PricingContainer>
207
+ <Typography variant="body" size="md" color="secondary">
208
+ {price}
209
+ </Typography>
210
+ {wasPrice && (
211
+ <Typography
212
+ variant="body"
213
+ size="md"
214
+ color="secondary"
215
+ textDecoration="line-through"
216
+ >
217
+ {wasPrice}
218
+ </Typography>
219
+ )}
220
+ </PricingContainer>
221
+ )}
222
+ </ContentContainer>
223
+ </StyledRoot>
224
+ )
225
+ }
226
+ )
227
+
228
+ _ProductListingCard.displayName = "ProductListingCard"
229
+
230
+ const ProductListingCardWithBadgeAndGrid = Object.assign(_ProductListingCard, {
231
+ Badge,
232
+ Grid
233
+ }) as typeof _ProductListingCard & { Badge: typeof Badge; Grid: typeof Grid }
234
+
235
+ export { ProductListingCardWithBadgeAndGrid as ProductListingCard }
@@ -0,0 +1,2 @@
1
+ export { ProductListingCard } from "./ProductListingCard"
2
+ export type { ProductListingCardProps } from "./ProductListingCard"
@@ -0,0 +1,183 @@
1
+ import React from "react"
2
+ import { View, StyleSheet } from "react-native"
3
+ import { TabNavigation } from "./TabNavigation"
4
+ import type { TabNavigationProps } from "./TabNavigation"
5
+ import { Typography } from "../../atoms/Typography"
6
+
7
+ export default {
8
+ title: "Molecules/TabNavigation",
9
+ component: TabNavigation,
10
+ argTypes: {
11
+ layout: {
12
+ control: { type: "select" },
13
+ options: ["fixed", "intrinsic"],
14
+ description:
15
+ "fixed: tabs share the width equally. intrinsic: tabs size to content and scroll horizontally on overflow."
16
+ },
17
+ animation: {
18
+ control: { type: "select" },
19
+ options: ["fade", "fadeUp", "fadeDown", "scale", "slideIn", "slideOut"],
20
+ description: "Animation preset applied to each panel's content"
21
+ },
22
+ defaultValue: {
23
+ control: { type: "text" },
24
+ description: "Uncontrolled initial selected tab value"
25
+ }
26
+ }
27
+ }
28
+
29
+ const PanelBody = ({ children }: { children: React.ReactNode }) => (
30
+ <View style={styles.panel}>
31
+ <Typography variant="body" size="md">
32
+ {children}
33
+ </Typography>
34
+ </View>
35
+ )
36
+
37
+ export const Playground = {
38
+ args: {
39
+ layout: "fixed",
40
+ animation: "slideIn",
41
+ defaultValue: "give"
42
+ },
43
+ render: (args: TabNavigationProps) => (
44
+ <View style={styles.container}>
45
+ <TabNavigation {...args}>
46
+ <TabNavigation.List>
47
+ <TabNavigation.Tab value="give">Give & get</TabNavigation.Tab>
48
+ <TabNavigation.Tab value="pack">My pack</TabNavigation.Tab>
49
+ </TabNavigation.List>
50
+ <TabNavigation.Panel value="give">
51
+ <PanelBody>Refer a friend and you both get a treat.</PanelBody>
52
+ </TabNavigation.Panel>
53
+ <TabNavigation.Panel value="pack">
54
+ <PanelBody>Everything in your pack lives here.</PanelBody>
55
+ </TabNavigation.Panel>
56
+ </TabNavigation>
57
+ </View>
58
+ )
59
+ }
60
+
61
+ export const FixedWidth = {
62
+ name: "Fixed Width",
63
+ render: () => (
64
+ <View style={styles.container}>
65
+ <TabNavigation defaultValue="give" layout="fixed">
66
+ <TabNavigation.List>
67
+ <TabNavigation.Tab value="give">Give & get</TabNavigation.Tab>
68
+ <TabNavigation.Tab value="pack">My pack</TabNavigation.Tab>
69
+ <TabNavigation.Tab value="orders">Orders</TabNavigation.Tab>
70
+ </TabNavigation.List>
71
+ <TabNavigation.Panel value="give">
72
+ <PanelBody>Give & get content.</PanelBody>
73
+ </TabNavigation.Panel>
74
+ <TabNavigation.Panel value="pack">
75
+ <PanelBody>My pack content.</PanelBody>
76
+ </TabNavigation.Panel>
77
+ <TabNavigation.Panel value="orders">
78
+ <PanelBody>Orders content.</PanelBody>
79
+ </TabNavigation.Panel>
80
+ </TabNavigation>
81
+ </View>
82
+ )
83
+ }
84
+
85
+ export const IntrinsicWidth = {
86
+ name: "Intrinsic Width (scrollable)",
87
+ render: () => (
88
+ <View style={styles.container}>
89
+ <TabNavigation defaultValue="home" layout="intrinsic">
90
+ <TabNavigation.List>
91
+ <TabNavigation.Tab value="home">Home</TabNavigation.Tab>
92
+ <TabNavigation.Tab value="dog">Your dog</TabNavigation.Tab>
93
+ <TabNavigation.Tab value="extras">Extras</TabNavigation.Tab>
94
+ <TabNavigation.Tab value="free">Free food</TabNavigation.Tab>
95
+ <TabNavigation.Tab value="account">Account</TabNavigation.Tab>
96
+ <TabNavigation.Tab value="help">Help</TabNavigation.Tab>
97
+ </TabNavigation.List>
98
+ <TabNavigation.Panel value="home">
99
+ <PanelBody>
100
+ The list scrolls horizontally when tabs overflow.
101
+ </PanelBody>
102
+ </TabNavigation.Panel>
103
+ <TabNavigation.Panel value="dog">
104
+ <PanelBody>Your dog content.</PanelBody>
105
+ </TabNavigation.Panel>
106
+ <TabNavigation.Panel value="extras">
107
+ <PanelBody>Extras content.</PanelBody>
108
+ </TabNavigation.Panel>
109
+ <TabNavigation.Panel value="free">
110
+ <PanelBody>Free food content.</PanelBody>
111
+ </TabNavigation.Panel>
112
+ <TabNavigation.Panel value="account">
113
+ <PanelBody>Account content.</PanelBody>
114
+ </TabNavigation.Panel>
115
+ <TabNavigation.Panel value="help">
116
+ <PanelBody>Help content.</PanelBody>
117
+ </TabNavigation.Panel>
118
+ </TabNavigation>
119
+ </View>
120
+ )
121
+ }
122
+
123
+ export const States = {
124
+ name: "States",
125
+ render: () => (
126
+ <View style={styles.container}>
127
+ <TabNavigation defaultValue="selected" layout="fixed">
128
+ <TabNavigation.List>
129
+ <TabNavigation.Tab value="selected">Selected</TabNavigation.Tab>
130
+ <TabNavigation.Tab value="unselected">Unselected</TabNavigation.Tab>
131
+ <TabNavigation.Tab value="disabled" disabled>
132
+ Inactive
133
+ </TabNavigation.Tab>
134
+ </TabNavigation.List>
135
+ <TabNavigation.Panel value="selected">
136
+ <PanelBody>
137
+ Selected has a brand underline + bold label. Unselected uses a
138
+ neutral underline. Inactive (disabled) is muted and non-interactive.
139
+ </PanelBody>
140
+ </TabNavigation.Panel>
141
+ <TabNavigation.Panel value="unselected">
142
+ <PanelBody>Unselected panel.</PanelBody>
143
+ </TabNavigation.Panel>
144
+ <TabNavigation.Panel value="disabled">
145
+ <PanelBody>Disabled panel.</PanelBody>
146
+ </TabNavigation.Panel>
147
+ </TabNavigation>
148
+ </View>
149
+ )
150
+ }
151
+
152
+ export const AnimatedContent = {
153
+ name: "Animated Content (fade)",
154
+ render: () => (
155
+ <View style={styles.container}>
156
+ <TabNavigation defaultValue="one" layout="fixed" animation="fade">
157
+ <TabNavigation.List>
158
+ <TabNavigation.Tab value="one">First</TabNavigation.Tab>
159
+ <TabNavigation.Tab value="two">Second</TabNavigation.Tab>
160
+ <TabNavigation.Tab value="three">Third</TabNavigation.Tab>
161
+ </TabNavigation.List>
162
+ <TabNavigation.Panel value="one">
163
+ <PanelBody>Switch tabs to see the content fade in.</PanelBody>
164
+ </TabNavigation.Panel>
165
+ <TabNavigation.Panel value="two">
166
+ <PanelBody>Second panel content.</PanelBody>
167
+ </TabNavigation.Panel>
168
+ <TabNavigation.Panel value="three">
169
+ <PanelBody>Third panel content.</PanelBody>
170
+ </TabNavigation.Panel>
171
+ </TabNavigation>
172
+ </View>
173
+ )
174
+ }
175
+
176
+ const styles = StyleSheet.create({
177
+ container: {
178
+ padding: 16
179
+ },
180
+ panel: {
181
+ paddingTop: 16
182
+ }
183
+ })