@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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +1012 -173
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +252 -5
- package/dist/index.d.ts +252 -5
- package/dist/index.js +1007 -173
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/components/atoms/Hint/Hint.tsx +1 -2
- package/src/components/atoms/Input/InputField.tsx +7 -1
- package/src/components/molecules/Animated/Animated.tsx +12 -3
- package/src/components/molecules/Countdown/Countdown.stories.tsx +218 -0
- package/src/components/molecules/Countdown/Countdown.tsx +315 -0
- package/src/components/molecules/Countdown/index.ts +2 -0
- package/src/components/molecules/NumberField/NumberFieldInput.tsx +14 -24
- package/src/components/molecules/ProductDisplayCard/ProductDisplayCard.stories.tsx +275 -0
- package/src/components/molecules/ProductDisplayCard/ProductDisplayCard.test.tsx +198 -0
- package/src/components/molecules/ProductDisplayCard/ProductDisplayCard.tsx +292 -0
- package/src/components/molecules/ProductDisplayCard/index.ts +5 -0
- package/src/components/molecules/ProductListingCard/Badge.tsx +65 -0
- package/src/components/molecules/ProductListingCard/Grid.tsx +59 -0
- package/src/components/molecules/ProductListingCard/ProductListingCard.stories.tsx +209 -0
- package/src/components/molecules/ProductListingCard/ProductListingCard.tsx +235 -0
- package/src/components/molecules/ProductListingCard/index.ts +2 -0
- package/src/components/molecules/TabNavigation/TabNavigation.stories.tsx +183 -0
- package/src/components/molecules/TabNavigation/TabNavigation.tsx +354 -0
- package/src/components/molecules/TabNavigation/index.ts +7 -0
- package/src/components/molecules/index.ts +4 -0
- package/src/utils/index.ts +1 -0
- 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,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
|
+
})
|