@butternutbox/pawprint-native 0.4.0 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@butternutbox/pawprint-native",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "ButternutBox Pawprint Design System - React Native Components",
6
6
  "main": "./dist/index.cjs",
@@ -74,29 +74,20 @@ const StyledTextInput = styled(TextInput)<{
74
74
  inputFontFamily: string
75
75
  inputFontWeight?: FontWeight
76
76
  inputFontSize: number
77
- inputLineHeight: number
78
- }>(
79
- ({
80
- inputColor,
81
- inputFontFamily,
82
- inputFontWeight,
83
- inputFontSize,
84
- inputLineHeight
85
- }) => ({
86
- textAlign: "center" as const,
87
- outlineStyle: "none" as unknown as undefined,
88
- padding: 0,
89
- minWidth: 0,
90
- minHeight: 0,
91
- width: "100%",
92
- height: "100%",
93
- color: inputColor,
94
- fontFamily: inputFontFamily,
95
- ...(inputFontWeight ? { fontWeight: inputFontWeight } : {}),
96
- fontSize: inputFontSize,
97
- lineHeight: inputLineHeight
98
- })
99
- )
77
+ }>(({ inputColor, inputFontFamily, inputFontWeight, inputFontSize }) => ({
78
+ textAlign: "center" as const,
79
+ outlineStyle: "none" as unknown as undefined,
80
+ padding: 0,
81
+ minWidth: 0,
82
+ minHeight: 0,
83
+ width: "100%",
84
+ height: "100%",
85
+ color: inputColor,
86
+ fontFamily: inputFontFamily,
87
+ ...(inputFontWeight ? { fontWeight: inputFontWeight } : {}),
88
+ fontSize: inputFontSize,
89
+ lineHeight: 0
90
+ }))
100
91
 
101
92
  const StyledRow = styled(View)<{
102
93
  rowGap: number
@@ -256,7 +247,6 @@ export const NumberFieldInput = React.forwardRef<
256
247
  : undefined
257
248
  }
258
249
  inputFontSize={parseTokenValue(typographyToken.fontSize)}
259
- inputLineHeight={parseTokenValue(typographyToken.lineHeight)}
260
250
  keyboardType="numeric"
261
251
  editable={!disabled}
262
252
  onFocus={handleFocus}
@@ -1,14 +1,9 @@
1
1
  import React from "react"
2
- import { View, StyleSheet, Image } from "react-native"
2
+ import { View, StyleSheet } from "react-native"
3
3
  import { ProductDisplayCard } from "./ProductDisplayCard"
4
4
  import type { ProductDisplayCardProps } from "./ProductDisplayCard"
5
5
 
6
- const PlaceholderImage = () => (
7
- <Image
8
- source={{ uri: "https://placeholder.co/148x148?text=Recipe" }}
9
- style={{ width: "100%", height: "100%" }}
10
- />
11
- )
6
+ const placeholderImageUrl = "https://placeholder.co/100x100?text=Recipe"
12
7
 
13
8
  const styles = StyleSheet.create({
14
9
  column: { flexDirection: "column", gap: 24, padding: 16 }
@@ -43,6 +38,10 @@ export default {
43
38
  control: { type: "boolean" },
44
39
  description: "Show/hide quantity picker"
45
40
  },
41
+ showImageQuantityBadge: {
42
+ control: { type: "boolean" },
43
+ description: "Show/hide quantity badge overlay on image"
44
+ },
46
45
  disableQuantityButtons: {
47
46
  control: { type: "boolean" },
48
47
  description: "Disable quantity buttons"
@@ -111,7 +110,7 @@ Playground.args = {
111
110
  bannerType: "info",
112
111
  showBannerIcon: false,
113
112
  banner: " High in protein • Natural ingredients • Vet approved",
114
- image: <PlaceholderImage />
113
+ image: placeholderImageUrl
115
114
  }
116
115
 
117
116
  export const Default = () => (
@@ -124,7 +123,7 @@ export const Default = () => (
124
123
  quantity={1}
125
124
  showQuantityPicker={false}
126
125
  showBanner={false}
127
- image={<PlaceholderImage />}
126
+ image={placeholderImageUrl}
128
127
  />
129
128
  </View>
130
129
  )
@@ -139,7 +138,7 @@ export const Desktop = () => (
139
138
  quantity={1}
140
139
  showQuantityPicker
141
140
  showBanner={false}
142
- image={<PlaceholderImage />}
141
+ image={placeholderImageUrl}
143
142
  />
144
143
  </View>
145
144
  )
@@ -154,7 +153,7 @@ export const Mobile = () => (
154
153
  quantity={1}
155
154
  showQuantityPicker
156
155
  showBanner={false}
157
- image={<PlaceholderImage />}
156
+ image={placeholderImageUrl}
158
157
  />
159
158
  </View>
160
159
  )
@@ -172,7 +171,7 @@ export const WithBanner = () => (
172
171
  bannerType="success"
173
172
  showBannerIcon={false}
174
173
  banner="High in protein • Natural ingredients • Vet approved"
175
- image={<PlaceholderImage />}
174
+ image={placeholderImageUrl}
176
175
  />
177
176
  </View>
178
177
  )
@@ -190,7 +189,7 @@ export const WithBannerIcon = () => (
190
189
  bannerType="success"
191
190
  showBannerIcon
192
191
  banner="High in protein • Natural ingredients • Vet approved"
193
- image={<PlaceholderImage />}
192
+ image={placeholderImageUrl}
194
193
  />
195
194
  </View>
196
195
  )
@@ -204,7 +203,35 @@ export const NoSubtext = () => (
204
203
  quantity={1}
205
204
  showQuantityPicker
206
205
  showBanner={false}
207
- image={<PlaceholderImage />}
206
+ image={placeholderImageUrl}
207
+ />
208
+ </View>
209
+ )
210
+
211
+ export const WithImageQuantityBadge = () => (
212
+ <View style={styles.column}>
213
+ <ProductDisplayCard
214
+ device="mobile"
215
+ title="Gobble Gobble Turkey"
216
+ subtext="+£0.50/pouch"
217
+ showSubtext
218
+ quantity={4}
219
+ showQuantityPicker={false}
220
+ showImageQuantityBadge
221
+ showBanner={false}
222
+ image={placeholderImageUrl}
223
+ />
224
+
225
+ <ProductDisplayCard
226
+ device="mobile"
227
+ title="Salmon Delight"
228
+ subtext="+£0.25/pouch"
229
+ showSubtext
230
+ quantity={2}
231
+ showQuantityPicker
232
+ showImageQuantityBadge
233
+ showBanner={false}
234
+ image={placeholderImageUrl}
208
235
  />
209
236
  </View>
210
237
  )
@@ -219,7 +246,7 @@ export const AllVariants = () => (
219
246
  quantity={1}
220
247
  showQuantityPicker
221
248
  showBanner={false}
222
- image={<PlaceholderImage />}
249
+ image={placeholderImageUrl}
223
250
  />
224
251
 
225
252
  <ProductDisplayCard
@@ -230,7 +257,7 @@ export const AllVariants = () => (
230
257
  quantity={2}
231
258
  showQuantityPicker
232
259
  showBanner={false}
233
- image={<PlaceholderImage />}
260
+ image={placeholderImageUrl}
234
261
  />
235
262
 
236
263
  <ProductDisplayCard
@@ -242,7 +269,7 @@ export const AllVariants = () => (
242
269
  showQuantityPicker
243
270
  showBanner
244
271
  banner=" High in protein • Natural ingredients"
245
- image={<PlaceholderImage />}
272
+ image={placeholderImageUrl}
246
273
  />
247
274
  </View>
248
275
  )
@@ -1,5 +1,5 @@
1
1
  import React from "react"
2
- import { View, ViewProps } from "react-native"
2
+ import { View, ViewProps, Image } from "react-native"
3
3
  import styled from "@emotion/native"
4
4
  import { Typography } from "../../atoms/Typography"
5
5
  import { NumberField } from "../../molecules/NumberField"
@@ -11,7 +11,7 @@ export type ProductDisplayCardDevice = "desktop" | "mobile"
11
11
  export type ProductDisplayCardProps = ViewProps & {
12
12
  device?: ProductDisplayCardDevice
13
13
  title: string
14
- subtext?: string
14
+ subtext?: string | React.ReactNode
15
15
  showSubtext?: boolean
16
16
  quantity?: number
17
17
  onQuantityChange?: (quantity: number) => void
@@ -22,7 +22,9 @@ export type ProductDisplayCardProps = ViewProps & {
22
22
  showDecrementButton?: boolean
23
23
  incrementDisabled?: boolean
24
24
  decrementDisabled?: boolean
25
- image?: React.ReactNode
25
+ image?: string | React.ReactNode
26
+ imageBackgroundColor?: string
27
+ showImageQuantityBadge?: boolean
26
28
  banner?: React.ReactNode
27
29
  showBanner?: boolean
28
30
  bannerType?: "error" | "success" | "warning" | "info"
@@ -55,13 +57,38 @@ const StyledCardContainer = styled(View)<{
55
57
  }
56
58
  })
57
59
 
58
- const StyledImage = styled(View)(() => ({
59
- flexShrink: 0,
60
- backgroundColor: "#f5f5f5",
61
- overflow: "hidden",
62
- aspectRatio: "1 / 1",
63
- height: "100%"
64
- }))
60
+ const StyledImage = styled(View)<{ imageBackgroundColor?: string }>(
61
+ ({ theme, imageBackgroundColor }) => ({
62
+ flexShrink: 0,
63
+ backgroundColor:
64
+ imageBackgroundColor ||
65
+ theme.tokens.semantics.colour.background.container.secondary,
66
+ overflow: "hidden",
67
+ aspectRatio: "1 / 1",
68
+ height: "100%",
69
+ position: "relative"
70
+ })
71
+ )
72
+
73
+ const StyledQuantityBadge = styled(View)(({ theme }) => {
74
+ const spacing = theme.tokens.semantics.dimensions.spacing
75
+ const borderRadius = theme.tokens.semantics.dimensions.borderRadius
76
+ const { colour } = theme.tokens.semantics
77
+
78
+ return {
79
+ position: "absolute",
80
+ top: parseTokenValue(spacing["2xs"]),
81
+ right: parseTokenValue(spacing["2xs"]),
82
+ backgroundColor: colour.background.container.brand,
83
+ borderRadius: parseTokenValue(borderRadius.xs),
84
+ paddingHorizontal: parseTokenValue(spacing.sm),
85
+ height: parseTokenValue(spacing["2xl"]),
86
+ minWidth: parseTokenValue(spacing["2xl"]),
87
+ alignItems: "center",
88
+ justifyContent: "center",
89
+ zIndex: 2
90
+ }
91
+ })
65
92
 
66
93
  const StyledContentArea = styled(View)<{
67
94
  contentDevice: ProductDisplayCardDevice
@@ -122,12 +149,14 @@ const StyledNotification = styled(Notification as any)(({ theme }) => {
122
149
  *
123
150
  * @param device - *(optional)* Device variant: "desktop" or "mobile" (default)
124
151
  * @param title - Product title
125
- * @param subtext - *(optional)* Subtitle text (e.g. price)
152
+ * @param subtext - *(optional)* Subtitle text or JSX element (e.g. price)
126
153
  * @param showSubtext - *(optional)* Show/hide subtext
127
154
  * @param quantity - *(optional)* Current quantity value
128
155
  * @param onQuantityChange - *(optional)* Callback when quantity changes
129
156
  * @param showQuantityPicker - *(optional)* Show/hide quantity picker
130
- * @param image - *(optional)* Image element or content
157
+ * @param image - *(optional)* Image URL string or React element/JSX content
158
+ * @param imageBackgroundColor - *(optional)* Background color for image container (default: theme background.container.secondary)
159
+ * @param showImageQuantityBadge - *(optional)* Show/hide quantity badge overlay on image
131
160
  * @param banner - *(optional)* Banner content to show below card
132
161
  * @param showBanner - *(optional)* Show/hide banner
133
162
  * @param bannerType - *(optional)* Banner notification type: "error", "success", "warning", or "info" (default)
@@ -153,6 +182,8 @@ export const ProductDisplayCard = React.forwardRef<
153
182
  incrementDisabled = false,
154
183
  decrementDisabled = false,
155
184
  image,
185
+ imageBackgroundColor,
186
+ showImageQuantityBadge = false,
156
187
  banner,
157
188
  showBanner = false,
158
189
  bannerType = "info",
@@ -169,7 +200,25 @@ export const ProductDisplayCard = React.forwardRef<
169
200
 
170
201
  const cardContent = (
171
202
  <StyledCardContainer ref={ref} cardDevice={device} {...rest}>
172
- {image && <StyledImage>{image}</StyledImage>}
203
+ {image && (
204
+ <StyledImage imageBackgroundColor={imageBackgroundColor}>
205
+ {typeof image === "string" ? (
206
+ <Image
207
+ source={{ uri: image }}
208
+ style={{ width: "100%", height: "100%" }}
209
+ />
210
+ ) : (
211
+ image
212
+ )}
213
+ {showImageQuantityBadge && quantity && (
214
+ <StyledQuantityBadge>
215
+ <Typography variant="body" size="sm" weight="bold">
216
+ {quantity}
217
+ </Typography>
218
+ </StyledQuantityBadge>
219
+ )}
220
+ </StyledImage>
221
+ )}
173
222
 
174
223
  <StyledContentArea contentDevice={device}>
175
224
  <StyledTextContainer>