Skip to content

Commit

Permalink
feat: allow SVGs in Avatar #146 (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
christianblandford committed Jul 14, 2022
1 parent f44da69 commit 8ba2767
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 9 deletions.
79 changes: 79 additions & 0 deletions src/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,82 @@ Placeholder.args = {
shape: 'circle',
letters: 'Y',
}

const reactLogoSvg = (
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="600px"
height="600px"
viewBox="0 0 600 600"
enable-background="new 0 0 600 600"
>
<g id="Layer_2">
<path
fill="none"
stroke="#E91E63"
stroke-width="24"
stroke-miterlimit="10"
d="M371.987,227.641
c47.628,47.628,85.039,98.708,106.914,143.552c26.358,54.033,30.096,99.722,11.103,118.714
c-19.793,19.793-68.267,15.884-125.731-12.979c-43.445-21.821-92.031-59.119-137.242-104.331
c-46.354-46.354-84.95-95.545-106.667-139.816c-27.48-56.023-30.057-103.743-10.643-123.157
c18.838-18.839,63.248-16.056,116.694,9.757C271.574,141.193,323.895,179.548,371.987,227.641z"
/>
<path
fill="none"
stroke="#E91E63"
stroke-width="24"
stroke-miterlimit="10"
d="M272.931,201.125
c65.052-17.465,127.989-24.354,177.767-20.902c59.974,4.16,101.42,23.747,108.385,49.688
c7.259,27.033-20.345,67.073-74.054,102.434c-40.608,26.733-97.189,50.188-158.941,66.769
c-63.312,16.998-125.207,25.858-174.408,22.553c-62.26-4.181-104.884-25.789-112.004-52.306
c-6.907-25.731,17.688-62.811,66.75-96.214C147.879,244.923,207.243,218.761,272.931,201.125z"
/>
<path
fill="none"
stroke="#E91E63"
stroke-width="24"
stroke-miterlimit="10"
d="M200.469,273.707
c17.357-65.081,42.82-123.05,70.671-164.45c33.556-49.882,71.225-76.008,97.178-69.086c27.045,7.212,47.949,51.123,51.76,115.315
c2.883,48.533-5.055,109.266-21.531,171.046c-16.892,63.341-40.126,121.389-67.562,162.365
c-34.716,51.852-74.723,77.988-101.252,70.913c-25.743-6.865-45.584-46.692-50.021-105.881
C175.963,403.92,182.944,339.424,200.469,273.707z"
/>
</g>
<g id="Layer_3">
<path
fill="#E91E63"
d="M300,349.369c-1.019,0-1.881-0.353-2.586-1.058l-36.679-35.386c-0.392-0.313-0.931-0.822-1.617-1.528
c-0.686-0.705-1.773-1.988-3.262-3.851c-1.489-1.86-2.822-3.771-3.997-5.73s-2.224-4.33-3.145-7.112
c-0.92-2.782-1.381-5.486-1.381-8.111c0-8.621,2.488-15.361,7.465-20.221c4.977-4.859,11.854-7.289,20.631-7.289
c2.43,0,4.909,0.421,7.436,1.264c2.527,0.843,4.879,1.979,7.054,3.41c2.174,1.43,4.046,2.772,5.613,4.026s3.057,2.586,4.467,3.997
c1.411-1.411,2.899-2.743,4.467-3.997c1.568-1.254,3.438-2.596,5.614-4.026c2.175-1.431,4.525-2.567,7.054-3.41
c2.527-0.842,5.006-1.264,7.435-1.264c8.778,0,15.655,2.43,20.632,7.289c4.978,4.859,7.466,11.6,7.466,20.221
c0,8.66-4.487,17.477-13.461,26.451l-36.619,35.268C301.881,349.017,301.019,349.369,300,349.369z"
/>
</g>
</svg>
)

export const WithSvg = Template.bind({})
WithSvg.args = {
shape: 'circle',
children: reactLogoSvg,
border: true,
}

export const MultipleChildren: Story<Omit<AvatarProps, 'children'>> = (
args
) => {
return (
<Avatar {...args}>
<span className="absolute text-center font-bold">React</span>
{reactLogoSvg}
</Avatar>
)
}
48 changes: 39 additions & 9 deletions src/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'
import { isSingleStringChild } from '../utils'

import AvatarGroup from './AvatarGroup'

Expand All @@ -22,6 +23,7 @@ export type AvatarProps = React.HTMLAttributes<HTMLDivElement> &
borderColor?: ComponentColor
online?: boolean
offline?: boolean
children?: React.ReactNode
}

const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
Expand All @@ -38,6 +40,7 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
offline,
dataTheme,
className,
children,
...props
},
ref
Expand Down Expand Up @@ -81,6 +84,41 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
const customImgDimension =
typeof size === 'number' ? { width: size, height: size } : {}

const renderAvatarContents = () => {
// Base case, if src is provided, render img
if (src) {
return (
<div className={imgClasses} style={customImgDimension}>
<img src={src} />
</div>
)
}
// Render a text avatar if letters are provided, or a single child that is a string
else if (letters || isSingleStringChild(children)) {
return (
<div className={placeholderClasses} style={customImgDimension}>
<span>{letters ? letters : children}</span>
</div>
)
}
// Render if a single, not string child was provided (allows for SVGs) and merges classes and styles
else if (React.Children.count(children) === 1) {
const firstChild = React.Children.only(children) as React.ReactElement
return React.cloneElement(firstChild, {
className: twMerge(imgClasses, firstChild.props.className),
style: { ...customImgDimension, ...firstChild.props.style },
})
}
// Render a wrapping div around all children if there is more than one child.
else {
return (
<div className={imgClasses} style={customImgDimension}>
{children}
</div>
)
}
}

return (
<div
aria-label="Avatar photo"
Expand All @@ -89,15 +127,7 @@ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
className={containerClasses}
ref={ref}
>
{src ? (
<div className={imgClasses} style={customImgDimension}>
<img src={src} />
</div>
) : (
<div className={placeholderClasses} style={customImgDimension}>
<span>{letters}</span>
</div>
)}
{renderAvatarContents()}
</div>
)
}
Expand Down
10 changes: 10 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,13 @@ export const wrapWithElementIfInvalid = ({
})
}
}

// Returns true if there is a single, string child element
export const isSingleStringChild = (children?: React.ReactNode) => {
return (
children &&
React.Children.count(children) === 1 &&
React.isValidElement(children) &&
typeof children.props.children === 'string'
)
}

0 comments on commit 8ba2767

Please sign in to comment.