@butternutbox/pawprint-native 0.11.1 → 0.13.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 +12 -0
- package/dist/index.cjs +922 -881
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -4
- package/dist/index.d.ts +39 -4
- package/dist/index.js +760 -720
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/atoms/Divider/Divider.stories.tsx +71 -0
- package/src/components/atoms/Divider/Divider.test.tsx +25 -0
- package/src/components/atoms/Divider/Divider.tsx +61 -0
- package/src/components/atoms/Divider/index.ts +2 -0
- package/src/components/atoms/index.ts +1 -0
- package/src/components/molecules/Checkbox/Checkbox.tsx +19 -3
- package/src/components/molecules/Checkbox/CheckboxGroup.tsx +31 -7
package/package.json
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View, StyleSheet } from "react-native"
|
|
3
|
+
import { Divider } from "./Divider"
|
|
4
|
+
import { Typography } from "../Typography"
|
|
5
|
+
import type { DividerProps } from "./Divider"
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: "Atoms/Divider",
|
|
9
|
+
component: Divider,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
"A horizontal rule used to separate content, group related elements, and establish a clear visual hierarchy. Native counterpart to the web Divider."
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
size: {
|
|
20
|
+
control: { type: "select" },
|
|
21
|
+
options: ["small", "medium", "large"],
|
|
22
|
+
description:
|
|
23
|
+
"Thickness of the divider (small = 1px, medium = 2px, large = 4px)"
|
|
24
|
+
},
|
|
25
|
+
colour: {
|
|
26
|
+
control: { type: "select" },
|
|
27
|
+
options: ["light", "dark"],
|
|
28
|
+
description: "Colour variant of the divider"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const sizes = ["small", "medium", "large"] as const
|
|
34
|
+
const colours = ["light", "dark"] as const
|
|
35
|
+
|
|
36
|
+
export const Default = (args: DividerProps) => (
|
|
37
|
+
<View style={styles.container}>
|
|
38
|
+
<Divider {...args} />
|
|
39
|
+
</View>
|
|
40
|
+
)
|
|
41
|
+
Default.args = {
|
|
42
|
+
size: "small",
|
|
43
|
+
colour: "light"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const AllVariants = () => (
|
|
47
|
+
<View style={styles.column}>
|
|
48
|
+
{colours.map((colour) => (
|
|
49
|
+
<View key={colour} style={styles.section}>
|
|
50
|
+
<Typography variant="heading" size="sm">
|
|
51
|
+
{colour}
|
|
52
|
+
</Typography>
|
|
53
|
+
{sizes.map((size) => (
|
|
54
|
+
<View key={size} style={styles.row}>
|
|
55
|
+
<Typography variant="body" size="sm">
|
|
56
|
+
{size}
|
|
57
|
+
</Typography>
|
|
58
|
+
<Divider size={size} colour={colour} />
|
|
59
|
+
</View>
|
|
60
|
+
))}
|
|
61
|
+
</View>
|
|
62
|
+
))}
|
|
63
|
+
</View>
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const styles = StyleSheet.create({
|
|
67
|
+
container: { width: 300 },
|
|
68
|
+
column: { width: 300, gap: 24 },
|
|
69
|
+
section: { gap: 12 },
|
|
70
|
+
row: { gap: 4 }
|
|
71
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { describe, it, expect } from "vitest"
|
|
3
|
+
import { renderWithTheme } from "../../../test-utils"
|
|
4
|
+
import { Divider } from "./Divider"
|
|
5
|
+
|
|
6
|
+
describe("Divider", () => {
|
|
7
|
+
it("renders with default size and colour", () => {
|
|
8
|
+
const { container } = renderWithTheme(<Divider />)
|
|
9
|
+
expect(container.firstChild).toBeInTheDocument()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it("renders size and colour variants", () => {
|
|
13
|
+
const { container } = renderWithTheme(
|
|
14
|
+
<Divider size="large" colour="dark" />
|
|
15
|
+
)
|
|
16
|
+
expect(container.firstChild).toBeInTheDocument()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it("forwards ref to the root element", () => {
|
|
20
|
+
const ref = React.createRef<any>()
|
|
21
|
+
renderWithTheme(<Divider ref={ref} />)
|
|
22
|
+
expect(ref.current).toBeTruthy()
|
|
23
|
+
expect(ref.current).toBeInTheDocument()
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { View } from "react-native"
|
|
3
|
+
import styled from "@emotion/native"
|
|
4
|
+
|
|
5
|
+
type DividerSize = "small" | "medium" | "large"
|
|
6
|
+
type DividerColour = "light" | "dark"
|
|
7
|
+
|
|
8
|
+
type DividerOwnProps = {
|
|
9
|
+
size?: DividerSize
|
|
10
|
+
colour?: DividerColour
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type DividerProps = DividerOwnProps &
|
|
14
|
+
Omit<React.ComponentProps<typeof View>, keyof DividerOwnProps>
|
|
15
|
+
|
|
16
|
+
const parseTokenValue = (value: string): number => parseFloat(value)
|
|
17
|
+
|
|
18
|
+
const StyledDivider = styled(View)<{
|
|
19
|
+
dividerSize: DividerSize
|
|
20
|
+
dividerColour: DividerColour
|
|
21
|
+
}>(({ theme, dividerSize, dividerColour }) => {
|
|
22
|
+
const { colour, sizing } = theme.tokens.components.dividers
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
width: "100%",
|
|
26
|
+
height: parseTokenValue(sizing[dividerSize].height),
|
|
27
|
+
backgroundColor: colour[dividerColour]
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A horizontal rule used to separate content, group related elements, and
|
|
33
|
+
* establish a clear visual hierarchy. Native counterpart to the web `Divider`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* import { Divider } from "@butternutbox/pawprint-native"
|
|
38
|
+
*
|
|
39
|
+
* <Divider />
|
|
40
|
+
* <Divider size="medium" colour="dark" />
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @param {"small" | "medium" | "large"} [size="small"] - Thickness variant
|
|
44
|
+
* (small = 1px, medium = 2px, large = 4px).
|
|
45
|
+
* @param {"light" | "dark"} [colour="light"] - Colour variant.
|
|
46
|
+
*/
|
|
47
|
+
const Divider = React.forwardRef<View, DividerProps>(
|
|
48
|
+
({ size = "small", colour = "light", ...rest }, ref) => (
|
|
49
|
+
<StyledDivider
|
|
50
|
+
ref={ref}
|
|
51
|
+
dividerSize={size}
|
|
52
|
+
dividerColour={colour}
|
|
53
|
+
{...rest}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
Divider.displayName = "Divider"
|
|
59
|
+
|
|
60
|
+
export { Divider }
|
|
61
|
+
export type { DividerProps, DividerSize, DividerColour }
|
|
@@ -19,6 +19,12 @@ type CheckboxOwnProps = {
|
|
|
19
19
|
disabled?: boolean
|
|
20
20
|
name?: string
|
|
21
21
|
value?: string
|
|
22
|
+
/**
|
|
23
|
+
* Tile only: hug the content width instead of filling the container, so tiles
|
|
24
|
+
* can sit side by side and wrap. Set automatically by a horizontal
|
|
25
|
+
* `CheckboxGroup`; rarely needs setting by hand.
|
|
26
|
+
*/
|
|
27
|
+
fitContent?: boolean
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
export type CheckboxProps = CheckboxOwnProps &
|
|
@@ -33,6 +39,7 @@ const StyledRootPressable = styled(Pressable)<{
|
|
|
33
39
|
rootPaddingVertical?: number
|
|
34
40
|
rootPaddingHorizontal?: number
|
|
35
41
|
rootMaxWidth?: number
|
|
42
|
+
rootFitContent?: boolean
|
|
36
43
|
rootBgColor?: string
|
|
37
44
|
rootBorderWidth?: number
|
|
38
45
|
rootBorderColor?: string
|
|
@@ -45,6 +52,7 @@ const StyledRootPressable = styled(Pressable)<{
|
|
|
45
52
|
rootPaddingVertical,
|
|
46
53
|
rootPaddingHorizontal,
|
|
47
54
|
rootMaxWidth,
|
|
55
|
+
rootFitContent,
|
|
48
56
|
rootBgColor,
|
|
49
57
|
rootBorderWidth,
|
|
50
58
|
rootBorderColor,
|
|
@@ -60,8 +68,12 @@ const StyledRootPressable = styled(Pressable)<{
|
|
|
60
68
|
...(rootPaddingHorizontal !== undefined
|
|
61
69
|
? { paddingHorizontal: rootPaddingHorizontal }
|
|
62
70
|
: {}),
|
|
71
|
+
// `fitContent` tiles hug their content (for wrapping rows); otherwise the
|
|
72
|
+
// tile fills its container width.
|
|
63
73
|
...(rootMaxWidth !== undefined
|
|
64
|
-
?
|
|
74
|
+
? rootFitContent
|
|
75
|
+
? { maxWidth: rootMaxWidth, alignSelf: "flex-start" }
|
|
76
|
+
: { maxWidth: rootMaxWidth, width: "100%" }
|
|
65
77
|
: {}),
|
|
66
78
|
...(rootBgColor ? { backgroundColor: rootBgColor } : {}),
|
|
67
79
|
...(rootBorderWidth !== undefined
|
|
@@ -101,10 +113,11 @@ const StyledControl = styled(View)<{
|
|
|
101
113
|
|
|
102
114
|
const StyledContent = styled(View)<{
|
|
103
115
|
contentGap: number
|
|
104
|
-
|
|
116
|
+
contentFlex: number
|
|
117
|
+
}>(({ contentGap, contentFlex }) => ({
|
|
105
118
|
flexDirection: "column",
|
|
106
119
|
gap: contentGap,
|
|
107
|
-
flex:
|
|
120
|
+
flex: contentFlex,
|
|
108
121
|
minWidth: 0
|
|
109
122
|
}))
|
|
110
123
|
|
|
@@ -144,6 +157,7 @@ export const Checkbox = React.forwardRef<View, CheckboxProps>(
|
|
|
144
157
|
defaultChecked = false,
|
|
145
158
|
onCheckedChange,
|
|
146
159
|
disabled = false,
|
|
160
|
+
fitContent = false,
|
|
147
161
|
style,
|
|
148
162
|
...rest
|
|
149
163
|
},
|
|
@@ -201,6 +215,7 @@ export const Checkbox = React.forwardRef<View, CheckboxProps>(
|
|
|
201
215
|
? parseTokenValue(checkbox.checkboxTile.size.maxWidth)
|
|
202
216
|
: undefined
|
|
203
217
|
}
|
|
218
|
+
rootFitContent={isTile && fitContent}
|
|
204
219
|
rootBgColor={
|
|
205
220
|
isTile
|
|
206
221
|
? isChecked
|
|
@@ -246,6 +261,7 @@ export const Checkbox = React.forwardRef<View, CheckboxProps>(
|
|
|
246
261
|
|
|
247
262
|
<StyledContent
|
|
248
263
|
contentGap={parseTokenValue(checkbox.spacing.content.gap)}
|
|
264
|
+
contentFlex={isTile && fitContent ? 0 : 1}
|
|
249
265
|
>
|
|
250
266
|
{label && (
|
|
251
267
|
<Typography
|
|
@@ -2,6 +2,7 @@ import React from "react"
|
|
|
2
2
|
import { View, ViewProps } from "react-native"
|
|
3
3
|
import styled from "@emotion/native"
|
|
4
4
|
import { useTheme } from "@emotion/react"
|
|
5
|
+
import type { CheckboxProps } from "./Checkbox"
|
|
5
6
|
|
|
6
7
|
type CheckboxGroupOwnProps = {
|
|
7
8
|
children: React.ReactNode
|
|
@@ -16,20 +17,26 @@ const parseTokenValue = (value: string): number => parseFloat(value)
|
|
|
16
17
|
const StyledGroup = styled(View)<{
|
|
17
18
|
groupDirection: "row" | "column"
|
|
18
19
|
groupGap: number
|
|
19
|
-
|
|
20
|
+
groupWrap: boolean
|
|
21
|
+
}>(({ groupDirection, groupGap, groupWrap }) => ({
|
|
20
22
|
flexDirection: groupDirection,
|
|
21
|
-
gap: groupGap
|
|
23
|
+
gap: groupGap,
|
|
24
|
+
flexWrap: groupWrap ? "wrap" : "nowrap"
|
|
22
25
|
}))
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* CheckboxGroup manages layout for a collection of Checkboxes.
|
|
26
29
|
*
|
|
30
|
+
* A `horizontal` group lays its tiles out in a wrapping row and makes them hug
|
|
31
|
+
* their content (via each Checkbox's `fitContent`) so they sit side by side;
|
|
32
|
+
* a `vertical` group stacks full-width tiles.
|
|
33
|
+
*
|
|
27
34
|
* @param {"horizontal" | "vertical"} [orientation="vertical"] - Layout direction.
|
|
28
35
|
*
|
|
29
36
|
* @example
|
|
30
|
-
* <CheckboxGroup>
|
|
31
|
-
* <Checkbox label="Chicken" />
|
|
32
|
-
* <Checkbox label="Beef" />
|
|
37
|
+
* <CheckboxGroup orientation="horizontal">
|
|
38
|
+
* <Checkbox variant="tile" label="Chicken" />
|
|
39
|
+
* <Checkbox variant="tile" label="Beef" />
|
|
33
40
|
* </CheckboxGroup>
|
|
34
41
|
*/
|
|
35
42
|
export const CheckboxGroup = React.forwardRef<View, CheckboxGroupProps>(
|
|
@@ -37,14 +44,31 @@ export const CheckboxGroup = React.forwardRef<View, CheckboxGroupProps>(
|
|
|
37
44
|
const theme = useTheme()
|
|
38
45
|
const { checkbox } = theme.tokens.components
|
|
39
46
|
|
|
47
|
+
const isHorizontal = orientation === "horizontal"
|
|
48
|
+
|
|
49
|
+
// Horizontal tiles hug their content so they can wrap; let an explicit
|
|
50
|
+
// per-Checkbox `fitContent` still win.
|
|
51
|
+
const renderChildren = (): React.ReactNode =>
|
|
52
|
+
isHorizontal
|
|
53
|
+
? React.Children.map(children, (child) => {
|
|
54
|
+
if (!React.isValidElement(child)) return child
|
|
55
|
+
const childProps = child.props as CheckboxProps
|
|
56
|
+
return React.cloneElement(
|
|
57
|
+
child as React.ReactElement<CheckboxProps>,
|
|
58
|
+
{ fitContent: childProps.fitContent ?? true }
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
: children
|
|
62
|
+
|
|
40
63
|
return (
|
|
41
64
|
<StyledGroup
|
|
42
65
|
ref={ref}
|
|
43
|
-
groupDirection={
|
|
66
|
+
groupDirection={isHorizontal ? "row" : "column"}
|
|
44
67
|
groupGap={parseTokenValue(checkbox.spacing.gap)}
|
|
68
|
+
groupWrap={isHorizontal}
|
|
45
69
|
{...rest}
|
|
46
70
|
>
|
|
47
|
-
{
|
|
71
|
+
{renderChildren()}
|
|
48
72
|
</StyledGroup>
|
|
49
73
|
)
|
|
50
74
|
}
|