diff --git a/README.md b/README.md index 14f1418..92288d1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@
-#
🌊 Awesome React Component Library 💦
+#
🐳 Awesome React Component Library 🐳
+ +

+ + License + + + npm downloads + + Github Stars + + +

+

+ + + +

## `Installing WAP-UI` diff --git a/package.json b/package.json index 0c58a6f..5fa778b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wap-ui", - "version": "1.1.8", + "version": "1.1.9", "repository": "https://github.com/pknu-wap/2022_2_WAP_WEB_TEAM1.git", "author": "neko113 ", "license": "MIT", diff --git a/packages/components/Loader/Loader.stories.tsx b/packages/components/Loader/Loader.stories.tsx new file mode 100644 index 0000000..b42b91b --- /dev/null +++ b/packages/components/Loader/Loader.stories.tsx @@ -0,0 +1,76 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { Loader, LoaderProps } from './Loader'; +import React from 'react'; +import styled from '@emotion/styled'; +import { BarsProps } from './Loaders/Bars/Bars'; +import { DotsProps } from './Loaders/Dots/Dots'; + +export default { + title: 'Components/Loader', + component: Loader, +} as ComponentMeta; + +const Template: ComponentStory = (args: LoaderProps) => { + return ( + + + + ); +}; + +export const Default = Template.bind({}); + +export const Spinner = ({ size }: Pick) => { + return ( + + + + + + + + + + ); +}; + +export const Bars = ({ size }: Pick) => { + return ( + + + + + + + + + + ); +}; + +export const Dots = ({ size }: Pick) => { + return ( + + + + + + + + + + ); +}; +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 3rem; + height: 100vh; + padding: 3rem; +`; + +const FlexRow = styled.div` + display: flex; + flex-direction: row; + gap: 1rem; +`; diff --git a/packages/components/Loader/Loader.tsx b/packages/components/Loader/Loader.tsx new file mode 100644 index 0000000..f4b8ef3 --- /dev/null +++ b/packages/components/Loader/Loader.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { NormalColorType } from '../../theme/types'; +import { Bars } from './Loaders/Bars/Bars'; +import { Dots } from './Loaders/Dots/Dots'; +import { Spinner } from './Loaders/Spinner/Spinner'; + +export interface LoaderProps { + /** + * @default 'md' + */ + size?: 'sm' | 'md' | 'lg'; + /** + * @default 'primary' + */ + color?: NormalColorType; + /** + * @default 'spinner' + */ + type?: 'spinner' | 'bars' | 'dots'; +} + +/** + * @example + * ```jsx + * const App = () => { + * return ( + * <> + * + * + * + * + * + * ) + * } + * ``` + */ + +export const Loader = ({ + color = 'primary', + size = 'md', + type = 'spinner', +}: LoaderProps) => { + return ( + <> + {type === 'spinner' ? ( + + ) : type === 'bars' ? ( + + ) : type === 'dots' ? ( + + ) : null} + + ); +}; diff --git a/packages/components/Loader/Loaders/Bars/Bars.styles.ts b/packages/components/Loader/Loaders/Bars/Bars.styles.ts new file mode 100644 index 0000000..5759663 --- /dev/null +++ b/packages/components/Loader/Loaders/Bars/Bars.styles.ts @@ -0,0 +1,58 @@ +import { css, keyframes } from '@emotion/react'; +import styled from '@emotion/styled'; +import { palette } from '../../../../theme/palette'; +import { NormalColorType } from '../../../../theme/types'; + +const BarKeyframes = keyframes` + 0% { + transform: none; + } + 25% { + transform: scaleY(2); + } + 50%, + 100% { + transform: none; + } +`; + +export const Container = styled.div<{ size: 'sm' | 'md' | 'lg' }>` + display: flex; + justify-content: space-around; + ${({ size }) => + size === 'sm' + ? css` + width: 1.75rem; + height: 0.875rem; + div { + width: 0.25rem; + height: 100%; + } + ` + : size === 'md' + ? css` + width: 2.7rem; + height: 1.375rem; + div { + width: 0.375rem; + height: 100%; + } + ` + : css` + width: 4.5rem; + height: 2.25rem; + div { + width: 0.6rem; + height: 100%; + } + `}; +`; + +export const Bar = styled.div<{ + color: NormalColorType; + delay: number; +}>` + animation: ${BarKeyframes} 1s infinite ease-in-out; + animation-delay: ${({ delay }) => delay + 's'}; + background-color: ${({ color }) => palette[color]}; +`; diff --git a/packages/components/Loader/Loaders/Bars/Bars.tsx b/packages/components/Loader/Loaders/Bars/Bars.tsx new file mode 100644 index 0000000..c0b6b22 --- /dev/null +++ b/packages/components/Loader/Loaders/Bars/Bars.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { NormalColorType } from '../../../../theme/types'; +import * as S from './Bars.styles'; + +export interface BarsProps { + size: 'sm' | 'md' | 'lg'; + color: NormalColorType; +} + +export const Bars = ({ size, color }: BarsProps) => { + return ( + + {[0, 0.1, 0.2, 0.3, 0.4].map((number) => ( + + ))} + + ); +}; diff --git a/packages/components/Loader/Loaders/Dots/Dots.styles.ts b/packages/components/Loader/Loaders/Dots/Dots.styles.ts new file mode 100644 index 0000000..4a2c497 --- /dev/null +++ b/packages/components/Loader/Loaders/Dots/Dots.styles.ts @@ -0,0 +1,56 @@ +import { css, keyframes } from '@emotion/react'; +import styled from '@emotion/styled'; +import { palette } from '../../../../theme/palette'; +import { NormalColorType } from '../../../../theme/types'; + +const DotsKeyframes = keyframes` + 0%{ + opacity: 1; + } + 50%,100%{ + opacity: 0.3; + } +`; + +export const Container = styled.div<{ size: 'sm' | 'md' | 'lg' }>` + display: flex; + justify-content: space-around; + align-items: center; + ${({ size }) => + size === 'sm' && + css` + width: 2.5rem; + div { + width: 0.5rem; + height: 0.5rem; + } + `} + ${({ size }) => + size === 'md' && + css` + width: 3.5rem; + div { + width: 0.7rem; + height: 0.7rem; + } + `} + ${({ size }) => + size === 'lg' && + css` + width: 5rem; + div { + width: 1rem; + height: 1rem; + } + `} +`; + +export const Dot = styled.div<{ + delay: number; + color: NormalColorType; +}>` + border-radius: 50%; + background-color: ${({ color }) => palette[color]}; + animation: ${DotsKeyframes} 1s infinite linear alternate; + animation-delay: ${({ delay }) => delay + 's'}; +`; diff --git a/packages/components/Loader/Loaders/Dots/Dots.tsx b/packages/components/Loader/Loaders/Dots/Dots.tsx new file mode 100644 index 0000000..e3028de --- /dev/null +++ b/packages/components/Loader/Loaders/Dots/Dots.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { NormalColorType } from '../../../../theme/types'; +import * as S from './Dots.styles'; + +export interface DotsProps { + size: 'sm' | 'md' | 'lg'; + color: NormalColorType; +} + +export const Dots = ({ size, color }: DotsProps) => { + return ( + + {[0, 0.5, 1].map((number) => ( + + ))} + + ); +}; diff --git a/packages/components/Loader/Loaders/Spinner/Spinner.styles.ts b/packages/components/Loader/Loaders/Spinner/Spinner.styles.ts new file mode 100644 index 0000000..6af2958 --- /dev/null +++ b/packages/components/Loader/Loaders/Spinner/Spinner.styles.ts @@ -0,0 +1,66 @@ +import { css, keyframes } from '@emotion/react'; +import styled from '@emotion/styled'; +import { palette } from '../../../../theme/palette'; +import { NormalColorType } from '../../../../theme/types'; + +const containerKeyframes = keyframes` + 100% { + transform: rotate(360deg) + } +`; + +const spinningDotKeyframes = keyframes` + 80%, 100% { + transform: rotate(360deg); + } +`; +const beforeKeyframes = keyframes` + 50% { + transform: scale(0.4); + } 100%, 0% { + transform: scale(1.0); + } +`; + +export const Container = styled.div<{ size: 'sm' | 'md' | 'lg' }>` + ${({ size }) => + size === 'sm' + ? css` + width: 1.375rem; + height: 1.375rem; + ` + : size === 'md' + ? css` + width: 2.25rem; + height: 2.25rem; + ` + : css` + width: 2.75rem; + height: 2.75rem; + `} + position: relative; + animation: ${containerKeyframes} 2.5s infinite linear both; +`; + +export const SpinningDot = styled.div<{ + delay: number; + color: NormalColorType; +}>` + width: 100%; + height: 100%; + position: absolute; + left: 0; + right: 0; + animation: ${spinningDotKeyframes} 2s infinite ease-in-out both; + animation-delay: ${({ delay }) => -1.2 + delay + 's'}; + ::before { + content: ' '; + display: block; + width: 25%; + height: 25%; + background-color: ${({ color }) => palette[color]}; + border-radius: 50%; + animation: ${beforeKeyframes} 2s infinite ease-in-out both; + animation-delay: ${({ delay }) => -1.2 + delay + 's'}; + } +`; diff --git a/packages/components/Loader/Loaders/Spinner/Spinner.tsx b/packages/components/Loader/Loaders/Spinner/Spinner.tsx new file mode 100644 index 0000000..a7d8a08 --- /dev/null +++ b/packages/components/Loader/Loaders/Spinner/Spinner.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { NormalColorType } from '../../../../theme/types'; +import * as S from './Spinner.styles'; + +interface SpinnerProps { + size: 'sm' | 'md' | 'lg'; + color: NormalColorType; +} + +export const Spinner = ({ size, color }: SpinnerProps) => { + return ( + + {[0.1, 0.2, 0.3, 0.4, 0.5, 0.6].map((number) => ( + + ))} + + ); +}; diff --git a/packages/components/Loader/index.ts b/packages/components/Loader/index.ts new file mode 100644 index 0000000..d702788 --- /dev/null +++ b/packages/components/Loader/index.ts @@ -0,0 +1 @@ +export { Loader } from './Loader'; diff --git a/packages/components/index.ts b/packages/components/index.ts index 5e44d26..984654e 100644 --- a/packages/components/index.ts +++ b/packages/components/index.ts @@ -7,3 +7,4 @@ export * from './Portal'; export * from './Accordion'; export * from './TextInput'; export * from './Toggle'; +export * from './Loader';