Skip to content

Commit

Permalink
feat(skeleton): adds skeleton component
Browse files Browse the repository at this point in the history
Adds a Skeleton component to provide a alternatives to the spinner.
 Also adds size props to the
Avatar.

fix #218
  • Loading branch information
stuarthendren committed Sep 5, 2021
1 parent 5b9cf95 commit 9619619
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 47 deletions.
18 changes: 5 additions & 13 deletions src/components/Avatar/Avatar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,13 @@ export const Default = () => (
)

/**
* Size can be controlled through the `css` prop, the default is `$7`.
* Size can be controlled through the `size` prop, with values `small`, `medium` (default) and `large`.
*/
export const Size = () => (
<Flex>
<Avatar
css={{ m: '$3', size: '$6' }}
alt="John Smith"
src="https://i.pravatar.cc"
/>
<Avatar css={{ m: '$3' }} alt="John Smith" src="https://i.pravatar.cc" />
<Avatar
css={{ m: '$3', size: '$8' }}
alt="John Smith"
src="https://i.pravatar.cc"
/>
<Flex css={{ gap: '$3' }}>
<Avatar size="small" alt="John Smith" src="https://i.pravatar.cc" />
<Avatar alt="John Smith" src="https://i.pravatar.cc" />
<Avatar size="large" alt="John Smith" src="https://i.pravatar.cc" />
</Flex>
)

Expand Down
29 changes: 19 additions & 10 deletions src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import { Fallback, Image, Root } from '@radix-ui/react-avatar'
import { VariantProps } from '@stitches/react'
import React from 'react'
import type { CSS } from '../../stitches.config'
import { styled } from '../../stitches.config'

interface AvatarProps {
src?: string
alt?: string
css?: CSS
color?: CSS['color']
backgroundColor?: CSS['backgroundColor']
}

const StyledRoot = styled(Root, {
display: 'inline-flex',
alignItems: 'center',
Expand All @@ -20,8 +13,16 @@ const StyledRoot = styled(Root, {
userSelect: 'none',
borderRadius: '$round',

width: '$7',
height: '$7',
variants: {
size: {
small: { size: '$5' },
medium: { size: '$7' },
large: { size: '$9' },
},
},
defaultVariants: {
size: 'medium',
},
})

const StyledImage = styled(Image, {
Expand All @@ -38,6 +39,14 @@ const StyledFallback = styled(Fallback, {
justifyContent: 'center',
})

interface AvatarProps extends VariantProps<typeof StyledRoot> {
src?: string
alt?: string
css?: CSS
color?: CSS['color']
backgroundColor?: CSS['backgroundColor']
}

/**
* The Avatar should be used for profile images. If an image is not available initials can be used.
*/
Expand Down
29 changes: 22 additions & 7 deletions src/components/Skeleton/Skeleton.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'

import { Flex } from '../Flex'
import { Story, Meta } from '@storybook/react'
import { Skeleton } from '.'

Expand All @@ -11,23 +11,38 @@ export default {
const Template: Story = (args) => <Skeleton {...args} />

export const Primary = Template.bind({})
export const Avatar = Template.bind({})
Avatar.args = {
variant: 'avatar',
}

export const Text = Template.bind({})
Text.args = {
variant: 'text',
}

export const Title = Template.bind({})
Title.args = {
variant: 'title',
}

/** An alternative animation, `pulse`, is available */
export const Animation = Template.bind({})
Animation.args = {
variant: 'title',
animation: 'pulse',
}

/** The `avatar` variant also supports an additional size prop to reflect the sizing on the `Avatar` component. */
export const Avatar = () => (
<Flex css={{ gap: '$3' }}>
<Skeleton variant="avatar" size="small" />
<Skeleton variant="avatar" />
<Skeleton variant="avatar" size="large" />
</Flex>
)

/** The `button` variant also supports an additional size prop to reflect the sizing on the `Button` component. */
export const Buttons = () => (
<>
<Flex css={{ gap: '$3' }}>
<Skeleton variant="button" size="small" />
<Skeleton variant="button" />
<Skeleton variant="button" size="large" />
</>
</Flex>
)
74 changes: 57 additions & 17 deletions src/components/Skeleton/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,55 @@
import type * as Polymorphic from '@radix-ui/react-polymorphic'
import React, { forwardRef } from 'react'
import type { CSSProps, VariantProps } from '../../stitches.config'
import { styled, keyframes } from '../../stitches.config'
import { keyframes, styled } from '../../stitches.config'

const DEFAULT_TAG = 'div'

const BASE_COLOR = '$grey5'
const HIGHLIGHT_COLOR = '$grey7'

export const ripple = keyframes({
'0%': {
backgroundPosition: '-200px 0',
backgroundPosition: '-1000px 0',
},
'100%': {
backgroundPosition: 'calc(200px + 100%) 0',
backgroundPosition: '1000px 0',
},
})

export const pulse = keyframes({
'0%': { opacity: 0 },
'50%': { opacity: '100%' },
'100%': { opacity: '100%' },
})

/**
* StyledSkeleton base component
*/
export const Skeleton = styled(DEFAULT_TAG, {
backgroundColor: '$grey4',
backgroundColor: BASE_COLOR,
position: 'relative',
overflow: 'hidden',

'&::after': {
animationName: `${pulse}`,
animationDuration: '500ms',
animationDirection: 'alternate',
animationIterationCount: 'infinite',
animationTimingFunction: 'ease-in-out',
backgroundColor: '$slate6',
borderRadius: 'inherit',
bottom: 0,
content: '""',
left: 0,
position: 'absolute',
right: 0,
top: 0,
},

variants: {
variant: {
avatar: {
borderRadius: '$round',
size: '$7',
},
text: {
height: '$1',
height: '$4',
},
title: {
height: '$5',
},
heading: {
height: '$3',
height: '$7',
},
button: {
borderRadius: '$default',
Expand All @@ -69,6 +62,38 @@ export const Skeleton = styled(DEFAULT_TAG, {
default: {},
large: {},
},
animation: {
ripple: {
'&::after': {
animationName: `${ripple}`,
animationDuration: '2s',
animationDirection: 'normal',
animationIterationCount: 'infinite',
animationTimingFunction: 'ease-in-out',
backgroundImage: `linear-gradient(
to right,
${BASE_COLOR} 4%,
${HIGHLIGHT_COLOR} 25%,
${BASE_COLOR} 35%
)`,
backgroundSize: '1000px 100%',
backgroundRepeat: 'no-repeat',
borderRadius: 'inherit',
lineHeight: '$default',
width: '100%',
},
},
pulse: {
'&::after': {
animationName: `${pulse}`,
animationDuration: '1s',
animationDirection: 'alternate',
animationIterationCount: 'infinite',
animationTimingFunction: 'ease-in-out',
backgroundColor: HIGHLIGHT_COLOR,
},
},
},
},
compoundVariants: [
{
Expand All @@ -87,8 +112,23 @@ export const Skeleton = styled(DEFAULT_TAG, {
width: '$9',
},
},
{
variant: 'avatar',
size: 'small',
css: {
size: '$5',
},
},
{
variant: 'avatar',
size: 'large',
css: {
size: '$9',
},
},
],
defaultVariants: {
variant: 'text',
animation: 'ripple',
},
})

0 comments on commit 9619619

Please sign in to comment.