Skip to content

Commit

Permalink
feat(ui): Skeleton 컴포넌트 구현 (#178)
Browse files Browse the repository at this point in the history
* chore : add radix/theme

* chore: install radix-ui/themes

* feat: publish Skeleton component

* cs

* chore: remove radix-ui/themes

* feat: publish Skeleton Component.

* docs: Add Skeleton Storybook Docs.

* feat: children 여부에 따라 자동으로 크기 조절, JSDdoc 작성

* publish storybook

* fix story name

* chore: upgrade chromatic

* fix: migrate eslint config file (https://eslint.org/docs/latest/use/configure/migration-guide)

* fix

* fix: eslint script and setting

* fix: file name
  • Loading branch information
Brokyeom authored Oct 27, 2024
1 parent cd574de commit be92ccf
Show file tree
Hide file tree
Showing 13 changed files with 2,536 additions and 5,986 deletions.
5 changes: 5 additions & 0 deletions .changeset/late-icons-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sopt-makers/ui': minor
---

Add Skeleton Component.
14 changes: 0 additions & 14 deletions apps/docs/.eslintrc.cjs

This file was deleted.

17 changes: 17 additions & 0 deletions apps/docs/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
files: ['**/*.ts', '**/*.tsx'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:storybook/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
};
4 changes: 2 additions & 2 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
Expand All @@ -33,7 +33,7 @@
"@typescript-eslint/parser": "^6.10.0",
"@vanilla-extract/vite-plugin": "^3.9.2",
"@vitejs/plugin-react": "^4.2.0",
"chromatic": "^10.0.0",
"chromatic": "^11.15.0",
"eslint": "^8.53.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
Expand Down
37 changes: 37 additions & 0 deletions apps/docs/src/stories/Skeleton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Skeleton, SkeletonProps, Button as MDSButton, Toggle } from '@sopt-makers/ui';
import { Meta, StoryObj } from '@storybook/react';

const meta: Meta = {
title: 'Components/Skeleton',
component: Skeleton,
tags: ['autodocs'],
};

export default meta;

export const Default: StoryObj<SkeletonProps> = {
render: () => {
return <Skeleton width={300} height={48} variant='default' />;
},
};

export const Circular: StoryObj<SkeletonProps> = {
render: () => {
return <Skeleton width={40} height={40} variant='circular' />;
},
};

export const HasChildren: StoryObj<SkeletonProps> = {
render: () => {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
<Skeleton>
<MDSButton>버튼버튼버튼</MDSButton>
</Skeleton>
<Skeleton>
<Toggle />
</Skeleton>
</div>
);
},
};
53 changes: 53 additions & 0 deletions packages/ui/Skeleton/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { forwardRef } from 'react';
import type { HTMLAttributes } from 'react';
import { root, hasChildren } from './style.css';
import createSkeletonStyle from './utils';
import type { SkeletonVariant } from './types';

export interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
/**
* ### Skeleton의 너비를 수동으로 지정할 떄 사용합니다.
*/
width?: number | string;
/**
* ### Skeleton의 높이를 수동으로 지정할 떄 사용합니다.
*/
height?: number | string;
/**
* ### `default` | `circular` | `rounded` 의 세 가지 값을 가집니다.
* @example
* default - borderRadius : '12px'
* circular && rounded - borderRadius: '100%'
*/
variant?: SkeletonVariant;
/**
* ### children prop을 사용할 경우 스켈레톤의 사이즈가 자식 요소에 맞춰집니다.
* @example
* ```tsx
* // width: 'fit-content', height: 'auto'
* <Skeleton>
* <Toggle />
* </Skeleton>
* ```
*/
children?: React.ReactNode;
}

export const Skeleton = forwardRef<HTMLSpanElement, SkeletonProps>((props, forwardedRef) => {
const { width, height, variant = 'default', children, ...restProps } = props;

const styleClass = createSkeletonStyle(variant);

return (
<span
className={`${root} ${styleClass} ${children ? hasChildren : ''}`}
ref={forwardedRef}
style={{ width, height }}
{...restProps}
>
{children}
</span>
);
});

Skeleton.displayName = 'Skeleton';
9 changes: 9 additions & 0 deletions packages/ui/Skeleton/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { SkeletonVariant } from './types';

const SkeletonBorderRadius: Record<SkeletonVariant, string> = {
default: '12px',
circular: '100%',
rounded: '100%',
};

export default SkeletonBorderRadius;
1 change: 1 addition & 0 deletions packages/ui/Skeleton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Skeleton';
39 changes: 39 additions & 0 deletions packages/ui/Skeleton/style.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { colors } from '@sopt-makers/colors';
import { keyframes, style, globalStyle } from '@vanilla-extract/css';
import { createSprinkles, defineProperties } from '@vanilla-extract/sprinkles';
import SkeletonBorderRadius from './constants';

const pulseKeyframe = keyframes({
'0%': {
opacity: 1,
},
'50%': {
opacity: 0.4,
},
'100%': {
opacity: 1,
},
});

export const root = style({
display: 'block',
backgroundColor: colors.gray700,
animation: `${pulseKeyframe} 2s ease-in-out 0.5s infinite`,
});

export const hasChildren = style({
maxWidth: 'fit-content',
height: 'auto',
});

globalStyle(`${hasChildren} > *`, {
visibility: 'hidden',
});

const sprinkleProperties = defineProperties({
properties: {
borderRadius: SkeletonBorderRadius,
},
});

export const sprinkles = createSprinkles(sprinkleProperties);
1 change: 1 addition & 0 deletions packages/ui/Skeleton/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type SkeletonVariant = 'default' | 'circular' | 'rounded';
8 changes: 8 additions & 0 deletions packages/ui/Skeleton/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { sprinkles } from './style.css';
import type { SkeletonVariant } from './types';

const createSkeletonStyle = (variant: SkeletonVariant) => {
return sprinkles({ borderRadius: variant });
};

export default createSkeletonStyle;
1 change: 1 addition & 0 deletions packages/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { default as Tag } from './Tag';
export { default as Chip } from './Chip';
export { default as Callout } from './Callout';
export { default as Tab } from './Tab';
export * from './Skeleton';
export * from './FieldBox';
// test component
export { default as Test } from './Test';
Loading

0 comments on commit be92ccf

Please sign in to comment.