Skip to content

Commit

Permalink
feat(skeleton): Skeleton 컴포넌트 추가 (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-moon authored Aug 8, 2021
1 parent 22bd2c6 commit 6d93782
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/alpha-storybook-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
storybook-publish:
if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.event.head_commit.message, '(docs)')"
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-ui-kit-lib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
ui-kit-publish:
if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.event.head_commit.message, '(ui-kit)')"
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/live-storybook-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
storybook-publish:
if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.event.head_commit.message, '(docs)')"
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
Expand Down
33 changes: 33 additions & 0 deletions src/components/Skeleton/Circle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { CombineElementProps } from 'src/types/utils';

type Props = Omit<
CombineElementProps<
'div',
{
width: number;
height: number;
backgroundStyle: string;
}
>,
'role'
>;

const Circle = ({ width, height, backgroundStyle, style, ...rest }: Props) => {
return (
<div
style={{
display: 'inline-block',
width: `${width}px`,
height: `${height}px`,
background: backgroundStyle,
borderRadius: '50%',
...style,
}}
role="img"
{...rest}
/>
);
};

export default Circle;
32 changes: 32 additions & 0 deletions src/components/Skeleton/Rect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { CombineElementProps } from 'src/types/utils';

type Props = Omit<
CombineElementProps<
'div',
{
width: number;
height: number;
backgroundStyle: string;
}
>,
'role'
>;

const Rect = ({ width, height, backgroundStyle, style, ...rest }: Props) => {
return (
<div
style={{
display: 'inline-block',
width: `${width}px`,
height: `${height}px`,
background: backgroundStyle,
...style,
}}
role="img"
{...rest}
/>
);
};

export default Rect;
45 changes: 45 additions & 0 deletions src/components/Skeleton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { CombineElementProps } from 'src/types/utils';
import { colors } from 'src/constants/colors';
import { useAnimateGradient } from './useAnimateGradient';
import Rect from './Rect';
import Circle from './Circle';

type Props = Omit<
CombineElementProps<
'div',
{
type?: 'rect' | 'circle';
width: number;
height: number;
backgroundColor?: string;
foregroundColor?: string;
}
>,
'role'
>;

const Skeleton = ({
type = 'rect',
width,
height,
backgroundColor = colors.gray30,
foregroundColor = colors.gray20,
...rest
}: Props) => {
const gradientStyle = useAnimateGradient({
foregroundColor,
backgroundColor,
});

if (type === 'rect') {
return <Rect width={width} height={height} backgroundStyle={gradientStyle} {...rest} />;
} else {
return <Circle width={width} height={height} backgroundStyle={gradientStyle} {...rest} />;
}
};

export default Skeleton;

Skeleton.Circle = Circle;
Skeleton.Rect = Rect;
44 changes: 44 additions & 0 deletions src/components/Skeleton/useAnimateGradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useCallback } from 'react';
import { useState } from 'react';
import { useAnimationFrame } from 'src/hooks/useAnimationFrame';

const gradientDegree = 90;

export interface GradientOptions {
foregroundColor: string;
backgroundColor: string;
progress: number;
width: number;
}
export function calcGradient({
foregroundColor,
backgroundColor,
progress,
width,
}: GradientOptions) {
const startPosition = Math.max(progress - width, 0);
const endPosition = Math.min(progress + width, 100);

return `linear-gradient(${gradientDegree}deg, ${backgroundColor} ${startPosition}%, ${foregroundColor} ${progress}%, ${backgroundColor} ${endPosition}%)`;
}

interface Options {
foregroundColor: string;
backgroundColor: string;
}
export function useAnimateGradient({ foregroundColor, backgroundColor }: Options) {
const [progress, setProgress] = useState(-200);

const updateProgress = useCallback(() => {
setProgress((prev) => (prev >= 200 ? -200 : prev + 3));
}, []);

useAnimationFrame(updateProgress);

return calcGradient({
foregroundColor,
backgroundColor,
progress,
width: 20,
});
}
21 changes: 21 additions & 0 deletions src/hooks/useAnimationFrame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect, useRef } from 'react';

export function useAnimationFrame(callback: () => void) {
const animateRequestRef = useRef<number>();

useEffect(() => {
const animate = () => {
callback();
console.log('callback');
animateRequestRef.current = requestAnimationFrame(animate);
};

animate();

return () => {
if (animateRequestRef.current != null) {
cancelAnimationFrame(animateRequestRef.current);
}
};
}, [callback]);
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { colors } from './constants/colors';
export { default as Icon } from './components/Icon';
export { default as Shadow } from './components/Shadow';
export { default as Spacing } from './components/Spacing';
export { default as Skeleton } from './components/Skeleton';
export { default as useProgress } from './hooks/useProgress';
export { default as useResizeObserver } from './hooks/useResizeObserver';
export { useOverlay } from './contexts/Overlay';
Expand Down
18 changes: 18 additions & 0 deletions src/stories/Components/Skeleton/index.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Skeleton, Spacing } from 'src';
import { Meta, Story, Canvas } from '@storybook/addon-docs/blocks';

<Meta title="components/Skeleton" components={Skeleton} />

# Skeleton

Skeleton 컴포넌트는

<Canvas>
<Story name="Skeleton">
<div style={{ display: 'flex', flexDirection: 'column' }}>
<Skeleton width={50} height={50} type="circle" />
<Spacing size={10} />
<Skeleton width={100} height={20} />
</div>
</Story>
</Canvas>

0 comments on commit 6d93782

Please sign in to comment.