Skip to content

Commit d29b6a8

Browse files
authored
Merge pull request #9868 from TylerAPfledderer/feat/new-avatar-theme
feat(Avatar): create component, theme, and stories
2 parents 14ea4de + a76ae3e commit d29b6a8

File tree

5 files changed

+245
-8
lines changed

5 files changed

+245
-8
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import {
2+
createMultiStyleConfigHelpers,
3+
cssVar,
4+
defineStyle,
5+
getToken,
6+
} from "@chakra-ui/react"
7+
import { avatarAnatomy } from "@chakra-ui/anatomy"
8+
import { avatarDefaultTheme, defineMergeStyles } from "./components.utils"
9+
import { pick } from "lodash"
10+
11+
const { defineMultiStyleConfig, definePartsStyle } =
12+
createMultiStyleConfigHelpers(avatarAnatomy.keys)
13+
14+
const { baseStyle: defaultBaseStyle, sizes: defaultSizes } = avatarDefaultTheme
15+
16+
const $border = cssVar("avatar-border-color", "transparent")
17+
const $mlBySize = cssVar("ml-by-size")
18+
19+
const baseStyleContainer = defineStyle((props) => {
20+
const primaryLowContrast = getToken(
21+
"colors",
22+
"primary.lowContrast"
23+
)(props.theme)
24+
25+
return defineMergeStyles(defaultBaseStyle?.(props).container, {
26+
[$border.variable]: "transparent",
27+
borderWidth: "1px",
28+
"&:hover, [data-peer]:hover ~ &": {
29+
boxShadow: `0.15em 0.15em 0 ${primaryLowContrast}`,
30+
},
31+
_focus: {
32+
outline: "4px solid",
33+
outlineColor: "primary.hover",
34+
outlineOffset: "-1px",
35+
},
36+
_active: {
37+
[$border.variable]: "colors.primary.hover",
38+
boxShadow: "none",
39+
"& img": {
40+
opacity: 0.7,
41+
},
42+
},
43+
"[role='group'] &": {
44+
[$border.variable]: "colors.background.base",
45+
_notLast: {
46+
marginLeft: $mlBySize.reference,
47+
},
48+
},
49+
})
50+
})
51+
52+
const baseStyleExessLabel = defineStyle((props) =>
53+
defineMergeStyles(defaultBaseStyle?.(props).excessLabel, {
54+
bg: "body.base",
55+
color: "background.base",
56+
ms: $mlBySize.reference,
57+
})
58+
)
59+
60+
const baseStyle = definePartsStyle((props) => ({
61+
container: baseStyleContainer(props),
62+
excessLabel: baseStyleExessLabel(props),
63+
}))
64+
65+
const USED_SIZES = ["xs", "sm", "md", "lg"] as const
66+
67+
const pickedDefaultSizes: { [k in (typeof USED_SIZES)[number]]?: object } =
68+
pick(defaultSizes, ...USED_SIZES)
69+
70+
const sizes = defineMergeStyles(pickedDefaultSizes, {
71+
xs: {
72+
group: {
73+
[$mlBySize.variable]: "space.-1",
74+
},
75+
excessLabel: {
76+
fontSize: "0.563rem",
77+
},
78+
},
79+
sm: {
80+
group: {
81+
[$mlBySize.variable]: "space.-2",
82+
},
83+
excessLabel: {
84+
fontSize: "sm",
85+
},
86+
},
87+
})
88+
89+
export const Avatar = defineMultiStyleConfig({
90+
baseStyle,
91+
// @ts-expect-error
92+
sizes,
93+
})

src/@chakra-ui/gatsby-plugin/components/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Avatar } from "./Avatar"
12
import { Badge } from "./Badge"
23
import { Button } from "./Button"
34
import { Link } from "./Link"
@@ -12,7 +13,6 @@ import { Switch } from "./Switch"
1213
import { Input } from "./Input"
1314
import {
1415
accordionDefaultTheme,
15-
avatarDefaultTheme,
1616
breadcrumbDefaultTheme,
1717
closeButtonDefaultTheme,
1818
codeDefaultTheme,
@@ -28,7 +28,7 @@ import {
2828

2929
export default {
3030
Accordion: accordionDefaultTheme,
31-
Avatar: avatarDefaultTheme,
31+
Avatar,
3232
Badge,
3333
Breadcrumb: breadcrumbDefaultTheme,
3434
Button,

src/@chakra-ui/gatsby-plugin/semanticTokens.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,6 @@ const semanticTokens = {
101101
"linear-gradient(102.7deg, rgba(185, 185, 241, 0.2) 0%, rgba(84, 132, 234, 0.2) 51.56%, rgba(58, 142, 137, 0.2) 100%)",
102102
},
103103
},
104-
shadows: {
105-
buttonHover: {
106-
_light: "4px 4px 0 var(--eth-colors-blue-100)",
107-
_dark: "4px 4px 0 #352313",
108-
},
109-
},
110104
}
111105

112106
export default semanticTokens
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import * as React from "react"
2+
import { Meta, StoryObj } from "@storybook/react"
3+
import { VStack, AvatarGroup, HStack } from "@chakra-ui/react"
4+
import Avatar from "."
5+
6+
type AvatarType = typeof Avatar
7+
8+
const meta: Meta<AvatarType> = {
9+
title: "Atoms / Media & Icons / Avatars",
10+
component: Avatar,
11+
}
12+
13+
export default meta
14+
15+
type Story = StoryObj<AvatarType>
16+
17+
export const Single: Story = {
18+
args: {
19+
name: "Dan Abrahmov",
20+
src: "https://bit.ly/dan-abramov",
21+
href: "#",
22+
},
23+
render: (args) => (
24+
<VStack spacing={4}>
25+
{["lg", "md", "sm", "xs"].map((size) => (
26+
<Avatar key={size} size={size} {...args} />
27+
))}
28+
</VStack>
29+
),
30+
}
31+
32+
export const Group: Story = {
33+
args: {
34+
name: "Dan Abrahmov",
35+
src: "https://bit.ly/dan-abramov",
36+
href: "#",
37+
},
38+
render: (args) => (
39+
<VStack spacing={4}>
40+
{["sm", "xs"].map((size) => (
41+
<AvatarGroup key={size} size={size} max={3}>
42+
<Avatar {...args} />
43+
<Avatar {...args} />
44+
<Avatar {...args} />
45+
<Avatar {...args} />
46+
</AvatarGroup>
47+
))}
48+
</VStack>
49+
),
50+
}
51+
52+
export const WithUsername: Story = {
53+
args: {
54+
name: "Dan Abrahmov",
55+
src: "https://bit.ly/dan-abramov",
56+
href: "#",
57+
label: "daneabrahmov",
58+
},
59+
render: (args) => (
60+
<HStack spacing={16}>
61+
<VStack>
62+
{["md", "sm"].map((size) => (
63+
<Avatar size={size} {...args} />
64+
))}
65+
</VStack>
66+
<VStack>
67+
{["md", "sm"].map((size) => (
68+
<Avatar size={size} direction="column" {...args} />
69+
))}
70+
</VStack>
71+
</HStack>
72+
),
73+
}

src/components/Avatar/index.tsx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import * as React from "react"
2+
import {
3+
Avatar as ChakraAvatar,
4+
AvatarProps,
5+
Center,
6+
CenterProps,
7+
FlexProps,
8+
forwardRef,
9+
Link,
10+
LinkBox,
11+
LinkOverlay,
12+
LinkProps,
13+
ThemingProps,
14+
} from "@chakra-ui/react"
15+
import { RxExternalLink } from "react-icons/rx"
16+
17+
type AssignAvatarProps = Required<Pick<AvatarProps, "name" | "src">> &
18+
AvatarProps
19+
20+
type AvatarLinkProps = AssignAvatarProps &
21+
Pick<LinkProps, "href"> &
22+
ThemingProps<"Avatar"> & {
23+
label?: string
24+
direction?: "column" | "row"
25+
}
26+
27+
const Avatar = forwardRef<AvatarLinkProps, "div" | "a">((props, ref) => {
28+
const { href, src, name, size = "md", label, direction = "row" } = props
29+
30+
const avatarProps = {
31+
src,
32+
name,
33+
size,
34+
}
35+
36+
const linkProps = {
37+
href,
38+
isExternal: true,
39+
color: "primary.base",
40+
}
41+
42+
if (label) {
43+
const _direction: "column-reverse" | "row-reverse" = `${direction}-reverse`
44+
return (
45+
<LinkBox as={Center} ref={ref} flexDirection={_direction} columnGap="1">
46+
<LinkOverlay
47+
as={Link}
48+
data-peer
49+
display="inline-flex"
50+
textDecoration="none"
51+
alignItems="center"
52+
gap="1"
53+
p="1"
54+
fontSize={size !== "md" ? "xs" : "sm"}
55+
zIndex="overlay"
56+
{...linkProps}
57+
>
58+
{label}
59+
<RxExternalLink />
60+
</LinkOverlay>
61+
<ChakraAvatar {...avatarProps} />
62+
</LinkBox>
63+
)
64+
}
65+
66+
return (
67+
<ChakraAvatar
68+
as={Link}
69+
ref={ref}
70+
showBorder
71+
{...avatarProps}
72+
{...linkProps}
73+
/>
74+
)
75+
})
76+
77+
export default Avatar

0 commit comments

Comments
 (0)