Introduction
Let us continue building our chakra components using styled-components
& styled-system
. In this tutorial we will be cloning the Chakra UI AvatarGroup
component.
- I would like you to first check the chakra docs for avatar.
- All the code for this tutorial can be found under the atom-avatar branch here.
Prerequisite
Please check the previous post where we have completed the Avatar
component. Also please check the Chakra Avatar Component code here. The theme / styles are here In this tutorial we will -
- Create an AvatarGroup component.
Setup
We will continue working in the
atom-avatar
branch we created in the last tutorial.Under the
atoms/avatar
folder create a new fileavatar-group.tsx
AvatarGroup Component
AvatarGroup displays a number of avatars grouped together in a stack.
Let us first create types for our props -
import * as React from "react";
import styled from "styled-components";
import { variant, SpaceProps, ResponsiveValue } from "styled-system";
import { filterUndefined, getValidChildren } from "../../../utils";
import { Flex, FlexProps } from "../layout";
import { AvatarSizes } from "./avatar";
interface AvatarGroupOptions {
spacing?: SpaceProps["margin"];
max?: number;
s?: ResponsiveValue<AvatarSizes>;
}
export interface AvatarGroupProps
extends AvatarGroupOptions,
Omit<FlexProps, "size"> {}
-
We extend FlexProps and accept additional props like -
- spacing - The space between the avatars in the group.
- max - The maximum number of visible avatars in the group.
- s - The size for the Avatars.
Next we will create a new styled component called
ExcessLabel
which will display the number +3 or +2 -
const ExcessLabel = styled(Flex)<AvatarGroupProps>`
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
text-transform: uppercase;
font-weight: medium;
position: relative;
flex-shrink: 0;
border-radius: 9999px;
background-color: ${({ theme }) => theme.colors.gray200};
${variant({
prop: "s",
variants: {
"2xs": {
size: "1rem",
},
xs: {
size: "1.5rem",
},
sm: {
size: "2rem",
},
md: {
size: "3rem",
},
lg: {
size: "4rem",
},
xl: {
size: "6rem",
},
"2xl": {
size: "8rem",
},
full: {
size: "100%",
},
},
})};
`;
Notice that we have a variant for the
Excess
label which same as theAvatar
component so that the styles remain the same.Next let us create our AvatarGroup Component -
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
(props, ref) => {
const {
borderColor,
max,
spacing = "-0.75rem",
borderRadius = "9999px",
s = "md",
children,
...delegated
} = props;
const validChildren = getValidChildren(children);
const childrenWithinMax = max ? validChildren.slice(0, max) : validChildren;
const excess = max != null && validChildren.length - max;
const reversedChildren = childrenWithinMax.reverse();
const clones = reversedChildren.map((child, index) => {
const isFirstAvatar = index === 0;
const childProps = {
marginEnd: isFirstAvatar ? 0 : spacing,
s,
borderColor: child.props.borderColor ?? borderColor,
showBorder: true,
};
return React.cloneElement(child, filterUndefined(childProps));
});
return (
<Flex
ref={ref}
role="group"
direction="row-reverse"
align="center"
justify="flex-end"
{...delegated}
>
{excess > 0 && (
<ExcessLabel
s={s}
borderRadius={borderRadius}
marginStart={spacing}
>{`+${excess}`}</ExcessLabel>
)}
{clones}
</Flex>
);
}
);
- With the
childrenWithinMax
we get the avatars within the max. - With
excess
we get the remaining avatar count. With
reverseChildren
- reversing the children is a great way to avoid using zIndex to overlap the avatars.Then we map over the visible avatars and apply the props, main thing here is marginEnd which is controlled by
spacing
prop passed toAvatarGroup
. Also notice theborderColor
it checks if that individualAvatar
has a prop for borderColor if not then we apply the value passed toAvatarGroup
.Also check to the
Flex
we passed direction = "row-reverse" because we reversed our children previously usingreverseChildren
function.
Story
- With the above our
AvatarGroup
component is completed. - Under the
src/components/atoms/avatar/avatar.stories.tsx
file we add the following for default story -
<Stack>
<AvatarGroup s="md" max={2}>
<Avatar name="Ryan Florence" src="https://bit.ly/ryan-florence" />
<Avatar name="Segun Adebayo" src="https://bit.ly/sage-adebayo" />
<Avatar name="Kent Dodds" src="https://bit.ly/kent-c-dodds" />
<Avatar name="Prosper Otemuyiwa" src="https://bit.ly/prosper-baba" />
<Avatar name="Christian Nwamba" src="https://bit.ly/code-beast" />
</AvatarGroup>
</Stack>
Build the Library
- Under the
avatar/index.ts
file paste the following -
export * from "./avatar";
export * from "./avatar-group";
Now
npm run build
.Under the folder
example/src/App.tsx
we can test ourAvatar
component. Copy paste the following and then runnpm run start
from theexample
directory -
import * as React from "react";
import { Stack, Avatar, AvatarBadge, AiOutlineUser } from "chakra-ui-clone";
export function App() {
return (
<Stack m="1rem" direction="column" spacing="xl">
<Stack>
<Avatar src="https://bit.ly/broken-link" />
<Avatar name="Ryan Florence" src="https://bit.ly/ryan-florence" />
<Avatar name="Segun Adebayo" />
<Avatar name="Kent Dodds" src="https://bit.ly/kent-c-dodds" />
<Avatar name="Prosper Otemuyiwa" src="https://bit.ly/prosper-baba" />
<Avatar name="Christian Nwamba" src="https://bit.ly/code-beast" />
</Stack>
<Stack>
<Avatar>
<AvatarBadge size="1.25em" bg="green500" />
</Avatar>
<Avatar>
<AvatarBadge borderColor="papayawhip" bg="tomato" size="1.25em" />
</Avatar>
</Stack>
<Stack>
<Avatar bg="red500" icon={<AiOutlineUser fontSize="1.5rem" />} />
<Avatar bg="teal500" />
</Stack>
<Stack>
<AvatarGroup s="md" max={2}>
<Avatar name="Ryan Florence" src="https://bit.ly/ryan-florence" />
<Avatar name="Segun Adebayo" src="https://bit.ly/sage-adebayo" />
<Avatar name="Kent Dodds" src="https://bit.ly/kent-c-dodds" />
<Avatar name="Prosper Otemuyiwa" src="https://bit.ly/prosper-baba" />
<Avatar name="Christian Nwamba" src="https://bit.ly/code-beast" />
</AvatarGroup>
</Stack>
</Stack>
);
}
Summary
There you go guys in this tutorial we created Avatar
component just like chakra ui. You can find the code for this tutorial under the atom-avatar branch here. In the next tutorial we will make our last component for this series Alert
component. Until next time PEACE.
Top comments (0)