@butternutbox/pawprint-native 1.1.3 → 1.2.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.
- package/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +269 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +270 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/molecules/FloatingActionButton/FloatingActionButton.stories.tsx +304 -0
- package/src/components/molecules/FloatingActionButton/FloatingActionButton.tsx +445 -0
- package/src/components/molecules/FloatingActionButton/index.ts +7 -0
- package/src/components/molecules/index.ts +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View, ScrollView, StyleSheet } from "react-native"
|
|
3
|
+
import {
|
|
4
|
+
FloatingActionButton,
|
|
5
|
+
type FloatingActionButtonProps
|
|
6
|
+
} from "./FloatingActionButton"
|
|
7
|
+
import { Typography } from "../../atoms/Typography"
|
|
8
|
+
import { Button } from "../../atoms/Button"
|
|
9
|
+
import { Basket } from "@butternutbox/pawprint-icons/core"
|
|
10
|
+
|
|
11
|
+
const Message = () => (
|
|
12
|
+
<>
|
|
13
|
+
<Typography variant="body" size="lg" weight="bold">
|
|
14
|
+
Free
|
|
15
|
+
</Typography>{" "}
|
|
16
|
+
gift in your next box
|
|
17
|
+
</>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
|
21
|
+
? Omit<T, K>
|
|
22
|
+
: never
|
|
23
|
+
|
|
24
|
+
// Applies the design pairing: with the `close` icon button the link reads
|
|
25
|
+
// "See more" (so there aren't two close actions) and the icon button dismisses;
|
|
26
|
+
// otherwise the link reads "Close" and dismisses.
|
|
27
|
+
const DismissibleFab = (
|
|
28
|
+
props: DistributiveOmit<
|
|
29
|
+
FloatingActionButtonProps,
|
|
30
|
+
"linkLabel" | "linkHref" | "onLinkPress"
|
|
31
|
+
>
|
|
32
|
+
) => {
|
|
33
|
+
const [dismissed, setDismissed] = React.useState(false)
|
|
34
|
+
const dismiss = () => setDismissed(true)
|
|
35
|
+
|
|
36
|
+
if (dismissed) {
|
|
37
|
+
return (
|
|
38
|
+
<Button variant="outlined" onPress={() => setDismissed(false)}>
|
|
39
|
+
Show again
|
|
40
|
+
</Button>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (props.iconButtonIcon === "close") {
|
|
45
|
+
return (
|
|
46
|
+
<FloatingActionButton
|
|
47
|
+
{...props}
|
|
48
|
+
linkLabel="See more"
|
|
49
|
+
onLinkPress={() => console.warn("See more pressed")}
|
|
50
|
+
onIconButtonPress={dismiss}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<FloatingActionButton {...props} linkLabel="Close" onLinkPress={dismiss} />
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default {
|
|
61
|
+
title: "Molecules/FloatingActionButton",
|
|
62
|
+
component: FloatingActionButton,
|
|
63
|
+
argTypes: {
|
|
64
|
+
variant: {
|
|
65
|
+
control: { type: "select" },
|
|
66
|
+
options: ["primary", "secondary"],
|
|
67
|
+
description:
|
|
68
|
+
"Colour scheme. `primary` is pink with a white icon container; `secondary` is white with a cream icon container."
|
|
69
|
+
},
|
|
70
|
+
children: {
|
|
71
|
+
control: { type: "text" },
|
|
72
|
+
description: "Message copy."
|
|
73
|
+
},
|
|
74
|
+
linkLabel: {
|
|
75
|
+
control: { type: "text" },
|
|
76
|
+
description:
|
|
77
|
+
"Link text (required). Pair with `linkHref` or `onLinkPress`."
|
|
78
|
+
},
|
|
79
|
+
linkHref: {
|
|
80
|
+
control: { type: "text" },
|
|
81
|
+
description: "URL opened via `Linking` when pressed."
|
|
82
|
+
},
|
|
83
|
+
iconButtonIcon: {
|
|
84
|
+
control: { type: "select" },
|
|
85
|
+
options: ["chevronUp", "chevronRight", "close"],
|
|
86
|
+
description:
|
|
87
|
+
"Icon button (required). Limited to `chevronUp`, `chevronRight` and `close`."
|
|
88
|
+
},
|
|
89
|
+
iconButtonVariant: {
|
|
90
|
+
control: { type: "select" },
|
|
91
|
+
options: ["filled", "outlined", "text"],
|
|
92
|
+
description:
|
|
93
|
+
"Icon button style. Defaults to `filled` for the chevrons and `text` for `close`."
|
|
94
|
+
},
|
|
95
|
+
iconButtonLabel: {
|
|
96
|
+
control: { type: "text" },
|
|
97
|
+
description: "Accessible label for the icon button."
|
|
98
|
+
},
|
|
99
|
+
animation: {
|
|
100
|
+
control: { type: "select" },
|
|
101
|
+
options: [undefined, "shine", "nudge"],
|
|
102
|
+
description:
|
|
103
|
+
"Optional attention animation (off by default). Runs 3 times after a short delay, then stops. Disabled with reduced motion."
|
|
104
|
+
},
|
|
105
|
+
icon: {
|
|
106
|
+
control: false,
|
|
107
|
+
description:
|
|
108
|
+
"Icon in the left container. Accepts any Pawprint icon; defaults to `Gift`. See the CustomIcon story."
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const Playground = (args: FloatingActionButtonProps) => (
|
|
114
|
+
<View style={styles.container}>
|
|
115
|
+
<FloatingActionButton
|
|
116
|
+
{...args}
|
|
117
|
+
onLinkPress={() => console.warn("Link pressed")}
|
|
118
|
+
/>
|
|
119
|
+
</View>
|
|
120
|
+
)
|
|
121
|
+
Playground.args = {
|
|
122
|
+
variant: "primary",
|
|
123
|
+
children: "Free gift in your next box",
|
|
124
|
+
linkLabel: "Close",
|
|
125
|
+
iconButtonIcon: "chevronUp",
|
|
126
|
+
iconButtonLabel: "View offer",
|
|
127
|
+
onIconButtonPress: () => console.warn("Icon button pressed")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const Primary = () => (
|
|
131
|
+
<View style={styles.container}>
|
|
132
|
+
<DismissibleFab
|
|
133
|
+
variant="primary"
|
|
134
|
+
iconButtonIcon="chevronUp"
|
|
135
|
+
iconButtonLabel="View offer"
|
|
136
|
+
>
|
|
137
|
+
<Message />
|
|
138
|
+
</DismissibleFab>
|
|
139
|
+
</View>
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
export const Secondary = () => (
|
|
143
|
+
<View style={styles.container}>
|
|
144
|
+
<DismissibleFab
|
|
145
|
+
variant="secondary"
|
|
146
|
+
iconButtonIcon="close"
|
|
147
|
+
iconButtonLabel="Dismiss"
|
|
148
|
+
>
|
|
149
|
+
<Message />
|
|
150
|
+
</DismissibleFab>
|
|
151
|
+
</View>
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
export const AllVariants = () => (
|
|
155
|
+
<View style={[styles.container, styles.column]}>
|
|
156
|
+
{(["primary", "secondary"] as const).map((variant) =>
|
|
157
|
+
(["chevronUp", "chevronRight", "close"] as const).map(
|
|
158
|
+
(iconButtonIcon) => (
|
|
159
|
+
<DismissibleFab
|
|
160
|
+
key={`${variant}-${iconButtonIcon}`}
|
|
161
|
+
variant={variant}
|
|
162
|
+
iconButtonIcon={iconButtonIcon}
|
|
163
|
+
iconButtonLabel={
|
|
164
|
+
iconButtonIcon === "close" ? "Dismiss" : "View offer"
|
|
165
|
+
}
|
|
166
|
+
>
|
|
167
|
+
<Message />
|
|
168
|
+
</DismissibleFab>
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
)}
|
|
172
|
+
</View>
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
export const CustomIcon = () => (
|
|
176
|
+
<View style={styles.container}>
|
|
177
|
+
<DismissibleFab
|
|
178
|
+
icon={Basket}
|
|
179
|
+
iconButtonIcon="chevronRight"
|
|
180
|
+
iconButtonLabel="Go to basket"
|
|
181
|
+
>
|
|
182
|
+
Your box is ready to check out
|
|
183
|
+
</DismissibleFab>
|
|
184
|
+
</View>
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
export const LinkOpensAction = () => {
|
|
188
|
+
const [count, setCount] = React.useState(0)
|
|
189
|
+
const [dismissed, setDismissed] = React.useState(false)
|
|
190
|
+
return (
|
|
191
|
+
<View style={[styles.container, styles.column]}>
|
|
192
|
+
{dismissed ? (
|
|
193
|
+
<Button variant="outlined" onPress={() => setDismissed(false)}>
|
|
194
|
+
Show again
|
|
195
|
+
</Button>
|
|
196
|
+
) : (
|
|
197
|
+
<FloatingActionButton
|
|
198
|
+
linkLabel="See more"
|
|
199
|
+
onLinkPress={() => setCount((c) => c + 1)}
|
|
200
|
+
iconButtonIcon="close"
|
|
201
|
+
iconButtonLabel="Dismiss"
|
|
202
|
+
onIconButtonPress={() => setDismissed(true)}
|
|
203
|
+
>
|
|
204
|
+
<Message />
|
|
205
|
+
</FloatingActionButton>
|
|
206
|
+
)}
|
|
207
|
+
<Typography size="sm">Link pressed {count} times</Typography>
|
|
208
|
+
</View>
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// The Storybook decorator wraps stories in a ScrollView, where `flex: 1` has
|
|
213
|
+
// no height to fill — so the mock screen gets a fixed height to keep the inner
|
|
214
|
+
// ScrollView scrolling beneath the pinned FAB.
|
|
215
|
+
export const PinnedBottomRight = () => (
|
|
216
|
+
<View style={styles.screen}>
|
|
217
|
+
<ScrollView nestedScrollEnabled contentContainerStyle={styles.column}>
|
|
218
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
219
|
+
<Typography key={i}>Screen content line {i + 1}</Typography>
|
|
220
|
+
))}
|
|
221
|
+
</ScrollView>
|
|
222
|
+
<View style={styles.pinned}>
|
|
223
|
+
<DismissibleFab iconButtonIcon="chevronUp" iconButtonLabel="View offer">
|
|
224
|
+
<Message />
|
|
225
|
+
</DismissibleFab>
|
|
226
|
+
</View>
|
|
227
|
+
</View>
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
export const IconButtonStyles = () => (
|
|
231
|
+
<View style={[styles.container, styles.column]}>
|
|
232
|
+
{(["filled", "outlined", "text"] as const).map((iconButtonVariant) =>
|
|
233
|
+
(["chevronUp", "chevronRight", "close"] as const).map(
|
|
234
|
+
(iconButtonIcon) => (
|
|
235
|
+
<DismissibleFab
|
|
236
|
+
key={`${iconButtonVariant}-${iconButtonIcon}`}
|
|
237
|
+
variant="secondary"
|
|
238
|
+
iconButtonIcon={iconButtonIcon}
|
|
239
|
+
iconButtonVariant={iconButtonVariant}
|
|
240
|
+
iconButtonLabel={`${iconButtonIcon} ${iconButtonVariant}`}
|
|
241
|
+
>
|
|
242
|
+
<Message />
|
|
243
|
+
</DismissibleFab>
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
)}
|
|
247
|
+
</View>
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
export const Animations = () => {
|
|
251
|
+
const [run, setRun] = React.useState(0)
|
|
252
|
+
return (
|
|
253
|
+
<View key={run} style={[styles.container, styles.column]}>
|
|
254
|
+
<Typography weight="semiBold">Shine</Typography>
|
|
255
|
+
<DismissibleFab
|
|
256
|
+
animation="shine"
|
|
257
|
+
iconButtonIcon="chevronUp"
|
|
258
|
+
iconButtonLabel="View offer"
|
|
259
|
+
>
|
|
260
|
+
<Message />
|
|
261
|
+
</DismissibleFab>
|
|
262
|
+
<DismissibleFab
|
|
263
|
+
animation="shine"
|
|
264
|
+
variant="secondary"
|
|
265
|
+
iconButtonIcon="close"
|
|
266
|
+
iconButtonLabel="Dismiss"
|
|
267
|
+
>
|
|
268
|
+
<Message />
|
|
269
|
+
</DismissibleFab>
|
|
270
|
+
<Typography weight="semiBold">Nudge</Typography>
|
|
271
|
+
<DismissibleFab
|
|
272
|
+
animation="nudge"
|
|
273
|
+
iconButtonIcon="chevronUp"
|
|
274
|
+
iconButtonLabel="View offer"
|
|
275
|
+
>
|
|
276
|
+
<Message />
|
|
277
|
+
</DismissibleFab>
|
|
278
|
+
<DismissibleFab
|
|
279
|
+
animation="nudge"
|
|
280
|
+
variant="secondary"
|
|
281
|
+
iconButtonIcon="chevronRight"
|
|
282
|
+
iconButtonLabel="View offer"
|
|
283
|
+
>
|
|
284
|
+
<Message />
|
|
285
|
+
</DismissibleFab>
|
|
286
|
+
<Button variant="outlined" onPress={() => setRun((r) => r + 1)}>
|
|
287
|
+
Replay
|
|
288
|
+
</Button>
|
|
289
|
+
</View>
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const styles = StyleSheet.create({
|
|
294
|
+
container: { padding: 20 },
|
|
295
|
+
column: { flexDirection: "column", gap: 20 },
|
|
296
|
+
screen: { height: 600, padding: 16 },
|
|
297
|
+
pinned: {
|
|
298
|
+
position: "absolute",
|
|
299
|
+
right: 16,
|
|
300
|
+
bottom: 16,
|
|
301
|
+
left: 16,
|
|
302
|
+
alignItems: "flex-end"
|
|
303
|
+
}
|
|
304
|
+
})
|