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-avatarbranch we created in the last tutorial.Under the
atoms/avatarfolder 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
ExcessLabelwhich 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
Excesslabel which same as theAvatarcomponent 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
childrenWithinMaxwe get the avatars within the max. - With
excesswe 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
spacingprop passed toAvatarGroup. Also notice theborderColorit checks if that individualAvatarhas a prop for borderColor if not then we apply the value passed toAvatarGroup.Also check to the
Flexwe passed direction = "row-reverse" because we reversed our children previously usingreverseChildrenfunction.
Story
- With the above our
AvatarGroupcomponent is completed. - Under the
src/components/atoms/avatar/avatar.stories.tsxfile 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.tsfile paste the following -
export * from "./avatar";
export * from "./avatar-group";
Now
npm run build.Under the folder
example/src/App.tsxwe can test ourAvatarcomponent. Copy paste the following and then runnpm run startfrom theexampledirectory -
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)