From 8d15fe6bf9511c7cf05b7bc0bd2a2edb95aa4c61 Mon Sep 17 00:00:00 2001 From: Evan Moon Date: Wed, 7 Apr 2021 22:13:35 +0900 Subject: [PATCH] feat(ui-kit): Beta Release (#68) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 배포 테스트 * chore: 배포테스트 * chore: 배포 테스트 * feat(ui-kit): Text 컴포넌트 추가 (#18) * chore: Alpha, Beta 브랜치 개설 [skip ci] (#14) * chore: 배포 테스트 * chore: 배포테스트 * chore: 배포 테스트 * chore: dry run test * feat(ui-kit): Text 컴포넌트 타이핑에 as 프로퍼티 추가 * 스토리북 세팅 * chore(ui-kit): Text 스토리 추가 * feat(ui-kit): Text에 Font Weight 스코어가 제대로 안 먹는 현상 수정 * chore(ui-kit): remove console log * chore(ui-kit): 스토리북 배포 * chore(ui-kit): fix workflow syntax * chore(ui-kit): action trigger test * chore(ui-kit): action trigger test * chore(ui-kit): 스토리북 배포 테스트 * chore(ui-kit): 스토리북 배포 테스트 * chore(ui-kit): 테스트 트리거 추가 * chore(ui-kit): storybook deploy test * chore(ui-kit): test storybook deploy * chore(ui-kit): 스토리북 배포 액션을 메뉴얼로 수정 * chore(ui-kit): Update README.md * chore(ui-kit): 망가진 CI 복구 * chore(ui-kit): 액션 조건이 헤드 커밋만 바라보도록 수정 * chore(ui-kit): 헤드 커밋 접근 수정 * feat(ui-kit): Text 컴포넌트 디자인 변경 (#20) * feat(ui-kit): Text 컴포넌트 디자인 변경 * chore(ui-kit): 스토리북에 깃 유저 정보 추가 * fix(ui-kit): 레퍼런스 타입이 이상하게 들어가 있던 부분 수정 * feat(ui-kit): 섀도우 스타일, 스토리북 추가 (#21) * feat(ui-kit): 타이포그래피 디자인 변경사항 반영 (#22) * feat(ui-kit): Radio 컴포넌트 추가 (#23) * feat(ui-kit): Radio 기본적인 스캐폴딩 완료 * feat(ui-kit) 라디오 컴포넌트 추가 * feat(ui-kit): Color 추가 (#24) * feat(ui-kit): scss 컬러 변수 정의 * feat(ui-kit): grayscale 스토리 작성 * feat(ui-kit): sementic color 스토리 작성 * feat(ui-kit): 컬러변수 context 추가 * feat(ui-kit): 컬러 헥스값 업데이트 * feat(ui-kit): 오타 수정 * feat(ui-kit): 불필요한 변수 제거 * feat(ui-kit): 상수 및 타입 정의 리뷰 반영 * feat(ui-kit): 불필요한 클래스 제거 * feat(ui-kit): 컬러 타입 추가 * feat(ui-kit): Grid Column (#25) * feat(ui-kit): Container 컴포넌트 추가 * feat(ui-kit): Column 컴포넌트 추가 * feat(ui-kit): Row 컴포넌트 추가 * feat(ui-kit): Grid Interface 선언 * feat(ui-kit): Column 컴포넌트 Props * feat(ui-kit):Column style 추가 * feat(ui-kit): Column scss mixin 및 functions 추가 * feat(ui-kit): Component 안에 Import 된 scss 삭제 * feat(ui-kit): Column NumberAttribute 타입 수정 * feat(ui-kit): Column ColumnResponsive 타입 수정 * feat(ui-kit): Column 내부 Immutable하게 변경 * feat(ui-kit): Row 컴포넌트 추가 및 그리드 인터페이스 확정 (#27) * feat(ui-kit): Row 컴포넌트 추가 * chore: PR Template 수정 * feat(ui-kit): add variable width story * feat(ui-kit): remove unused row component * feat(ui-kit): Row컴포넌트의 direction 스토리 추가 * Feat/checkbox (#28) * feat(ui-kit): checkbox 컴포넌트 추가 * feat(ui-kit): checkbox 컴포넌트 스타일 추가 * feat(ui-kit): checkbox 컴포넌트 스토리 추가 * feat(ui-kit): Checkbox 리뷰 내용 반영 * feat(ui-kit): alias 및 utils index 추가 * fix(ui-kit): remove duplicated export syntax * Feat/switch (#29) * feat(ui-kit): switch 컴포넌트 추가 * feat(ui-kit): label 추가 * feat(ui-kit): inline 케이스 추가 및 스타일 일부 수정 * feat(ui-kit): 컴포넌트 수정에 따른 스토리 수정 * feat(ui-kit): Switch transform 퍼센트 값으로 변경 * feat(ui-kit): 스위치 슬라이더 x축 이동 롤백 * style(ui-kit): Radio 컴포넌트의 색을 green50으로 변경 (#30) * refactor(ui-kit): 타이포그래피의 content, content2를 p1, p2로 변경 (#31) * feat(ui-kit): Selection 컴포넌트 추가 (#32) * feat(ui-kit): Selection 컴포넌트 추가 * feat(ui-kit): Selection 컴포넌트에 아이콘 추가 * feat(ui-kit): Selection 컴포넌트 스토리 추가 * feat(ui-kit): 타입 픽스 * 타입 픽스 * feat(ui-kit): Selection 컴포넌트를 Borderless로 변경 * feat(ui-kit): 리뷰 반영 * fix(ui-kit): 타이포그래피 타입 깨지는 이슈 수정 * fea(ui-kit): Toast 추가 (#33) * feat(ui-kit): Toast 컴포넌트 추가 * feat(ui-kit): sleep 유틸 함수 추가 * feat(ui-kit): Portal Context 추가 * feat(ui-kit): LubyconUIKitProvider 추가 * feat(ui-kit): Toast가 Portal에 렌더되도록 변경 * fix(ui-kit): 깨진 디펜던시 수정 * feat(ui-kit): Toast 스토리 추가 * feat(ui-kit): useToast 훅 추가 * feat(ui-kit): 토스트 애니메이션을 useSpring이 아니라 useTransition으로 변경 * feat(ui-kit): 토스트 스토리 업데이트 * feat(ui-kit): 토스트 애니메이션 개선 * feat(ui-kit): fix lint * Feat/button (#34) * feat(ui-kit): init * feat(ui-kit): Button 컴포넌트 스토리 추가 * feat(ui-kit): 리뷰 내용 반영 * feat(ui-kit): 토스트에 Align 기능 추가 (#36) * 탭 컴포넌트 (#35) * feat(ui-kit): Container 컴포넌트 추가 * feat(ui-kit): Tab 컴포넌트 골격 * feat(ui-kit): Tab 스타일 * feat(ui-kit): Tab active 기능 * feat(ui-kit): Tab Bar 애니메이션 추가 * feat(ui-kit): Fixed Width Tab 구현 * feat(ui-kit): Tab Content 구현 * fix(ui-kit): QA 피드백 반영 * fix(ui-kit): 탭 컴포넌트 타입 깨지는 부분 수정 (#38) * fix(ui-kit): 탭 컴포넌트 타입 깨지는 부분 수정 * fix(ui-kit): 탭이 초기화 될 때 자동 선택되지 않는 이슈 수정 * remove console * feat(ui-kit): 툴팁 컴포넌트 추가 (#37) * feat(ui-kit): child ref 포지션으로 툴팁 렌더 * 툴팁 애로우 추가 * 버튼 스타일 수정 * 툴팁이 ref에 반응하도록 수정 * feat(ui-kit): 툴팁 포지션 세팅 완료 * fix lint * feat(ui-kit): 오타 수정 * feat(tooltip): 부모 중 relative 요소가 있어도 툴팁이 올바른 offset을 가지도록 수정 * feat(ui-kit): 토스트 포지션 및 스타일 수정 (#40) * feat(ui-kit): 스낵바 컴포넌트 추가 (#41) * feat(ui-kit): 스낵바 컴포넌트 추가 * feat(ui-kit): 스낵바 타입 업데이트 * feat(ui-kit): Snackbar 컴포넌트 타입에서 children 제거 * feat(ui-kit): 스낵바에 버튼이 연속으로 등장할 때 간격 설정 * feat(ui-kit): 의미없는 모듈 임포트 제거 * style(ui-kit): 수민님 디자인 피드백 반영 (#42) * feat(ui-kit): 카드 컴포넌트 추가 (#39) * feat(ui-kit): 카드 컴포넌트 추가 * feat(ui-kit): 모듈 관계 재정의 * feat(ui-kit): 카드 타입 수정 * feat(ui-kit): Alert, Container 컴포넌트 추가 (#43) * feat(ui-kit): Icon 컴포넌트가 className을 받을 수 있도록 수정 * feat(ui-kit): Column, Container 스타일 버그 수정 * feat(ui-kit): Alert 컴포넌트가 Div 속성을 상속받도록 변경 * feat(ui-kit): Alert 스토리 업데이트 * feat(ui-kit): className을 template string이 아니라 classnames 함수로만 처리하도록 변경 * refactor(ui-kit): 코드 정리 (#44) * refactor(ui-kit): 모듈 트리 변경 * refactor(ui-kit): clxs를 classnames로 변경 * refactor(ui-kit): BEM 방법론에 따른 클래스 명명 * refactor(ui-kit): 스토리북 경로 변경 * refactor(ui-kit): 스토리북 경로 원복 * feat(ui-kit): Input 컴포넌트 추가 (#46) * feat(ui-kit): input 컴포넌트 추가 * feat(ui-kit): Input 컴포넌트 라벨 스토리 수정 * feat(ui-kit): Input 컴포넌트 타입 스토리 추가 * feat(ui-kit): List, ListItem, ListItemImage 컴포넌트 추가 (#45) * feat(ui-kit): 리스트 기본 모양 잡음 * feat(ui-kit): 리스트에 left, right area 추가 * feat(ui-kit): 리스트 스토리북 수정 * chore: UPDATE README [skip ci] (#49) * Update README.md [skip ci] * Update README.md [skip ci] * feat(ui-kit): Accordion 컴포넌트 추가 (#47) * feat(ui-kit): Accordion 컴포넌트 추가 * feat(ui-kit): 아코디언 라벨 스타일 수정 * feat(ui-kit): input 컴포넌트 가현님 피드백 적용 * feat(ui-kit): 아코디언 상태 변경 이벤트 추가 * feat(ui-kit): ProgressBar 컴포넌트 추가 (#48) * feat(ui-kit): Button 컴포넌트 타입 추가 (#51) * feat(ui-kit): Button 컴포넌트 타입 추가 * refactor(ui-kit): change Combine type to CombineElementProps type * Update README.md [skip ci] * chore(ui-kit): 커스텀 도메인을 위한 CNAME 추가 (#52) * 알파 타겟 레포 변경 [skip ci] * CNAME 업데이트 [skip ci] * feat(ui-kit): Modal 컴포넌트 추가 (#50) * feat(ui-kit): Modal 컴포넌트 추가 * feat(ui-kit): Modal 스토리 추가 * feat(ui-kit): Modal TODO * feat(ui-kit): Modal 컴포넌트 추가 * feat(ui-kit): 타이틀 부분 태그 타입 수정 * chore: 리뷰 내용 반영-백드롭 forwradRef * chore: 리뷰 내용 반영-컴포넌트 합성 방식으로 변경 * chore: 모달 컴포넌트 스토리 수정 * chore: 리뷰반영 - 타입 변경, 엘리먼트 유효성 검사 * chore: 리뷰반영 - onOpen 이벤트, 백드롭 이벤트명 * chore: 리뷰반영 - 컴포넌트 제네릭 * chore: 린트 에러 * chore: 스토리 업데이트 * fix(ui-kit): Fix Modal Type Error * feat(ui-kit): Tag 컴포넌트 추가 (#53) * feat(ui-kit): 태그 컴포넌트 추가 * feat(ui-kit): Input 컴포넌트에 left 영역 추가 * feat(ui-kit): TagsInput 컴포넌트 추가 * 의미없는 generateId 제거 * feat(ui-kit): remove tagsInput * Feat/modal (#54) * feat(ui-kit): Modal 컴포넌트 추가 * feat(ui-kit): Modal 스토리 추가 * feat(ui-kit): Modal TODO * feat(ui-kit): Modal 컴포넌트 추가 * feat(ui-kit): 타이틀 부분 태그 타입 수정 * chore: 리뷰 내용 반영-백드롭 forwradRef * chore: 리뷰 내용 반영-컴포넌트 합성 방식으로 변경 * chore: 모달 컴포넌트 스토리 수정 * chore: 리뷰반영 - 타입 변경, 엘리먼트 유효성 검사 * chore: 리뷰반영 - onOpen 이벤트, 백드롭 이벤트명 * chore: 리뷰반영 - 컴포넌트 제네릭 * chore: 린트 에러 * chore: 스토리 업데이트 * chore: 단일 엘리먼트가 주입된 경우 추가 * chore: 모달 컨텍스트 수정 * chore: 모달 컴포넌트 스토리 추가 * chore: 리뷰 내용 반영 - 변수명, children 배열 처리 * Feat/table re (#56) * chore: 컴포넌트, 스타일 import * feat(ui-kit): 테이블 컴포넌트 추가 * chore: 린트 픽스 * feat(ui-kit): Modal 컴포넌트 애니메이션 추가 (#57) * feat(ui-kit): Modal 컴포넌트 애니메이션 추가 * 사용하지 않는 Param 제거 * fix type * fix lint * 모달 hooks 애니메이션 수정 * 외부에서 closeModal을 호출해도 애니메이션이 종료된 후 모달이 사라지도록 수정 * remove eqeq * 의미없는 classnames 호출 제거 * feat(ui-kit): Tabs 컴포넌트 리팩토링 및 모바일 대응 추가 (#58) * feat(ui-kit): Tab 컴포넌트 리팩토링 및 모바일 대응 추가 * 탭 타이포그래피 적용 * feat(docs): 개발에 필요한 기본적인 세팅 완료 (#59) * fix(docs): 사용하지 않는 버튼 제거 * chore(docs): 배포 테스트 * chore(docs): 잡 네임 변경 * chore(docs): fix unknwon workspace * chore(docs): set git conf * chore(docs): 환경에 따라 CNAME 다르게 설정 * chore(docs): 빠진 모듈 추가 * chore(docs): 빠진 환경변수 추가 * chore(docs): CNAME 옮기는 경로 변경 * feat(docs): Docs 개발에 필요한 기본 세팅 완료 * chore(docs): live 배포 스크립트 주석 처리 * chore(docs): 라이브 CICD 다시 살림 * fix(ui-kit): Row 컴포넌트 상하 마진 제거 (#60) * fix(ui-kit): Grid 컴포넌트에 클래스 넣을 수 있도록 변경 (#61) * feat(docs): UI Kit Docs 페이지 와꾸 추가 (#62) * feat(docs): 대략적인 레이아웃 추가 * feat(docs): BasicLayout 추가 * Docs Ui Kit 버전 업 * feat(docs): 사이드 메뉴 대충 와꾸 잡음 * feat(docs): Getting Started 추가 * delete CNAME (#63) * Create CNAME * Delete CNAME Co-authored-by: Daniel kim * chore(docs): static resource 404 떨어지는 이슈 해결 (#64) * fix(docs): .nojekyll 파일 생성 * fix(docs): .nojekyll 파일 재생성 * fix(docs): 배포 테스트 * chore(docs): 배포 테스트 * chore(docs): 배포 테스트 * CI/CD 타겟 브랜치 수정 * Feat/toast snackbar combine (#65) * feat: 스낵바와 토스트 하나로 합침 * feat: 스낵바 스타일 정돈 * 스낵바 모바일 대응 * 의미없는 스타일 제거 * feat(ui-kit): 토스트 모바일 적용 배포 트리거 * Feat/text mobile (#66) * feat: Icon 컴포넌트 추가 * feat: 기존에 아이콘 모듈을 사용하던 곳 변경 * fix(ui-kit): 사용하지 않는 디펜던시 제거 (#67) * feat: Icon 컴포넌트 추가 * feat: 기존에 아이콘 모듈을 사용하던 곳 변경 * fix(ui-kit): fix lint Co-authored-by: KIM HYUNJUN <93kimhyunjun@gmail.com> Co-authored-by: 권재원 Co-authored-by: Daniel kim --- .eslintrc.js | 2 + .github/pull_request_template.md | 10 + .github/workflows/alpha-release-docs.yml | 46 + .github/workflows/deploy-ui-kit-lib.yml | 2 +- .github/workflows/live-release-docs.yml | 45 + .github/workflows/publish-storybook.yml | 25 + .gitignore | 1 + LICENSE | 22 + README.md | 108 ++- docs/.babelrc | 4 + docs/CNAME.alpha | 1 + docs/CNAME.live | 1 + docs/next-env.d.ts | 1 + docs/package.json | 12 +- docs/pages/_app.tsx | 22 + docs/pages/_document.tsx | 23 + docs/pages/about.tsx | 17 - docs/pages/api/.gitkeep | 0 docs/pages/api/users/index.ts | 16 - docs/pages/getting-started.ts | 1 + docs/pages/index.ts | 1 + docs/pages/index.tsx | 16 - docs/pages/users/[id].tsx | 57 -- docs/pages/users/index.tsx | 37 - docs/scripts/deploy.js | 32 + docs/src/components/BasicLayout/index.tsx | 66 ++ docs/src/components/GlobalHeader/index.tsx | 33 + docs/src/components/GlobalSidebar/index.tsx | 60 ++ docs/src/components/Layout.tsx | 43 - docs/src/components/List.tsx | 19 - docs/src/components/ListDetail.tsx | 16 - docs/src/components/ListItem.tsx | 18 - docs/src/components/PostSection/index.tsx | 38 + docs/src/constants/menu.ts | 30 + docs/src/constants/resources.ts | 2 + docs/src/pages/GettingStartedPage/index.tsx | 89 ++ docs/src/pages/HomePage/index.tsx | 41 + docs/src/utils/menu.ts | 9 + docs/tsconfig.json | 1 + ui-kit/.storybook/deploy.js | 18 + ui-kit/.storybook/main.js | 20 +- ui-kit/.storybook/preview.js | 11 + ui-kit/package.json | 28 +- ui-kit/public/CNAME | 1 + ui-kit/rollup.config.js | 15 +- ui-kit/src/components/Accordion/index.tsx | 85 ++ ui-kit/src/components/Alert/index.tsx | 70 ++ .../src/components/Button/index.stories.tsx | 10 - ui-kit/src/components/Button/index.tsx | 40 +- ui-kit/src/components/Button/style.scss | 3 - ui-kit/src/components/Card/CardContent.tsx | 12 + ui-kit/src/components/Card/CardFooter.tsx | 22 + ui-kit/src/components/Card/CardHeader.tsx | 16 + .../src/components/Card/CardImageContent.tsx | 20 + ui-kit/src/components/Card/index.tsx | 21 + ui-kit/src/components/Checkbox/index.tsx | 48 + ui-kit/src/components/Container/index.tsx | 27 + ui-kit/src/components/Grid/Column.tsx | 42 + ui-kit/src/components/Grid/Row.tsx | 42 + ui-kit/src/components/Grid/index.ts | 2 + ui-kit/src/components/Grid/types.ts | 5 + ui-kit/src/components/Icon/index.tsx | 113 +++ ui-kit/src/components/Input/index.tsx | 101 +++ ui-kit/src/components/List/ListItem.tsx | 60 ++ ui-kit/src/components/List/ListItemImage.tsx | 22 + ui-kit/src/components/List/index.tsx | 18 + .../components/LubyconUIKitProvider/index.tsx | 20 + ui-kit/src/components/Modal/ModalBackdrop.tsx | 23 + ui-kit/src/components/Modal/ModalContent.tsx | 24 + ui-kit/src/components/Modal/ModalFooter.tsx | 11 + ui-kit/src/components/Modal/ModalHeader.tsx | 20 + ui-kit/src/components/Modal/ModalWindow.tsx | 17 + ui-kit/src/components/Modal/index.tsx | 92 ++ ui-kit/src/components/ProgressBar/index.tsx | 54 ++ ui-kit/src/components/Radio/index.tsx | 42 + ui-kit/src/components/Selection/index.tsx | 62 ++ .../src/components/Snackbar/SnackbarBody.tsx | 29 + ui-kit/src/components/Snackbar/index.tsx | 102 +++ ui-kit/src/components/Snackbar/utils.ts | 21 + ui-kit/src/components/Switch/index.tsx | 32 + ui-kit/src/components/Table/TableBody.tsx | 13 + ui-kit/src/components/Table/TableCell.tsx | 22 + ui-kit/src/components/Table/TableHead.tsx | 19 + ui-kit/src/components/Table/TableRow.tsx | 8 + ui-kit/src/components/Table/index.tsx | 16 + ui-kit/src/components/Tabs/TabsContext.ts | 22 + ui-kit/src/components/Tabs/TabsIndicator.tsx | 23 + ui-kit/src/components/Tabs/TabsItem.tsx | 75 ++ ui-kit/src/components/Tabs/index.tsx | 77 ++ ui-kit/src/components/Tag/index.tsx | 53 ++ ui-kit/src/components/Text/index.tsx | 34 + ui-kit/src/components/Text/types.ts | 18 + ui-kit/src/components/Tooltip/TooltipBody.tsx | 37 + ui-kit/src/components/Tooltip/index.tsx | 64 ++ ui-kit/src/components/Tooltip/types.ts | 19 + ui-kit/src/components/Tooltip/utils.ts | 96 ++ ui-kit/src/components/index.scss | 1 - ui-kit/src/components/index.ts | 1 - ui-kit/src/constants/colors.ts | 32 + ui-kit/src/contexts/Modal.tsx | 85 ++ ui-kit/src/contexts/Portal.tsx | 36 + ui-kit/src/contexts/Snackbar.tsx | 97 ++ ui-kit/src/hooks/useCombinedRefs.ts | 18 + ui-kit/src/hooks/useResizeObserver.ts | 23 + ui-kit/src/index.ts | 31 + ui-kit/src/sass/components/_Accordion.scss | 42 + ui-kit/src/sass/components/_Alert.scss | 29 + ui-kit/src/sass/components/_Button.scss | 60 ++ ui-kit/src/sass/components/_Card.scss | 40 + ui-kit/src/sass/components/_Checkbox.scss | 74 ++ ui-kit/src/sass/components/_Column.scss | 25 + ui-kit/src/sass/components/_Container.scss | 11 + ui-kit/src/sass/components/_Icon.scss | 29 + ui-kit/src/sass/components/_Input.scss | 81 ++ ui-kit/src/sass/components/_List.scss | 69 ++ ui-kit/src/sass/components/_Modal.scss | 46 + ui-kit/src/sass/components/_ProgressBar.scss | 48 + ui-kit/src/sass/components/_Radio.scss | 75 ++ ui-kit/src/sass/components/_Row.scss | 40 + ui-kit/src/sass/components/_Selection.scss | 57 ++ ui-kit/src/sass/components/_Snackbar.scss | 96 ++ ui-kit/src/sass/components/_Switch.scss | 49 + ui-kit/src/sass/components/_Table.scss | 30 + ui-kit/src/sass/components/_Tabs.scss | 58 ++ ui-kit/src/sass/components/_Tag.scss | 64 ++ ui-kit/src/sass/components/_Text.scss | 7 + ui-kit/src/sass/components/_Tooltip.scss | 93 ++ ui-kit/src/sass/components/_index.scss | 22 + ui-kit/src/sass/functions/_index.scss | 1 + ui-kit/src/sass/functions/_strip-unit.scss | 7 + ui-kit/src/sass/index.scss | 7 + ui-kit/src/sass/modules/_test.scss | 1 - ui-kit/src/sass/utils/_breakpoints.scss | 13 + ui-kit/src/sass/utils/_colors.scss | 40 + ui-kit/src/sass/utils/_font-weights.scss | 24 + ui-kit/src/sass/utils/_font.scss | 5 + ui-kit/src/sass/utils/_grid.scss | 2 + ui-kit/src/sass/utils/_index.scss | 8 + ui-kit/src/sass/utils/_scrollbar.scss | 7 + ui-kit/src/sass/utils/_shadows.scss | 18 + ui-kit/src/sass/utils/_typography.scss | 30 + ui-kit/src/stories/Accordion.stories.tsx | 42 + ui-kit/src/stories/Alert.stories.tsx | 45 + ui-kit/src/stories/Button.stories.tsx | 55 ++ ui-kit/src/stories/Card.stories.tsx | 97 ++ ui-kit/src/stories/Checkbox.stories.tsx | 36 + ui-kit/src/stories/Colors.stories.tsx | 134 +++ ui-kit/src/stories/Grid.stories.tsx | 97 ++ ui-kit/src/stories/Icon.stories.tsx | 77 ++ ui-kit/src/stories/Input.stories.tsx | 90 ++ ui-kit/src/stories/List.stories.tsx | 135 +++ ui-kit/src/stories/Modal.stories.tsx | 213 +++++ ui-kit/src/stories/ProgressBar.stories.tsx | 90 ++ ui-kit/src/stories/Radio.stories.tsx | 37 + ui-kit/src/stories/Selection.stories.tsx | 62 ++ ui-kit/src/stories/Shadows.stories.tsx | 34 + ui-kit/src/stories/Snackbar.stories.tsx | 133 +++ ui-kit/src/stories/Switch.stories.tsx | 30 + ui-kit/src/stories/Table.stories.tsx | 34 + ui-kit/src/stories/Tabs.stories.tsx | 61 ++ ui-kit/src/stories/Tag.stories.tsx | 60 ++ ui-kit/src/stories/Text.stories.tsx | 67 ++ ui-kit/src/stories/Tooltip.stories.tsx | 69 ++ ui-kit/src/types/OverridableProps.ts | 9 + ui-kit/src/types/utils.ts | 13 + ui-kit/src/utils/dom.ts | 15 + ui-kit/src/utils/generateID.ts | 6 + ui-kit/src/utils/index.ts | 1 + ui-kit/src/utils/mediaQuery.ts | 35 + ui-kit/src/utils/sleep.ts | 5 + ui-kit/tsconfig.json | 11 +- yarn.lock | 845 ++++++++++-------- 172 files changed, 6597 insertions(+), 657 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/alpha-release-docs.yml create mode 100644 .github/workflows/live-release-docs.yml create mode 100644 .github/workflows/publish-storybook.yml create mode 100644 LICENSE create mode 100644 docs/.babelrc create mode 100644 docs/CNAME.alpha create mode 100644 docs/CNAME.live create mode 100644 docs/pages/_app.tsx create mode 100644 docs/pages/_document.tsx delete mode 100644 docs/pages/about.tsx create mode 100644 docs/pages/api/.gitkeep delete mode 100644 docs/pages/api/users/index.ts create mode 100644 docs/pages/getting-started.ts create mode 100644 docs/pages/index.ts delete mode 100644 docs/pages/index.tsx delete mode 100644 docs/pages/users/[id].tsx delete mode 100644 docs/pages/users/index.tsx create mode 100644 docs/scripts/deploy.js create mode 100644 docs/src/components/BasicLayout/index.tsx create mode 100644 docs/src/components/GlobalHeader/index.tsx create mode 100644 docs/src/components/GlobalSidebar/index.tsx delete mode 100644 docs/src/components/Layout.tsx delete mode 100644 docs/src/components/List.tsx delete mode 100644 docs/src/components/ListDetail.tsx delete mode 100644 docs/src/components/ListItem.tsx create mode 100644 docs/src/components/PostSection/index.tsx create mode 100644 docs/src/constants/menu.ts create mode 100644 docs/src/constants/resources.ts create mode 100644 docs/src/pages/GettingStartedPage/index.tsx create mode 100644 docs/src/pages/HomePage/index.tsx create mode 100644 docs/src/utils/menu.ts create mode 100644 ui-kit/.storybook/deploy.js create mode 100644 ui-kit/.storybook/preview.js create mode 100644 ui-kit/public/CNAME create mode 100644 ui-kit/src/components/Accordion/index.tsx create mode 100644 ui-kit/src/components/Alert/index.tsx delete mode 100644 ui-kit/src/components/Button/index.stories.tsx delete mode 100644 ui-kit/src/components/Button/style.scss create mode 100644 ui-kit/src/components/Card/CardContent.tsx create mode 100644 ui-kit/src/components/Card/CardFooter.tsx create mode 100644 ui-kit/src/components/Card/CardHeader.tsx create mode 100644 ui-kit/src/components/Card/CardImageContent.tsx create mode 100644 ui-kit/src/components/Card/index.tsx create mode 100644 ui-kit/src/components/Checkbox/index.tsx create mode 100644 ui-kit/src/components/Container/index.tsx create mode 100644 ui-kit/src/components/Grid/Column.tsx create mode 100644 ui-kit/src/components/Grid/Row.tsx create mode 100644 ui-kit/src/components/Grid/index.ts create mode 100644 ui-kit/src/components/Grid/types.ts create mode 100644 ui-kit/src/components/Icon/index.tsx create mode 100644 ui-kit/src/components/Input/index.tsx create mode 100644 ui-kit/src/components/List/ListItem.tsx create mode 100644 ui-kit/src/components/List/ListItemImage.tsx create mode 100644 ui-kit/src/components/List/index.tsx create mode 100644 ui-kit/src/components/LubyconUIKitProvider/index.tsx create mode 100644 ui-kit/src/components/Modal/ModalBackdrop.tsx create mode 100644 ui-kit/src/components/Modal/ModalContent.tsx create mode 100644 ui-kit/src/components/Modal/ModalFooter.tsx create mode 100644 ui-kit/src/components/Modal/ModalHeader.tsx create mode 100644 ui-kit/src/components/Modal/ModalWindow.tsx create mode 100644 ui-kit/src/components/Modal/index.tsx create mode 100644 ui-kit/src/components/ProgressBar/index.tsx create mode 100644 ui-kit/src/components/Radio/index.tsx create mode 100644 ui-kit/src/components/Selection/index.tsx create mode 100644 ui-kit/src/components/Snackbar/SnackbarBody.tsx create mode 100644 ui-kit/src/components/Snackbar/index.tsx create mode 100644 ui-kit/src/components/Snackbar/utils.ts create mode 100644 ui-kit/src/components/Switch/index.tsx create mode 100644 ui-kit/src/components/Table/TableBody.tsx create mode 100644 ui-kit/src/components/Table/TableCell.tsx create mode 100644 ui-kit/src/components/Table/TableHead.tsx create mode 100644 ui-kit/src/components/Table/TableRow.tsx create mode 100644 ui-kit/src/components/Table/index.tsx create mode 100644 ui-kit/src/components/Tabs/TabsContext.ts create mode 100644 ui-kit/src/components/Tabs/TabsIndicator.tsx create mode 100644 ui-kit/src/components/Tabs/TabsItem.tsx create mode 100644 ui-kit/src/components/Tabs/index.tsx create mode 100644 ui-kit/src/components/Tag/index.tsx create mode 100644 ui-kit/src/components/Text/index.tsx create mode 100644 ui-kit/src/components/Text/types.ts create mode 100644 ui-kit/src/components/Tooltip/TooltipBody.tsx create mode 100644 ui-kit/src/components/Tooltip/index.tsx create mode 100644 ui-kit/src/components/Tooltip/types.ts create mode 100644 ui-kit/src/components/Tooltip/utils.ts delete mode 100644 ui-kit/src/components/index.scss delete mode 100644 ui-kit/src/components/index.ts create mode 100644 ui-kit/src/constants/colors.ts create mode 100644 ui-kit/src/contexts/Modal.tsx create mode 100644 ui-kit/src/contexts/Portal.tsx create mode 100644 ui-kit/src/contexts/Snackbar.tsx create mode 100644 ui-kit/src/hooks/useCombinedRefs.ts create mode 100644 ui-kit/src/hooks/useResizeObserver.ts create mode 100644 ui-kit/src/index.ts create mode 100644 ui-kit/src/sass/components/_Accordion.scss create mode 100644 ui-kit/src/sass/components/_Alert.scss create mode 100644 ui-kit/src/sass/components/_Button.scss create mode 100644 ui-kit/src/sass/components/_Card.scss create mode 100644 ui-kit/src/sass/components/_Checkbox.scss create mode 100644 ui-kit/src/sass/components/_Column.scss create mode 100644 ui-kit/src/sass/components/_Container.scss create mode 100644 ui-kit/src/sass/components/_Icon.scss create mode 100644 ui-kit/src/sass/components/_Input.scss create mode 100644 ui-kit/src/sass/components/_List.scss create mode 100644 ui-kit/src/sass/components/_Modal.scss create mode 100644 ui-kit/src/sass/components/_ProgressBar.scss create mode 100644 ui-kit/src/sass/components/_Radio.scss create mode 100644 ui-kit/src/sass/components/_Row.scss create mode 100644 ui-kit/src/sass/components/_Selection.scss create mode 100644 ui-kit/src/sass/components/_Snackbar.scss create mode 100644 ui-kit/src/sass/components/_Switch.scss create mode 100644 ui-kit/src/sass/components/_Table.scss create mode 100644 ui-kit/src/sass/components/_Tabs.scss create mode 100644 ui-kit/src/sass/components/_Tag.scss create mode 100644 ui-kit/src/sass/components/_Text.scss create mode 100644 ui-kit/src/sass/components/_Tooltip.scss create mode 100644 ui-kit/src/sass/components/_index.scss create mode 100644 ui-kit/src/sass/functions/_index.scss create mode 100644 ui-kit/src/sass/functions/_strip-unit.scss create mode 100644 ui-kit/src/sass/index.scss delete mode 100644 ui-kit/src/sass/modules/_test.scss create mode 100644 ui-kit/src/sass/utils/_breakpoints.scss create mode 100644 ui-kit/src/sass/utils/_colors.scss create mode 100644 ui-kit/src/sass/utils/_font-weights.scss create mode 100644 ui-kit/src/sass/utils/_font.scss create mode 100644 ui-kit/src/sass/utils/_grid.scss create mode 100644 ui-kit/src/sass/utils/_index.scss create mode 100644 ui-kit/src/sass/utils/_scrollbar.scss create mode 100644 ui-kit/src/sass/utils/_shadows.scss create mode 100644 ui-kit/src/sass/utils/_typography.scss create mode 100644 ui-kit/src/stories/Accordion.stories.tsx create mode 100644 ui-kit/src/stories/Alert.stories.tsx create mode 100644 ui-kit/src/stories/Button.stories.tsx create mode 100644 ui-kit/src/stories/Card.stories.tsx create mode 100644 ui-kit/src/stories/Checkbox.stories.tsx create mode 100644 ui-kit/src/stories/Colors.stories.tsx create mode 100644 ui-kit/src/stories/Grid.stories.tsx create mode 100644 ui-kit/src/stories/Icon.stories.tsx create mode 100644 ui-kit/src/stories/Input.stories.tsx create mode 100644 ui-kit/src/stories/List.stories.tsx create mode 100644 ui-kit/src/stories/Modal.stories.tsx create mode 100644 ui-kit/src/stories/ProgressBar.stories.tsx create mode 100644 ui-kit/src/stories/Radio.stories.tsx create mode 100644 ui-kit/src/stories/Selection.stories.tsx create mode 100644 ui-kit/src/stories/Shadows.stories.tsx create mode 100644 ui-kit/src/stories/Snackbar.stories.tsx create mode 100644 ui-kit/src/stories/Switch.stories.tsx create mode 100644 ui-kit/src/stories/Table.stories.tsx create mode 100644 ui-kit/src/stories/Tabs.stories.tsx create mode 100644 ui-kit/src/stories/Tag.stories.tsx create mode 100644 ui-kit/src/stories/Text.stories.tsx create mode 100644 ui-kit/src/stories/Tooltip.stories.tsx create mode 100644 ui-kit/src/types/OverridableProps.ts create mode 100644 ui-kit/src/types/utils.ts create mode 100644 ui-kit/src/utils/dom.ts create mode 100644 ui-kit/src/utils/generateID.ts create mode 100644 ui-kit/src/utils/index.ts create mode 100644 ui-kit/src/utils/mediaQuery.ts create mode 100644 ui-kit/src/utils/sleep.ts diff --git a/.eslintrc.js b/.eslintrc.js index e605cc5f..6a2ea0f3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,5 +21,7 @@ module.exports = { ], rules: { '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + 'react/prop-types': 'off', }, }; diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..31e222df --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +> 이 PR이 BREAKING_CHANGE를 포함하고 있다면 반드시 명시해주세요! + +## 변경사항 +어떤 사항이 변경되었는지 자세히 적어주세요! + +## 디자인 시안 링크 +디자인 시안 링크를 첨부해주세요! + +## 집중적으로 리뷰 받고 싶은 부분이 있나요? + diff --git a/.github/workflows/alpha-release-docs.yml b/.github/workflows/alpha-release-docs.yml new file mode 100644 index 00000000..687a272b --- /dev/null +++ b/.github/workflows/alpha-release-docs.yml @@ -0,0 +1,46 @@ +name: Alpha Release UI Kit Docs + +on: + push: + branches: + - master + - alpha + +jobs: + alpha-release-ui-kit-docs: + if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.event.head_commit.message, '(docs)')" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 0 + - uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Set Git Configulations + run: | + git config --global user.email "lubycon@gmail.com" + git config --global user.name "lubycon" + - name: Caching Node Modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + - name: Install depedencies + run: yarn + - name: Build + run: yarn workspace lubycon-ui-kit-docs build + - name: Publish + run: yarn workspace lubycon-ui-kit-docs deploy + env: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + ENV: 'alpha' + - uses: 8398a7/action-slack@v3 # 슬랙 노티피케이션 + with: + status: ${{ job.status }} + fields: repo,message,commit,author,action,eventName,ref,workflow + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DEPLOY_BOT_WEBHOOK_URL }} + if: always() + \ No newline at end of file diff --git a/.github/workflows/deploy-ui-kit-lib.yml b/.github/workflows/deploy-ui-kit-lib.yml index 62a62004..ee4612be 100644 --- a/.github/workflows/deploy-ui-kit-lib.yml +++ b/.github/workflows/deploy-ui-kit-lib.yml @@ -9,7 +9,7 @@ on: jobs: ui-kit-publish: - if: "!contains(toJSON(github.event.commits.*.msg), '[skip ci]') && contains(toJSON(github.event.commits.*.msg), '(ui-kit)')" + if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.event.head_commit.message, '(ui-kit)')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 diff --git a/.github/workflows/live-release-docs.yml b/.github/workflows/live-release-docs.yml new file mode 100644 index 00000000..c445a962 --- /dev/null +++ b/.github/workflows/live-release-docs.yml @@ -0,0 +1,45 @@ +name: Live Release UI Kit Docs + +on: + push: + branches: + - master + +jobs: + alpha-release-ui-kit-docs: + if: "!contains(github.event.head_commit.message, '[skip ci]') && contains(github.event.head_commit.message, '(docs)')" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 0 + - uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Set Git Configulations + run: | + git config --global user.email "lubycon@gmail.com" + git config --global user.name "lubycon" + - name: Caching Node Modules + uses: actions/cache@v2 + with: + path: '**/node_modules' + key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} + - name: Install depedencies + run: yarn + - name: Build + run: yarn workspace lubycon-ui-kit-docs build + - name: Publish + run: yarn workspace lubycon-ui-kit-docs deploy + env: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + ENV: 'live' + - uses: 8398a7/action-slack@v3 # 슬랙 노티피케이션 + with: + status: ${{ job.status }} + fields: repo,message,commit,author,action,eventName,ref,workflow + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DEPLOY_BOT_WEBHOOK_URL }} + if: always() + \ No newline at end of file diff --git a/.github/workflows/publish-storybook.yml b/.github/workflows/publish-storybook.yml new file mode 100644 index 00000000..700d9116 --- /dev/null +++ b/.github/workflows/publish-storybook.yml @@ -0,0 +1,25 @@ +name: Publish Dev Storybook + +on: [workflow_dispatch] + +jobs: + storybook-publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Set Git Configulations + run: | + git config --global user.email "lubycon@gmail.com" + git config --global user.name "lubycon" + - name: Install depedencies + run: yarn + - name: Build Storybook + run: yarn workspace @lubycon/ui-kit build-stories + - name: Publish + run: yarn workspace @lubycon/ui-kit publish-stories + env: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} + \ No newline at end of file diff --git a/.gitignore b/.gitignore index a7181ef6..cc98647f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ out/ # production build dist +storybook-static # misc .DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b819cf4c --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Call-Em-All + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index dcb1d87c..b5b9af9e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,106 @@ -# Lubycon UI Kit 🖌 -Lubycon 멘토링 프로젝트의 UI Kit +

+ Lubycon logo +

+ +

Lubycon UI Kit

+ +

+ Lubycon UI Kit은 한국의 고유 문자인 한글을 기반으로 설계된 UI 라이브러리입니다.
+ 국내 디자이너와 개발자들이 동일한 도메인 언어로 커뮤니케이션하는 것을 지향하며, 이를 위해 FE 라이브러리와 Figma 플러그인을 동시에 제공합니다.
+

+ +
+ Typescript + React + HTML5 + Sass + rollup.js + Yarn + NPM +
+ +
+ + + + [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/mui-org/material-ui/blob/master/LICENSE) + [![workflow](https://github.com/Lubycon/lubycon-ui-kit/workflows/Release%20UI%20Kit/badge.svg)](https://github.com/Lubycon/lubycon-ui-kit) + [![workflow](https://github.com/Lubycon/lubycon-ui-kit/workflows/Publish%20Dev%20Storybook/badge.svg)](https://github.com/Lubycon/lubycon-ui-kit) + [![npm alpha package](https://img.shields.io/npm/v/@lubycon/ui-kit/alpha.svg)](https://www.npmjs.com/package/@lubycon/ui-kit) + [![npm downloads](https://img.shields.io/npm/dm/@lubycon/ui-kit.svg)](https://www.npmjs.com/package/@lubycon/ui-kit) + +
+ +## Installation + +Lubycon UI Kit은 아직 알파 버전만 배포된 상태입니다. 따라서 문서 상단의 버전을 확인하고 `latest`가 아닌 정확한 버전을 명시하여 설치하시는 것을 추천합니다. + +```sh +$ npm install @lubycon/ui-kit@v1.1.0-alpha.24 +// or +$ yarn add @lubycon/ui-kit@v1.1.0-alpha.24 +``` + +## Usage + +Lubycon UI Kit 내부의 몇몇 컴포넌트는 컴포넌트 트리와 분리된 상태와 렌더 트리를 가지고 있기 때문에 `LubyconUIKitProvider`을 필요로 합니다. + +```jsx +// App.tsx + +import React, { PropsWithChildren } from 'react'; +import { LubyconUIKitProvider } from '@lubycon/ui-kit'; + +function App({ children }: PropsWithChildren<{}>) { + return {children}; +} +``` + +### 선언적 렌더링 vs Hooks + +Lubycon UI Kit의 많은 컴포넌트들은 선언적 렌더링을 지원하지만, 간혹 선언적 렌더링만으로 처리하기에는 컴포넌트 렌더 트리가 장황하다고 느껴질 수도 있습니다. + +그래서 이런 경우 컴포넌트 렌더 트리에 영향을 받지 않는 몇몇 컴포넌트들에 한해 사용자가 선언적인 방법과 명령적인 방법을 선택해서 사용할 수 있도록 지원하고 있습니다. + +#### 선언적 렌더링 + +```jsx +import React, { useState } from 'react'; +import { Toast } from '@lubycon/ui-kit'; + +function Foo() { + const [show, setShow] = useState(false); + + return ( + <> + + + + ); +} + +export default Foo; +``` + +#### Hooks + +```jsx +import React from 'react'; +import { useToast, Button } from '@lubycon/ui-kit'; + +function Foo() { + const { openToast } = useToast(); + + return ( + + ); +} + +export default Foo; +``` diff --git a/docs/.babelrc b/docs/.babelrc new file mode 100644 index 00000000..4d92efaa --- /dev/null +++ b/docs/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["next/babel", "@emotion/babel-preset-css-prop"], + "plugins": [] +} \ No newline at end of file diff --git a/docs/CNAME.alpha b/docs/CNAME.alpha new file mode 100644 index 00000000..50640918 --- /dev/null +++ b/docs/CNAME.alpha @@ -0,0 +1 @@ +ui-kit.alpha.lubycon.io \ No newline at end of file diff --git a/docs/CNAME.live b/docs/CNAME.live new file mode 100644 index 00000000..49bb1c02 --- /dev/null +++ b/docs/CNAME.live @@ -0,0 +1 @@ +ui-kit.lubycon.io \ No newline at end of file diff --git a/docs/next-env.d.ts b/docs/next-env.d.ts index 7b7aa2c7..6bd29805 100644 --- a/docs/next-env.d.ts +++ b/docs/next-env.d.ts @@ -1,2 +1,3 @@ /// /// +/// diff --git a/docs/package.json b/docs/package.json index 01a56986..563bf2eb 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,18 +1,26 @@ { "name": "lubycon-ui-kit-docs", "version": "1.0.0", + "private": true, "scripts": { "dev": "next", - "build": "next build", + "build": "next build && next export", "start": "next start", - "typecheck": "tsc" + "typecheck": "tsc", + "deploy": "node ./scripts/deploy" }, "dependencies": { + "@emotion/react": "^11.1.5", + "@emotion/styled": "^11.1.5", + "@lubycon/ui-kit": "^1.1.0-alpha.30", + "gh-pages": "^3.1.0", "next": "latest", + "normalize.css": "^8.0.1", "react": "^16.12.0", "react-dom": "^16.12.0" }, "devDependencies": { + "@emotion/babel-preset-css-prop": "^11.2.0", "@types/node": "^12.12.21", "@types/react": "^16.9.16", "@types/react-dom": "^16.9.4", diff --git a/docs/pages/_app.tsx b/docs/pages/_app.tsx new file mode 100644 index 00000000..9af48a67 --- /dev/null +++ b/docs/pages/_app.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { AppProps } from 'next/app'; +import { LubyconUIKitProvider } from '@lubycon/ui-kit'; + +import 'normalize.css'; +import '@lubycon/ui-kit/css/lubycon-ui-kit.min.css'; +import Head from 'next/head'; + +export default function LubyconUIKitDocsApp({ Component, pageProps }: AppProps) { + return ( + <> + + Lubycon UI Kit + + + + + + + + ); +} diff --git a/docs/pages/_document.tsx b/docs/pages/_document.tsx new file mode 100644 index 00000000..4cb44a4f --- /dev/null +++ b/docs/pages/_document.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document'; + +class LubyconUIKitDocsDocument extends Document { + static async getInitialProps(ctx: DocumentContext) { + const initialProps = await Document.getInitialProps(ctx); + return { ...initialProps }; + } + + render() { + return ( + + + +
+ + + + ); + } +} + +export default LubyconUIKitDocsDocument; diff --git a/docs/pages/about.tsx b/docs/pages/about.tsx deleted file mode 100644 index 92a21d5f..00000000 --- a/docs/pages/about.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; -import Layout from 'components/Layout'; - -const AboutPage = () => ( - -

About

-

This is the about page

-

- - Go home - -

-
-); - -export default AboutPage; diff --git a/docs/pages/api/.gitkeep b/docs/pages/api/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/pages/api/users/index.ts b/docs/pages/api/users/index.ts deleted file mode 100644 index c868197c..00000000 --- a/docs/pages/api/users/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; -import { sampleUserData } from 'utils/sample-data'; - -const handler = (_req: NextApiRequest, res: NextApiResponse) => { - try { - if (!Array.isArray(sampleUserData)) { - throw new Error('Cannot find user data'); - } - - res.status(200).json(sampleUserData); - } catch (err) { - res.status(500).json({ statusCode: 500, message: err.message }); - } -}; - -export default handler; diff --git a/docs/pages/getting-started.ts b/docs/pages/getting-started.ts new file mode 100644 index 00000000..3df50e00 --- /dev/null +++ b/docs/pages/getting-started.ts @@ -0,0 +1 @@ +export { default } from 'pages/GettingStartedPage'; diff --git a/docs/pages/index.ts b/docs/pages/index.ts new file mode 100644 index 00000000..b7398203 --- /dev/null +++ b/docs/pages/index.ts @@ -0,0 +1 @@ +export { default } from 'pages/HomePage'; diff --git a/docs/pages/index.tsx b/docs/pages/index.tsx deleted file mode 100644 index 01fe2af8..00000000 --- a/docs/pages/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; -import Layout from '../src/components/Layout'; - -const IndexPage = () => ( - -

Hello Next.js 👋

-

- - About - -

-
-); - -export default IndexPage; diff --git a/docs/pages/users/[id].tsx b/docs/pages/users/[id].tsx deleted file mode 100644 index 9d1ed5e3..00000000 --- a/docs/pages/users/[id].tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { GetStaticProps, GetStaticPaths } from 'next'; -import { User } from 'models'; -import { sampleUserData } from '../../src/utils/sample-data'; -import Layout from 'components/Layout'; -import ListDetail from 'components/ListDetail'; - -type Props = { - item?: User; - errors?: string; -}; - -const StaticPropsDetail = ({ item, errors }: Props) => { - if (errors) { - return ( - -

- Error: {errors} -

-
- ); - } - - return ( - - {item && } - - ); -}; - -export default StaticPropsDetail; - -export const getStaticPaths: GetStaticPaths = async () => { - // Get the paths we want to pre-render based on users - const paths = sampleUserData.map((user) => ({ - params: { id: user.id.toString() }, - })); - - // We'll pre-render only these paths at build time. - // { fallback: false } means other routes should 404. - return { paths, fallback: false }; -}; - -// This function gets called at build time on server-side. -// It won't be called on client-side, so you can even do -// direct database queries. -export const getStaticProps: GetStaticProps = async ({ params }) => { - try { - const id = params?.id; - const item = sampleUserData.find((data) => data.id === Number(id)); - // By returning { props: item }, the StaticPropsDetail component - // will receive `item` as a prop at build time - return { props: { item } }; - } catch (err) { - return { props: { errors: err.message } }; - } -}; diff --git a/docs/pages/users/index.tsx b/docs/pages/users/index.tsx deleted file mode 100644 index a2c372ec..00000000 --- a/docs/pages/users/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { GetStaticProps } from 'next'; -import Link from 'next/link'; -import { User } from 'models'; -import { sampleUserData } from 'utils/sample-data'; -import Layout from 'components/Layout'; -import List from 'components/List'; - -type Props = { - items: User[]; -}; - -const WithStaticProps = ({ items }: Props) => ( - -

Users List

-

- Example fetching data from inside getStaticProps(). -

-

You are currently on: /users

- -

- - Go home - -

-
-); - -export const getStaticProps: GetStaticProps = async () => { - // Example for including static props in a Next.js function component page. - // Don't forget to include the respective types for any props passed into - // the component. - const items: User[] = sampleUserData; - return { props: { items } }; -}; - -export default WithStaticProps; diff --git a/docs/scripts/deploy.js b/docs/scripts/deploy.js new file mode 100644 index 00000000..0f253c95 --- /dev/null +++ b/docs/scripts/deploy.js @@ -0,0 +1,32 @@ +const ghpages = require('gh-pages'); +const path = require('path'); +const fs = require('fs'); + +const env = process.env.ENV; +const token = process.env.ACCESS_TOKEN; +const deployTarget = env === 'alpha' ? 'ui-kit.alpha.lubycon.io' : 'ui-kit.lubycon.io'; + +console.log('📦 UI Kit 문서 배포를 준비 중 입니다...'); + +console.log('🌱 CNAME 만드는 중...'); +fs.renameSync(path.resolve(`./CNAME.${env}`), path.resolve('./out/CNAME')); +fs.closeSync(fs.openSync(path.resolve('./out/.nojekyll'), 'w')); +console.log('🌱 CNAME 생성 완료'); + +ghpages.publish( + path.join(__dirname, '../out'), + { + branch: 'master', + remote: 'origin', + repo: `https://${token}@github.com/Lubycon/${deployTarget}`, + message: `UI Kit 문서 배포`, + dotfiles: true, + }, + (err) => { + if (err) { + throw err; + } else { + console.log('🚀 UI Kit 문서 배포가 완료되었습니다!'); + } + } +); diff --git a/docs/src/components/BasicLayout/index.tsx b/docs/src/components/BasicLayout/index.tsx new file mode 100644 index 00000000..18d04b82 --- /dev/null +++ b/docs/src/components/BasicLayout/index.tsx @@ -0,0 +1,66 @@ +import { Row, Column, Container } from '@lubycon/ui-kit'; +import GlobalHeader from 'components/GlobalHeader'; +import GlobalSidebar from 'components/GlobalSidebar'; +import React, { PropsWithChildren, useState, useEffect, useRef } from 'react'; + +const BasicLayout = ({ children }: PropsWithChildren) => { + const headerRef = useRef(null); + const [headerHeight, setHeaderHeight] = useState(0); + + const contentsHeight = `calc(100vh - ${headerHeight}px)`; + + useEffect(() => { + if (headerRef.current != null) { + const rect = headerRef.current.getBoundingClientRect(); + setHeaderHeight(rect.height); + } + }, [headerRef.current]); + + return ( +
+
+ +
+
+ + + + + + + {children} + + + +
+
+ ); +}; + +export default BasicLayout; diff --git a/docs/src/components/GlobalHeader/index.tsx b/docs/src/components/GlobalHeader/index.tsx new file mode 100644 index 00000000..fe558cd8 --- /dev/null +++ b/docs/src/components/GlobalHeader/index.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Row, Column, Container } from '@lubycon/ui-kit'; +import Link from 'next/link'; +import { logoSrc } from 'constants/resources'; + +const GlobalHeader = () => { + return ( + + ); +}; + +export default GlobalHeader; diff --git a/docs/src/components/GlobalSidebar/index.tsx b/docs/src/components/GlobalSidebar/index.tsx new file mode 100644 index 00000000..d343c33a --- /dev/null +++ b/docs/src/components/GlobalSidebar/index.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Menu, menu } from 'constants/menu'; +import Link from 'next/link'; +import { isLinkMenu } from 'utils/menu'; +import { colors, Text } from '@lubycon/ui-kit'; +import styled from '@emotion/styled'; + +const StyledList = styled.ul` + margin: 0; + padding: 0; +`; +const StyledListItem = styled.li` + list-style: none; + margin: 0; + padding: 0; +`; + +const Item = ({ menu }: { menu: Menu }) => { + return isLinkMenu(menu) ? ( + + + {menu.title} + + + ) : ( +
+ {menu.title} + + {menu.children.map((childrenMenu, index) => ( + + + + ))} + +
+ ); +}; + +const GlobalSidebar = () => { + return ( +
+ + {menu.map((item, index) => ( + + + + ))} + +
+ ); +}; + +export default GlobalSidebar; diff --git a/docs/src/components/Layout.tsx b/docs/src/components/Layout.tsx deleted file mode 100644 index 8877b2f5..00000000 --- a/docs/src/components/Layout.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { ReactNode } from 'react'; -import Link from 'next/link'; -import Head from 'next/head'; -import { Button } from '@lubycon/ui-kit'; - -type Props = { - children?: ReactNode; - title?: string; -}; - -const Layout = ({ children, title = 'This is the default title' }: Props) => ( -
- - {title} - - - -
- -
- {children} - -
-
- Test -
-
-); - -export default Layout; diff --git a/docs/src/components/List.tsx b/docs/src/components/List.tsx deleted file mode 100644 index 5765291b..00000000 --- a/docs/src/components/List.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from 'react'; -import ListItem from './ListItem'; -import { User } from 'models'; - -type Props = { - items: User[]; -}; - -const List = ({ items }: Props) => ( -
    - {items.map((item) => ( -
  • - -
  • - ))} -
-); - -export default List; diff --git a/docs/src/components/ListDetail.tsx b/docs/src/components/ListDetail.tsx deleted file mode 100644 index fcadc652..00000000 --- a/docs/src/components/ListDetail.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import * as React from 'react'; - -import { User } from 'models'; - -type ListDetailProps = { - item: User; -}; - -const ListDetail = ({ item: user }: ListDetailProps) => ( -
-

Detail for {user.name}

-

ID: {user.id}

-
-); - -export default ListDetail; diff --git a/docs/src/components/ListItem.tsx b/docs/src/components/ListItem.tsx deleted file mode 100644 index 014b3f8e..00000000 --- a/docs/src/components/ListItem.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import Link from 'next/link'; - -import { User } from 'models'; - -interface Props { - data: User; -} - -const ListItem = ({ data }: Props) => ( - - - {data.id}: {data.name} - - -); - -export default ListItem; diff --git a/docs/src/components/PostSection/index.tsx b/docs/src/components/PostSection/index.tsx new file mode 100644 index 00000000..a7710f61 --- /dev/null +++ b/docs/src/components/PostSection/index.tsx @@ -0,0 +1,38 @@ +import { colors, Text } from '@lubycon/ui-kit'; +import React, { PropsWithChildren, ReactNode } from 'react'; + +const Title = ({ children }: PropsWithChildren) => ( + + {children} + +); + +const Subtitle = ({ children }: PropsWithChildren) => ( + + {children} + +); + +const Contents = ({ children }: PropsWithChildren) => ( + + {children} + +); + +interface Props { + title: ReactNode; +} +const PostSection = ({ title, children }: PropsWithChildren) => { + return ( +
+
{title}
+
{children}
+
+ ); +}; + +PostSection.Title = Title; +PostSection.Subtitle = Subtitle; +PostSection.Contents = Contents; + +export default PostSection; diff --git a/docs/src/constants/menu.ts b/docs/src/constants/menu.ts new file mode 100644 index 00000000..0b4d3293 --- /dev/null +++ b/docs/src/constants/menu.ts @@ -0,0 +1,30 @@ +export interface LinkMenu { + type: 'link'; + title: string; + link: string; +} + +export interface LabelMenu { + type: 'label'; + title: string; + children: LinkMenu[]; +} + +export type Menu = LinkMenu | LabelMenu; + +export const menu: Menu[] = [ + { + type: 'link', + title: 'Getting Started', + link: '/getting-started', + }, + { + type: 'label', + title: 'Components', + children: [ + { type: 'link', title: 'Alert', link: '/components/alert' }, + { type: 'link', title: 'Button', link: '/components/button' }, + { type: 'link', title: 'Card', link: '/components/card' }, + ], + }, +]; diff --git a/docs/src/constants/resources.ts b/docs/src/constants/resources.ts new file mode 100644 index 00000000..1a25c408 --- /dev/null +++ b/docs/src/constants/resources.ts @@ -0,0 +1,2 @@ +export const assetsHost = 'https://d2x9jxyr47nlkc.cloudfront.net'; +export const logoSrc = `${assetsHost}/logo/logo-color.svg`; diff --git a/docs/src/pages/GettingStartedPage/index.tsx b/docs/src/pages/GettingStartedPage/index.tsx new file mode 100644 index 00000000..375a1f7f --- /dev/null +++ b/docs/src/pages/GettingStartedPage/index.tsx @@ -0,0 +1,89 @@ +import BasicLayout from 'components/BasicLayout'; +import PostSection from 'components/PostSection'; +import React from 'react'; + +const GettingStartedPage = () => { + return ( + +
+ Installation}> + + Lubycon UI Kit은 아직 알파 버전만 배포된 상태입니다. 따라서 문서 상단의 버전을 확인하고 + latest가 아닌 정확한 버전을 명시하여 설치하시는 것을 추천합니다. + +
+            {`$ npm install @lubycon/ui-kit@v1.1.0-alpha.24
+// or
+$ yarn add @lubycon/ui-kit@v1.1.0-alpha.24`}
+          
+
+ Usage}> + + Lubycon UI Kit 내부의 몇몇 컴포넌트는 컴포넌트 트리와 분리된 상태와 렌더 트리를 가지고 + 있기 때문에 LubyconUIKitProvider을 필요로 합니다. + +
+            {`// App.tsx
+
+import React, { PropsWithChildren } from 'react';
+import { LubyconUIKitProvider } from '@lubycon/ui-kit';
+
+function App({ children }: PropsWithChildren<{}>) {
+  return {children};
+}`}
+          
+
+ 선언적 렌더링 vs Hooks}> + + Lubycon UI Kit의 많은 컴포넌트들은 선언적 렌더링을 지원하지만, 간혹 선언적 렌더링만으로 + 처리하기에는 컴포넌트 렌더 트리가 장황하다고 느껴질 수도 있습니다. +
+ 그래서 이런 경우 컴포넌트 렌더 트리에 영향을 받지 않는 몇몇 컴포넌트들에 한해 사용자가 + 선언적인 방법과 명령적인 방법을 선택해서 사용할 수 있도록 지원하고 있습니다. +
+ 선언적 렌더링 +
+            {`import React, { useState } from 'react';
+import { Toast } from '@lubycon/ui-kit';
+
+function Foo() {
+  const [show, setShow] = useState(false);
+
+  return (
+    <>
+      
+      
+    
+  );
+}
+
+export default Foo;`}
+          
+ Hooks +
+            {`import React from 'react';
+import { useToast, Button } from '@lubycon/ui-kit';
+
+function Foo() {
+  const { openToast } = useToast();
+
+  return (
+    
+  );
+}
+
+export default Foo;`}
+          
+
+
+
+ ); +}; + +export default GettingStartedPage; diff --git a/docs/src/pages/HomePage/index.tsx b/docs/src/pages/HomePage/index.tsx new file mode 100644 index 00000000..d39a3445 --- /dev/null +++ b/docs/src/pages/HomePage/index.tsx @@ -0,0 +1,41 @@ +import BasicLayout from 'components/BasicLayout'; +import PostSection from 'components/PostSection'; +import { logoSrc } from 'constants/resources'; +import React from 'react'; + +const HomePage = () => { + return ( + +
+
+ +
+
+ Introduction}> + + Lubycon UI Kit은 한국의 고유 문자인 한글을 기반으로 설계된 UI 라이브러리입니다. 국내 + 디자이너와 개발자들이 동일한 도메인 언어로 커뮤니케이션하는 것을 지향하며, 이를 위해 + FE 라이브러리와 Figma 플러그인을 동시에 제공합니다. + + + Guidelines}> + + Lubycon UI Kit은 한국의 고유 문자인 한글을 기반으로 설계된 UI 라이브러리입니다. 국내 + 디자이너와 개발자들이 동일한 도메인 언어로 커뮤니케이션하는 것을 지향하며, 이를 위해 + FE 라이브러리와 Figma 플러그인을 동시에 제공합니다. + + + Links}> + + Lubycon UI Kit은 한국의 고유 문자인 한글을 기반으로 설계된 UI 라이브러리입니다. 국내 + 디자이너와 개발자들이 동일한 도메인 언어로 커뮤니케이션하는 것을 지향하며, 이를 위해 + FE 라이브러리와 Figma 플러그인을 동시에 제공합니다. + + +
+
+
+ ); +}; + +export default HomePage; diff --git a/docs/src/utils/menu.ts b/docs/src/utils/menu.ts new file mode 100644 index 00000000..c777e3b5 --- /dev/null +++ b/docs/src/utils/menu.ts @@ -0,0 +1,9 @@ +import { Menu, LabelMenu, LinkMenu } from 'constants/menu'; + +export function isLabelMenu(menu: Menu): menu is LabelMenu { + return menu.type === 'label'; +} + +export function isLinkMenu(menu: Menu): menu is LinkMenu { + return menu.type === 'link'; +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json index de4883e6..b43a1f64 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -29,6 +29,7 @@ ] }, "allowJs": true + // "jsxImportSource": "@emotion/react" }, "exclude": [ "./node_modules" diff --git a/ui-kit/.storybook/deploy.js b/ui-kit/.storybook/deploy.js new file mode 100644 index 00000000..76eaa5eb --- /dev/null +++ b/ui-kit/.storybook/deploy.js @@ -0,0 +1,18 @@ +const ghpages = require('gh-pages'); +const path = require('path'); +const token = process.env.ACCESS_TOKEN; + +console.log('📦 개발용 스토리북 배포를 준비 중 입니다...'); + +ghpages.publish(path.join(__dirname, '../storybook-static'), { + branch: 'master', + remote: 'origin', + repo: `https://${token}@github.com/Lubycon/ui-kit.storybook.lubycon.io.git`, + message: `개발용 스토리북 배포`, +}, (err) => { + if (err) { + throw err; + } else { + console.log('🚀 개발용 스토리북 배포가 완료되었습니다!') + } +}); diff --git a/ui-kit/.storybook/main.js b/ui-kit/.storybook/main.js index 6866f2e6..dac0759a 100644 --- a/ui-kit/.storybook/main.js +++ b/ui-kit/.storybook/main.js @@ -1,10 +1,16 @@ +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); + module.exports = { - "stories": [ - "../src/**/*.stories.mdx", - "../src/**/*.stories.@(ts|tsx)" + stories: [ + '../src/**/*.stories.mdx', + '../src/**/*.stories.@(ts|tsx)', ], - "addons": [ - "@storybook/addon-essentials", - "@storybook/preset-create-react-app" - ] + addons: [ + '@storybook/addon-essentials', + '@storybook/preset-create-react-app', + ], + webpackFinal: async (config) => { + config.resolve.plugins.push(new TsconfigPathsPlugin({})); + return config; + }, } \ No newline at end of file diff --git a/ui-kit/.storybook/preview.js b/ui-kit/.storybook/preview.js new file mode 100644 index 00000000..97be83ba --- /dev/null +++ b/ui-kit/.storybook/preview.js @@ -0,0 +1,11 @@ +import '../src/sass/index.scss'; +import React from 'react'; +import { LubyconUIKitProvider, Container } from 'src'; + +export const decorators = [(Story => ( + + + + + +))]; diff --git a/ui-kit/package.json b/ui-kit/package.json index 39121257..f5c4b7fa 100644 --- a/ui-kit/package.json +++ b/ui-kit/package.json @@ -20,15 +20,23 @@ "lubycon" ], "author": "Lubycon", - "dependencies": {}, + "dependencies": { + "classnames": "^2.2.6", + "react-spring": "^8.0.27", + "resize-observer-polyfill": "^1.5.1" + }, "scripts": { "start": "start-storybook -p 6006 --no-dll", "build": "yarn clean && rollup -c && yarn copy && tsc --emitDeclarationOnly -p ./tsconfig.build.json", + "build-stories": "yarn build-storybook -o ./storybook-static -s ./public", + "publish-stories": "node ./.storybook/deploy.js", "test": "react-scripts test", "eject": "react-scripts eject", "clean": "rm -rf dist && mkdir dist", - "copy": "ncp src/sass/modules dist/sass && ncp package.json dist/package.json", - "copy-version": "ncp dist/package.json package.json" + "copy": "ncp package.json dist/package.json", + "copy-sass": "ncp src/sass/modules dist/sass", + "copy-version": "ncp dist/package.json package.json", + "typecheck": "tsc --noEmit" }, "eslintConfig": { "extends": [ @@ -53,20 +61,23 @@ "@babel/core": "^7.12.3", "@rollup/plugin-commonjs": "^16.0.0", "@rollup/plugin-node-resolve": "^10.0.0", - "@storybook/addon-actions": "^6.0.28", - "@storybook/addon-essentials": "^6.0.28", - "@storybook/node-logger": "^6.0.28", + "@storybook/addon-actions": "^6.1.14", + "@storybook/addon-essentials": "^6.1.14", + "@storybook/node-logger": "^6.1.14", "@storybook/preset-create-react-app": "^3.1.5", - "@storybook/react": "^6.0.28", + "@storybook/react": "^6.1.14", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", + "@types/classnames": "^2.2.11", "@types/jest": "^26.0.15", "@types/node": "^12.0.0", "@types/react": "^16.9.53", "@types/react-dom": "^16.9.8", "autoprefixer": "^9.0.0", "babel-loader": "^8.2.1", + "classnames": "^2.2.6", + "gh-pages": "^3.1.0", "postcss": "^7.0.2", "react": "^17.0.1", "react-dom": "^17.0.1", @@ -75,6 +86,7 @@ "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-postcss": "^3.1.8", "rollup-plugin-typescript2": "^0.29.0", - "sass": "^1.29.0" + "sass": "^1.29.0", + "tsconfig-paths-webpack-plugin": "^3.3.0" } } diff --git a/ui-kit/public/CNAME b/ui-kit/public/CNAME new file mode 100644 index 00000000..056338c1 --- /dev/null +++ b/ui-kit/public/CNAME @@ -0,0 +1 @@ +ui-kit.storybook.lubycon.io \ No newline at end of file diff --git a/ui-kit/rollup.config.js b/ui-kit/rollup.config.js index f20015a4..319418bf 100644 --- a/ui-kit/rollup.config.js +++ b/ui-kit/rollup.config.js @@ -1,19 +1,17 @@ import path from 'path'; - import autoprefixer from 'autoprefixer'; import commonjs from '@rollup/plugin-commonjs'; import resolve from '@rollup/plugin-node-resolve'; import postcss from 'rollup-plugin-postcss'; import typescript from 'rollup-plugin-typescript2'; -// import babel from 'rollup-plugin-babel'; const extensions = ['.js', '.jsx', '.ts', '.tsx']; export default [ - buildCJS('src/components/index.ts'), - buildESM('src/components/index.ts'), - buildCSS('src/components/index.scss', 'css/lubycon-ui-kit.css'), - buildCSS('src/components/index.scss', 'css/lubycon-ui-kit.min.css', { + buildCJS('src/index.ts'), + buildESM('src/index.ts'), + buildCSS('src/sass/index.scss', 'css/lubycon-ui-kit.css'), + buildCSS('src/sass/index.scss', 'css/lubycon-ui-kit.min.css', { minimize: { preset: ['default'], }, @@ -29,11 +27,6 @@ function buildJS(input, output, format) { typescript({ tsconfig: 'tsconfig.json', }), - // babel({ - // extensions, - // runtimeHelpers: true, - // include: ['src/**'], - // }), resolve({ extensions }), commonjs({ namedExports: { diff --git a/ui-kit/src/components/Accordion/index.tsx b/ui-kit/src/components/Accordion/index.tsx new file mode 100644 index 00000000..eb7b400b --- /dev/null +++ b/ui-kit/src/components/Accordion/index.tsx @@ -0,0 +1,85 @@ +import React, { forwardRef, useEffect, useRef, useState } from 'react'; +import { CombineElementProps } from 'src/types/utils'; +import classnames from 'classnames'; +import Icon from '../Icon'; +import Text from '../Text'; +import { useResizeObserver } from 'src/hooks/useResizeObserver'; +import { colors } from 'src/constants/colors'; + +type Props = CombineElementProps< + 'div', + { + label: string; + defaultOpen?: boolean; + onChange?: (state: boolean) => void; + onOpen?: () => void; + onClose?: () => void; + } +>; +const Accordion = forwardRef(function Accordion( + { label, className, children, defaultOpen = false, onChange, onOpen, onClose, ...props }, + ref +) { + const [open, setOpen] = useState(defaultOpen); + const contentRef = useRef(null); + const [bodyHeight, setBodyHeight] = useState(0); + + const toggleContentOpen = () => { + setOpen((state) => !state); + }; + + const updateContentHeight = () => + setBodyHeight(contentRef.current?.getBoundingClientRect().height ?? 0); + + useResizeObserver(contentRef, updateContentHeight); + + useEffect(() => { + onChange?.(open); + }, [open]); + + useEffect(() => { + if (open === true) { + onOpen?.(); + } else { + onClose?.(); + } + }, [open]); + + return ( +
+
+ + + {label} + +
+
+
+ {children} +
+
+
+ ); +}); + +export default Accordion; diff --git a/ui-kit/src/components/Alert/index.tsx b/ui-kit/src/components/Alert/index.tsx new file mode 100644 index 00000000..77a2787c --- /dev/null +++ b/ui-kit/src/components/Alert/index.tsx @@ -0,0 +1,70 @@ +import React, { forwardRef } from 'react'; +import { colors, SemanticColor } from 'src/constants/colors'; +import classnames from 'classnames'; +import Text from '../Text'; +import Icon from '../Icon'; +import { CombineElementProps } from 'src/types/utils'; + +interface AlertIcon { + icon: string; + color: string; +} +const alertIconMap: { + [key in SemanticColor]: AlertIcon; +} = { + negative: { + icon: 'close-circle', + color: colors.red50, + }, + notice: { + icon: 'alert-circle', + color: colors.yellow50, + }, + informative: { + icon: 'information-circle', + color: colors.blue50, + }, + positive: { + icon: 'checkmark-circle', + color: colors.green50, + }, +}; + +type AlertProps = CombineElementProps< + 'div', + { + type?: SemanticColor; + title?: string; + } +>; + +const Alert = forwardRef(function Alert( + { type = 'informative', title, children, className, ...props }, + ref +) { + const { icon: iconName, color: iconColor } = alertIconMap[type]; + + return ( +
+ + {title ? ( + + {title} + + ) : null} + {children} +
+ ); +}); + +export default Alert; diff --git a/ui-kit/src/components/Button/index.stories.tsx b/ui-kit/src/components/Button/index.stories.tsx deleted file mode 100644 index ef7a8c31..00000000 --- a/ui-kit/src/components/Button/index.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import Button from './index'; -import { Meta } from '@storybook/react/types-6-0'; - -export default { - title: 'Lubycon UI Kit/Button', - component: Button, -} as Meta; - -export const Default = () => ; diff --git a/ui-kit/src/components/Button/index.tsx b/ui-kit/src/components/Button/index.tsx index 9a569c96..cecbf4c4 100644 --- a/ui-kit/src/components/Button/index.tsx +++ b/ui-kit/src/components/Button/index.tsx @@ -1,5 +1,39 @@ -import React, { HTMLAttributes } from 'react'; +import React, { Ref, forwardRef } from 'react'; +import classnames from 'classnames'; +import { CombineElementProps } from 'src/types/utils'; +import Text from '../Text'; +import { SemanticColor } from 'src/constants/colors'; -export default function Button(props: HTMLAttributes): JSX.Element { - return + ); +}; + +export default forwardRef(Button) as typeof Button; diff --git a/ui-kit/src/components/Button/style.scss b/ui-kit/src/components/Button/style.scss deleted file mode 100644 index 21bef857..00000000 --- a/ui-kit/src/components/Button/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.button { - outline: 0; -} diff --git a/ui-kit/src/components/Card/CardContent.tsx b/ui-kit/src/components/Card/CardContent.tsx new file mode 100644 index 00000000..ee6c5d44 --- /dev/null +++ b/ui-kit/src/components/Card/CardContent.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { ReactNode } from 'react'; +import classnames from 'classnames'; + +interface CardContentProps { + children?: ReactNode; +} +const CardContent = ({ children }: CardContentProps) => { + return
{children}
; +}; + +export default CardContent; diff --git a/ui-kit/src/components/Card/CardFooter.tsx b/ui-kit/src/components/Card/CardFooter.tsx new file mode 100644 index 00000000..811c1b58 --- /dev/null +++ b/ui-kit/src/components/Card/CardFooter.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { ReactNode } from 'react'; +import classnames from 'classnames'; + +interface CardFooterProps { + children?: ReactNode; + justifyContent?: 'flex-start' | 'center' | 'flex-end'; +} +const CardFooter = ({ children, justifyContent = 'flex-start' }: CardFooterProps) => { + return ( +
+ {children} +
+ ); +}; + +export default CardFooter; diff --git a/ui-kit/src/components/Card/CardHeader.tsx b/ui-kit/src/components/Card/CardHeader.tsx new file mode 100644 index 00000000..1c63a100 --- /dev/null +++ b/ui-kit/src/components/Card/CardHeader.tsx @@ -0,0 +1,16 @@ +import React, { ReactNode, isValidElement } from 'react'; +import classnames from 'classnames'; +import Text from '../Text'; + +interface CardHeaderProps { + children?: ReactNode; +} +const CardHeader = ({ children }: CardHeaderProps) => { + return ( +
+ {isValidElement(children) ? children : {children}} +
+ ); +}; + +export default CardHeader; diff --git a/ui-kit/src/components/Card/CardImageContent.tsx b/ui-kit/src/components/Card/CardImageContent.tsx new file mode 100644 index 00000000..af2ebe12 --- /dev/null +++ b/ui-kit/src/components/Card/CardImageContent.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import classnames from 'classnames'; +import { CombineElementProps } from 'src/types/utils'; + +type CardImageContentProps = CombineElementProps< + 'img', + { + src: string; + alt: string; + } +>; +const CardImageContent = ({ className, ...props }: CardImageContentProps) => { + return ( +
+ +
+ ); +}; + +export default CardImageContent; diff --git a/ui-kit/src/components/Card/index.tsx b/ui-kit/src/components/Card/index.tsx new file mode 100644 index 00000000..e3f1b860 --- /dev/null +++ b/ui-kit/src/components/Card/index.tsx @@ -0,0 +1,21 @@ +import React, { forwardRef } from 'react'; +import { ReactNode } from 'react'; +import classnames from 'classnames'; + +interface Props { + children: ReactNode; +} + +const Card = forwardRef(function Card({ children }, ref) { + return ( +
+ {children} +
+ ); +}); + +export default Card; +export { default as CardHeader } from './CardHeader'; +export { default as CardContent } from './CardContent'; +export { default as CardImageContent } from './CardImageContent'; +export { default as CardFooter } from './CardFooter'; diff --git a/ui-kit/src/components/Checkbox/index.tsx b/ui-kit/src/components/Checkbox/index.tsx new file mode 100644 index 00000000..009f6d19 --- /dev/null +++ b/ui-kit/src/components/Checkbox/index.tsx @@ -0,0 +1,48 @@ +import React, { forwardRef, Ref } from 'react'; +import { CombineElementProps } from 'src/types/utils'; +import classnames from 'classnames'; +import { generateID } from 'utils/index'; +import Text from '../Text'; + +interface CheckboxBaseProps { + label?: string; + display?: 'block' | 'inline'; +} +type CheckboxProps = Omit, 'type'>; + +const Checkbox = ( + { label, display = 'block', style, disabled, ...props }: CheckboxProps, + ref: Ref +) => { + const id = generateID('checkbox'); + + return ( + + ); +}; + +export default forwardRef(Checkbox) as typeof Checkbox; diff --git a/ui-kit/src/components/Container/index.tsx b/ui-kit/src/components/Container/index.tsx new file mode 100644 index 00000000..b1b264e4 --- /dev/null +++ b/ui-kit/src/components/Container/index.tsx @@ -0,0 +1,27 @@ +import React, { HTMLAttributes } from 'react'; +import classnames from 'classnames'; +interface ContainerProps extends HTMLAttributes { + fluid?: boolean; +} + +export default function Container({ + children, + fluid = false, + className, + ...props +}: ContainerProps): JSX.Element { + return ( +
+ {children} +
+ ); +} diff --git a/ui-kit/src/components/Grid/Column.tsx b/ui-kit/src/components/Grid/Column.tsx new file mode 100644 index 00000000..121f145d --- /dev/null +++ b/ui-kit/src/components/Grid/Column.tsx @@ -0,0 +1,42 @@ +import React, { ElementType, useMemo } from 'react'; +import { ColumnSize, ColumnResponsive, DEFAULT_ELEMENT } from './types'; +import { OverridableProps } from 'types/OverridableProps'; +import classNames from 'classnames'; + +const sizes: ColumnResponsive[] = ['xl', 'lg', 'md', 'sm', 'xs']; + +type ColumnBaseProps = { + [key in ColumnResponsive]?: ColumnSize; +}; + +type ColumnProps = OverridableProps< + T, + ColumnBaseProps +>; + +const Column = ( + { as, className, ...props }: ColumnProps, + ref: React.Ref +) => { + const spanClasses = useMemo( + () => + sizes.map((size) => { + const { [size]: sizeValue } = props; + return sizeValue ? `lubycon-grid__column--${size}--${sizeValue}` : ''; + }), + [] + ); + + const target = as ?? DEFAULT_ELEMENT; + const Component = target; + + return ( + + ); +}; + +export default React.forwardRef(Column) as typeof Column; diff --git a/ui-kit/src/components/Grid/Row.tsx b/ui-kit/src/components/Grid/Row.tsx new file mode 100644 index 00000000..8334ab26 --- /dev/null +++ b/ui-kit/src/components/Grid/Row.tsx @@ -0,0 +1,42 @@ +import React, { ElementType, Ref, forwardRef } from 'react'; +import { DEFAULT_ELEMENT } from './types'; +import classnames from 'classnames'; +import { OverridableProps } from 'src/types/OverridableProps'; + +type BaseAlign = 'flex-start' | 'center' | 'flex-end'; +interface RowBaseProps { + direction?: 'column' | 'row' | 'row-reverse' | 'column-reverse'; + justify?: BaseAlign | 'space-between' | 'space-around' | 'space-evenly'; + alignItems?: BaseAlign | 'stretch' | 'baseline'; +} +type RowProps = OverridableProps; + +const Row = ( + { + as, + direction = 'row', + justify = 'flex-start', + alignItems = 'flex-start', + className, + ...props + }: RowProps, + ref: Ref +) => { + const Component = as ?? DEFAULT_ELEMENT; + + return ( + + ); +}; + +export default forwardRef(Row) as typeof Row; diff --git a/ui-kit/src/components/Grid/index.ts b/ui-kit/src/components/Grid/index.ts new file mode 100644 index 00000000..54468411 --- /dev/null +++ b/ui-kit/src/components/Grid/index.ts @@ -0,0 +1,2 @@ +export { default as Row } from './Row'; +export { default as Column } from './Column'; diff --git a/ui-kit/src/components/Grid/types.ts b/ui-kit/src/components/Grid/types.ts new file mode 100644 index 00000000..d408b490 --- /dev/null +++ b/ui-kit/src/components/Grid/types.ts @@ -0,0 +1,5 @@ +export const DEFAULT_ELEMENT = 'div' as const; + +type ColumnNumberSize = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; +export type ColumnSize = 'auto' | ColumnNumberSize; +export type ColumnResponsive = 'xl' | 'lg' | 'md' | 'sm' | 'xs'; diff --git a/ui-kit/src/components/Icon/index.tsx b/ui-kit/src/components/Icon/index.tsx new file mode 100644 index 00000000..be38aa2c --- /dev/null +++ b/ui-kit/src/components/Icon/index.tsx @@ -0,0 +1,113 @@ +import React, { useEffect, useState } from 'react'; +import classnames from 'classnames'; +import { colors } from 'src/constants/colors'; +import { CombineElementProps } from 'src/types/utils'; + +const iconCache: Record = {}; + +export type IconType = 'outline' | 'filled' | 'sharp'; +type Props = CombineElementProps< + 'span', + { + name: string; + size?: number; + type?: IconType; + color?: string; + className?: string; + } +>; + +/** ionicons의 아이콘을 사용합니다 + * https://ionicons.com/ + */ +const Icon = ({ + name, + size = 16, + type = 'filled', + color = colors.gray100, + className, + ...rest +}: Props) => { + const targetAttr = type === 'outline' ? 'stroke' : 'fill'; + const iconName = getIconName(name, type); + + const [iconHTML, setIconHTML] = useState(iconCache[iconName]); + const [showFallbackIcon, setShowFallbackIcon] = useState(false); + + useEffect(() => { + if (iconHTML != null) { + return; + } + + let ignore = false; + + (async function () { + try { + const data = await fetchIcon(iconName); + if (!ignore) { + setIconHTML(data); + iconCache[iconName] = data; + } + } catch { + setShowFallbackIcon(true); + } + })(); + + return () => { + ignore = true; + }; + }, []); + + return ( + + + {name} + + ); +}; + +export default Icon; + +async function fetchIcon(name: string) { + const response = await fetch(getIconUrl(name)); + const body = await response.text(); + if (response.ok) { + return body; + } else { + throw new Error(body); + } +} + +function getIconName(name: string, type: IconType) { + return type === 'filled' ? name : `${name}-${type}`; +} + +function getIconUrl(name: string) { + return `https://icons.lubycon.io/${name}.svg`; +} diff --git a/ui-kit/src/components/Input/index.tsx b/ui-kit/src/components/Input/index.tsx new file mode 100644 index 00000000..22716bb5 --- /dev/null +++ b/ui-kit/src/components/Input/index.tsx @@ -0,0 +1,101 @@ +import React, { forwardRef, isValidElement, ReactNode, useMemo, useState } from 'react'; +import classnames from 'classnames'; +import { CombineElementProps } from 'src/types/utils'; +import Text from '../Text'; + +export type TextInputType = 'text' | 'tel' | 'url' | 'email' | 'number' | 'password' | 'search'; +type Props = CombineElementProps< + 'input', + { + label?: ReactNode; + type?: TextInputType; + left?: ReactNode; + right?: ReactNode; + hasError?: boolean; + description?: string; + } +>; +const Input = forwardRef(function Input( + { + style, + label, + className, + disabled, + type = 'text', + left, + right, + hasError, + description, + onFocus, + onBlur, + ...props + }, + ref +) { + const [isFocused, setFocus] = useState(false); + const hasLabel = label != null; + const hasDescription = description != null; + const hasLeftArea = left != null; + const hasRightArea = right != null; + + const labelElement = useMemo(() => { + if (hasLabel) { + return ( + + {isValidElement(label) ? label : {label}} + + ); + } + return null; + }, [label]); + + return ( + + ); +}); + +export default Input; diff --git a/ui-kit/src/components/List/ListItem.tsx b/ui-kit/src/components/List/ListItem.tsx new file mode 100644 index 00000000..9b619197 --- /dev/null +++ b/ui-kit/src/components/List/ListItem.tsx @@ -0,0 +1,60 @@ +import React, { forwardRef, ReactNode } from 'react'; +import classnames from 'classnames'; +import { CombineElementProps } from 'src/types/utils'; +import Text from '../Text'; + +type Props = Omit< + CombineElementProps< + 'li', + { + left?: ReactNode; + right?: ReactNode; + title?: string; + content: string; + caption?: string; + } + >, + 'children' | 'role' +>; +const ListItem = forwardRef(function ListItem( + { className, left, right, title, content, caption, onClick, ...props }, + ref +) { + const isClickable = onClick != null; + + return ( +
  • + {left ?
    {left}
    : null} +
    + {title ? ( + + {title} + + ) : null} + + {content} + + {caption ? ( + + {caption} + + ) : null} +
    + {right ?
    {right}
    : null} +
  • + ); +}); + +export default ListItem; diff --git a/ui-kit/src/components/List/ListItemImage.tsx b/ui-kit/src/components/List/ListItemImage.tsx new file mode 100644 index 00000000..a036214d --- /dev/null +++ b/ui-kit/src/components/List/ListItemImage.tsx @@ -0,0 +1,22 @@ +import React, { ImgHTMLAttributes } from 'react'; +import classnames from 'classnames'; + +const ListItemImage = ({ + className, + src, + alt, + tabIndex, + ...props +}: ImgHTMLAttributes) => ( +
    + {alt} +
    +); + +export default ListItemImage; diff --git a/ui-kit/src/components/List/index.tsx b/ui-kit/src/components/List/index.tsx new file mode 100644 index 00000000..d7dd1f17 --- /dev/null +++ b/ui-kit/src/components/List/index.tsx @@ -0,0 +1,18 @@ +import React, { HTMLAttributes, forwardRef } from 'react'; +import classnames from 'classnames'; + +type Props = Omit, 'role'>; +const List = forwardRef(function List( + { children, className, ...props }, + ref +) { + return ( +
      + {children} +
    + ); +}); + +export default List; +export { default as ListItem } from './ListItem'; +export { default as ListItemImage } from './ListItemImage'; diff --git a/ui-kit/src/components/LubyconUIKitProvider/index.tsx b/ui-kit/src/components/LubyconUIKitProvider/index.tsx new file mode 100644 index 00000000..e08fea1e --- /dev/null +++ b/ui-kit/src/components/LubyconUIKitProvider/index.tsx @@ -0,0 +1,20 @@ +import React, { ReactNode } from 'react'; +import { PortalProvider } from 'contexts/Portal'; +import { SnackbarProvider } from 'src/contexts/Snackbar'; +import { ModalProvider } from 'src/contexts/Modal'; + +interface Props { + children: ReactNode; +} + +function LubyconUIKitProvider({ children }: Props) { + return ( + + + {children} + + + ); +} + +export default LubyconUIKitProvider; diff --git a/ui-kit/src/components/Modal/ModalBackdrop.tsx b/ui-kit/src/components/Modal/ModalBackdrop.tsx new file mode 100644 index 00000000..83397573 --- /dev/null +++ b/ui-kit/src/components/Modal/ModalBackdrop.tsx @@ -0,0 +1,23 @@ +import React, { forwardRef } from 'react'; +import classnames from 'classnames'; + +interface ModalBackdropProps { + onClick: (event: React.MouseEvent) => void; +} + +const ModalBackdrop = forwardRef(function ModalBackdrop( + { onClick }, + ref +) { + return ( +
    + ); +}); + +export default ModalBackdrop; diff --git a/ui-kit/src/components/Modal/ModalContent.tsx b/ui-kit/src/components/Modal/ModalContent.tsx new file mode 100644 index 00000000..545289fd --- /dev/null +++ b/ui-kit/src/components/Modal/ModalContent.tsx @@ -0,0 +1,24 @@ +import React, { ReactNode, isValidElement } from 'react'; +import classnames from 'classnames'; +import Text from 'components/Text'; +import { Typographys } from 'components/Text/types'; +import { CombineElementProps } from 'types/utils'; + +interface BaseProps { + size?: 'small' | 'medium'; + children?: ReactNode; +} + +type ModalContentProps = CombineElementProps<'div', BaseProps>; + +const ModalContent = ({ children, size }: ModalContentProps) => { + const typography: Typographys = size === 'small' ? 'p2' : 'p1'; + + return ( +
    + {isValidElement(children) ? children : {children}} +
    + ); +}; + +export default ModalContent; diff --git a/ui-kit/src/components/Modal/ModalFooter.tsx b/ui-kit/src/components/Modal/ModalFooter.tsx new file mode 100644 index 00000000..c9a338f5 --- /dev/null +++ b/ui-kit/src/components/Modal/ModalFooter.tsx @@ -0,0 +1,11 @@ +import React, { ReactNode } from 'react'; + +interface ModalFooterProps { + children?: ReactNode; +} + +const ModalFooter = ({ children }: ModalFooterProps) => { + return
    {children}
    ; +}; + +export default ModalFooter; diff --git a/ui-kit/src/components/Modal/ModalHeader.tsx b/ui-kit/src/components/Modal/ModalHeader.tsx new file mode 100644 index 00000000..04ca2bd4 --- /dev/null +++ b/ui-kit/src/components/Modal/ModalHeader.tsx @@ -0,0 +1,20 @@ +import React, { ReactNode, isValidElement } from 'react'; +import Text from 'components/Text'; +import { Typographys } from 'components/Text/types'; + +interface ModalHeaderProps { + size?: 'small' | 'medium'; + children?: ReactNode; +} + +const ModalHeader = ({ size, children }: ModalHeaderProps) => { + const typography: Typographys = size === 'small' ? 'subtitle' : 'h6'; + + return ( +
    + {isValidElement(children) ? children : {children}} +
    + ); +}; + +export default ModalHeader; diff --git a/ui-kit/src/components/Modal/ModalWindow.tsx b/ui-kit/src/components/Modal/ModalWindow.tsx new file mode 100644 index 00000000..467d0ba2 --- /dev/null +++ b/ui-kit/src/components/Modal/ModalWindow.tsx @@ -0,0 +1,17 @@ +import React, { ReactNode } from 'react'; +import classnames from 'classnames'; + +interface ModalWindowProps { + children: ReactNode; + size: 'small' | 'medium'; +} + +const ModalWindow = ({ children, size }: ModalWindowProps) => { + return ( +
    + {children} +
    + ); +}; + +export default ModalWindow; diff --git a/ui-kit/src/components/Modal/index.tsx b/ui-kit/src/components/Modal/index.tsx new file mode 100644 index 00000000..dc71b172 --- /dev/null +++ b/ui-kit/src/components/Modal/index.tsx @@ -0,0 +1,92 @@ +import React, { ReactElement, cloneElement, useRef, useCallback, useEffect, Children } from 'react'; +import ModalBackdrop from './ModalBackdrop'; +import ModalWindow from './ModalWindow'; +import { generateID } from 'utils/index'; +import { animated, useTransition } from 'react-spring'; +import { useState } from 'react'; + +export interface ModalProps extends React.HTMLAttributes { + show: boolean; + size?: 'small' | 'medium'; + children: ReactElement | ReactElement[]; + onOpen?: () => void; + onClose?: () => void; +} + +const Modal = ({ show, size = 'small', children, onOpen, onClose }: ModalProps) => { + const [showModal, setShowModal] = useState(show); + const backdropRef = useRef(null); + const backdropTransition = useTransition(showModal, null, { + from: { opacity: 0 }, + enter: { opacity: 1 }, + leave: { opacity: 0 }, + }); + const modalTransition = useTransition(showModal, null, { + from: { transform: 'translate(-50%, 100%)', opacity: 0 }, + enter: { transform: 'translate(-50%, -50%)', opacity: 1 }, + leave: { transform: 'translate(-50%, 100%)', opacity: 0 }, + onStart: () => onOpen?.(), + onDestroyed: () => onClose?.(), + }); + + const handleBackdropClick = useCallback( + (event: React.MouseEvent) => { + if (backdropRef.current == null) { + return; + } else if (event.target === backdropRef.current) { + setShowModal(false); + } + }, + [onClose] + ); + + const onKeydown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + setShowModal(false); + } + }; + + useEffect(() => { + window.addEventListener('keydown', onKeydown); + return () => { + window.removeEventListener('keydown', onKeydown); + }; + }, []); + + useEffect(() => { + setShowModal(show); + }, [show]); + + return ( +
    + {backdropTransition.map( + ({ item: show, key, props }) => + show && ( + + + + ) + )} + {modalTransition.map( + ({ item: show, key, props }) => + show && ( + + + {Children.map(children, (child) => { + return cloneElement(child, { + key: generateID('lubycon-modal__children'), + size: size, + }); + })} + + + ) + )} +
    + ); +}; + +export default Modal; +export { default as ModalHeader } from './ModalHeader'; +export { default as ModalContent } from './ModalContent'; +export { default as ModalFooter } from './ModalFooter'; diff --git a/ui-kit/src/components/ProgressBar/index.tsx b/ui-kit/src/components/ProgressBar/index.tsx new file mode 100644 index 00000000..e1efbfa3 --- /dev/null +++ b/ui-kit/src/components/ProgressBar/index.tsx @@ -0,0 +1,54 @@ +import React, { forwardRef } from 'react'; +import classnames from 'classnames'; +import { CombineElementProps } from 'src/types/utils'; +import Text from '../Text'; + +const noop = (value: number) => value; + +export type ProgressBarLabelPosition = 'top' | 'bottom' | 'left' | 'right'; +type Props = CombineElementProps< + 'div', + { + value: number; + max: number; + showLabel?: boolean; + labelPosition?: ProgressBarLabelPosition; + labelFormatter?: (value: number) => string; + } +>; +const ProgressBar = forwardRef(function ProgressBar( + { value, max, className, labelFormatter = noop, showLabel, labelPosition = 'top', ...props }, + ref +) { + const layoutDirection = ['top', 'bottom'].includes(labelPosition) ? 'column' : 'row'; + const ratio = (value / max) * 100; + + return ( +
    + {showLabel === true ? ( + + {labelFormatter(value)} + + ) : null} +
    +
    +
    +
    + ); +}); + +export default ProgressBar; diff --git a/ui-kit/src/components/Radio/index.tsx b/ui-kit/src/components/Radio/index.tsx new file mode 100644 index 00000000..4681a598 --- /dev/null +++ b/ui-kit/src/components/Radio/index.tsx @@ -0,0 +1,42 @@ +import React, { forwardRef, Ref } from 'react'; +import { CombineElementProps } from 'src/types/utils'; +import classnames from 'classnames'; +import { generateID } from 'src/utils/generateID'; +import Text from '../Text'; + +interface RadioBaseProps { + label?: string; + display?: 'block' | 'inline'; +} +type RadioProps = Omit, 'type'>; + +const Radio = ( + { label, display = 'block', style, disabled, ...props }: RadioProps, + ref: Ref +) => { + const id = generateID('radio'); + + return ( + + + + ); +}; + +export default forwardRef(Radio) as typeof Radio; diff --git a/ui-kit/src/components/Selection/index.tsx b/ui-kit/src/components/Selection/index.tsx new file mode 100644 index 00000000..119d5847 --- /dev/null +++ b/ui-kit/src/components/Selection/index.tsx @@ -0,0 +1,62 @@ +import React, { forwardRef, Ref } from 'react'; +import { CombineElementProps } from 'src/types/utils'; +import classnames from 'classnames'; +import Icon from 'components/Icon'; +import { colors } from 'src/constants/colors'; +import { useState } from 'react'; +import { useEffect } from 'react'; +import { Typographys } from '../Text/types'; + +interface SelectionBaseProps { + placeholder?: string; + size?: 'small' | 'medium' | 'large'; +} +type SelectionProps = Omit, 'multiple'>; + +const Selection = ( + { + placeholder = '옵션을 선택하세요', + disabled, + children, + value, + onChange, + size = 'medium', + ...props + }: SelectionProps, + ref: Ref +) => { + const [innerValue, setInnerValue] = useState(value ?? ''); + const iconColor = disabled ? colors.gray60 : colors.gray40; + const typography: Typographys = size === 'large' ? 'p1' : 'p2'; + + useEffect(() => setInnerValue(value ?? ''), [value]); + + return ( +
    + + +
    + ); +}; + +export default forwardRef(Selection) as typeof Selection; diff --git a/ui-kit/src/components/Snackbar/SnackbarBody.tsx b/ui-kit/src/components/Snackbar/SnackbarBody.tsx new file mode 100644 index 00000000..6a58db72 --- /dev/null +++ b/ui-kit/src/components/Snackbar/SnackbarBody.tsx @@ -0,0 +1,29 @@ +import React, { ReactNode, isValidElement, useMemo } from 'react'; +import classnames from 'classnames'; +import Text from 'components/Text'; +import Button from '../Button'; + +interface Props { + message: string; + button?: ReactNode; + onClick?: () => void; +} + +const SnackbarBody = ({ message, button: buttonProp, onClick }: Props) => { + const button = useMemo( + () => + isValidElement(buttonProp) ? buttonProp : , + [buttonProp] + ); + + return ( +
    + + {message} + +
    {buttonProp == null ? null : button}
    +
    + ); +}; + +export default SnackbarBody; diff --git a/ui-kit/src/components/Snackbar/index.tsx b/ui-kit/src/components/Snackbar/index.tsx new file mode 100644 index 00000000..413f8894 --- /dev/null +++ b/ui-kit/src/components/Snackbar/index.tsx @@ -0,0 +1,102 @@ +import React, { useEffect, useState, ReactNode, useMemo } from 'react'; +import { animated, useTransition } from 'react-spring'; +import classnames from 'classnames'; +import SnackbarBody from './SnackbarBody'; +import { CombineElementProps } from 'src/types/utils'; +import { getTranslateAnimation } from './utils'; + +export type SnackbarAlign = 'left' | 'center' | 'right'; + +export type SnackbarProps = Omit< + CombineElementProps< + 'div', + { + show: boolean; + message: string; + button?: ReactNode; + autoHideDuration?: number; + onShow?: () => void; + onHide?: () => void; + onClick?: () => void; + align?: SnackbarAlign; + } + >, + 'children' +>; + +const Snackbar = ({ + show, + message, + button, + autoHideDuration, + onShow, + onHide, + onClick, + className, + style, + align = 'left', + ...rest +}: SnackbarProps) => { + const [isOpen, setOpen] = useState(show); + const translateAnimation = useMemo(() => getTranslateAnimation(align), [align]); + const transition = useTransition(isOpen, null, { + from: { + opacity: 0, + transform: translateAnimation.from, + height: 60, + }, + enter: [ + { height: 60 }, + { + opacity: 1, + transform: translateAnimation.to, + }, + ], + leave: [ + { + opacity: 0, + transform: translateAnimation.from, + }, + { height: 0 }, + ], + onStart: () => { + onShow?.(); + }, + onDestroyed: () => { + onHide?.(); + }, + }); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (autoHideDuration != null && isOpen === true) { + timer = setTimeout(() => { + setOpen(false); + }, autoHideDuration); + } + + return () => clearTimeout(timer); + }, []); + + return ( + <> + {transition.map(({ item, key, props }) => { + return item ? ( + + + + ) : null; + })} + + ); +}; + +export default Snackbar; diff --git a/ui-kit/src/components/Snackbar/utils.ts b/ui-kit/src/components/Snackbar/utils.ts new file mode 100644 index 00000000..1c48ba27 --- /dev/null +++ b/ui-kit/src/components/Snackbar/utils.ts @@ -0,0 +1,21 @@ +import { SnackbarAlign } from '.'; + +export const getTranslateAnimation = (align: SnackbarAlign) => { + switch (align) { + case 'left': + return { + from: 'translateX(-100%)', + to: 'translateX(0)', + }; + case 'center': + return { + from: 'translateY(100%)', + to: 'translateY(0)', + }; + case 'right': + return { + from: 'translateX(100%)', + to: 'translateX(0)', + }; + } +}; diff --git a/ui-kit/src/components/Switch/index.tsx b/ui-kit/src/components/Switch/index.tsx new file mode 100644 index 00000000..755ce63e --- /dev/null +++ b/ui-kit/src/components/Switch/index.tsx @@ -0,0 +1,32 @@ +import React, { forwardRef, Ref } from 'react'; +import { CombineElementProps } from 'src/types/utils'; +import classnames from 'classnames'; +import { generateID } from 'src/utils/generateID'; +import Text from '../Text'; + +interface SwitchBaseProps { + label?: string; + display?: 'block' | 'inline'; +} +type SwitchProps = Omit, 'type'>; + +const Switch = ( + { label, display = 'block', style, ...props }: SwitchProps, + ref: Ref +) => { + const id = generateID('switch'); + + return ( + + ); +}; + +export default forwardRef(Switch) as typeof Switch; diff --git a/ui-kit/src/components/Table/TableBody.tsx b/ui-kit/src/components/Table/TableBody.tsx new file mode 100644 index 00000000..e1c521c2 --- /dev/null +++ b/ui-kit/src/components/Table/TableBody.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import classnames from 'classnames'; +import { TableProps } from './index'; + +const TableBody = ({ children }: TableProps) => { + return ( + + {children} + + ); +}; + +export default TableBody; diff --git a/ui-kit/src/components/Table/TableCell.tsx b/ui-kit/src/components/Table/TableCell.tsx new file mode 100644 index 00000000..7811f74f --- /dev/null +++ b/ui-kit/src/components/Table/TableCell.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import classnames from 'classnames'; +import { TableProps } from './index'; +import { useTableHeadContext } from './TableHead'; + +interface TableCellProps extends TableProps { + as?: 'th' | 'td'; +} + +const TableCell = ({ children, align = 'left', as }: TableCellProps) => { + const { variant } = useTableHeadContext(); + const isHeadCell = variant === 'head' ? 'th' : 'td'; + const Component = as ?? isHeadCell; + + return ( + + {children} + + ); +}; + +export default TableCell; diff --git a/ui-kit/src/components/Table/TableHead.tsx b/ui-kit/src/components/Table/TableHead.tsx new file mode 100644 index 00000000..4cc0a923 --- /dev/null +++ b/ui-kit/src/components/Table/TableHead.tsx @@ -0,0 +1,19 @@ +import React, { createContext } from 'react'; +import { useContext } from 'react'; +import { TableProps } from './index'; + +const TableHeadContext = createContext({ variant: '' }); + +const TableHead = ({ children }: TableProps) => { + return ( + + {children} + + ); +}; + +export function useTableHeadContext() { + return useContext(TableHeadContext); +} + +export default TableHead; diff --git a/ui-kit/src/components/Table/TableRow.tsx b/ui-kit/src/components/Table/TableRow.tsx new file mode 100644 index 00000000..4fbfedae --- /dev/null +++ b/ui-kit/src/components/Table/TableRow.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { TableProps } from './index'; + +const TableRow = ({ children }: TableProps) => { + return {children}; +}; + +export default TableRow; diff --git a/ui-kit/src/components/Table/index.tsx b/ui-kit/src/components/Table/index.tsx new file mode 100644 index 00000000..5779ba2a --- /dev/null +++ b/ui-kit/src/components/Table/index.tsx @@ -0,0 +1,16 @@ +import React, { HTMLAttributes } from 'react'; +import classnames from 'classnames'; + +export interface TableProps extends Omit, 'align' | 'bgcolor'> { + align?: 'left' | 'center' | 'right'; +} + +const Table = ({ children }: TableProps) => { + return {children}
    ; +}; + +export default Table; +export { default as TableHead } from './TableHead'; +export { default as TableBody } from './TableBody'; +export { default as TableRow } from './TableRow'; +export { default as TableCell } from './TableCell'; diff --git a/ui-kit/src/components/Tabs/TabsContext.ts b/ui-kit/src/components/Tabs/TabsContext.ts new file mode 100644 index 00000000..3ffd7bb1 --- /dev/null +++ b/ui-kit/src/components/Tabs/TabsContext.ts @@ -0,0 +1,22 @@ +import { createContext } from 'react'; + +export interface TabsIndicatorPosition { + width: number; + left: number; +} + +export interface TabsContextValue { + onSelect: (value: string) => void; + indicatorPosition: TabsIndicatorPosition | null; + setIndicatorPosition: (indicator: TabsIndicatorPosition) => void; + selectedValue: string; +} + +const TabsContext = createContext({ + selectedValue: '', + onSelect: () => null, + indicatorPosition: null, + setIndicatorPosition: () => null, +}); + +export default TabsContext; diff --git a/ui-kit/src/components/Tabs/TabsIndicator.tsx b/ui-kit/src/components/Tabs/TabsIndicator.tsx new file mode 100644 index 00000000..9d3d01ec --- /dev/null +++ b/ui-kit/src/components/Tabs/TabsIndicator.tsx @@ -0,0 +1,23 @@ +import React, { memo } from 'react'; +import { animated, useSpring, config } from 'react-spring'; +import { TabsIndicatorPosition } from './TabsContext'; + +const TabsIndicator = memo(({ width, left }: TabsIndicatorPosition) => { + const animation = useSpring({ + width, + left, + config: config.gentle, + }); + + return ( + `translateX(${value}px)`), + }} + /> + ); +}); + +export default TabsIndicator; diff --git a/ui-kit/src/components/Tabs/TabsItem.tsx b/ui-kit/src/components/Tabs/TabsItem.tsx new file mode 100644 index 00000000..0f220c3d --- /dev/null +++ b/ui-kit/src/components/Tabs/TabsItem.tsx @@ -0,0 +1,75 @@ +import React, { ReactNode, useRef, HTMLProps, Ref, forwardRef, useContext, useEffect } from 'react'; +import classnames from 'classnames'; +import useCombinedRefs from 'src/hooks/useCombinedRefs'; +import TabsContext from './TabsContext'; +import Text from 'components/Text'; + +export interface Props + extends Omit, 'role' | 'aria-disabled' | 'aria-selected'> { + children: ReactNode; + disabled?: boolean; + value?: string; +} + +function TabItem( + { children, disabled = false, value = String(children), className, onClick, ...props }: Props, + forwardedRef: Ref +) { + const { selectedValue, onSelect, indicatorPosition, setIndicatorPosition } = useContext( + TabsContext + ); + const isSelected = selectedValue === value; + const internalRef = useRef(null); + + const ref = useCombinedRefs(internalRef, forwardedRef); + + useEffect(() => { + if (isSelected) { + const width = internalRef.current!.clientWidth; + const left = internalRef.current!.offsetLeft; + setIndicatorPosition({ width, left }); + } + }, [isSelected, value, setIndicatorPosition]); + + return ( +
    { + ref(element); + + if (indicatorPosition == null && element != null && isSelected) { + const width = element.clientWidth; + const left = element.offsetLeft; + + setIndicatorPosition({ width, left }); + } + }} + className={classnames( + 'lubycon-tabs__item', + { + 'lubycon-tabs__item--selected': isSelected, + 'lubycon-tabs__item--disabled': disabled, + }, + className + )} + role="tab" + aria-disabled={disabled} + aria-selected={isSelected} + {...props} + onClick={(event) => { + if (disabled) { + event.preventDefault(); + return; + } + + onClick?.(event); + onSelect(value); + }} + > + + {children} + +
    + ); +} + +export default forwardRef(TabItem); diff --git a/ui-kit/src/components/Tabs/index.tsx b/ui-kit/src/components/Tabs/index.tsx new file mode 100644 index 00000000..4ff56145 --- /dev/null +++ b/ui-kit/src/components/Tabs/index.tsx @@ -0,0 +1,77 @@ +import React, { HTMLProps, ReactElement, useEffect, useRef, useState } from 'react'; +import classnames from 'classnames'; +import TabsContext, { TabsIndicatorPosition } from './TabsContext'; +import TabsIndicator from './TabsIndicator'; +import { useSpring, config } from 'react-spring'; + +interface Props extends Omit, 'onChange' | 'role'> { + children: ReactElement | ReactElement[]; + onChange?: (value: string) => void; + selectedValue: string; +} + +const Tabs = ({ selectedValue, onChange, children, className, ...props }: Props) => { + const [indicatorPosition, setIndicatorPosition] = useState(null); + const tabRef = useRef(null); + + const [, setScrollLeft] = useSpring(() => { + return { + scrollLeft: tabRef.current?.scrollLeft ?? 0, + config: config.gentle, + onFrame: ({ scrollLeft }: { scrollLeft: number }) => { + if (tabRef.current == null) { + return; + } + + tabRef.current.scrollTo({ + left: scrollLeft, + }); + }, + }; + }); + + useEffect(() => { + if (indicatorPosition == null || tabRef.current == null) { + return; + } + + setScrollLeft({ + immediate: true, + scrollLeft: tabRef.current.scrollLeft, + }); + + const scrollLeft = Math.min( + Math.max( + indicatorPosition.left + indicatorPosition.width / 2 - tabRef.current.clientWidth / 2, + 0 + ), + tabRef.current.scrollWidth - window.innerWidth + ); + + setScrollLeft({ + immediate: false, + scrollLeft, + }); + }, [indicatorPosition, setScrollLeft]); + + return ( + { + onChange?.(value); + }, + indicatorPosition, + setIndicatorPosition, + }} + > +
    + {children} + {indicatorPosition != null ? : null} +
    +
    + ); +}; + +export default Tabs; +export { default as TabsItem } from './TabsItem'; diff --git a/ui-kit/src/components/Tag/index.tsx b/ui-kit/src/components/Tag/index.tsx new file mode 100644 index 00000000..b4af16b1 --- /dev/null +++ b/ui-kit/src/components/Tag/index.tsx @@ -0,0 +1,53 @@ +import React, { isValidElement, ReactText } from 'react'; +import { colors, SemanticColor } from 'src/constants/colors'; +import { CombineElementProps } from 'src/types/utils'; +import classnames from 'classnames'; +import Text from '../Text'; +import Icon from '../Icon'; + +export type TagType = SemanticColor | 'default'; + +type Props = CombineElementProps< + 'div', + { + type?: TagType; + onDelete?: (label: ReactText) => void; + children: ReactText; + } +>; + +const Tag = ({ + type = 'default', + className, + children: label, + onClick, + onDelete, + ...props +}: Props) => { + const isClickable = onClick != null || onDelete != null; + return ( +
    + + {isValidElement(label) ? label : {label}} + + {onDelete != null ? ( + onDelete?.(label)}> + + + ) : null} +
    + ); +}; + +export default Tag; diff --git a/ui-kit/src/components/Text/index.tsx b/ui-kit/src/components/Text/index.tsx new file mode 100644 index 00000000..14e33530 --- /dev/null +++ b/ui-kit/src/components/Text/index.tsx @@ -0,0 +1,34 @@ +import React, { ElementType, Ref, forwardRef } from 'react'; +import { DEFAULT_ELEMENT, FontWeights, Typographys } from './types'; +import { OverridableProps } from 'types/OverridableProps'; +import classnames from 'classnames'; + +interface TextBaseProps { + typography?: Typographys; + fontWeight?: FontWeights; +} +type TextProps = OverridableProps; + +const Text = ( + { typography = 'p1', fontWeight = 'regular', as, className, ...props }: TextProps, + ref: Ref +) => { + const target = as ?? DEFAULT_ELEMENT; + const Component = target; + return ( + + ); +}; + +export default forwardRef(Text) as typeof Text; diff --git a/ui-kit/src/components/Text/types.ts b/ui-kit/src/components/Text/types.ts new file mode 100644 index 00000000..51269a77 --- /dev/null +++ b/ui-kit/src/components/Text/types.ts @@ -0,0 +1,18 @@ +export const typographys = [ + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'subtitle', + 'p1', + 'p2', + 'caption', +] as const; +export type Typographys = typeof typographys[number]; + +export const fontWeights = ['light', 'regular', 'bold', 'black'] as const; +export type FontWeights = typeof fontWeights[number]; + +export const DEFAULT_ELEMENT = 'span' as const; diff --git a/ui-kit/src/components/Tooltip/TooltipBody.tsx b/ui-kit/src/components/Tooltip/TooltipBody.tsx new file mode 100644 index 00000000..0bdf4e87 --- /dev/null +++ b/ui-kit/src/components/Tooltip/TooltipBody.tsx @@ -0,0 +1,37 @@ +import React, { forwardRef, Ref } from 'react'; +import classnames from 'classnames'; +import Text from '../Text'; + +export type TooltipArrowDirection = + | 'top-left' + | 'top-center' + | 'top-right' + | 'left' + | 'right' + | 'bottom-left' + | 'bottom-center' + | 'bottom-right'; + +interface Props { + children: string; + arrowDirection: TooltipArrowDirection; +} +const TooltipBody = forwardRef(function TooltipBody( + { children, arrowDirection }: Props, + forwardedRef: Ref +) { + return ( +
    + {children} +
    + ); +}); + +export default TooltipBody; diff --git a/ui-kit/src/components/Tooltip/index.tsx b/ui-kit/src/components/Tooltip/index.tsx new file mode 100644 index 00000000..08685a0d --- /dev/null +++ b/ui-kit/src/components/Tooltip/index.tsx @@ -0,0 +1,64 @@ +import React, { cloneElement, ReactElement, useState, useMemo, useCallback } from 'react'; +import { animated, useSpring } from 'react-spring'; +import { Portal } from 'src/contexts/Portal'; +import TooltipBody from './TooltipBody'; +import { OffsetPosition, TooltipElementSize, TooltipPosition } from './types'; +import { getArrowDirection, getTooltipPosition } from './utils'; + +interface Props { + show: boolean; + children: ReactElement; + message: string; + position?: TooltipPosition; +} +const Tooltip = ({ show, children, message, position = 'top-center' }: Props) => { + const [tooltipSize, setTooltipSize] = useState({ width: 0, height: 0 }); + const [tooltipOffset, setTooltipOffset] = useState({ + top: -1, + left: -1, + }); + const arrowDirection = useMemo(() => getArrowDirection(position), [position]); + + const childRef = useCallback( + (childElement: HTMLElement | null) => { + if (childElement !== null) { + setTooltipOffset(getTooltipPosition(childElement, tooltipSize, position)); + } + }, + [tooltipSize, position] + ); + + const tooltipRef = useCallback((node: HTMLDivElement | null) => { + if (node !== null) { + setTooltipSize({ + width: node.clientWidth, + height: node.clientHeight, + }); + } + }, []); + + const animation = useSpring({ + visibility: show ? 'visible' : 'hidden', + opacity: show ? 1 : 0, + }); + + return ( + <> + {cloneElement(children, { + ref: childRef, + })} + + + + {message} + + + + + ); +}; + +export default Tooltip; diff --git a/ui-kit/src/components/Tooltip/types.ts b/ui-kit/src/components/Tooltip/types.ts new file mode 100644 index 00000000..02173e8b --- /dev/null +++ b/ui-kit/src/components/Tooltip/types.ts @@ -0,0 +1,19 @@ +export type TooltipPosition = + | 'top-left' + | 'top-center' + | 'top-right' + | 'left' + | 'right' + | 'bottom-left' + | 'bottom-center' + | 'bottom-right'; + +export interface OffsetPosition { + top: number; + left: number; +} + +export interface TooltipElementSize { + width: number; + height: number; +} diff --git a/ui-kit/src/components/Tooltip/utils.ts b/ui-kit/src/components/Tooltip/utils.ts new file mode 100644 index 00000000..6c6b0007 --- /dev/null +++ b/ui-kit/src/components/Tooltip/utils.ts @@ -0,0 +1,96 @@ +import { getAbsoluteOffset } from 'utils/dom'; +import { TooltipArrowDirection } from './TooltipBody'; +import { OffsetPosition, TooltipElementSize, TooltipPosition } from './types'; + +export function getArrowDirection(position: TooltipPosition): TooltipArrowDirection { + switch (position) { + case 'top-left': + return 'bottom-left'; + case 'top-center': + return 'bottom-center'; + case 'top-right': + return 'bottom-right'; + case 'bottom-left': + return 'top-left'; + case 'bottom-center': + return 'top-center'; + case 'bottom-right': + return 'top-right'; + case 'left': + return 'right'; + case 'right': + return 'left'; + default: + return position; + } +} + +export function getTooltipPosition( + childElement: HTMLElement, + tooltipSize: TooltipElementSize, + position: TooltipPosition +): OffsetPosition { + const arrowHeight = 8; + const spacing = 8; + const { clientWidth, clientHeight } = childElement; + + const { top: offsetTop, left: offsetLeft } = getAbsoluteOffset(childElement); + const offsetRight = offsetLeft + clientWidth; + const offsetBottom = offsetTop + clientHeight; + + const { width: tooltipWidth, height: tooltipHeight } = tooltipSize; + + const topPosition = offsetTop - tooltipHeight - arrowHeight - spacing; + const bottomPosition = offsetBottom + arrowHeight + spacing; + const horizontalCenterOfChildren = offsetLeft + clientWidth / 2 - tooltipWidth / 2; + const verticalCenterOfChildren = offsetTop + clientHeight / 2 - tooltipHeight / 2; + + switch (position) { + case 'top-left': + return { + top: topPosition, + left: offsetLeft, + }; + case 'top-center': + return { + top: topPosition, + left: horizontalCenterOfChildren, + }; + case 'top-right': + return { + top: topPosition, + left: offsetRight - tooltipWidth, + }; + case 'bottom-left': + return { + top: bottomPosition, + left: offsetLeft, + }; + case 'bottom-center': + return { + top: bottomPosition, + left: horizontalCenterOfChildren, + }; + case 'bottom-right': + return { + top: bottomPosition, + left: offsetLeft + clientWidth - tooltipWidth, + }; + case 'left': + return { + top: verticalCenterOfChildren, + left: offsetLeft - tooltipWidth - arrowHeight - spacing, + }; + case 'right': + return { + top: verticalCenterOfChildren, + left: offsetRight + arrowHeight + spacing, + }; + + default: + return { + top: offsetTop - arrowHeight - spacing, + left: offsetLeft, + }; + } +} diff --git a/ui-kit/src/components/index.scss b/ui-kit/src/components/index.scss deleted file mode 100644 index ed38bfe5..00000000 --- a/ui-kit/src/components/index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './Button/style.scss'; diff --git a/ui-kit/src/components/index.ts b/ui-kit/src/components/index.ts deleted file mode 100644 index eae9c8e3..00000000 --- a/ui-kit/src/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Button } from './Button'; diff --git a/ui-kit/src/constants/colors.ts b/ui-kit/src/constants/colors.ts new file mode 100644 index 00000000..7dbcbe2b --- /dev/null +++ b/ui-kit/src/constants/colors.ts @@ -0,0 +1,32 @@ +export const colors = { + green40: '#dff6e7', + green50: '#13bc4c', + green60: '#00a438', + blue40: '#e6effe', + blue50: '#135ce9', + blue60: '#013cad', + red40: '#fae7e8', + red50: '#cb121c', + red60: '#9b0b13', + yellow40: '#fdf5ce', + yellow50: '#f0ca08', + yellow60: '#aa8f00', + gray100: '#1b1b1c', + gray90: '#2a2a2c', + gray80: '#4c4d53', + gray70: '#76777d', + gray60: '#8e9095', + gray50: '#b5b6b9', + gray40: '#d0d1d3', + gray30: '#e3e4e5', + gray20: '#f3f4f5', + gray10: '#fcfcfd', + white: '#ffffff', + black: '#000000', +} as const; + +export type SemanticColor = 'positive' | 'informative' | 'negative' | 'notice'; + +export type ColorProperty = keyof typeof colors; + +export type Colors = typeof colors[keyof typeof colors]; diff --git a/ui-kit/src/contexts/Modal.tsx b/ui-kit/src/contexts/Modal.tsx new file mode 100644 index 00000000..ae84c961 --- /dev/null +++ b/ui-kit/src/contexts/Modal.tsx @@ -0,0 +1,85 @@ +import React, { useContext, ReactNode, createContext, useState, useCallback } from 'react'; +import Modal, { ModalContent, ModalFooter, ModalHeader, ModalProps } from 'components/Modal'; +import { generateID } from 'src/utils'; +import { Portal } from './Portal'; + +interface ModalHookOption { + header?: ReactNode; + content?: ReactNode; + footer?: ReactNode; +} + +type ModalOptions = ModalHookOption & Omit; +type ModalStackOptions = ModalHookOption & Omit; + +interface ModalGlobalState { + openModal: (option: ModalOptions) => string; + closeModal: (modalId: string) => void; +} +interface ModalProviderProps { + children: ReactNode; +} + +const ModalContext = createContext({ + openModal: () => '', + closeModal: () => {}, +}); + +export function ModalProvider({ children }: ModalProviderProps) { + const [openedModalStack, setOpenedModalStack] = useState([]); + + const openModal = useCallback( + ({ id = generateID('lubycon-modal'), ...option }: ModalOptions) => { + const modal = { id, show: true, ...option }; + setOpenedModalStack([...openedModalStack, modal]); + return id; + }, + [openedModalStack] + ); + + const closeModal = useCallback((closedModalId: string) => { + setOpenedModalStack((stack) => + stack.map((modal) => { + return modal.id === closedModalId + ? { + ...modal, + show: false, + } + : modal; + }) + ); + }, []); + + const removeModalFromStack = (closedModalId: string) => { + setOpenedModalStack((stack) => stack.filter((modal) => modal.id !== closedModalId)); + }; + + return ( + + {children} + + {openedModalStack.map(({ id, show, title, content, footer, ...modalProps }) => ( + removeModalFromStack(id ?? '')} + {...modalProps} + > + {title} + {content} + {footer} + + ))} + + + ); +} + +export function useModal() { + return useContext(ModalContext); +} diff --git a/ui-kit/src/contexts/Portal.tsx b/ui-kit/src/contexts/Portal.tsx new file mode 100644 index 00000000..d4cb704e --- /dev/null +++ b/ui-kit/src/contexts/Portal.tsx @@ -0,0 +1,36 @@ +import React, { createContext, ReactNode, useContext, useState } from 'react'; +import { createPortal } from 'react-dom'; + +export const PortalContext = createContext(null); + +interface PortalProviderProps { + children: ReactNode; +} + +export function PortalProvider({ children }: PortalProviderProps) { + const [portalRef, setPortalRef] = useState(null); + + return ( + + {children} +
    { + if (portalRef !== null || element === null) { + return; + } + + setPortalRef(element); + }} + /> + + ); +} + +interface PortalConsumerProps { + children: ReactNode; +} +export function Portal({ children }: PortalConsumerProps) { + const portalRef = useContext(PortalContext); + return portalRef == null ? null : createPortal(children, portalRef); +} diff --git a/ui-kit/src/contexts/Snackbar.tsx b/ui-kit/src/contexts/Snackbar.tsx new file mode 100644 index 00000000..1f75507c --- /dev/null +++ b/ui-kit/src/contexts/Snackbar.tsx @@ -0,0 +1,97 @@ +import React, { ReactNode, createContext, useState, useCallback, useContext } from 'react'; +import classnames from 'classnames'; +import Snackbar, { SnackbarAlign, SnackbarProps } from 'components/Snackbar'; +import { generateID } from 'src/utils'; +import { Portal } from './Portal'; +import { isMatchedSM } from 'src/utils/mediaQuery'; + +type SnackbarOptions = Omit; + +const aligns: SnackbarAlign[] = ['left', 'center', 'right']; + +interface SnackbarGlobalState { + openSnackbar: (option: SnackbarOptions) => void; + closeSnackbar: (toastId: string) => void; +} +const SnackbarContext = createContext({ + openSnackbar: () => {}, + closeSnackbar: () => {}, +}); + +interface SnackbarProviderProps { + children: ReactNode; + maxStack?: number; +} +export function SnackbarProvider({ children, maxStack = 3 }: SnackbarProviderProps) { + const [openedSnackbarQueue, setOpenedSnackbarQueue] = useState([]); + + const openSnackbar = useCallback( + ({ + id = generateID('lubycon-snackbar'), + align: rawAlign = 'left', + ...option + }: SnackbarOptions) => { + const align = isMatchedSM() ? 'center' : rawAlign; + const snackbar = { id, align, ...option }; + const [, ...rest] = openedSnackbarQueue; + + if (openedSnackbarQueue.length >= maxStack) { + setOpenedSnackbarQueue([...rest, snackbar]); + } else { + setOpenedSnackbarQueue([...openedSnackbarQueue, snackbar]); + } + }, + [openedSnackbarQueue] + ); + + const closeSnackbar = useCallback( + (closedSnackbarId: string) => { + setOpenedSnackbarQueue( + openedSnackbarQueue.filter((snackbar) => snackbar.id !== closedSnackbarId) + ); + }, + [openedSnackbarQueue] + ); + + return ( + + {children} + + {aligns.map((align) => ( +
    + {openedSnackbarQueue + .filter((snackbar) => snackbar.align === align) + .map(({ id, onHide, autoHideDuration = 3000, ...snackbarProps }) => ( + { + closeSnackbar(id ?? ''); + onHide?.(); + }} + align={align} + {...snackbarProps} + /> + ))} +
    + ))} +
    +
    + ); +} + +export function useSnackbar() { + return useContext(SnackbarContext); +} diff --git a/ui-kit/src/hooks/useCombinedRefs.ts b/ui-kit/src/hooks/useCombinedRefs.ts new file mode 100644 index 00000000..14f529e9 --- /dev/null +++ b/ui-kit/src/hooks/useCombinedRefs.ts @@ -0,0 +1,18 @@ +import { Ref, useCallback, MutableRefObject, RefCallback } from 'react'; + +export default function useCombinedRefs( + ...refs: Array | RefCallback> +): RefCallback { + return useCallback( + (value: T) => { + for (const ref of refs) { + if (typeof ref === 'function') { + ref(value); + } else if (ref != null) { + (ref as MutableRefObject).current = value; + } + } + }, + [refs] + ); +} diff --git a/ui-kit/src/hooks/useResizeObserver.ts b/ui-kit/src/hooks/useResizeObserver.ts new file mode 100644 index 00000000..c24bbbb5 --- /dev/null +++ b/ui-kit/src/hooks/useResizeObserver.ts @@ -0,0 +1,23 @@ +import { RefObject, useEffect, useRef } from 'react'; +import ResizeObserver from 'resize-observer-polyfill'; + +export function useResizeObserver( + ref: RefObject, + resizeCallback: (arg: ResizeObserverEntry['contentRect']) => void +) { + const resizeObsesrverRef = useRef(null); + const onResize = useRef(resizeCallback); + + useEffect(() => { + if (ref.current === null) { + return; + } + + resizeObsesrverRef.current = new ResizeObserver((entries) => { + onResize.current(entries[0].contentRect); + }); + resizeObsesrverRef.current.observe(ref.current); + + return () => resizeObsesrverRef.current?.disconnect(); + }, [ref]); +} diff --git a/ui-kit/src/index.ts b/ui-kit/src/index.ts new file mode 100644 index 00000000..a9f4c235 --- /dev/null +++ b/ui-kit/src/index.ts @@ -0,0 +1,31 @@ +export { default as Alert } from './components/Alert'; +export { default as Button } from './components/Button'; +export { default as Checkbox } from './components/Checkbox'; +export { Row, Column } from './components/Grid'; +export { default as Container } from './components/Container'; +export { default as Radio } from './components/Radio'; +export { default as Selection } from './components/Selection'; +export { default as Switch } from './components/Switch'; +export { default as Text } from './components/Text'; +export { default as LubyconUIKitProvider } from './components/LubyconUIKitProvider'; +export { default as Tooltip } from './components/Tooltip'; +export { default as Tabs, TabsItem } from './components/Tabs'; +export { + default as Card, + CardHeader, + CardContent, + CardImageContent, + CardFooter, +} from './components/Card'; +export { default as Snackbar } from './components/Snackbar'; +export { default as List, ListItem, ListItemImage } from './components/List'; +export { default as Input } from './components/Input'; +export { default as ProgressBar } from './components/ProgressBar'; +export { default as Accordion } from './components/Accordion'; +export { default as Tag } from './components/Tag'; +export { default as Modal, ModalHeader, ModalContent, ModalFooter } from './components/Modal'; +export { default as Table, TableHead, TableBody, TableRow, TableCell } from './components/Table'; +export { Portal } from './contexts/Portal'; +export { useSnackbar } from './contexts/Snackbar'; +export { colors } from './constants/colors'; +export { default as Icon } from './components/Icon'; diff --git a/ui-kit/src/sass/components/_Accordion.scss b/ui-kit/src/sass/components/_Accordion.scss new file mode 100644 index 00000000..b31ac4f3 --- /dev/null +++ b/ui-kit/src/sass/components/_Accordion.scss @@ -0,0 +1,42 @@ +$animation-duration: 0.3s ease-in-out; + +.lubycon-accordion { + border: { + top: 1px solid get-color('gray20'); + bottom: 1px solid get-color('gray20'); + } + & + & { + border-top: none; + } + &--opened { + .lubycon-accordion__label__icon { + transform: rotate(180deg); + } + } + &__label { + display: flex; + align-items: center; + padding: 16px; + user-select: none; + cursor: pointer; + transition: background-color 0.1s ease-in-out; + &:hover { + background-color: get-color('gray10'); + } + &__icon { + margin-right: 16px; + transition: transform $animation-duration; + } + &__text { + color: get-color('gray90'); + } + } + &__cover { + transition: height $animation-duration; + overflow: hidden; + &__content { + padding: 8px 16px 16px 50px; + transition: opacity $animation-duration; + } + } +} diff --git a/ui-kit/src/sass/components/_Alert.scss b/ui-kit/src/sass/components/_Alert.scss new file mode 100644 index 00000000..09b9e091 --- /dev/null +++ b/ui-kit/src/sass/components/_Alert.scss @@ -0,0 +1,29 @@ +$alert-horizental-padding: 14px; + +.lubycon-alert { + display: flex; + justify-content: flex-start; + align-items: center; + border-radius: 4px; + padding: 10px $alert-horizental-padding; + + &--type-negative { + background-color: get-color('red40'); + } + &--type-notice { + background-color: get-color('yellow40'); + } + &--type-informative { + background-color: get-color('blue40'); + } + &--type-positive { + background-color: get-color('green40'); + } + + &__title { + margin-right: 8px; + } + &__icon { + margin-right: $alert-horizental-padding; + } +} diff --git a/ui-kit/src/sass/components/_Button.scss b/ui-kit/src/sass/components/_Button.scss new file mode 100644 index 00000000..0bddbafc --- /dev/null +++ b/ui-kit/src/sass/components/_Button.scss @@ -0,0 +1,60 @@ +@mixin hoverAndActiveStyle($color) { + &:hover, + &:active { + background-color: get-color($color); + } +} + +.lubycon-button { + font: inherit; + text-align: inherit; + outline: none; + border: 0; + margin: 0; + background-color: transparent; + border-radius: 4px; + color: get-color('gray90'); + cursor: pointer; + -webkit-font-smoothing: antialiased; + transition: background-color 0.2s ease-in-out; + + @include hoverAndActiveStyle('gray20'); + + &--small { + padding: 4px 16px; + } + &--medium { + padding: 8px 16px; + } + &--large { + padding: 12px 32px; + border-radius: 8px; + } + + &--type-informative { + background-color: get-color('blue50'); + color: get-color('white'); + @include hoverAndActiveStyle('blue60'); + } + &--type-positive { + background-color: get-color('green50'); + color: get-color('white'); + @include hoverAndActiveStyle('green60'); + } + &--type-notice { + background-color: get-color('yellow50'); + color: get-color('white'); + @include hoverAndActiveStyle('yellow60'); + } + &--type-negative { + background-color: get-color('red50'); + color: get-color('white'); + @include hoverAndActiveStyle('red60'); + } + + &:disabled { + color: get-color('gray60'); + background-color: get-color('gray40'); + cursor: not-allowed; + } +} diff --git a/ui-kit/src/sass/components/_Card.scss b/ui-kit/src/sass/components/_Card.scss new file mode 100644 index 00000000..fd35c694 --- /dev/null +++ b/ui-kit/src/sass/components/_Card.scss @@ -0,0 +1,40 @@ +$card-padding: 16px 20px; +$default-radius: 4px; + +.lubycon-card { + display: flex; + flex-direction: column; + border-radius: $default-radius; + overflow: hidden; + background-color: get-color('white'); + &__header { + order: 0; + padding: $card-padding; + border-bottom: 1px solid get-color('gray30'); + overflow: hidden; + } + &__content { + order: 1; + padding: $card-padding; + } + &__image-content { + img { + width: 100%; + vertical-align: top; + } + } + &__footer { + display: flex; + order: 2; + padding: $card-padding; + &--align-flex-start { + justify-content: flex-start; + } + &--align-center { + justify-content: center; + } + &--align-flex-end { + justify-content: flex-end; + } + } +} diff --git a/ui-kit/src/sass/components/_Checkbox.scss b/ui-kit/src/sass/components/_Checkbox.scss new file mode 100644 index 00000000..d6886e69 --- /dev/null +++ b/ui-kit/src/sass/components/_Checkbox.scss @@ -0,0 +1,74 @@ +$box-size: 16px; + +.lubycon-checkbox { + display: flex; + align-items: center; + cursor: pointer; + + &:hover:not(&--disabled) { + .lubycon-checkbox__control { + border-color: get-color('green50'); + } + } + + &__input { + display: grid; + grid-template-areas: 'checkbox'; + + > input, + > span { + grid-area: checkbox; + } + > input { + opacity: 0; + width: $box-size; + height: $box-size; + padding: 0; + margin: 0; + cursor: pointer; + } + } + + &__control { + display: flex; + align-items: center; + justify-content: center; + width: $box-size; + height: $box-size; + border-radius: 2px; + box-sizing: border-box; + border: 1px solid get-color('gray40'); + margin-right: 8px; + + > svg { + transition: transform 0.1s; + transform: scale(0); + } + } + + &__input input:checked + &__control svg { + transform: scale(1); + } + &__input input:checked + &__control { + background-color: get-color('green50'); + border-color: get-color('green50'); + } + + &--display-inline { + display: inline-flex; + } + + &--disabled { + cursor: not-allowed; + color: get-color('gray50'); + + input { + cursor: not-allowed; + } + + .lubycon-checkbox__input > input:checked + .lubycon-checkbox__control { + border-color: get-color('gray40'); + background-color: get-color('gray40'); + } + } +} diff --git a/ui-kit/src/sass/components/_Column.scss b/ui-kit/src/sass/components/_Column.scss new file mode 100644 index 00000000..744bd869 --- /dev/null +++ b/ui-kit/src/sass/components/_Column.scss @@ -0,0 +1,25 @@ +.lubycon-grid__column { + position: relative; + padding-right: ($grid-gutter / 2); + padding-left: ($grid-gutter / 2); + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; + box-sizing: border-box; +} + +@each $breakpoint, $size in $breakpoints { + .lubycon-grid__column--#{$breakpoint}--auto { + flex: 0 0 auto; + } + + @for $i from 1 through $max-columns { + .lubycon-grid__column--#{$breakpoint}--#{$i} { + @include media-breakpoint($breakpoint) { + flex: 0 0 percentage($i / $max-columns); + width: percentage($i / $max-columns); + } + } + } +} diff --git a/ui-kit/src/sass/components/_Container.scss b/ui-kit/src/sass/components/_Container.scss new file mode 100644 index 00000000..4e7deb91 --- /dev/null +++ b/ui-kit/src/sass/components/_Container.scss @@ -0,0 +1,11 @@ +.lubycon-container { + width: 100%; + max-width: none; + margin: 0 auto; + &--fluid { + max-width: auto; + } + @include media-breakpoint(sm) { + max-width: 1200px; + } +} diff --git a/ui-kit/src/sass/components/_Icon.scss b/ui-kit/src/sass/components/_Icon.scss new file mode 100644 index 00000000..fb346a53 --- /dev/null +++ b/ui-kit/src/sass/components/_Icon.scss @@ -0,0 +1,29 @@ +.lubycon-icon { + display: inline-block; + &--outline { + path { + fill: transparent; + } + } + &--filled { + path { + stroke: transparent; + } + } + &__icon-body { + &--hide-origin-icon { + display: none; + } + } + &__fallback-icon { + display: none; + vertical-align: top; + &--show-fallback-icon { + display: inline-block; + } + } + svg { + width: 100%; + vertical-align: top; + } +} diff --git a/ui-kit/src/sass/components/_Input.scss b/ui-kit/src/sass/components/_Input.scss new file mode 100644 index 00000000..bbf66222 --- /dev/null +++ b/ui-kit/src/sass/components/_Input.scss @@ -0,0 +1,81 @@ +$label-position: 34px; +$description-position: 30px; + +.lubycon-input { + position: relative; + display: inline-block; + background-color: get-color('gray20'); + padding: 10px; + border-radius: 8px; + border: 1px solid transparent; + transition: border 0.1s ease-in-out, background-color 0.1s ease-in-out; + box-sizing: border-box; + + &--disabled { + background-color: get-color('gray30'); + .lubycon-input__form__input-element { + cursor: not-allowed; + &::placeholder { + color: get-color('gray60'); + } + } + .lubycon-input__label { + color: get-color('gray60'); + } + } + + &--focused { + border-color: get-color('gray100'); + background-color: get-color('gray10'); + } + + &--with-label { + margin-top: $label-position; + } + &--with-description { + margin-bottom: $description-position; + } + &--has-error { + border-color: get-color('red50'); + .lubycon-input__label { + color: get-color('red50'); + } + .lubycon-input__description { + color: get-color('red50'); + } + } + + &__label { + position: absolute; + left: 0; + top: -$label-position; + transition: color 0.1s ease-in-out; + } + + &__form { + display: flex; + align-items: center; + &__input-element { + flex-grow: 1; + background-color: transparent; + border: none; + outline: none; + line-height: 16px; + margin-top: 3px; + &::placeholder { + color: get-color('gray70'); + } + } + &__left, + &__right { + display: inline-flex; + } + } + + &__description { + position: absolute; + left: 0; + bottom: -$description-position; + color: get-color('gray70'); + } +} diff --git a/ui-kit/src/sass/components/_List.scss b/ui-kit/src/sass/components/_List.scss new file mode 100644 index 00000000..76cd2a37 --- /dev/null +++ b/ui-kit/src/sass/components/_List.scss @@ -0,0 +1,69 @@ +.lubycon-list { + padding: 0; + margin: 0; + display: flex; + flex-direction: column; +} + +.lubycon-list__item { + display: flex; + align-items: center; + list-style: none; + margin: 0; + padding: 13px 0; + & + & { + border-top: 1px solid get-color('gray20'); + } + &--clickable { + transition: background-color 0.1s ease-in-out; + cursor: pointer; + &:hover { + background-color: get-color('gray10'); + } + &:active { + background-color: get-color('gray20'); + } + } + + &__left { + padding-left: 12px; + width: auto; + flex: 0 0 auto; + } + &__center { + padding: 0 16px; + display: flex; + flex-direction: column; + flex-grow: 1; + &__title { + color: get-color('gray90'); + } + &__content { + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + display: -webkit-box; + overflow: hidden; + } + &__caption { + margin-top: 4px; + color: get-color('gray60'); + } + } + &__right { + padding-right: 8px; + } + + &__image { + position: relative; + width: 48px; + height: 48px; + border-radius: 4px; + overflow: hidden; + background-repeat: no-repeat; + background-size: cover; + background-position: 50% 50%; + > img { + display: none; + } + } +} diff --git a/ui-kit/src/sass/components/_Modal.scss b/ui-kit/src/sass/components/_Modal.scss new file mode 100644 index 00000000..16cd0476 --- /dev/null +++ b/ui-kit/src/sass/components/_Modal.scss @@ -0,0 +1,46 @@ +.lubycon-modal { + &__overlay { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: get-color('gray100'); + opacity: 0.5; + z-index: 1000; + } + &__window-wrapper { + position: fixed; + left: 50%; + top: 50%; + z-index: 1001; + } + &__window { + background-color: get-color('gray10'); + border-radius: 4px; + box-sizing: border-box; + &--small { + width: 280px; + padding: 16px 20px; + } + &--medium { + width: 400px; + padding: 20px 24px; + } + } + &__title { + color: get-color('gray100'); + margin-top: 0; + margin-bottom: 12px; + } + &__content { + color: get-color('gray70'); + margin-bottom: 24px; + white-space: pre-wrap; + } + &__footer { + display: flex; + align-items: center; + justify-content: flex-end; + } +} diff --git a/ui-kit/src/sass/components/_ProgressBar.scss b/ui-kit/src/sass/components/_ProgressBar.scss new file mode 100644 index 00000000..9a524ea9 --- /dev/null +++ b/ui-kit/src/sass/components/_ProgressBar.scss @@ -0,0 +1,48 @@ +$label-vertical-space: 4px; +$label-horizontal-space: 12px; + +.lubycon-progress-bar { + display: flex; + + &--direction-row { + flex-direction: row; + align-items: center; + } + &--direction-column { + flex-direction: column; + } + + &__label { + text-align: center; + &--position-top { + margin-bottom: $label-vertical-space; + order: 0; + } + &--position-bottom { + margin-top: $label-vertical-space; + order: 2; + } + &--position-right { + margin-left: $label-horizontal-space; + order: 2; + } + &--position-left { + margin-right: $label-horizontal-space; + order: 0; + } + } + &__bar { + display: flex; + flex-grow: 1; + order: 1; + width: 100%; + background-color: get-color('gray30'); + height: 4px; + border-radius: 100px; + overflow: hidden; + &__fill { + transition: width 0.3s ease-out; + background-color: get-color('blue50'); + } + } +} diff --git a/ui-kit/src/sass/components/_Radio.scss b/ui-kit/src/sass/components/_Radio.scss new file mode 100644 index 00000000..ebf64ab8 --- /dev/null +++ b/ui-kit/src/sass/components/_Radio.scss @@ -0,0 +1,75 @@ +$indicator-size: 16px; + +.lubycon-radio { + position: relative; + cursor: pointer; + display: flex; + align-items: center; + justify-content: flex-start; + + label { + cursor: pointer; + } + + .lubycon-radio__indicator { + position: relative; + width: $indicator-size; + height: $indicator-size; + border: 1px solid get-color('gray40'); + margin-right: 10px; + border-radius: 50%; + overflow: hidden; + &::before { + content: ''; + width: $indicator-size / 2; + height: $indicator-size / 2; + position: absolute; + background-color: #ffffff; + border-radius: 50%; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + transition: transform 0.3s ease-in-out; + } + } + + .lubycon-radio__label { + display: flex; + align-items: center; + } + + .lubycon-radio__input { + position: absolute; + visibility: hidden; + appearance: none; + &:checked + .lubycon-radio__indicator { + border-color: get-color('green50'); + background-color: get-color('green50'); + &::before { + transform: translate(-50%, -50%) scale(1); + } + } + } + + &:hover:not(&--disabled) { + .lubycon-radio__indicator { + border-color: get-color('green50'); + } + } + + &--display-inline { + display: inline-flex; + } + + &--disabled { + cursor: not-allowed; + label { + color: get-color('gray40'); + cursor: not-allowed; + } + .lubycon-radio__input:checked + .lubycon-radio__indicator { + border-color: get-color('gray40'); + background-color: get-color('gray40'); + } + } +} diff --git a/ui-kit/src/sass/components/_Row.scss b/ui-kit/src/sass/components/_Row.scss new file mode 100644 index 00000000..65436b5c --- /dev/null +++ b/ui-kit/src/sass/components/_Row.scss @@ -0,0 +1,40 @@ +@mixin direction($direction) { + &--direction-#{$direction} { + flex-direction: $direction; + } +} +@mixin justify($align) { + &--justify-#{$align} { + justify-content: $align; + } +} +@mixin alignItems($align) { + &--align-items-#{$align} { + align-items: $align; + } +} + +.lubycon-grid__row { + display: flex; + margin: 0 #{-$grid-gutter / 2}; + flex-wrap: wrap; + box-sizing: border-box; + + @include direction(row); + @include direction(column); + @include direction(row-reverse); + @include direction(column-reverse); + + @include justify(flex-start); + @include justify(center); + @include justify(flex-end); + @include justify(space-between); + @include justify(space-around); + @include justify(space-evenly); + + @include alignItems(flex-start); + @include alignItems(center); + @include alignItems(flex-end); + @include alignItems(stretch); + @include alignItems(baseline); +} diff --git a/ui-kit/src/sass/components/_Selection.scss b/ui-kit/src/sass/components/_Selection.scss new file mode 100644 index 00000000..192af8b2 --- /dev/null +++ b/ui-kit/src/sass/components/_Selection.scss @@ -0,0 +1,57 @@ +.lubycon-selection { + position: relative; + display: inline-block; + background-color: white; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + &:hover { + background-color: get-color('gray20'); + } + + &--disabled { + background-color: get-color('gray40'); + cursor: not-allowed; + &:hover { + background-color: get-color('gray40'); + } + } + + &--empty select.lubycon-selection__select { + color: get-color('gray50'); + } + + &--size-small { + .lubycon-selection__select { + padding: 4px 32px 4px 16px; + } + } + &--size-medium { + .lubycon-selection__select { + padding: 8px 36px 8px 16px; + } + } + &--size-large { + .lubycon-selection__select { + padding: 12px 60px 12px 20px; + } + } + + .lubycon-selection__select { + width: 100%; + background-color: transparent; + appearance: none; + outline: none; + border: none; + cursor: inherit; + } + + .lubycon-icon { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + stroke: get-color('grey40'); + } +} diff --git a/ui-kit/src/sass/components/_Snackbar.scss b/ui-kit/src/sass/components/_Snackbar.scss new file mode 100644 index 00000000..ac00ef7b --- /dev/null +++ b/ui-kit/src/sass/components/_Snackbar.scss @@ -0,0 +1,96 @@ +$snackbar-margin: 12px; +$snackbar-container-padding: 48px; +$snackbar-container-mobile-padding: 12px; + +.lubycon-snackbar { + overflow: visible; + & + & { + padding-bottom: $snackbar-margin; + } + + .lubycon-snackbar__body { + display: inline-flex; + justify-content: space-between; + align-items: center; + padding: 12px 8px 12px 16px; + width: 100%; + border-radius: 4px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; + background-color: get-color('gray90'); + color: get-color('white'); + @include media-breakpoint(sm) { + width: 400px; + } + } + + .lubycon-snackbar__text { + white-space: pre-line; + } + + .lubycon-snackbar__body__buttons { + margin-left: 16px; + .lubycon-button { + color: get-color('blue40'); + white-space: pre; + &:hover { + background-color: get-color('gray100'); + } + & + & { + margin-left: 8px; + } + } + } +} + +%context-container { + position: fixed; + display: flex; + flex-direction: column-reverse; + top: auto; + bottom: 0; + margin-bottom: $snackbar-container-mobile-padding; + z-index: 5000; + box-sizing: border-box; + @include media-breakpoint(sm) { + margin-bottom: $snackbar-container-padding; + } +} + +.lubycon-snackbar__context-container { + &--align-left { + @extend %context-container; + left: 0; + right: auto; + margin-left: $snackbar-container-mobile-padding; + @include media-breakpoint(sm) { + margin-left: $snackbar-container-padding; + } + } + &--align-center { + @extend %context-container; + left: 50%; + transform: translateX(-50%); + width: 100%; + padding: { + left: $snackbar-container-mobile-padding; + right: $snackbar-container-mobile-padding; + } + @include media-breakpoint(sm) { + width: auto; + padding: { + left: 0; + right: 0; + } + } + } + &--align-right { + @extend %context-container; + left: auto; + right: 0; + margin-right: $snackbar-container-mobile-padding; + @include media-breakpoint(sm) { + margin-right: $snackbar-container-padding; + } + } +} diff --git a/ui-kit/src/sass/components/_Switch.scss b/ui-kit/src/sass/components/_Switch.scss new file mode 100644 index 00000000..805a9693 --- /dev/null +++ b/ui-kit/src/sass/components/_Switch.scss @@ -0,0 +1,49 @@ +.lubycon-switch { + border-radius: 32px; + display: flex; + align-items: center; + position: relative; + cursor: pointer; + + &__input { + display: none; + } + + &__slider { + position: relative; + width: 40px; + height: 22px; + margin-right: 8px; + border-radius: 32px; + background-color: get-color('gray40'); + transition: background-color 0.3s; + + &::before { + content: ''; + position: absolute; + width: 15px; + height: 15px; + left: 4px; + top: 50%; + transform: translateY(-50%); + background-color: #ffffff; + border-radius: 50%; + transition: transform 0.3s; + } + } + + &__input:checked + &__slider { + background-color: get-color('green50'); + } + + &__input:checked + &__slider::before { + transform: translate(17px, -50%); + } + + &--display-inline { + display: inline-flex; + } + &__label { + margin-top: -2px; + } +} diff --git a/ui-kit/src/sass/components/_Table.scss b/ui-kit/src/sass/components/_Table.scss new file mode 100644 index 00000000..67177956 --- /dev/null +++ b/ui-kit/src/sass/components/_Table.scss @@ -0,0 +1,30 @@ +.lubycon-table { + word-break: break-all; + border-collapse: collapse; + color: get-color('gray100'); + + &__head { + background-color: get-color('gray20'); + } + &__body { + background-color: get-color('gray10'); + .lubycon-table__row:not(:last-child) { + border-bottom: 1px solid get-color('gray20'); + } + } + &__cell { + padding: 8px 20px; + } + + &--align { + &-left { + text-align: left; + } + &-center { + text-align: center; + } + &-right { + text-align: right; + } + } +} diff --git a/ui-kit/src/sass/components/_Tabs.scss b/ui-kit/src/sass/components/_Tabs.scss new file mode 100644 index 00000000..0bed04f0 --- /dev/null +++ b/ui-kit/src/sass/components/_Tabs.scss @@ -0,0 +1,58 @@ +@mixin tab-indicator-position { + content: ''; + position: absolute; + width: 100%; + left: 0; + bottom: 0; +} + +.lubycon-tabs { + display: flex; + position: relative; + max-width: 100%; + overflow-x: auto; + @include remove-scroll-bar; + + &__item { + position: relative; + padding: 12px 24px; + transition: color 0.2s ease-out; + display: flex; + justify-content: center; + flex-wrap: wrap; + cursor: pointer; + white-space: nowrap; + + &__text { + color: get-color('gray80'); + } + + &--selected .lubycon-tabs__item__text { + font-weight: bold; + color: get-color('gray100'); + } + + &--disabled .lubycon-tabs__item__text { + cursor: not-allowed; + color: get-color('gray40'); + } + + &::after { + @include tab-indicator-position; + background-color: get-color('gray30'); + height: 1px; + transition: background-color 0.2s ease-out; + } + + &:not([aria-disabled='true']):hover::after { + background-color: get-color('gray80'); + } + } + + &__indicator { + @include tab-indicator-position; + background-color: get-color('blue50'); + border-radius: 1px; + height: 2px; + } +} diff --git a/ui-kit/src/sass/components/_Tag.scss b/ui-kit/src/sass/components/_Tag.scss new file mode 100644 index 00000000..faedc77e --- /dev/null +++ b/ui-kit/src/sass/components/_Tag.scss @@ -0,0 +1,64 @@ +@mixin tagColor($color, $hover-color) { + background-color: $color; + &:hover { + background-color: $hover-color; + } +} + +.lubycon-tag { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 0 4px 12px; + border-radius: 4px; + transition: background-color 0.1s ease-in-out; + + &--type { + &-default { + background-color: get-color('gray30'); + } + &-positive { + background-color: get-color('green40'); + } + &-informative { + background-color: get-color('blue40'); + } + &-notice { + background-color: get-color('yellow40'); + } + &-negative { + background-color: get-color('red40'); + } + } + + &--clickable { + cursor: pointer; + &.lubycon-tag--type-default:hover { + background-color: get-color('gray40'); + } + &.lubycon-tag--type-positive:hover { + background-color: #c9f5d8; + } + &.lubycon-tag--type-informative:hover { + background-color: #d0e1fe; + } + &.lubycon-tag--type-notice:hover { + background-color: #fdf1b6; + } + &.lubycon-tag--type-negative:hover { + background-color: #fad2d4; + } + } + + &__label { + margin-right: 12px; + color: get-color('gray90'); + user-select: none; + } + + &__delete-button { + cursor: pointer; + display: flex; + margin-right: 8px; + } +} diff --git a/ui-kit/src/sass/components/_Text.scss b/ui-kit/src/sass/components/_Text.scss new file mode 100644 index 00000000..8a8de41b --- /dev/null +++ b/ui-kit/src/sass/components/_Text.scss @@ -0,0 +1,7 @@ +.lubycon-text { + @each $name, $value in $font-weights { + &.lubycon-text--font-weight-#{$name} { + @include font-weight($name, map-get($font-weights, $name)); + } + } +} diff --git a/ui-kit/src/sass/components/_Tooltip.scss b/ui-kit/src/sass/components/_Tooltip.scss new file mode 100644 index 00000000..1d0d43fa --- /dev/null +++ b/ui-kit/src/sass/components/_Tooltip.scss @@ -0,0 +1,93 @@ +$tooltip-h-padding: 12px; + +.lubycon-tooltip__positioner { + position: absolute; +} + +.lubycon-tooltip__body { + position: relative; + display: inline-flex; + background-color: get-color('gray100'); + color: #ffffff; + padding: 4px $tooltip-h-padding; + border-radius: 4px; + -webkit-font-smoothing: antialiased; +} + +%tooltip-arrow-position { + position: absolute; + width: 0; + height: 0; + content: ''; +} +%tooltip-bottom-arrow { + @extend %tooltip-arrow-position; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 8px solid get-color('gray100'); +} +%tooltip-left-arrow { + @extend %tooltip-arrow-position; + border-bottom: 6px solid transparent; + border-right: 8px solid get-color('gray100'); + border-top: 6px solid transparent; +} +%tooltip-right-arrow { + @extend %tooltip-arrow-position; + border-left: 8px solid get-color('gray100'); + border-bottom: 6px solid transparent; + border-top: 6px solid transparent; +} +%tooltip-top-arrow { + @extend %tooltip-arrow-position; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 8px solid get-color('gray100'); +} + +.lubycon-tooltip__body--arrow { + &-bottom-center::before { + @extend %tooltip-bottom-arrow; + top: 100%; + left: 50%; + transform: translateX(-50%); + } + &-bottom-left::before { + @extend %tooltip-bottom-arrow; + top: 100%; + left: $tooltip-h-padding; + } + &-bottom-right::before { + @extend %tooltip-bottom-arrow; + top: 100%; + right: $tooltip-h-padding; + } + &-right::before { + @extend %tooltip-right-arrow; + top: 50%; + transform: translateY(-50%); + left: 100%; + } + &-left::before { + @extend %tooltip-left-arrow; + top: 50%; + transform: translateY(-50%); + right: 100%; + } + &-top-center::before { + @extend %tooltip-top-arrow; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + } + &-top-left::before { + @extend %tooltip-top-arrow; + bottom: 100%; + left: $tooltip-h-padding; + } + &-top-right::before { + @extend %tooltip-top-arrow; + bottom: 100%; + right: $tooltip-h-padding; + } +} diff --git a/ui-kit/src/sass/components/_index.scss b/ui-kit/src/sass/components/_index.scss new file mode 100644 index 00000000..99fc38f1 --- /dev/null +++ b/ui-kit/src/sass/components/_index.scss @@ -0,0 +1,22 @@ +@import './Accordion'; +@import './Alert'; +@import './Button'; +@import './Text'; +@import './Radio'; +@import './Row'; +@import './Column'; +@import './Checkbox'; +@import './Switch'; +@import './Selection'; +@import './Icon'; +@import './Tooltip'; +@import './Tabs'; +@import './Card'; +@import './Snackbar'; +@import './Container'; +@import './List'; +@import './Input'; +@import './ProgressBar'; +@import './Tag'; +@import './Modal'; +@import './Table'; diff --git a/ui-kit/src/sass/functions/_index.scss b/ui-kit/src/sass/functions/_index.scss new file mode 100644 index 00000000..0e8d1292 --- /dev/null +++ b/ui-kit/src/sass/functions/_index.scss @@ -0,0 +1 @@ +@import './strip-unit'; diff --git a/ui-kit/src/sass/functions/_strip-unit.scss b/ui-kit/src/sass/functions/_strip-unit.scss new file mode 100644 index 00000000..7d5322e8 --- /dev/null +++ b/ui-kit/src/sass/functions/_strip-unit.scss @@ -0,0 +1,7 @@ +@function strip-unit($number) { + @if type-of($number) == 'number' and not unitless($number) { + @return $number / ($number * 0 + 1); + } + + @return $number; +} diff --git a/ui-kit/src/sass/index.scss b/ui-kit/src/sass/index.scss new file mode 100644 index 00000000..6ea120ee --- /dev/null +++ b/ui-kit/src/sass/index.scss @@ -0,0 +1,7 @@ +@import './functions/index'; +@import './utils/index'; +@import './components/index.scss'; + +body { + font-size: 16px; +} diff --git a/ui-kit/src/sass/modules/_test.scss b/ui-kit/src/sass/modules/_test.scss deleted file mode 100644 index 34e76bef..00000000 --- a/ui-kit/src/sass/modules/_test.scss +++ /dev/null @@ -1 +0,0 @@ -// SASS 모듈은 여기에 작성 diff --git a/ui-kit/src/sass/utils/_breakpoints.scss b/ui-kit/src/sass/utils/_breakpoints.scss new file mode 100644 index 00000000..11389afb --- /dev/null +++ b/ui-kit/src/sass/utils/_breakpoints.scss @@ -0,0 +1,13 @@ +$breakpoints: ( + xs: 0, + sm: 576px, + md: 768px, + lg: 992px, + xl: 1200px, +) !default; + +@mixin media-breakpoint($size) { + @media screen and (min-width: map-get($breakpoints, $size)) { + @content; + } +} diff --git a/ui-kit/src/sass/utils/_colors.scss b/ui-kit/src/sass/utils/_colors.scss new file mode 100644 index 00000000..875f92a2 --- /dev/null +++ b/ui-kit/src/sass/utils/_colors.scss @@ -0,0 +1,40 @@ +$colors: ( + 'green40': #dff6e7, + 'green50': #13bc4c, + 'green60': #00a438, + 'blue50': #135ce9, + 'blue40': #e6effe, + 'blue60': #013cad, + 'red50': #cb121c, + 'red40': #fae7e8, + 'red60': #9b0b13, + 'yellow50': #f0ca08, + 'yellow40': #fdf5ce, + 'yellow60': #aa8f00, + 'gray100': #1b1b1c, + 'gray90': #2a2a2c, + 'gray80': #4c4d53, + 'gray70': #76777d, + 'gray60': #8e9095, + 'gray50': #b5b6b9, + 'gray40': #d0d1d3, + 'gray30': #e3e4e5, + 'gray20': #f3f4f5, + 'gray10': #fcfcfd, + 'white': #ffffff, + 'black': #000000, +); + +@mixin color($name, $value) { + :root { + --lubycon-#{$name}: #{$value}; + } +} + +@each $name, $value in $colors { + @include color($name, $value); +} + +@function get-color($name) { + @return map-get($colors, $name); +} diff --git a/ui-kit/src/sass/utils/_font-weights.scss b/ui-kit/src/sass/utils/_font-weights.scss new file mode 100644 index 00000000..ab1a9530 --- /dev/null +++ b/ui-kit/src/sass/utils/_font-weights.scss @@ -0,0 +1,24 @@ +$font-weights: ( + 'light': 300, + 'regular': 400, + 'bold': 700, + 'black': 900, +); + +@mixin font-weight($name, $value) { + font-weight: $value; + font-weight: var(--lubycon-font-weight-#{$name}); +} + +@mixin global-font-weight($name, $value) { + :root { + --lubycon-font-weight-#{$name}: #{$value}; + } + .lubycon-font-weight--#{$name} { + @include font-weight($name, $value); + } +} + +@each $name, $value in $font-weights { + @include global-font-weight($name, $value); +} diff --git a/ui-kit/src/sass/utils/_font.scss b/ui-kit/src/sass/utils/_font.scss new file mode 100644 index 00000000..43685e6e --- /dev/null +++ b/ui-kit/src/sass/utils/_font.scss @@ -0,0 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;700;900&display=swap'); + +html { + font-family: 'Noto Sans KR', sans-serif; +} diff --git a/ui-kit/src/sass/utils/_grid.scss b/ui-kit/src/sass/utils/_grid.scss new file mode 100644 index 00000000..22e1b4fc --- /dev/null +++ b/ui-kit/src/sass/utils/_grid.scss @@ -0,0 +1,2 @@ +$max-columns: 12; +$grid-gutter: 12px; diff --git a/ui-kit/src/sass/utils/_index.scss b/ui-kit/src/sass/utils/_index.scss new file mode 100644 index 00000000..5d061da7 --- /dev/null +++ b/ui-kit/src/sass/utils/_index.scss @@ -0,0 +1,8 @@ +@import './breakpoints'; +@import './grid'; +@import './font'; +@import './font-weights'; +@import './typography'; +@import './shadows'; +@import './colors'; +@import './scrollbar'; diff --git a/ui-kit/src/sass/utils/_scrollbar.scss b/ui-kit/src/sass/utils/_scrollbar.scss new file mode 100644 index 00000000..c36ca4a1 --- /dev/null +++ b/ui-kit/src/sass/utils/_scrollbar.scss @@ -0,0 +1,7 @@ +@mixin remove-scroll-bar { + scrollbar-width: none; + -ms-overflow-style: none; + &::-webkit-scrollbar { + display: none; + } +} diff --git a/ui-kit/src/sass/utils/_shadows.scss b/ui-kit/src/sass/utils/_shadows.scss new file mode 100644 index 00000000..f82dd662 --- /dev/null +++ b/ui-kit/src/sass/utils/_shadows.scss @@ -0,0 +1,18 @@ +@mixin shadow($shadow-level, $offset-y, $blur-radius) { + $shadow: '0px #{$offset-y} #{$blur-radius} rgba(0, 0, 0, 0.1)'; + + :root { + --lubycon-shadow-#{$shadow-level}: #{$shadow}; + } + + .lubycon-shadow--#{$shadow-level} { + box-shadow: #{$shadow}; + box-shadow: var(--lubycon-shadow-#{$shadow-level}); + } +} + +@include shadow(1, 2px, 5px); +@include shadow(2, 3px, 5px); +@include shadow(3, 6px, 10px); +@include shadow(4, 8px, 12px); +@include shadow(5, 24px, 48px); diff --git a/ui-kit/src/sass/utils/_typography.scss b/ui-kit/src/sass/utils/_typography.scss new file mode 100644 index 00000000..88a79674 --- /dev/null +++ b/ui-kit/src/sass/utils/_typography.scss @@ -0,0 +1,30 @@ +@mixin _typography($name, $font-size, $line-height) { + $font-size-number: strip-unit($font-size); + + :root { + --lubycon-font-size-#{$name}: #{$font-size}; + --lubycon-line-height-#{$name}: #{$line-height}; + } + + .lubycon-font-size-#{$name} { + font-size: $font-size; + font-size: var(--lubycon-font-size-#{$name}); + line-height: $line-height; + line-height: var(--lubycon-line-height-#{$name}); + } + + .lubycon-typography-#{$name} { + @extend .lubycon-font-size-#{$name}; + } +} + +@include _typography('h1', 2.625rem, 1.5); +@include _typography('h2', 2rem, 1.5); +@include _typography('h3', 1.75rem, 1.5); +@include _typography('h4', 1.625rem, 1.5); +@include _typography('h5', 1.5rem, 1.5); +@include _typography('h6', 1.25rem, 1.5); +@include _typography('subtitle', 1.125rem, 1.7); +@include _typography('p1', 1rem, 2); +@include _typography('p2', 0.9375rem, 1.7); +@include _typography('caption', 0.75rem, 1.6); diff --git a/ui-kit/src/stories/Accordion.stories.tsx b/ui-kit/src/stories/Accordion.stories.tsx new file mode 100644 index 00000000..5dbfd3fb --- /dev/null +++ b/ui-kit/src/stories/Accordion.stories.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Accordion } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; + +export default { + title: 'Lubycon UI Kit/Accordion', +} as Meta; + +export const Default = () => { + return ( + <> + console.log(`onChange: ${v}`)} + onOpen={() => console.log('handleOpen')} + onClose={() => console.log('handleClose')} + > + 아코디언이 펼쳐지면 아래에 내용이 나옵니다. +
    + 아코디언이 펼쳐지면 아래에 내용이 나옵니다. +
    + 아코디언이 펼쳐지면 아래에 내용이 나옵니다. +
    +
    + + 귀여운 에비츄 + + + 아코디언이 펼쳐지면 아래에 내용이 나옵니다. +
    + 아코디언이 펼쳐지면 아래에 내용이 나옵니다. +
    + 아코디언이 펼쳐지면 아래에 내용이 나옵니다. +
    +
    + + ); +}; diff --git a/ui-kit/src/stories/Alert.stories.tsx b/ui-kit/src/stories/Alert.stories.tsx new file mode 100644 index 00000000..9612ad47 --- /dev/null +++ b/ui-kit/src/stories/Alert.stories.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import Alert from 'components/Alert'; +import { Meta } from '@storybook/react/types-6-0'; +import { Column, Row } from 'src/components/Grid'; +import { SemanticColor } from 'src/constants/colors'; + +export default { + title: 'Lubycon UI Kit/Alert', +} as Meta; + +const alerts: Array<{ type: SemanticColor; title: string }> = [ + { + type: 'negative', + title: '오류', + }, + { + type: 'notice', + title: '경고', + }, + { + type: 'informative', + title: '정보', + }, + { + type: 'positive', + title: '완료', + }, +]; + +export const Default = () => { + return ( + <> + + {alerts.map(({ type, title }) => ( + + + 서브 타이틀을 넣어주세요 + + {title} 메세지를 넣어주세요 + + ))} + + + ); +}; diff --git a/ui-kit/src/stories/Button.stories.tsx b/ui-kit/src/stories/Button.stories.tsx new file mode 100644 index 00000000..70765eff --- /dev/null +++ b/ui-kit/src/stories/Button.stories.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Button, Text, Column, Row } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; +import { SemanticColor } from 'src/constants/colors'; + +export default { + title: 'Lubycon UI Kit/Button', +} as Meta; + +const sizeList = ['small', 'medium', 'large'] as const; +const btnText = '버튼 텍스트'; + +export const Default = () => { + return ( +
    + {sizeList.map((size, index) => ( + + + {size.charAt(0).toUpperCase() + size.slice(1)} + + + + + + + + + ))} +
    + ); +}; + +const semanticColors: SemanticColor[] = ['informative', 'negative', 'notice', 'positive']; +export const Types = () => { + return ( +
    + {semanticColors.map((type, index) => ( + + + {type.charAt(0).toUpperCase() + type.slice(1)} + + + + + + ))} +
    + ); +}; diff --git a/ui-kit/src/stories/Card.stories.tsx b/ui-kit/src/stories/Card.stories.tsx new file mode 100644 index 00000000..7965be40 --- /dev/null +++ b/ui-kit/src/stories/Card.stories.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { + Text, + Button, + Row, + Column, + Card, + CardHeader, + CardContent, + CardImageContent, + CardFooter, + colors, +} from 'src'; +import Icon from 'components/Icon'; +import { Meta } from '@storybook/react/types-6-0'; + +export default { + title: 'Lubycon UI Kit/Card', +} as Meta; + +export const Default = () => { + return ( +
    + + 제목 + + + 내용을 입력하세요. +
    + 내용을 입력하세요. +
    + 내용을 입력하세요. +
    + 내용을 입력하세요. +
    +
    + + + + 더보기 + + + + +
    +
    + ); +}; + +export const ImageCard = () => { + return ( + + + + 제목 + + + + + 더보기 + + + + + + + + + 춤추는 에비츄 + + + + + + + + + 장보는 에비츄 + + + + + ); +}; diff --git a/ui-kit/src/stories/Checkbox.stories.tsx b/ui-kit/src/stories/Checkbox.stories.tsx new file mode 100644 index 00000000..22a4f378 --- /dev/null +++ b/ui-kit/src/stories/Checkbox.stories.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Checkbox } from 'src'; + +export default { + title: 'Lubycon UI Kit/Checkbox', +} as Meta; + +export const Default = () => { + return ( +
    + + +
    + ); +}; + +export const Disabled = () => { + return ( +
    + + + + +
    + ); +}; + +export const inline = () => { + return ( +
    + + +
    + ); +}; diff --git a/ui-kit/src/stories/Colors.stories.tsx b/ui-kit/src/stories/Colors.stories.tsx new file mode 100644 index 00000000..7f0457d7 --- /dev/null +++ b/ui-kit/src/stories/Colors.stories.tsx @@ -0,0 +1,134 @@ +import React from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { colors, Text } from 'src'; +import { ColorProperty, SemanticColor } from 'src/constants/colors'; + +export default { + title: 'Lubycon UI Kit/Colors', +} as Meta; + +const grayScaleNames = [ + 'gray100', + 'gray90', + 'gray80', + 'gray70', + 'gray60', + 'gray50', + 'gray40', + 'gray30', + 'gray20', + 'gray10', +] as const; + +type SemanticColorMap = { + [key in SemanticColor]: Array; +}; + +const semanticColors: SemanticColorMap = { + positive: ['green50', 'green40', 'green60'], + informative: ['blue50', 'blue40', 'blue60'], + negative: ['red50', 'red40', 'red60'], + notice: ['yellow50', 'yellow40', 'yellow60'], +}; + +const semanticColorNames = Object.keys(semanticColors) as Array; + +type IndexMap = { + [key: number]: 'a' | 'b' | 'c'; +}; + +const indexMap: IndexMap = { + 0: 'a', + 1: 'b', + 2: 'c', +}; + +export const Default = () => { + return ( +
    + + 회색 명암(Gray Scale) + +
      + {grayScaleNames.map((name, index) => ( +
    • +
      + 4 ? '100' : '10'})` }} + > + {name.replace(/gray/g, 'gray ')} + +
      +
    • + ))} +
    + + + 의미론적 색상(Semantic Color) + +
    + {semanticColorNames.map((name, index) => ( +
      + {semanticColors[name].map((colorName, colorIndex) => ( +
    • + + {/50/g.test(colorName) ? name[0].toUpperCase() + name.slice(1) : ''} + + + {colorName} + +
    • + ))} +
    + ))} +
    +
    + ); +}; diff --git a/ui-kit/src/stories/Grid.stories.tsx b/ui-kit/src/stories/Grid.stories.tsx new file mode 100644 index 00000000..ed3236b7 --- /dev/null +++ b/ui-kit/src/stories/Grid.stories.tsx @@ -0,0 +1,97 @@ +import React, { CSSProperties } from 'react'; +import { Column, Row, Text, colors } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; + +export default { + title: 'Lubycon UI Kit/Grid', + subcomponents: { Column, Row }, +} as Meta; + +const columns = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; +const columnStyle: CSSProperties = { + border: `1px solid ${colors.blue50}`, + backgroundColor: colors.blue40, + height: 100, + color: colors.gray10, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +}; + +export const Default = () => ( + + {columns.map((column) => ( + + + Column{column} + + + ))} + +); + +export const Stretched = () => ( + + {columns + .filter((v) => v % 2 === 0) + .map((column) => ( + + + Column{column} + + + ))} + +); + +export const Direction = () => ( + + {columns.map((column) => ( + + + Column{column} + + + ))} + +); + +export const Responsive = () => ( + + + + lg=8 md=4, xs=2 + + + + + auto + + + + + lg=1, md=5, xs=8 + + + +); + +export const VariableWidth = () => ( + + + + width: 40px + + + + + Column + + + + + Column + + + +); diff --git a/ui-kit/src/stories/Icon.stories.tsx b/ui-kit/src/stories/Icon.stories.tsx new file mode 100644 index 00000000..5fdf0cc4 --- /dev/null +++ b/ui-kit/src/stories/Icon.stories.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { Icon, Text } from 'src'; +import { IconType } from 'src/components/Icon'; +import { Meta } from '@storybook/react/types-6-0'; +import { ColorProperty, colors } from 'src/constants/colors'; +import { Fragment } from 'react'; + +export default { + title: 'Lubycon UI Kit/Icon', + component: Icon, +} as Meta; + +const Spacer = () => { + return
    ; +}; + +const icons = ['code', 'accessibility', 'alarm', 'airplane']; +export const Default = () => { + return ( +
    + + 아이콘 이름은 + + https://github.com/Lubycon/lubycon-icons + + 를 참고하세요. + +
    +
    + {icons.map((icon) => ( + + + + + ))} +
    +
    + ); +}; + +const colorKeys = Object.keys(colors); +export const Color = () => { + return ( + <> + {colorKeys.map((key) => { + const colorKey = key as ColorProperty; + return ( + +
    + + + +
    + +
    + ); + })} + + ); +}; + +const types: IconType[] = ['filled', 'outline', 'sharp']; + +export const Types = () => { + return ( + <> + {types.map((type) => ( + + {type} +
    + + +
    + ))} + + ); +}; diff --git a/ui-kit/src/stories/Input.stories.tsx b/ui-kit/src/stories/Input.stories.tsx new file mode 100644 index 00000000..56120d4f --- /dev/null +++ b/ui-kit/src/stories/Input.stories.tsx @@ -0,0 +1,90 @@ +import React, { useState } from 'react'; +import { Input, Text, colors } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; +import Icon from 'src/components/Icon'; +import { TextInputType } from 'components/Input'; + +export default { + title: 'Lubycon UI Kit/Input', +} as Meta; + +export const Default = () => { + const [state, setState] = useState(''); + return ( +
    + 입력된 값: {state} + setState(e.target.value)} /> +
    + ); +}; + +export const Label = () => { + return ( +
    + +
    + ); +}; + +export const Placeholder = () => { + return ; +}; + +export const Disabled = () => { + return ; +}; + +export const Error = () => { + const [value, setValue] = useState(''); + const isError = value === ''; + + return ( + setValue(e.target.value)} + right={ + isError ? null : ( + + ) + } + /> + ); +}; + +const types: TextInputType[] = ['text', 'email', 'number', 'password', 'search', 'tel', 'url']; +const covertToTitlecase = (s: string) => `${s.charAt(0).toUpperCase()}${s.slice(1, s.length)}`; +export const Types = () => { + return ( +
    + + 모바일에서는 인풋의 타입에 따라 다른 키보드가 노출되니, 모바일 환경에서 확인해보시는 것을 + 추천합니다. + +
      + {types.map((type) => ( +
    • + +
    • + ))} +
    +
    + ); +}; + +export const LeftAndRight = () => { + return ( +
    + } /> + } /> + } + right={} + /> +
    + ); +}; diff --git a/ui-kit/src/stories/List.stories.tsx b/ui-kit/src/stories/List.stories.tsx new file mode 100644 index 00000000..ae1bebae --- /dev/null +++ b/ui-kit/src/stories/List.stories.tsx @@ -0,0 +1,135 @@ +import React from 'react'; +import { Button, List, ListItem, ListItemImage, colors } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; +import Icon from 'src/components/Icon'; + +export default { + title: 'Lubycon UI Kit/List', +} as Meta; + +const DummyItem = ({ onClick }: { onClick?: () => void }) => ( + +); +const DummyItemWithoutCaption = () => ( + +); +const DummyItemWithoutTitle = () => ( + +); +const noop = () => {}; + +export const Default = () => { + return ( + + + + + + + + ); +}; + +export const WithoutCaption = () => { + return ( + + + + + + + + ); +}; + +export const WithoutTitle = () => { + return ( + + + + + + + + ); +}; + +export const LeftRight = () => { + return ( + + } + title="썸네일 조합형" + content="UI Kit에서 제공되는 이미지 컴포넌트를 사용한 예시입니다" + /> + } + title="썸네일 조합형" + content="UI Kit에서 제공되는 이미지 컴포넌트를 사용한 예시입니다" + caption="5일전" + /> + + } + title="썸네일 조합형" + content="일반 img 태그를 사용한 예시입니다" + caption="5일전" + /> + } + onClick={noop} + /> + 보러가기} + /> + } + title="썸네일 + 버튼 조합형" + content="자세히 보려면 클릭하세요" + right={} + onClick={noop} + /> + + ); +}; + +export const Clickable = () => { + return ( + + + + + + + + ); +}; + +export const Multiline = () => { + return ( + + + + + + + + ); +}; diff --git a/ui-kit/src/stories/Modal.stories.tsx b/ui-kit/src/stories/Modal.stories.tsx new file mode 100644 index 00000000..f8f3fea6 --- /dev/null +++ b/ui-kit/src/stories/Modal.stories.tsx @@ -0,0 +1,213 @@ +import { Meta } from '@storybook/react/types-6-0'; +import React, { useState } from 'react'; +import { Modal, ModalHeader, ModalContent, ModalFooter } from 'src'; +import Button from 'components/Button'; +import { useModal } from 'contexts/Modal'; +import { Column, Row } from 'src/components/Grid'; + +interface FooterProps { + size: 'small' | 'medium'; + showCancelBtn?: boolean; + closeModal: () => void; +} + +const DefaultModalHeader = () => 타이틀입니다; +const DefaultModdalFooter = ({ size, showCancelBtn = true, closeModal }: FooterProps) => { + return ( + + {showCancelBtn ? ( + + ) : null} + + + ); +}; + +const margin = { + marginRight: 16, + marginBottom: 32, +}; + +export default { + title: 'Lubycon UI kit/Modal', + component: Modal, +} as Meta; + +export const Default = () => { + const [showModal, setShowSmallModal] = useState(false); + const [showModal2, setShowMediumModal] = useState(false); + + const closeModal = () => setShowSmallModal(false); + const closeModal2 = () => setShowMediumModal(false); + const handleOpen = () => console.info('open'); + + return ( + + + + + +
    여기에 본문 텍스트가 들어갑니다
    +
    여기에 본문 텍스트가 들어갑니다
    +
    + +
    + + + + + + 텍스트 내용이 많을 경우에는 중간 크기의 모달 사용을 권장합니다. 여기에 본문 텍스트를 + 입력해 주세요. + + + +
    + ); +}; + +export const ModalHooks = () => { + const { openModal, closeModal } = useModal(); + + return ( + + + + + + + + + + + ); +}; diff --git a/ui-kit/src/stories/ProgressBar.stories.tsx b/ui-kit/src/stories/ProgressBar.stories.tsx new file mode 100644 index 00000000..49641e81 --- /dev/null +++ b/ui-kit/src/stories/ProgressBar.stories.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { ProgressBar, Text } from 'src'; +import { ProgressBarLabelPosition } from 'src/components/ProgressBar'; + +export default { + title: 'Lubycon UI Kit/ProgressBar', +} as Meta; + +const MAX_VALUE = 100; +const getProgressValue = (value: number) => (value === MAX_VALUE ? 0 : value + 1); + +export const Default = () => { + const [value, setValue] = useState(0); + + useEffect(() => { + const interval = setInterval(() => setValue(getProgressValue), 100); + return () => { + clearInterval(interval); + }; + }, []); + + return ( +
    + +
    + ); +}; + +const labelPosition: ProgressBarLabelPosition[] = ['top', 'bottom', 'left', 'right']; +export const Label = () => { + const [value, setValue] = useState(0); + + useEffect(() => { + const interval = setInterval(() => setValue(getProgressValue), 100); + return () => { + clearInterval(interval); + }; + }, []); + + return ( +
      + {labelPosition.map((position) => ( +
    • + {position} + +
    • + ))} +
    + ); +}; + +export const LabelFormatter = () => { + const [value, setValue] = useState(0); + + useEffect(() => { + const interval = setInterval(() => setValue(getProgressValue), 100); + return () => { + clearInterval(interval); + }; + }, []); + + return ( + <> + `${value}/${100}`} + /> + + value > MAX_VALUE * 0.5 ? '거의 다 왔어요!' : `현재 값은 ${value}입니다` + } + /> + `${Math.floor((value / MAX_VALUE) * 100)}%`} + /> + + ); +}; diff --git a/ui-kit/src/stories/Radio.stories.tsx b/ui-kit/src/stories/Radio.stories.tsx new file mode 100644 index 00000000..e3ddb0b6 --- /dev/null +++ b/ui-kit/src/stories/Radio.stories.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Radio } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; + +export default { + title: 'Lubycon UI Kit/Radio', + component: Radio, +} as Meta; + +export const Default = () => { + return ( +
    + + +
    + ); +}; + +export const Disabled = () => { + return ( +
    + + + + +
    + ); +}; + +export const Inline = () => { + return ( +
    + + +
    + ); +}; diff --git a/ui-kit/src/stories/Selection.stories.tsx b/ui-kit/src/stories/Selection.stories.tsx new file mode 100644 index 00000000..efe0448b --- /dev/null +++ b/ui-kit/src/stories/Selection.stories.tsx @@ -0,0 +1,62 @@ +import React, { useMemo, useState } from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Selection, Text } from 'src'; + +export default { + title: 'Lubycon UI Kit/Selection', +} as Meta; + +export const Default = () => { + const [state, setState] = useState(''); + + return ( +
    + setState(e.target.value)}> + + + + + 선택된 값은 {state}입니다. +
    + ); +}; + +export const Sizes = () => { + const selections = useMemo(() => { + return ['small', 'medium', 'large'].map((size) => ( +
  • + + + + + +
  • + )); + }, []); + return
      {selections}
    ; +}; + +export const Disabled = () => { + return ( +
    + + + + + +
    + ); +}; + +export const Placeholder = () => { + return ( +
    + + + + + + +
    + ); +}; diff --git a/ui-kit/src/stories/Shadows.stories.tsx b/ui-kit/src/stories/Shadows.stories.tsx new file mode 100644 index 00000000..785daca3 --- /dev/null +++ b/ui-kit/src/stories/Shadows.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import classnames from 'classnames'; +import { Text } from 'src'; + +export default { + title: 'Lubycon UI Kit/Shadows', +} as Meta; + +const shadows = ['0px', '2px 드랍다운', '3px 버튼, 카드', '6px 토스트', '8px 탭', '24px 모달 팝업']; + +export const Default = () => { + return ( +
      + {shadows.map((shadow, index) => ( +
    • +
      + {shadow} +
      +
    • + ))} +
    + ); +}; diff --git a/ui-kit/src/stories/Snackbar.stories.tsx b/ui-kit/src/stories/Snackbar.stories.tsx new file mode 100644 index 00000000..740c219c --- /dev/null +++ b/ui-kit/src/stories/Snackbar.stories.tsx @@ -0,0 +1,133 @@ +import React, { useState } from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Snackbar, Button, useSnackbar } from 'src'; +import { SnackbarAlign } from 'src/components/Snackbar'; + +export default { + title: 'Lubycon UI Kit/Snackbar', + component: Snackbar, +} as Meta; + +export const Default = () => { + return ( +
    + + +
    + ); +}; + +export const LongText = () => { + return ( +
    + +
    + ); +}; + +export const AutoHide = () => { + const [show, setShow] = useState(true); + return ( +
    + + setShow(true)} + message={`16개의 이미지가\n“동물" 폴더에 추가되었습니다.`} + button="실행취소" + /> +
    + ); +}; + +export const SnackbarHooks = () => { + const { openSnackbar } = useSnackbar(); + return ( +
    + +
    + ); +}; + +const aligns: SnackbarAlign[] = ['left', 'center', 'right']; +export const Aligns = () => { + const { openSnackbar } = useSnackbar(); + return ( +
    + {aligns.map((align) => ( + + ))} +
    + ); +}; + +export const onClick = () => { + const { openSnackbar } = useSnackbar(); + return ( +
    + +
    + ); +}; + +export const multipleButton = () => { + const { openSnackbar } = useSnackbar(); + return ( +
    + + + + ), + }) + } + > + 스낵바 열기 + +
    + ); +}; diff --git a/ui-kit/src/stories/Switch.stories.tsx b/ui-kit/src/stories/Switch.stories.tsx new file mode 100644 index 00000000..1660f2a8 --- /dev/null +++ b/ui-kit/src/stories/Switch.stories.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Text, Switch, colors } from 'src'; + +export default { + title: 'Lubycon UI Kit/Switch', +} as Meta; + +export const Default = () => { + return ( +
    + + 스위치 + +
    + + +
    +
    + ); +}; + +export const Inline = () => { + return ( +
    + + +
    + ); +}; diff --git a/ui-kit/src/stories/Table.stories.tsx b/ui-kit/src/stories/Table.stories.tsx new file mode 100644 index 00000000..d353af00 --- /dev/null +++ b/ui-kit/src/stories/Table.stories.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Table, TableHead, TableBody, TableRow, TableCell } from 'src'; + +export default { + title: 'Lubycon UI Kit/Table', +} as Meta; + +const header = ['제목', '제목', '제목', '제목', '제목']; +const contents = ['내용', '내용을 입력하세요', '내용', '내용을 입력하세요', '내용']; +const iterator = [undefined, undefined, undefined, undefined, undefined, undefined]; + +export const Default = () => { + return ( + + + + {header.map((name, i) => ( + {name} + ))} + + + + {iterator.map((_, rowIdx) => ( + + {contents.map((content, contentIdx) => ( + {content} + ))} + + ))} + +
    + ); +}; diff --git a/ui-kit/src/stories/Tabs.stories.tsx b/ui-kit/src/stories/Tabs.stories.tsx new file mode 100644 index 00000000..c0eb4506 --- /dev/null +++ b/ui-kit/src/stories/Tabs.stories.tsx @@ -0,0 +1,61 @@ +import React, { PropsWithChildren, useState } from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Tabs, TabsItem } from 'src'; + +export default { + title: 'Lubycon UI Kit/Tabs', + component: Tabs, +} as Meta; + +const tabs = [ + { + value: 'first', + text: '첫번째 탭', + disabled: false, + }, + { + value: 'second', + text: '두번째 태애애애애앱', + disabled: false, + }, + { + value: 'third', + text: '세번째 탭', + disabled: false, + }, + { + value: 'forth', + text: '네번째 탭', + disabled: true, + }, + { + value: 'fifth', + text: '다섯번째 탭', + disabled: false, + }, +]; + +const TabContent = ({ children }: PropsWithChildren) => { + return
    {children}
    ; +}; + +export const Default = () => { + const [selectedTab, selectTab] = useState(tabs[0].value); + + return ( +
    + + {tabs.map((item) => ( + + {item.text} + + ))} + + {tabs.map((item) => { + return item.value === selectedTab ? ( + {item.text}이 선택되었습니다! + ) : null; + })} +
    + ); +}; diff --git a/ui-kit/src/stories/Tag.stories.tsx b/ui-kit/src/stories/Tag.stories.tsx new file mode 100644 index 00000000..c628f778 --- /dev/null +++ b/ui-kit/src/stories/Tag.stories.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Tag } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; +import { SemanticColor } from 'src/constants/colors'; + +export default { + title: 'Lubycon UI Kit/Tag', +} as Meta; + +const samples: Array<{ label: string; type?: SemanticColor }> = [ + { + label: 'chore', + type: undefined, + }, + { + label: '디자인 챕터', + type: 'positive', + }, + { + label: '프론트엔드 챕터', + type: 'informative', + }, + { + label: 'MVP', + type: 'negative', + }, + { + label: 'feature', + type: 'notice', + }, +]; + +export const Default = () => { + return ( +
    + {samples.map(({ label, type }, index) => ( + + {label} + + ))} +
    + ); +}; + +export const DeleteButton = () => { + return ( +
    + {samples.map(({ label, type }, index) => ( + console.log(`${label} is deleted`)} + > + {label} + + ))} +
    + ); +}; diff --git a/ui-kit/src/stories/Text.stories.tsx b/ui-kit/src/stories/Text.stories.tsx new file mode 100644 index 00000000..a244c20a --- /dev/null +++ b/ui-kit/src/stories/Text.stories.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { Text } from 'src'; +import { Meta } from '@storybook/react/types-6-0'; +import { typographys, Typographys, FontWeights, fontWeights } from 'components/Text/types'; + +export default { + title: 'Lubycon UI Kit/Text', + component: Text, +} as Meta; + +const typographyNames: { [key in Typographys]: string } = { + h1: '머릿말 1', + h2: '머릿말 2', + h3: '머릿말 3', + h4: '머릿말 4', + h5: '머릿말 5', + h6: '머릿말 6', + subtitle: '부제', + p1: '본문 1', + p2: '본문 2', + caption: '캡션', +}; + +const fontWeightNames: { [key in FontWeights]: string } = { + light: '노토 산스 KR Light', + regular: '노토 산스 KR Regular', + bold: '노토 산스 KR Bold', + black: '노토 산스 KR Black', +}; + +export const Default = () => { + return ( +
      + {typographys.map((typography) => ( +
    • + {typographyNames[typography]} +
    • + ))} +
    + ); +}; + +export const FontWeight = () => { + return ( +
      + {fontWeights.map((fontWeight) => ( +
    • + {fontWeightNames[fontWeight]} +
    • + ))} +
    + ); +}; + +export const As = () => { + return ( +
      +
    • + + 앵커 태그를 사용해보자 + + h1으로 렌더해도 기본적으로는 Typography: p1이 적용됩니다 + 버튼도 가능하기는 함 +
    • +
    + ); +}; diff --git a/ui-kit/src/stories/Tooltip.stories.tsx b/ui-kit/src/stories/Tooltip.stories.tsx new file mode 100644 index 00000000..1b799667 --- /dev/null +++ b/ui-kit/src/stories/Tooltip.stories.tsx @@ -0,0 +1,69 @@ +import React, { useState } from 'react'; +import { Meta } from '@storybook/react/types-6-0'; +import { Tooltip, Button } from 'src'; +import { TooltipPosition } from 'src/components/Tooltip/types'; + +export default { + title: 'Lubycon UI Kit/Tooltip', + component: Tooltip, +} as Meta; + +const positions: TooltipPosition[] = [ + 'top-left', + 'top-center', + 'top-right', + 'left', + 'right', + 'bottom-left', + 'bottom-center', + 'bottom-right', +]; + +export const Default = () => { + return ( +
    + + + +
    + ); +}; + +const TooltipButton = ({ children }: { children: TooltipPosition }) => { + const [show, setShow] = useState(false); + return ( + + + + ); +}; + +export const Position = () => { + return ( +
      + {positions.map((p) => ( +
    • + {p} +
    • + ))} +
    + ); +}; + +export const AbsolutePositionTest = () => { + return ( +
    +
    + + + +
    +
    + ); +}; diff --git a/ui-kit/src/types/OverridableProps.ts b/ui-kit/src/types/OverridableProps.ts new file mode 100644 index 00000000..90d6c8a9 --- /dev/null +++ b/ui-kit/src/types/OverridableProps.ts @@ -0,0 +1,9 @@ +import { ElementType } from 'react'; +import { CombineElementProps } from './utils'; + +/** + * @desc 원하는 엘리먼트로 렌더링할 수 있는 as 프로퍼티와 커스텀 Props를 병합합니다. + */ +export type OverridableProps = { + as?: E; +} & CombineElementProps; diff --git a/ui-kit/src/types/utils.ts b/ui-kit/src/types/utils.ts new file mode 100644 index 00000000..db867b3b --- /dev/null +++ b/ui-kit/src/types/utils.ts @@ -0,0 +1,13 @@ +import { ComponentPropsWithoutRef, ComponentType, ElementType, ReactElement } from 'react'; + +/** + * @description T와 K에서 T의 프로퍼티를 제거한 타입을 병합합니다. + */ +export type Combine = T & Omit; + +export type CombineElementProps = Combine< + P, + ComponentPropsWithoutRef +>; + +export type ComponentElementType = ReactElement>; diff --git a/ui-kit/src/utils/dom.ts b/ui-kit/src/utils/dom.ts new file mode 100644 index 00000000..8d53b3e8 --- /dev/null +++ b/ui-kit/src/utils/dom.ts @@ -0,0 +1,15 @@ +export function getAbsoluteOffset(el: HTMLElement) { + if (document?.body == null) { + return { + top: -1, + left: -1, + }; + } + const bodyRect = document.body.getBoundingClientRect(); + const elRect = el.getBoundingClientRect(); + + return { + top: elRect.top - bodyRect.top, + left: elRect.left - bodyRect.left, + }; +} diff --git a/ui-kit/src/utils/generateID.ts b/ui-kit/src/utils/generateID.ts new file mode 100644 index 00000000..79854cf4 --- /dev/null +++ b/ui-kit/src/utils/generateID.ts @@ -0,0 +1,6 @@ +let idIndex = 0; + +export function generateID(prefix: string) { + idIndex++; + return `${prefix}-${idIndex}`; +} diff --git a/ui-kit/src/utils/index.ts b/ui-kit/src/utils/index.ts new file mode 100644 index 00000000..14be0d6d --- /dev/null +++ b/ui-kit/src/utils/index.ts @@ -0,0 +1 @@ +export { generateID } from './generateID'; diff --git a/ui-kit/src/utils/mediaQuery.ts b/ui-kit/src/utils/mediaQuery.ts new file mode 100644 index 00000000..f3cf9ba7 --- /dev/null +++ b/ui-kit/src/utils/mediaQuery.ts @@ -0,0 +1,35 @@ +const breakpoints = { + xs: 0, + sm: 576, + md: 768, + lg: 992, + xl: 1200, +}; +type MediaQueryKeys = keyof typeof breakpoints; + +/** + * 미디어쿼리가 주어진 쿼리 키에 지정된 width보다 클 경우 true를 반환한다. + **/ +function isMatchMinWidth(key: MediaQueryKeys) { + if (window == null) { + return null; + } + + return window.matchMedia(`screen and (min-width: ${breakpoints[key]}px)`).matches; +} + +export function isMatchedXS() { + return !isMatchMinWidth('xs'); +} +export function isMatchedSM() { + return !isMatchMinWidth('sm'); +} +export function isMatchedMD() { + return !isMatchMinWidth('md'); +} +export function isMatchedLG() { + return !isMatchMinWidth('lg'); +} +export function isMatchedXL() { + return !isMatchMinWidth('xs'); +} diff --git a/ui-kit/src/utils/sleep.ts b/ui-kit/src/utils/sleep.ts new file mode 100644 index 00000000..8b27999e --- /dev/null +++ b/ui-kit/src/utils/sleep.ts @@ -0,0 +1,5 @@ +export function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} diff --git a/ui-kit/tsconfig.json b/ui-kit/tsconfig.json index 8861e58a..a8e643cf 100644 --- a/ui-kit/tsconfig.json +++ b/ui-kit/tsconfig.json @@ -1,9 +1,14 @@ { "extends": "../tsconfig.common.json", "compilerOptions": { - "baseUrl": "./src", + "baseUrl": ".", "paths": { - "src/*": ["./src/*"] - }, + "src/*": ["./src/*"], + "components/*": ["./src/components/*"], + "types/*": ["./src/types/*"], + "constants/*": ["./src/constants/*"], + "utils/*": ["./src/utils/*"], + "contexts/*": ["./src/contexts/*"] + } } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 5edf3f8e..c610b255 100644 --- a/yarn.lock +++ b/yarn.lock @@ -261,6 +261,13 @@ dependencies: "@babel/types" "^7.12.5" +"@babel/helper-module-imports@^7.7.0": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== + dependencies: + "@babel/types" "^7.13.12" + "@babel/helper-module-transforms@^7.11.0", "@babel/helper-module-transforms@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" @@ -288,6 +295,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== +"@babel/helper-plugin-utils@^7.12.13": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== + "@babel/helper-remap-async-to-generator@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd" @@ -333,6 +345,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + "@babel/helper-validator-option@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" @@ -605,6 +622,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-jsx@^7.2.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz#044fb81ebad6698fe62c478875575bcbb9b70f15" + integrity sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -853,7 +877,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-constant-elements@^7.12.1", "@babel/plugin-transform-react-constant-elements@^7.9.0": +"@babel/plugin-transform-react-constant-elements@^7.9.0": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.1.tgz#4471f0851feec3231cc9aaa0dccde39947c1ac1e" integrity sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA== @@ -1168,7 +1192,7 @@ "@babel/plugin-transform-react-jsx-source" "^7.12.1" "@babel/plugin-transform-react-pure-annotations" "^7.12.1" -"@babel/preset-react@^7.12.1", "@babel/preset-react@^7.12.5", "@babel/preset-react@^7.9.4": +"@babel/preset-react@^7.12.1", "@babel/preset-react@^7.9.4": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.7.tgz#36d61d83223b07b6ac4ec55cf016abb0f70be83b" integrity sha512-wKeTdnGUP5AEYCYQIMeXMMwU7j+2opxrG0WzuZfxuuW9nhKvvALBjl67653CWamZJVefuJGI219G591RSldrqQ== @@ -1273,6 +1297,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.12.tgz#edbf99208ef48852acdff1c8a681a1e4ade580cd" + integrity sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047" @@ -1301,6 +1334,41 @@ resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg== +"@emotion/babel-plugin-jsx-pragmatic@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.1.5.tgz#27debfe9c27c4d83574d509787ae553bf8a34d7e" + integrity sha512-y+3AJ0SItMDaAgGPVkQBC/S/BaqaPACkQ6MyCI2CUlrjTxKttTVfD3TMtcs7vLEcLxqzZ1xiG0vzwCXjhopawQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@emotion/babel-plugin@^11.1.2", "@emotion/babel-plugin@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.2.0.tgz#f25c6df8ec045dad5ae6ca63df0791673b98c920" + integrity sha512-lsnQBnl3l4wu/FJoyHnYRpHJeIPNkOBMbtDUIXcO8luulwRKZXPvA10zd2eXVN6dABIWNX4E34en/jkejIg/yA== + dependencies: + "@babel/helper-module-imports" "^7.7.0" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/runtime" "^7.7.2" + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.5" + "@emotion/serialize" "^1.0.0" + babel-plugin-macros "^2.6.1" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "^4.0.3" + +"@emotion/babel-preset-css-prop@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-preset-css-prop/-/babel-preset-css-prop-11.2.0.tgz#c7e945f56b2610b438f0dc8ae5253fc55488de0e" + integrity sha512-9XLQm2eLPYTho+Cx1LQTDA1rATjoAaB4O+ds55XDvoAa+Z16Hhg8y5Vihj3C8E6+ilDM8SV5A9Z6z+yj0YIRBg== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.12.1" + "@babel/runtime" "^7.7.2" + "@emotion/babel-plugin" "^11.2.0" + "@emotion/babel-plugin-jsx-pragmatic" "^0.1.5" + "@emotion/cache@^10.0.27": version "10.0.29" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" @@ -1311,6 +1379,17 @@ "@emotion/utils" "0.11.3" "@emotion/weak-memoize" "0.2.5" +"@emotion/cache@^11.1.3": + version "11.1.3" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.1.3.tgz#c7683a9484bcd38d5562f2b9947873cf66829afd" + integrity sha512-n4OWinUPJVaP6fXxWZD9OUeQ0lY7DvtmtSuqtRWT0Ofo/sBLCVSgb4/Oa0Q5eFxcwablRKjUXqXtNZVyEwCAuA== + dependencies: + "@emotion/memoize" "^0.7.4" + "@emotion/sheet" "^1.0.0" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + stylis "^4.0.3" + "@emotion/core@^10.1.1": version "10.1.1" resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3" @@ -1332,7 +1411,7 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@0.8.0": +"@emotion/hash@0.8.0", "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== @@ -1344,11 +1423,36 @@ dependencies: "@emotion/memoize" "0.7.4" +"@emotion/is-prop-valid@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.0.tgz#29ef6be1e946fb4739f9707def860f316f668cde" + integrity sha512-9RkilvXAufQHsSsjQ3PIzSns+pxuX4EW8EbGeSPjZMHuMx6z/MOzb9LpqNieQX4F3mre3NWS2+X3JNRHTQztUQ== + dependencies: + "@emotion/memoize" "^0.7.4" + "@emotion/memoize@0.7.4": version "0.7.4" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== +"@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" + integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== + +"@emotion/react@^11.1.5": + version "11.1.5" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.1.5.tgz#15e78f9822894cdc296e6f4e0688bac8120dfe66" + integrity sha512-xfnZ9NJEv9SU9K2sxXM06lzjK245xSeHRpUh67eARBm3PBHjjKIZlfWZ7UQvD0Obvw6ZKjlC79uHrlzFYpOB/Q== + dependencies: + "@babel/runtime" "^7.7.2" + "@emotion/cache" "^11.1.3" + "@emotion/serialize" "^1.0.0" + "@emotion/sheet" "^1.0.1" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + hoist-non-react-statics "^3.3.1" + "@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": version "0.11.16" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" @@ -1360,11 +1464,27 @@ "@emotion/utils" "0.11.3" csstype "^2.5.7" +"@emotion/serialize@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.1.tgz#322cdebfdbb5a88946f17006548191859b9b0855" + integrity sha512-TXlKs5sgUKhFlszp/rg4lIAZd7UUSmJpwaf9/lAEFcUh2vPi32i7x4wk7O8TN8L8v2Ol8k0CxnhRBY0zQalTxA== + dependencies: + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.4" + "@emotion/unitless" "^0.7.5" + "@emotion/utils" "^1.0.0" + csstype "^3.0.2" + "@emotion/sheet@0.9.4": version "0.9.4" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== +"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.1.tgz#245f54abb02dfd82326e28689f34c27aa9b2a698" + integrity sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g== + "@emotion/styled-base@^10.0.27": version "10.0.31" resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a" @@ -1383,12 +1503,23 @@ "@emotion/styled-base" "^10.0.27" babel-plugin-emotion "^10.0.27" +"@emotion/styled@^11.1.5": + version "11.1.5" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.1.5.tgz#3d7bfa58b346e48315f65ee956aeef81f0bea8e0" + integrity sha512-nIq7pOBEDqT5xSFbclQ3XFy0q8C9EUU8ECqKN2kJKGxKh+vLz/x26kEih4aOpoAsyzc+R60rQxh7VJiLTUEdmg== + dependencies: + "@babel/runtime" "^7.7.2" + "@emotion/babel-plugin" "^11.1.2" + "@emotion/is-prop-valid" "^1.1.0" + "@emotion/serialize" "^1.0.0" + "@emotion/utils" "^1.0.0" + "@emotion/stylis@0.8.5": version "0.8.5" resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== -"@emotion/unitless@0.7.5": +"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.5": version "0.7.5" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== @@ -1398,7 +1529,12 @@ resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== -"@emotion/weak-memoize@0.2.5": +"@emotion/utils@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" + integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== + +"@emotion/weak-memoize@0.2.5", "@emotion/weak-memoize@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== @@ -1672,6 +1808,16 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@lubycon/ui-kit@^1.1.0-alpha.30": + version "1.1.0-alpha.30" + resolved "https://registry.yarnpkg.com/@lubycon/ui-kit/-/ui-kit-1.1.0-alpha.30.tgz#041d6ea26d1601d77417e4f615eaf23efb68ee3a" + integrity sha512-7ztVuxJRW6oSRjWVNJ39BO+9+Ip29drnj5LCuL/0rwr5bKpoAIfWDCU90Cl2MOMtTmAp12rSI/aPWfIlUlw6iw== + dependencies: + classnames "^2.2.6" + ionicons "^5.2.3" + react-spring "^8.0.27" + resize-observer-polyfill "^1.5.1" + "@mdx-js/loader@^1.6.19": version "1.6.21" resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.6.21.tgz#e5b2b5c48d182e495d36104b0c7a5da96964a2dd" @@ -1909,7 +2055,7 @@ schema-utils "^2.6.5" source-map "^0.7.3" -"@popperjs/core@^2.4.4", "@popperjs/core@^2.5.4": +"@popperjs/core@^2.5.4": version "2.5.4" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.5.4.tgz#de25b5da9f727985a3757fd59b5d028aba75841a" integrity sha512-ZpKr+WTb8zsajqgDkvCEWgp6d5eJT6Q63Ng2neTbzBO76Lbe91vX/iVIW9dikq+Fs3yEo+ls4cxeXABD2LtcbQ== @@ -2078,18 +2224,6 @@ lodash "^4.17.4" read-pkg-up "^7.0.0" -"@sindresorhus/df@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@sindresorhus/df/-/df-1.0.1.tgz#c69b66f52f6fcdd287c807df210305dbaf78500d" - integrity sha1-xptm9S9vzdKHyAffIQMF2694UA0= - -"@sindresorhus/df@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/df/-/df-2.1.0.tgz#d208cf27e06f0bb476d14d7deccd7d726e9aa389" - integrity sha1-0gjPJ+BvC7R20U197M19cm6ao4k= - dependencies: - execa "^0.2.2" - "@sinonjs/commons@^1.7.0": version "1.8.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" @@ -2104,17 +2238,17 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@storybook/addon-actions@6.1.2", "@storybook/addon-actions@^6.0.28": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.1.2.tgz#6df92f5ac4ed5a5d56c5ff2f52de47cf43e3c42b" - integrity sha512-LBsHgN9BNekagdw5a4Yi6noHH64K+/6/phpz6GgD9HIhULOtHNrL2pG94Zj10rdCdu1y7nscSkfwA52niqyYYg== - dependencies: - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/client-api" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/core-events" "6.1.2" - "@storybook/theming" "6.1.2" +"@storybook/addon-actions@6.1.14", "@storybook/addon-actions@^6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.1.14.tgz#20a173d781d99368a1af769ed75551a3784f3135" + integrity sha512-mXvrL8B34Rtq1WPxbQ1eUip8spqQP43HWGRH0ZmCO3Iwwcmxd6250LY3q+95QqJYsli0XJoOnS97VOLXABpaPg== + dependencies: + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/client-api" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/theming" "6.1.14" core-js "^3.0.1" fast-deep-equal "^3.1.1" global "^4.3.2" @@ -2127,17 +2261,17 @@ util-deprecate "^1.0.2" uuid "^8.0.0" -"@storybook/addon-backgrounds@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.1.2.tgz#f768e1180a0d2838bbd47eac7884896118963624" - integrity sha512-FUNqS94lpkCi1OqsNZQZFkk56nEcxCSrkCauwa6+CeusZoQ2X7I2sHLPnGYKcBzoPX6E2l2map/k0Bk0cDDyJg== - dependencies: - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/core-events" "6.1.2" - "@storybook/theming" "6.1.2" +"@storybook/addon-backgrounds@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.1.14.tgz#5c65c16e12558a3bc071fdf916305fa0531bb2f1" + integrity sha512-ckRB1//D75XALLUaGZxHnKtgJMLi3A59M1AYDnpx6MwK2cjJLFwadCiyri9tDl2mY3aOYHD4C52MrHzxT9u5fQ== + dependencies: + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/theming" "6.1.14" core-js "^3.0.1" global "^4.3.2" memoizerific "^1.11.3" @@ -2145,24 +2279,24 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-controls@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.1.2.tgz#86b7d26226ac437ebd8708098a7bed5a95e951d5" - integrity sha512-0knHFTxGmyReS0/LU81YrXzkCB7vUHgV8QOE3efNjEHj0zDr4t4GFNvl+FLt+wlkZAJ6sOyIXOks7ezdxFm4sw== - dependencies: - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/client-api" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/node-logger" "6.1.2" - "@storybook/theming" "6.1.2" +"@storybook/addon-controls@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.1.14.tgz#3f4f9e60763facecb654867881533785454ea16d" + integrity sha512-4KzTD5J9pUHFe2kBE1gfDw0wjiSsXjMqX82L+l0vzt1GGqQR1Bkaqodg4eGgCM2SU50ysVWvgC3N5BYEiFeZkw== + dependencies: + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/client-api" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/node-logger" "6.1.14" + "@storybook/theming" "6.1.14" core-js "^3.0.1" ts-dedent "^2.0.0" -"@storybook/addon-docs@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.1.2.tgz#7bb79fd60de516e29cbcae88ed286c48831d848b" - integrity sha512-h9oyd7D0zgSc/LM73oTaVHF8H+et5tzXQ1CDtnh9Fpe9YgxItsJ7jke9z8dNapH6X+pmHE2rN1itqvasjpTsgA== +"@storybook/addon-docs@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.1.14.tgz#fc1feac0e7e8b9c46efcd0b9c1f8ba2d306a9b3b" + integrity sha512-Skj9crqaEEISghobjtu3EKbSTwGVK2e0gTu94WqPL3GOugvGgk7b1VrCgf5fXKcdwbtZktm48CtdmeP5R5U9NQ== dependencies: "@babel/core" "^7.12.1" "@babel/generator" "^7.12.1" @@ -2173,18 +2307,18 @@ "@mdx-js/loader" "^1.6.19" "@mdx-js/mdx" "^1.6.19" "@mdx-js/react" "^1.6.19" - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/client-api" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/core" "6.1.2" - "@storybook/core-events" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/client-api" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core" "6.1.14" + "@storybook/core-events" "6.1.14" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.1.2" - "@storybook/postinstall" "6.1.2" - "@storybook/source-loader" "6.1.2" - "@storybook/theming" "6.1.2" + "@storybook/node-logger" "6.1.14" + "@storybook/postinstall" "6.1.14" + "@storybook/source-loader" "6.1.14" + "@storybook/theming" "6.1.14" acorn "^7.1.0" acorn-jsx "^5.1.0" acorn-walk "^7.0.0" @@ -2205,80 +2339,80 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-essentials@^6.0.28": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.1.2.tgz#5cc5146bb257937e0602d1a4b9f8d05dffa8531b" - integrity sha512-wQmDD9+p36K26PMxpMcuZblyXFAjJ0pG6JNxy3XULuPGZfeRPQGlmcU2JTDUVbryOJfxHu0nzDI7D+CJ8Q2wAg== - dependencies: - "@storybook/addon-actions" "6.1.2" - "@storybook/addon-backgrounds" "6.1.2" - "@storybook/addon-controls" "6.1.2" - "@storybook/addon-docs" "6.1.2" - "@storybook/addon-toolbars" "6.1.2" - "@storybook/addon-viewport" "6.1.2" - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/node-logger" "6.1.2" +"@storybook/addon-essentials@^6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.1.14.tgz#5dbd90417fe0bebded547cc9131072acef4aef84" + integrity sha512-JcBq6wqO5C0JM/8GPBTiqBqbh5yYZzJJyRAwH8uZ44aaX94kTIuCF3wgNRCfA0Ed2ub+aMjd+ZFjpRD7dhkRSA== + dependencies: + "@storybook/addon-actions" "6.1.14" + "@storybook/addon-backgrounds" "6.1.14" + "@storybook/addon-controls" "6.1.14" + "@storybook/addon-docs" "6.1.14" + "@storybook/addon-toolbars" "6.1.14" + "@storybook/addon-viewport" "6.1.14" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/node-logger" "6.1.14" core-js "^3.0.1" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-toolbars@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.1.2.tgz#ed59dc005c2a47c35c18f00fdac98e182934b032" - integrity sha512-eudMdRrMjNDJ8TctROqiCyUxHJZJhhcU6GCvZxU3JT2uiWeARx6zmGPnZh7BlMG+YCNUboSAxy3nXbeEJAkQuw== +"@storybook/addon-toolbars@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.1.14.tgz#d28c9f8e980fcb542645c833a20183b4e0d07425" + integrity sha512-vYmMsfNwvAKwbD65tgNwKUUOebqKnzyc359r+5tgOu5U2HegXvPrgLTMbke4KkkSJTj5EAHE6SHusdjEzq8/dA== dependencies: - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/client-api" "6.1.2" - "@storybook/components" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/client-api" "6.1.14" + "@storybook/components" "6.1.14" core-js "^3.0.1" -"@storybook/addon-viewport@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.1.2.tgz#61d7f1e8597f177c5d68a4a46bce94ed7d2df464" - integrity sha512-da2zrACzkrMtsUpn9hXKgH5FqJZZLCkxeZVGh9juBg3OOmOE6mPEF3WLtZiYVcYCPeoobJNScsnC1o+x97CGtw== - dependencies: - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/core-events" "6.1.2" - "@storybook/theming" "6.1.2" +"@storybook/addon-viewport@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.1.14.tgz#db92b2c1fb5bbaf8240e26f433e6c6d9829de33a" + integrity sha512-5u9Atyfmz8fNHo0CCp1e5bHKmdHIchhzel9gIzSYnwCDDILaB8iPmQwxdb9v2nerUCHGIH9CNJbTYpECqpBK2A== + dependencies: + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/theming" "6.1.14" core-js "^3.0.1" global "^4.3.2" memoizerific "^1.11.3" prop-types "^15.7.2" regenerator-runtime "^0.13.7" -"@storybook/addons@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.1.2.tgz#e073d4d930d84cb5d54cef5bd4376724369b56de" - integrity sha512-Q+xTYikVz4cfS3IWw4Ns9YDY8jzC4E8C7meuoAMWZMaQn3Y8CdvTNrnoPZ1XnxaArVvkROCF45cdsDX7t0GNRQ== - dependencies: - "@storybook/api" "6.1.2" - "@storybook/channels" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/core-events" "6.1.2" - "@storybook/router" "6.1.2" - "@storybook/theming" "6.1.2" +"@storybook/addons@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.1.14.tgz#2b81304bbe696923df95cdcf85cfc592d10f4065" + integrity sha512-HlpmV7aejp/MeW8bo/WKME3i71gi0men9qcwoovjDjnSF6jXoNLT336a5udKXdHqYSZgzdyURlgLtilCWkWaJQ== + dependencies: + "@storybook/api" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/router" "6.1.14" + "@storybook/theming" "6.1.14" core-js "^3.0.1" global "^4.3.2" regenerator-runtime "^0.13.7" -"@storybook/api@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.1.2.tgz#d941f4b6d449e204d8bb42e251a92b62274451f3" - integrity sha512-tuYpQiBPjHse6OwodWXPdUiwwYuFNIlgQmGS2gKRv5Aw3dZEIvB7BLymi+cgz8KqMf5T1NaZhsQO/+3FPiQ+Kw== +"@storybook/api@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.1.14.tgz#20035dd336aba1c5a0f8c83c8c14a2edaf4db891" + integrity sha512-gWcC/xEW8HL5DsocLujHBUdoNsl4YW1Zx1Y4SBbLCyrhj8v4JudJpylwJpOUBDe/GESXq1zqvNKvUPtI8DQNyw== dependencies: "@reach/router" "^1.3.3" - "@storybook/channels" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/core-events" "6.1.2" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" "@storybook/csf" "0.0.1" - "@storybook/router" "6.1.2" + "@storybook/router" "6.1.14" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.1.2" + "@storybook/theming" "6.1.14" "@types/reach__router" "^1.3.5" core-js "^3.0.1" fast-deep-equal "^3.1.1" @@ -2291,38 +2425,38 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/channel-postmessage@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.1.2.tgz#92205f381059d4d155679aba4a1d89d2f438f9b5" - integrity sha512-zKEPK4MAgBy3RanCpl4afJpcYJMlb56gpIFALm2WJZ8zsHUUFb45NgruqNPiBsF0IthD6bvemcCJ5mmyn5D4xg== +"@storybook/channel-postmessage@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.1.14.tgz#41f3115895010dad9fb30f4ac381e4f904b1e50c" + integrity sha512-If83dXXW9mKIRuvuWhWa/zkEw/F0FDgikp33x8436J3rWCh3recp27kffFRrKG0YDMpFSk/Ci5G47E9zn9SCjw== dependencies: - "@storybook/channels" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/core-events" "6.1.2" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" core-js "^3.0.1" global "^4.3.2" qs "^6.6.0" telejson "^5.0.2" -"@storybook/channels@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.1.2.tgz#e42b2cb3502291cfde36082b4844bd6de839ce86" - integrity sha512-jek1xyLXgMnk3XbNjjVxx4xirOE7QwbwCbiESS7g2ELhRum4C636iVzLYUpt8rujXFmGibqwnoeOH4rPNIFuvA== +"@storybook/channels@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.1.14.tgz#c479190ebb853a603f3ed90fc470534a02eb46eb" + integrity sha512-vP19IB2FXj8SiFbQ9ETljEBienL+KRMLgMzz3Ta3nZj/OfjJJbIuj42ZfexQGV4mS0Bo+OW+qT7VMIY6fulnFw== dependencies: core-js "^3.0.1" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-api@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.1.2.tgz#52fc44883ad6004ace16638ec9837e841ae38c47" - integrity sha512-E5iS9vcElKpL8ve9Ifr2gLLWl+Ig7SXty0DjX7B5rtBFZmq7JENtdWXCOKbHvYiP/wQfxdoQLMDFW1dsg3FnAA== +"@storybook/client-api@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.1.14.tgz#6daf56743cc72e13f05fff3d2ac554897cc9f9fd" + integrity sha512-pIDSlS48bhJdtgNg7sXV1NmLJtB0ebRHJI9htIiqtL7EGQenb4+Bbwflhj1j51OEkuM+bQsAAZxq5deiUQEGVw== dependencies: - "@storybook/addons" "6.1.2" - "@storybook/channel-postmessage" "6.1.2" - "@storybook/channels" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/core-events" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/channel-postmessage" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" "@storybook/csf" "0.0.1" "@types/qs" "^6.9.0" "@types/webpack-env" "^1.15.3" @@ -2337,23 +2471,23 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.1.2.tgz#4036d6fdd8d8865da17c4da3dd8e23322c89140b" - integrity sha512-s/OGSBxVQ5PAWeoXtVoPJXOaJ8eL4V8a7+szd/3Gvl81WLDDqJnH9x/mTM4qzRpfEL8LXfCRBOilt7R3x6EG/A== +"@storybook/client-logger@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.1.14.tgz#216b9c1332ffa3a3473dad837780a3b14f686bae" + integrity sha512-NSO8nVsp6o0eoQ1Drlu66KXpl6DPuq02Kj8AhttGzvqSYB50SV4CV+wceBcg77tIVu5QmQ+71hAEVXhx7sjRHA== dependencies: core-js "^3.0.1" global "^4.3.2" -"@storybook/components@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.1.2.tgz#8d543206a662d0ca95ce47c1ab912523a7b487d4" - integrity sha512-JUW3KxRuX7Fzp7rEtQ7MhpozXcKlQJJbIctgsUdie+1CkWQHXma7gPUqDcBJjrU4nPV1EoTC2xs1c7TduavlpQ== +"@storybook/components@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.1.14.tgz#4ea47edfa0a3e4a26882aa5a1eb90c1ec86e6f71" + integrity sha512-Nxsp/9o1tqfY8s6RBWNHyM03A5D9k56Kr/4VNa++CbDrz1+TIxpYlDgS4sllUlXyvICLfk3sUtg3KS5CPl2iZA== dependencies: - "@popperjs/core" "^2.4.4" - "@storybook/client-logger" "6.1.2" + "@popperjs/core" "^2.5.4" + "@storybook/client-logger" "6.1.14" "@storybook/csf" "0.0.1" - "@storybook/theming" "6.1.2" + "@storybook/theming" "6.1.14" "@types/overlayscrollbars" "^1.9.0" "@types/react-color" "^3.0.1" "@types/react-syntax-highlighter" "11.0.4" @@ -2366,22 +2500,22 @@ overlayscrollbars "^1.10.2" polished "^3.4.4" react-color "^2.17.0" - react-popper-tooltip "^3.1.0" + react-popper-tooltip "^3.1.1" react-syntax-highlighter "^13.5.0" react-textarea-autosize "^8.1.1" ts-dedent "^2.0.0" -"@storybook/core-events@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.1.2.tgz#d29e9a8c7ef6bb41135282f4da8027bb6f399545" - integrity sha512-Vhv22JyDLLJ9kqPCiXyMmePFYVbvYHREzXOtA3vgcf/leKxp6aGHE+OId1e4fycQN4ZU6kPbVnRrI9WFJwV+pA== +"@storybook/core-events@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.1.14.tgz#a3165e32cefd6be7326bbad4b8140653bdfa0426" + integrity sha512-tpM3VDvzqgRY7S17CRglgt1625rxNoyEwrLQiNcZkUPyO0rpaacPqVEbPCtcTmUeboI1bLdnSQIjT9B0/Y2Pww== dependencies: core-js "^3.0.1" -"@storybook/core@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.1.2.tgz#999f80da29f7e476a51a34e953c837278917aeb0" - integrity sha512-LR5YnqrEciIOYc5MkAAOKW4/ijibC7x+eM9sB/pjwdq/+Rspj4XKUzVl1J5oAUHKl8EAdtUGkdWB8fpU6l4fdg== +"@storybook/core@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.1.14.tgz#17e724a5b94d6e1bb557e213b8176660d2d14762" + integrity sha512-lHKZmfLAo2VGtF/yrZkkWMYgmFRNKbzIDxYJGp8USyUQyTfEpz2qqJlBdoD6rxr1hFPM2954tIKwh8iPhT2PFQ== dependencies: "@babel/core" "^7.12.3" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -2405,20 +2539,20 @@ "@babel/preset-react" "^7.12.1" "@babel/preset-typescript" "^7.12.1" "@babel/register" "^7.12.1" - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/channel-postmessage" "6.1.2" - "@storybook/channels" "6.1.2" - "@storybook/client-api" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/core-events" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/channel-postmessage" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-api" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.1.2" - "@storybook/router" "6.1.2" + "@storybook/node-logger" "6.1.14" + "@storybook/router" "6.1.14" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.1.2" - "@storybook/ui" "6.1.2" + "@storybook/theming" "6.1.14" + "@storybook/ui" "6.1.14" "@types/glob-base" "^0.3.0" "@types/micromatch" "^4.0.1" "@types/node-fetch" "^2.5.4" @@ -2475,7 +2609,6 @@ style-loader "^1.2.1" telejson "^5.0.2" terser-webpack-plugin "^3.0.0" - trash "^6.1.1" ts-dedent "^2.0.0" unfetch "^4.1.0" url-loader "^4.0.0" @@ -2493,10 +2626,10 @@ dependencies: lodash "^4.17.15" -"@storybook/node-logger@6.1.2", "@storybook/node-logger@^6.0.28": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.1.2.tgz#fed92ebd537f9e5888e42cc46f393936fcd382ae" - integrity sha512-T/lIUVActNhvNhmuF8HxBT87e3N012HqnYsCo+blTpOqsntmP2m2D7Ipu46EwShMYiBUAr4JaGSt/yjhITcvag== +"@storybook/node-logger@6.1.14", "@storybook/node-logger@^6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.1.14.tgz#e5294f986e3ec5c67b2738895b9d16c9a2b667fa" + integrity sha512-3jrw7coAwFXZu4qK1vm54bCPhNRvxjG+7jISbhhocDoNIv0nLWL3+tJyrC5/k/XHQiUlLkhEzpMaASADmkttNw== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.0.0" @@ -2504,10 +2637,10 @@ npmlog "^4.1.2" pretty-hrtime "^1.0.3" -"@storybook/postinstall@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.1.2.tgz#2d07588b1a3946be9eea70a6d069a0db1be4fba3" - integrity sha512-/+1AM/5OaGK2Hg/JygxLgYnUVkiKcMQ3/5JCGbDd/px9SE8kL570tS3LvMS+t3jnMCGKXQW0B5iIVTndDG0AYQ== +"@storybook/postinstall@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.1.14.tgz#ec931739d566bbd3b15b742c32be82701e4910b9" + integrity sha512-A2ytqaoNjZoxmK3kZ2FxeQki6gZugGdPnEMbs8q+EJq7IN3UEbxisdGj6vxKXf/rlyZ1G1t2jSQ4xmkBF8+fZg== dependencies: core-js "^3.0.1" @@ -2523,19 +2656,18 @@ react-docgen-typescript-plugin "^0.6.2" semver "^7.3.2" -"@storybook/react@^6.0.28": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.1.2.tgz#863662543a18c3a2ecea7a5646ec711c29163b05" - integrity sha512-q7Nj++ucjk9pVt/0jUlhoqzeypbzKdmRyN80hPAsSmvpzONlJtK82VA0iujBh9G6T+cT289nq2pf555tQfiAdQ== +"@storybook/react@^6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.1.14.tgz#436e9b90096b1d7c83f7f073b5baf47212b2e425" + integrity sha512-M99wHjc/5z+Wz1FdFaScVs6dyAi/6PdcIx5Fyip6Qd8aKwm1XyYoOMql5Vu3Cf560feDYCKS4phzyEZ7EJy+EQ== dependencies: "@babel/preset-flow" "^7.12.1" "@babel/preset-react" "^7.12.1" "@pmmmwh/react-refresh-webpack-plugin" "^0.4.2" - "@storybook/addons" "6.1.2" - "@storybook/core" "6.1.2" - "@storybook/node-logger" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/core" "6.1.14" + "@storybook/node-logger" "6.1.14" "@storybook/semver" "^7.3.2" - "@svgr/webpack" "^5.4.0" "@types/webpack-env" "^1.15.3" babel-plugin-add-react-displayname "^0.0.5" babel-plugin-named-asset-import "^0.3.1" @@ -2551,10 +2683,10 @@ ts-dedent "^2.0.0" webpack "^4.44.2" -"@storybook/router@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.1.2.tgz#606790a89e5f25d9bd808de765e9292b609ad191" - integrity sha512-fNKWCEJlA0uCFBNbRgI9VD3a6hyelr528MEfdWcNvjffYXsF4DnMwewWYEPx01JUrWiAeZ3Qs5FYphuHn8BKEw== +"@storybook/router@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.1.14.tgz#f6aef8c9dabf19bf06dddd80907e66369261fdde" + integrity sha512-rMaUCYzgfVLwFWo3A1Q/weSv8FBqCLmHY+3+t6ao7OV6NYjR0XgLKRzHrXq1uYdbMxWeIKhN2tIt/LR43bmDjQ== dependencies: "@reach/router" "^1.3.3" "@types/reach__router" "^1.3.5" @@ -2571,13 +2703,13 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.1.2.tgz#0982fcfa252c72f139da8bac7f478edbb3a7db13" - integrity sha512-rxfqecE4AaG+hyGUJRh4yXz/bqVmboo0X39AEObq7zwf3vz+6iw3j4MmepGHnyBotCVLw2AmKZ1405b4wO4fBg== +"@storybook/source-loader@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.1.14.tgz#507ff52cc6627e4d996020de05bf023004230953" + integrity sha512-JY3hJGTJSNmohmDDE3BLE3vPW1rSAIRToq0vpo9ZhFTFUWHm3RlhcS8+5z8Mvn9+TLDuf5WnSib5lfGmCtLmJQ== dependencies: - "@storybook/addons" "6.1.2" - "@storybook/client-logger" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/client-logger" "6.1.14" "@storybook/csf" "0.0.1" core-js "^3.0.1" estraverse "^4.2.0" @@ -2588,15 +2720,15 @@ regenerator-runtime "^0.13.7" source-map "^0.7.3" -"@storybook/theming@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.1.2.tgz#55fb60017acdf952450cc0cb9b530d36aa039178" - integrity sha512-MC89aDLIrng3zqpoTys0gylP76rlBYyEKUWtcZqrnFm6b+IqhmeP8e6KV/e5wmBfjuFqmDsKLLVMN6BwwZJlgg== +"@storybook/theming@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.1.14.tgz#fecb66cab22d3b3218b4a98a9c210eb8a7be91e8" + integrity sha512-S+t30y4FqBTXWoVr+dtxVJ/ywiQGHBclBd9aUunbdCV4mMFra5InNo2CWn+RJlNEauLZ93gRIEzSFchIbzLk1A== dependencies: "@emotion/core" "^10.1.1" "@emotion/is-prop-valid" "^0.8.6" "@emotion/styled" "^10.0.23" - "@storybook/client-logger" "6.1.2" + "@storybook/client-logger" "6.1.14" core-js "^3.0.1" deep-object-diff "^1.1.0" emotion-theming "^10.0.19" @@ -2606,21 +2738,21 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" -"@storybook/ui@6.1.2": - version "6.1.2" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.1.2.tgz#ee288141b70f908a8dcea646cb2ec52cf69a2483" - integrity sha512-ZBfL4x+E43DR+GnC/wjJ9QYP0sdXyZxxKWLQoDkNPIdDnNtFGyKFQyElo9GgtyGxIukssO0sW6fwWWpnErKTyw== +"@storybook/ui@6.1.14": + version "6.1.14" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.1.14.tgz#766d696480ee6f6a5a0454ccb2f101c38a0eb9d2" + integrity sha512-DTW2TM05jTMKxh8LzUGk3g5a528PgJxrtgODFU6zzwSg2+LwdmSDtd1HAxopt2vpfTyQyX+6WN2H+lMNwfQTAQ== dependencies: "@emotion/core" "^10.1.1" - "@storybook/addons" "6.1.2" - "@storybook/api" "6.1.2" - "@storybook/channels" "6.1.2" - "@storybook/client-logger" "6.1.2" - "@storybook/components" "6.1.2" - "@storybook/core-events" "6.1.2" - "@storybook/router" "6.1.2" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/router" "6.1.14" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.1.2" + "@storybook/theming" "6.1.14" "@types/markdown-to-jsx" "^6.11.0" copy-to-clipboard "^3.0.8" core-js "^3.0.1" @@ -2642,11 +2774,6 @@ resolve-from "^5.0.0" store2 "^2.7.1" -"@stroncium/procfs@^1.0.0": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@stroncium/procfs/-/procfs-1.2.1.tgz#6b9be6fd20fb0a4c20e99a8695e083c699bb2b45" - integrity sha512-X1Iui3FUNZP18EUvysTHxt+Avu2nlVzyf90YM8OYgP6SGzTzzX/0JgObfO1AQQDzuZtNNz29bVh8h5R97JrjxA== - "@surma/rollup-plugin-off-main-thread@^1.1.1": version "1.4.2" resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58" @@ -2709,7 +2836,7 @@ "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" "@svgr/babel-plugin-transform-svg-component" "^5.5.0" -"@svgr/core@^5.4.0", "@svgr/core@^5.5.0": +"@svgr/core@^5.4.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ== @@ -2735,7 +2862,7 @@ "@svgr/hast-util-to-babel-ast" "^5.5.0" svg-parser "^2.0.2" -"@svgr/plugin-svgo@^5.4.0", "@svgr/plugin-svgo@^5.5.0": +"@svgr/plugin-svgo@^5.4.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ== @@ -2758,20 +2885,6 @@ "@svgr/plugin-svgo" "^5.4.0" loader-utils "^2.0.0" -"@svgr/webpack@^5.4.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" - integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g== - dependencies: - "@babel/core" "^7.12.3" - "@babel/plugin-transform-react-constant-elements" "^7.12.1" - "@babel/preset-env" "^7.12.1" - "@babel/preset-react" "^7.12.5" - "@svgr/core" "^5.5.0" - "@svgr/plugin-jsx" "^5.5.0" - "@svgr/plugin-svgo" "^5.5.0" - loader-utils "^2.0.0" - "@testing-library/dom@^7.28.1": version "7.28.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.28.1.tgz#dea78be6e1e6db32ddcb29a449e94d9700c79eb9" @@ -2868,6 +2981,11 @@ resolved "https://registry.yarnpkg.com/@types/braces/-/braces-3.0.0.tgz#7da1c0d44ff1c7eb660a36ec078ea61ba7eb42cb" integrity sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw== +"@types/classnames@^2.2.11": + version "2.2.11" + resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.11.tgz#2521cc86f69d15c5b90664e4829d84566052c1cf" + integrity sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw== + "@types/eslint@^7.2.4": version "7.2.5" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.5.tgz#92172ecf490c2fce4b076739693d75f30376d610" @@ -3986,7 +4104,7 @@ async@0.9.x: resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= -async@^2.6.2: +async@^2.6.1, async@^2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -4204,7 +4322,7 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@2.8.0, babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: +babel-plugin-macros@2.8.0, babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.6.1, babel-plugin-macros@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -5085,7 +5203,7 @@ ccount@^1.0.0: resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5250,7 +5368,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@2.2.6, classnames@^2.2.5: +classnames@2.2.6, classnames@^2.2.5, classnames@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== @@ -5499,7 +5617,7 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@^2.19.0, commander@^2.20.0: +commander@^2.18.0, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -5804,17 +5922,6 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cp-file@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-6.2.0.tgz#40d5ea4a1def2a9acdd07ba5c0b0246ef73dc10d" - integrity sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA== - dependencies: - graceful-fs "^4.1.2" - make-dir "^2.0.0" - nested-error-stacks "^2.0.0" - pify "^4.0.1" - safe-buffer "^5.0.1" - cp-file@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd" @@ -5900,14 +6007,6 @@ cross-fetch@3.0.6: dependencies: node-fetch "2.6.1" -cross-spawn-async@^2.1.1: - version "2.2.5" - resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc" - integrity sha1-hF/wwINKPe2dFg2sptOQkGuyiMw= - dependencies: - lru-cache "^4.0.0" - which "^1.2.8" - cross-spawn@7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" @@ -6629,7 +6728,7 @@ dir-glob@2.0.0: arrify "^1.0.1" path-type "^3.0.0" -dir-glob@^2.0.0, dir-glob@^2.2.2: +dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== @@ -6953,6 +7052,11 @@ elliptic@^6.5.3: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +email-addresses@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.1.0.tgz#cabf7e085cbdb63008a70319a74e6136188812fb" + integrity sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg== + emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -7025,7 +7129,7 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.4" -enhanced-resolve@^4.3.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== @@ -7221,6 +7325,11 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@^1.12.0, escodegen@^1.14.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" @@ -7535,17 +7644,6 @@ exec-sh@^0.3.2: resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== -execa@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.2.2.tgz#e2ead472c2c31aad6f73f1ac956eef45e12320cb" - integrity sha1-4urUcsLDGq1vc/GslW7vReEjIMs= - dependencies: - cross-spawn-async "^2.1.1" - npm-run-path "^1.0.0" - object-assign "^4.0.1" - path-key "^1.0.0" - strip-eof "^1.0.0" - execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -7870,6 +7968,28 @@ filelist@^1.0.1: dependencies: minimatch "^3.0.4" +filename-reserved-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz#e61cf805f0de1c984567d0386dc5df50ee5af7e4" + integrity sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q= + +filenamify-url@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/filenamify-url/-/filenamify-url-1.0.0.tgz#b32bd81319ef5863b73078bed50f46a4f7975f50" + integrity sha1-syvYExnvWGO3MHi+1Q9GpPeXX1A= + dependencies: + filenamify "^1.0.0" + humanize-url "^1.0.0" + +filenamify@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-1.2.1.tgz#a9f2ffd11c503bed300015029272378f1f1365a5" + integrity sha1-qfL/0RxQO+0wABUCknI3jx8TZaU= + dependencies: + filename-reserved-regex "^1.0.0" + strip-outer "^1.0.0" + trim-repeated "^1.0.0" + filesize@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.0.1.tgz#f850b509909c7c86f7e450ea19006c31c2ed3d2f" @@ -8334,6 +8454,19 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +gh-pages@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/gh-pages/-/gh-pages-3.1.0.tgz#ec3ed0f6a6e3fc3d888758fa018f08191c96bd55" + integrity sha512-3b1rly9kuf3/dXsT8+ZxP0UhNLOo1CItj+3e31yUVcaph/yDsJ9RzD7JOw5o5zpBTJVQLlJAASNkUfepi9fe2w== + dependencies: + async "^2.6.1" + commander "^2.18.0" + email-addresses "^3.0.1" + filenamify-url "^1.0.0" + find-cache-dir "^3.3.1" + fs-extra "^8.1.0" + globby "^6.1.0" + git-log-parser@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/git-log-parser/-/git-log-parser-1.2.0.tgz#2e6a4c1b13fc00028207ba795a7ac31667b9fd4a" @@ -8503,18 +8636,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -8802,7 +8923,7 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -9058,6 +9179,14 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +humanize-url@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/humanize-url/-/humanize-url-1.0.1.tgz#f4ab99e0d288174ca4e1e50407c55fbae464efff" + integrity sha1-9KuZ4NKIF0yk4eUEB8VfuuRk7/8= + dependencies: + normalize-url "^1.0.0" + strip-url-auth "^1.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -9355,6 +9484,11 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= +ionicons@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/ionicons/-/ionicons-5.2.3.tgz#af4018ea7585b1e9ae11757731c77f2f36e0c7ac" + integrity sha512-87qtgBkieKVFagwYA9Cf91B3PCahQbEOMwMt8bSvlQSgflZ4eE5qI4MGj2ZlIyadeX0dgo+0CzZsy3ow0CsBAg== + ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -11115,7 +11249,7 @@ lru-cache@6.0.0, lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^4.0.0, lru-cache@^4.0.1: +lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -11628,15 +11762,6 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -mount-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mount-point/-/mount-point-3.0.0.tgz#665cb9edebe80d110e658db56c31d0aef51a8f97" - integrity sha1-Zly57evoDREOZY21bDHQrvUaj5c= - dependencies: - "@sindresorhus/df" "^1.0.1" - pify "^2.3.0" - pinkie-promise "^2.0.1" - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -11649,15 +11774,6 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" -move-file@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/move-file/-/move-file-1.2.0.tgz#789f92d276c62511d214b1b285aa16e015c2f2fc" - integrity sha512-USHrRmxzGowUWAGBbJPdFjHzEqtxDU03pLHY0Rfqgtnq+q8FOIs8wvkkf+Udmg77SJKs47y9sI0jJvQeYsmiCA== - dependencies: - cp-file "^6.1.0" - make-dir "^3.0.0" - path-exists "^3.0.0" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -12022,7 +12138,7 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= -normalize-url@1.9.1: +normalize-url@1.9.1, normalize-url@^1.0.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= @@ -12042,6 +12158,11 @@ normalize-url@^5.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-5.3.0.tgz#8959b3cdaa295b61592c1f245dded34b117618dd" integrity sha512-9/nOVLYYe/dO/eJeQUNaGUF4m4Z5E7cb9oNTKabH+bNf19mqj60txTcveQxL0GlcWLXCxkOu2/LwL8oW0idIDA== +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + npm-audit-report@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/npm-audit-report/-/npm-audit-report-1.3.3.tgz#8226deeb253b55176ed147592a3995442f2179ed" @@ -12143,13 +12264,6 @@ npm-registry-fetch@^4.0.0, npm-registry-fetch@^4.0.7: npm-package-arg "^6.1.0" safe-buffer "^5.2.0" -npm-run-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-1.0.0.tgz#f5c32bf595fe81ae927daec52e82f8b000ac3c8f" - integrity sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8= - dependencies: - path-key "^1.0.0" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -12689,7 +12803,7 @@ p-try@^1.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= -p-try@^2.0.0, p-try@^2.2.0: +p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== @@ -12880,11 +12994,6 @@ path-is-inside@^1.0.1, path-is-inside@^1.0.2, path-is-inside@~1.0.2: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= -path-key@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-1.0.0.tgz#5d53d578019646c0d68800db4e146e6bdc2ac7af" - integrity sha1-XVPVeAGWRsDWiADbThRua9wqx68= - path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -12945,7 +13054,7 @@ picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: +pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -12965,7 +13074,7 @@ pify@^5.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== -pinkie-promise@^2.0.0, pinkie-promise@^2.0.1: +pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= @@ -13974,7 +14083,7 @@ promzard@^0.3.0: dependencies: read "1" -prop-types@15.7.2, prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.7.2, prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -14426,7 +14535,7 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-popper-tooltip@^3.1.0: +react-popper-tooltip@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" integrity sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ== @@ -14523,6 +14632,14 @@ react-sizeme@^2.6.7: shallowequal "^1.1.0" throttle-debounce "^2.1.0" +react-spring@^8.0.27: + version "8.0.27" + resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-8.0.27.tgz#97d4dee677f41e0b2adcb696f3839680a3aa356a" + integrity sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g== + dependencies: + "@babel/runtime" "^7.3.1" + prop-types "^15.5.8" + react-syntax-highlighter@^13.5.0: version "13.5.3" resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz#9712850f883a3e19eb858cf93fad7bb357eea9c6" @@ -15013,6 +15130,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -16371,6 +16493,18 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-outer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" + integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== + dependencies: + escape-string-regexp "^1.0.2" + +strip-url-auth@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-url-auth/-/strip-url-auth-1.0.1.tgz#22b0fa3a41385b33be3f331551bbb837fa0cd7ae" + integrity sha1-IrD6OkE4WzO+PzMVUbu4N/oM164= + style-inject@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/style-inject/-/style-inject-0.3.0.tgz#d21c477affec91811cc82355832a700d22bf8dd3" @@ -16432,6 +16566,11 @@ stylis@3.5.4: resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== +stylis@^4.0.3: + version "4.0.9" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.9.tgz#ae3d5283aa439225cf79dd2d0cf46f8bfd4ad393" + integrity sha512-ci7pEFNVW3YJiWEzqPOMsAjY6kgraZ3ZgBfQ5HYbNtLJEsQ0G46ejWZpfSSCp/FaSiCSGGhzL9O2lN+2cB6ong== + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -16888,21 +17027,6 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" -trash@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/trash/-/trash-6.1.1.tgz#8fb863421b31f32571f2650b53534934d5e63025" - integrity sha512-4i56lCmz2RG6WZN018hf4L75L5HboaFuKkHx3wDG/ihevI99e0OgFyl8w6G4ioqBm62V4EJqCy5xw3vQSNXU8A== - dependencies: - "@stroncium/procfs" "^1.0.0" - globby "^7.1.1" - is-path-inside "^3.0.2" - make-dir "^3.0.0" - move-file "^1.1.0" - p-map "^3.0.0" - p-try "^2.2.0" - uuid "^3.3.2" - xdg-trashdir "^2.1.1" - traverse@0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" @@ -16918,6 +17042,13 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= +trim-repeated@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" + integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= + dependencies: + escape-string-regexp "^1.0.2" + trim-trailing-lines@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" @@ -16953,7 +17084,16 @@ ts-pnp@1.2.0, ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tsconfig-paths@^3.9.0: +tsconfig-paths-webpack-plugin@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.3.0.tgz#a7461723c20623ca9148621a5ce36532682ad2ff" + integrity sha512-MpQeZpwPY4gYASCUjY4yt2Zj8yv86O8f++3Ai4o0yI0fUC6G1syvnL9VuY71PBgimRYDQU47f12BEmJq9wRaSw== + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + tsconfig-paths "^3.4.0" + +tsconfig-paths@^3.4.0, tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== @@ -17409,13 +17549,6 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= - dependencies: - os-homedir "^1.0.0" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -17882,7 +18015,7 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -which@^1.2.8, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -18163,29 +18296,11 @@ ws@^7.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== -xdg-basedir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2" - integrity sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I= - dependencies: - os-homedir "^1.0.0" - xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= -xdg-trashdir@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/xdg-trashdir/-/xdg-trashdir-2.1.1.tgz#59a60aaf8e6f9240c1daed9a0944b2f514c27d8e" - integrity sha512-KcVhPaOu2ZurYNHSRTf1+ZHORkTZGCQ+u0JHN17QixRISJq4pXOnjt/lQcehvtHL5QAKhSzKgyjrcNnPdkPBHA== - dependencies: - "@sindresorhus/df" "^2.1.0" - mount-point "^3.0.0" - pify "^2.2.0" - user-home "^2.0.0" - xdg-basedir "^2.0.0" - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"