mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-06-15 17:19:10 +00:00
feat: add skeleton masthead and generic loading page (#182)
* feat: add skeleton masthead and generic loading page * add generic loader to picker page * smooth out spinner, add no-motion state
This commit is contained in:
parent
fa85b30cf0
commit
e0fcfc310e
28 changed files with 380 additions and 30 deletions
|
@ -7,3 +7,4 @@ export default {
|
|||
|
||||
export const Dark = () => <DotOverlay />;
|
||||
export const Light = () => <DotOverlay light />;
|
||||
export const Skeleton = () => <DotOverlay skeleton />;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { animateOpacity } from '@roleypoly/design-system/atoms/placeholder';
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
|
@ -33,6 +34,22 @@ const DotOverlayLight = styled(dotOverlayBase)`
|
|||
);
|
||||
`;
|
||||
|
||||
export const DotOverlay = ({ light }: { light?: boolean }) => {
|
||||
return light ? <DotOverlayLight /> : <DotOverlayDark />;
|
||||
const DotOverlaySkeleton = styled(DotOverlayDark)`
|
||||
${animateOpacity}
|
||||
`;
|
||||
|
||||
export const DotOverlay = ({
|
||||
light,
|
||||
skeleton,
|
||||
}: {
|
||||
light?: boolean;
|
||||
skeleton?: boolean;
|
||||
}) => {
|
||||
return skeleton ? (
|
||||
<DotOverlaySkeleton />
|
||||
) : light ? (
|
||||
<DotOverlayLight />
|
||||
) : (
|
||||
<DotOverlayDark />
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { Hero } from '@roleypoly/design-system/atoms/hero';
|
||||
import { LoadingFill } from './Loading';
|
||||
export default {
|
||||
title: 'Atoms/Loading Text',
|
||||
component: LoadingFill,
|
||||
};
|
||||
|
||||
export const loading = (args) => (
|
||||
<Hero>
|
||||
<LoadingFill {...args} />
|
||||
</Hero>
|
||||
);
|
30
packages/design-system/atoms/loading-text/Loading.tsx
Normal file
30
packages/design-system/atoms/loading-text/Loading.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
const loadingTexts = [
|
||||
'Loading Roleypoly...',
|
||||
'Reticulating splines...',
|
||||
'Mining cryptocoins...',
|
||||
'Going to Mars...',
|
||||
'Building the Box Signature Edition...',
|
||||
'Hiring a new CEO...',
|
||||
'Firing the new CEO...',
|
||||
'Doing a calculation...',
|
||||
'Doin a fixy boi...',
|
||||
'Feeling like a plastic bag...',
|
||||
'Levelling up...',
|
||||
'Your Roleypoly is evolving!!!',
|
||||
'Adding subtitles...',
|
||||
'Rolling... Rolling...',
|
||||
];
|
||||
|
||||
export const LoadingFill = (props: { forceIndex?: keyof typeof loadingTexts }) => {
|
||||
const useEasterEgg = Math.floor(Math.random() * 10) === 0;
|
||||
const index =
|
||||
props.forceIndex !== undefined
|
||||
? props.forceIndex
|
||||
: useEasterEgg
|
||||
? Math.floor(Math.random() * loadingTexts.length)
|
||||
: 0;
|
||||
|
||||
const text = loadingTexts[index];
|
||||
|
||||
return <>{text}</>;
|
||||
};
|
1
packages/design-system/atoms/loading-text/index.ts
Normal file
1
packages/design-system/atoms/loading-text/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Loading';
|
|
@ -0,0 +1,13 @@
|
|||
import { palette } from '../colors';
|
||||
import { PlaceholderBox } from './Placeholder';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/Placeholder',
|
||||
component: PlaceholderBox,
|
||||
args: {
|
||||
firstColor: palette.taupe100,
|
||||
secondColor: palette.taupe300,
|
||||
},
|
||||
};
|
||||
|
||||
export const placeholderBox = (args) => <PlaceholderBox {...args} />;
|
55
packages/design-system/atoms/placeholder/Placeholder.tsx
Normal file
55
packages/design-system/atoms/placeholder/Placeholder.tsx
Normal file
|
@ -0,0 +1,55 @@
|
|||
import styled, { css, keyframes } from 'styled-components';
|
||||
import { palette } from '../colors';
|
||||
|
||||
export const fadeInOut = keyframes`
|
||||
from {
|
||||
background-color: var(--placeholder-first-color);
|
||||
}
|
||||
to {
|
||||
background-color: var(--placeholder-second-color);
|
||||
}
|
||||
`;
|
||||
|
||||
export const animateFade = (firstColor?: string, secondColor?: string) => css`
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
animation: ${fadeInOut} 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
--placeholder-first-color: ${firstColor};
|
||||
--placeholder-second-color: ${secondColor};
|
||||
`;
|
||||
|
||||
type PlaceholderProps = {
|
||||
forceReduceMotion?: boolean;
|
||||
firstColor?: string;
|
||||
secondColor?: string;
|
||||
};
|
||||
|
||||
export const PlaceholderBox = styled.div<PlaceholderProps>`
|
||||
width: 7em;
|
||||
height: 1em;
|
||||
background-color: ${(props) => props.firstColor || palette.taupe200};
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
top: 0.2em;
|
||||
${(props) =>
|
||||
props.secondColor &&
|
||||
!props.forceReduceMotion &&
|
||||
animateFade(props.firstColor, props.secondColor)}
|
||||
`;
|
||||
|
||||
export const opacityInOut = keyframes`
|
||||
from {
|
||||
opacity: 0.6;
|
||||
}
|
||||
to {
|
||||
opacity: 0.3;
|
||||
}
|
||||
`;
|
||||
|
||||
export const animateOpacity = css`
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
animation: ${opacityInOut} 5s ease-in-out infinite alternate;
|
||||
}
|
||||
`;
|
1
packages/design-system/atoms/placeholder/index.ts
Normal file
1
packages/design-system/atoms/placeholder/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Placeholder';
|
13
packages/design-system/atoms/spinner/Spinner.stories.tsx
Normal file
13
packages/design-system/atoms/spinner/Spinner.stories.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { Hero } from '@roleypoly/design-system/atoms/hero';
|
||||
import { Spinner } from './Spinner';
|
||||
|
||||
export default {
|
||||
title: 'Atoms/Spinner',
|
||||
component: Spinner,
|
||||
};
|
||||
|
||||
export const spinner = (args) => (
|
||||
<Hero>
|
||||
<Spinner {...args} />
|
||||
</Hero>
|
||||
);
|
78
packages/design-system/atoms/spinner/Spinner.tsx
Normal file
78
packages/design-system/atoms/spinner/Spinner.tsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
import styled, { css, keyframes } from 'styled-components';
|
||||
import { palette } from '../colors';
|
||||
|
||||
type SpinnerProps = {
|
||||
size?: number;
|
||||
reverse?: boolean;
|
||||
color?: string;
|
||||
speed?: number;
|
||||
};
|
||||
|
||||
const spinnerKeyframes = keyframes`
|
||||
0%, 100% {
|
||||
border-width: 0.5px;
|
||||
border-right-width: 3px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
25% {
|
||||
border-width: 0.5px;
|
||||
border-top-width: 3px;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
50% {
|
||||
border-width: 0.5px;
|
||||
border-left-width: 3px;
|
||||
border-right-width: 0;
|
||||
}
|
||||
75% {
|
||||
border-width: 0.5px;
|
||||
border-bottom-width: 3px;
|
||||
border-top-width: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const SpinnerStyled = styled.div<Required<SpinnerProps>>`
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
animation: ${spinnerKeyframes} ${(props) => props.speed}s linear infinite
|
||||
${(props) => (props.reverse ? `reverse` : '')};
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
|
||||
border: 0.5px solid ${(props) => props.color};
|
||||
box-sizing: border-box;
|
||||
width: ${(props) => props.size}px;
|
||||
height: ${(props) => props.size}px;
|
||||
border-radius: ${(props) => props.size * 0.7}px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
${(props) =>
|
||||
props.reverse
|
||||
? css`
|
||||
border-right-width: 0;
|
||||
border-left-width: 3px;
|
||||
`
|
||||
: css`
|
||||
border-right-width: 3px;
|
||||
border-left-width: 0;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const Spinner = (
|
||||
props: SpinnerProps & { innerColor?: string; outerColor?: string }
|
||||
) => (
|
||||
<SpinnerStyled
|
||||
size={props.size || 50}
|
||||
color={props.outerColor || palette.taupe400}
|
||||
reverse={!!props.reverse}
|
||||
speed={props.speed || 1}
|
||||
>
|
||||
<SpinnerStyled
|
||||
size={(props.size || 50) * 0.75}
|
||||
color={props.innerColor || palette.taupe100}
|
||||
reverse={!props.reverse}
|
||||
speed={(props.speed || 1) * 1.25}
|
||||
/>
|
||||
</SpinnerStyled>
|
||||
);
|
1
packages/design-system/atoms/spinner/index.ts
Normal file
1
packages/design-system/atoms/spinner/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Spinner';
|
Loading…
Add table
Add a link
Reference in a new issue