From 11ce718b68ca5b04f4fdb71e488889c21ee79214 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 19:56:35 +0900 Subject: [PATCH 01/19] feat: add `css-` namespace for beautiful className --- src/navigator/components/Card.tsx | 16 ++++++++-------- src/navigator/components/Navbar.tsx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index acaf87b45..a3adee79c 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -38,7 +38,7 @@ const Card: React.FC = (props) => { const $frameContainer = useRef(null) const $hiddenDims = useMemo( // eslint-disable-next-line - () => document.getElementsByClassName('kf-dim_hidden') as HTMLCollectionOf, + () => document.getElementsByClassName('css-kf-dim_hidden') as HTMLCollectionOf, [] ) @@ -159,12 +159,12 @@ const Card: React.FC = (props) => { )} {props.children} @@ -266,10 +266,10 @@ const Container = styled.div` ${(props) => (props.enterActive || props.enterDone) && css` - .kf-dim { + .css-kf-dim { background-color: rgba(0, 0, 0, 0.2); } - .kf-frame-container { + .css-kf-frame-container { transform: translateX(0); } `} @@ -277,14 +277,14 @@ const Container = styled.div` ${(props) => (props.exitActive || props.exitDone) && css` - .kf-dim { + .css-kf-dim { background-color: rgba(0, 0, 0, 0); transform: translateX(0); } - .kf-frame-container { + .css-kf-frame-container { transform: translateX(100%); } - .kf-navbar-container { + .css-kf-navbar-container { display: none; } `} diff --git a/src/navigator/components/Navbar.tsx b/src/navigator/components/Navbar.tsx index aff0f62f5..b811d9870 100644 --- a/src/navigator/components/Navbar.tsx +++ b/src/navigator/components/Navbar.tsx @@ -25,7 +25,7 @@ const Navbar: React.FC = (props) => { return ( {props.environment === 'Cupertino' && ( From fb1030a3162f610b667362b2ba403c931fce0a42 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 20:11:48 +0900 Subject: [PATCH 02/19] feat: add options --- src/navigator/Navigator.tsx | 45 +++++++++++++------- src/navigator/Screen.tsx | 6 ++- src/navigator/ScreenHelmet.tsx | 25 +++++++++-- src/navigator/atoms/ScreenEdge.ts | 2 +- src/navigator/atoms/ScreenInstanceOptions.ts | 6 +-- src/navigator/atoms/ScreenInstancePointer.ts | 2 +- src/navigator/atoms/ScreenInstances.ts | 2 +- src/navigator/atoms/Screens.ts | 2 +- src/navigator/components/Navbar.tsx | 21 +++++---- 9 files changed, 73 insertions(+), 38 deletions(-) diff --git a/src/navigator/Navigator.tsx b/src/navigator/Navigator.tsx index 0d238cbc9..abeb1829c 100644 --- a/src/navigator/Navigator.tsx +++ b/src/navigator/Navigator.tsx @@ -26,34 +26,49 @@ let isNavigatorInitialized = false interface NavigatorProps { /** - * 환경 + * 환경 (기본값: Web) */ - environment: Environment + environment?: Environment /** - * 애니메이션 지속시간 + * 애니메이션 지속시간 (기본값: 350) */ animationDuration?: number + /** + * 빌트인 된 RecoilRoot를 없애고, 사용자가 직접 RecoilRoot를 셋팅합니다 + */ + useCustomRecoilRoot?: boolean + + /** + * 빌트인 된 react-router-dom의 HashRouter를 없애고, 사용자가 직접 Router를 셋팅합니다 + */ + useCustomRouter?: boolean + /** * 닫기 버튼을 눌렀을때 해당 콜백이 호출됩니다 */ onClose?: () => void } const Navigator: React.FC = (props) => { - return ( - - - - {props.children} - - - + let h = ( + + {props.children} + ) + + if (!props.useCustomRecoilRoot) { + h = {h} + } + if (!props.useCustomRouter) { + h = {h} + } + + return h } const NavigatorScreens: React.FC> = (props) => { diff --git a/src/navigator/Screen.tsx b/src/navigator/Screen.tsx index 40f5b7bcd..ea1d7da45 100644 --- a/src/navigator/Screen.tsx +++ b/src/navigator/Screen.tsx @@ -11,8 +11,12 @@ interface ScreenProps { */ path: string - children?: React.ReactNode + /** + * 해당 스크린에 표시할 컴포넌트 + */ component?: React.ComponentType + + children?: React.ReactNode } const Screen: React.FC = (props) => { const Component = props.component diff --git a/src/navigator/ScreenHelmet.tsx b/src/navigator/ScreenHelmet.tsx index f19de59b4..c528c649e 100644 --- a/src/navigator/ScreenHelmet.tsx +++ b/src/navigator/ScreenHelmet.tsx @@ -3,10 +3,27 @@ import React, { useEffect } from 'react' import { useScreenInstanceOptions } from './contexts' interface ScreenHelmetProps { - title?: string + /** + * 네비게이션의 타이틀 + */ + title?: React.ReactNode + + /** + * 네비게이션의 왼쪽에 요소를 추가 + * (이전 버튼 오른쪽에 표시됩니다) + */ left?: React.ReactNode + + /** + * 네비게이션의 오른쪽 요소를 추가 + * (닫기 버튼 왼쪽에 표시됩니다) + */ right?: React.ReactNode - center?: React.ReactNode + + /** + * 이전 버튼을 사용자화합니다 + */ + back?: React.ReactNode } const ScreenHelmet: React.FC = (props) => { const screen = useScreenInstanceOptions() @@ -14,10 +31,10 @@ const ScreenHelmet: React.FC = (props) => { useEffect(() => { screen.setNavbar({ visible: true, - title: props.title ?? '', + title: props.title ?? null, left: props.left ?? null, right: props.right ?? null, - center: props.center ?? null, + back: props.back ?? null, }) }, [props]) diff --git a/src/navigator/atoms/ScreenEdge.ts b/src/navigator/atoms/ScreenEdge.ts index 165f1313a..7f10471cd 100644 --- a/src/navigator/atoms/ScreenEdge.ts +++ b/src/navigator/atoms/ScreenEdge.ts @@ -4,7 +4,7 @@ export const AtomScreenEdge = atom<{ startX: number | null startTime: number | null }>({ - key: 'ScreenEdge', + key: 'KFScreenEdge', default: { startX: null, startTime: null, diff --git a/src/navigator/atoms/ScreenInstanceOptions.ts b/src/navigator/atoms/ScreenInstanceOptions.ts index c9c6d8cae..ee7afb2d5 100644 --- a/src/navigator/atoms/ScreenInstanceOptions.ts +++ b/src/navigator/atoms/ScreenInstanceOptions.ts @@ -2,11 +2,11 @@ import React from 'react' import { atom } from 'recoil' export interface NavbarOptions { - title: string visible: boolean + title: React.ReactNode | null left: React.ReactNode | null right: React.ReactNode | null - center: React.ReactNode | null + back: React.ReactNode | null } export const AtomScreenInstanceOptions = atom<{ @@ -14,6 +14,6 @@ export const AtomScreenInstanceOptions = atom<{ navbar: NavbarOptions } }>({ - key: 'ScreenInstanceOptions', + key: 'KFScreenInstanceOptions', default: {}, }) diff --git a/src/navigator/atoms/ScreenInstancePointer.ts b/src/navigator/atoms/ScreenInstancePointer.ts index 88e4395d9..65924143b 100644 --- a/src/navigator/atoms/ScreenInstancePointer.ts +++ b/src/navigator/atoms/ScreenInstancePointer.ts @@ -1,6 +1,6 @@ import { atom } from 'recoil' export const AtomScreenInstancePointer = atom({ - key: 'ScreenInstancePointer', + key: 'KFScreenInstancePointer', default: -1, }) diff --git a/src/navigator/atoms/ScreenInstances.ts b/src/navigator/atoms/ScreenInstances.ts index 3523a3cfd..69600fd9a 100644 --- a/src/navigator/atoms/ScreenInstances.ts +++ b/src/navigator/atoms/ScreenInstances.ts @@ -5,6 +5,6 @@ export interface ScreenInstance { screenId: string } export const AtomScreenInstances = atom({ - key: 'ScreenInstances', + key: 'KFScreenInstances', default: [], }) diff --git a/src/navigator/atoms/Screens.ts b/src/navigator/atoms/Screens.ts index 35370d9c3..da5d22844 100644 --- a/src/navigator/atoms/Screens.ts +++ b/src/navigator/atoms/Screens.ts @@ -13,6 +13,6 @@ export interface Screen { export const AtomScreens = atom<{ [id: string]: Screen }>({ - key: 'Screens', + key: 'KFScreens', default: {}, }) diff --git a/src/navigator/components/Navbar.tsx b/src/navigator/components/Navbar.tsx index b811d9870..0f9b49cbb 100644 --- a/src/navigator/components/Navbar.tsx +++ b/src/navigator/components/Navbar.tsx @@ -29,25 +29,24 @@ const Navbar: React.FC = (props) => { environment={props.environment} animationDuration={navigatorOptions.animationDuration}> {props.environment === 'Cupertino' && ( -
- {screenInstanceOption?.navbar.center || screenInstanceOption?.navbar.title} -
+
{screenInstanceOption?.navbar.title}
)} {(!props.isRoot || screenInstanceOption?.navbar.left) && ( - {!props.isRoot && ( - - - - )} + {!props.isRoot && + (screenInstanceOption?.navbar.back ? ( +
{screenInstanceOption.navbar.back}
+ ) : ( + + + + ))} {screenInstanceOption?.navbar.left}
)} {(props.environment === 'Android' || props.environment === 'Web') && ( -
- {screenInstanceOption?.navbar.center || screenInstanceOption?.navbar.title} -
+
{screenInstanceOption?.navbar.title}
)} {(props.isRoot || screenInstanceOption?.navbar.right) && ( From 37d8339cdbb35fdf89edd599022ddb4ee99e814f Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 20:28:02 +0900 Subject: [PATCH 03/19] feat: add android platform animation --- example/src/App.tsx | 2 +- src/navigator/components/Card.tsx | 77 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index c55d8c4a4..84deea8df 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -15,7 +15,7 @@ const bridge = new Bridge() function App() { return ( { bridge.router.close() diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index a3adee79c..d37f8c70b 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -38,7 +38,7 @@ const Card: React.FC = (props) => { const $frameContainer = useRef(null) const $hiddenDims = useMemo( // eslint-disable-next-line - () => document.getElementsByClassName('css-kf-dim_hidden') as HTMLCollectionOf, + () => document.getElementsByClassName('css-kf-card-dim_hidden') as HTMLCollectionOf, [] ) @@ -142,8 +142,11 @@ const Card: React.FC = (props) => { return ( = (props) => { )} {props.children} - {!props.isRoot && ( + {navigatorOptions.environment === 'Cupertino' && !props.isRoot && ( )} @@ -178,6 +183,7 @@ const Card: React.FC = (props) => { } interface DimProps { + environment: Environment isTop: boolean animationDuration: number } @@ -185,10 +191,15 @@ const Dim = styled.div` width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0); - transform: translateX(-5rem); transition: background-color ${(props) => props.animationDuration}ms, transform ${(props) => props.animationDuration}ms; + ${(props) => + props.environment === 'Cupertino' && + css` + transform: translateX(-5rem); + `} + ${(props) => props.isTop && css` @@ -197,6 +208,7 @@ const Dim = styled.div` ` interface FrameContainerProps { + environment: Environment isRoot: boolean animationDuration: number } @@ -210,6 +222,7 @@ const FrameContainer = styled.div` ${(props) => !props.isRoot && + props.environment === 'Cupertino' && css` transform: translateX(100%); `}; @@ -231,6 +244,8 @@ const Edge = styled.div` interface ContainerProps { navbarVisible?: boolean environment: Environment + animationDuration: number + isRoot: boolean enterActive: boolean enterDone: boolean exitActive: boolean @@ -264,29 +279,45 @@ const Container = styled.div` }} ${(props) => - (props.enterActive || props.enterDone) && + props.environment === 'Cupertino' && css` - .css-kf-dim { - background-color: rgba(0, 0, 0, 0.2); - } - .css-kf-frame-container { - transform: translateX(0); - } + ${(props.enterActive || props.enterDone) && + css` + .css-kf-card-dim { + background-color: rgba(0, 0, 0, 0.2); + } + .css-card-kf-frame-container { + transform: translateX(0); + } + `} + ${(props.exitActive || props.exitDone) && + css` + .css-kf-card-dim { + background-color: rgba(0, 0, 0, 0); + transform: translateX(0); + } + .css-card-kf-frame-container { + transform: translateX(100%); + } + .css-kf-navbar-container { + display: none; + } + `} `} ${(props) => - (props.exitActive || props.exitDone) && + (props.environment === 'Android' || props.environment === 'Web') && + !props.isRoot && css` - .css-kf-dim { - background-color: rgba(0, 0, 0, 0); - transform: translateX(0); - } - .css-kf-frame-container { - transform: translateX(100%); - } - .css-kf-navbar-container { - display: none; - } + opacity: 0; + transform: translateY(3rem); + transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration / 2}ms; + + ${(props.enterActive || props.enterDone) && + css` + opacity: 1; + transform: translateY(0); + `} `} ${(props) => From beb6fe100cc91ab69757155e80c314af0343e2d6 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 20:34:16 +0900 Subject: [PATCH 04/19] =?UTF-8?q?refactor:=20css=20=EC=88=9C=EC=84=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/navigator/components/Card.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index d37f8c70b..c5698fb4c 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -196,15 +196,10 @@ const Dim = styled.div` ${(props) => props.environment === 'Cupertino' && + !props.isTop && css` transform: translateX(-5rem); `} - - ${(props) => - props.isTop && - css` - transform: translateX(0); - `} ` interface FrameContainerProps { @@ -221,8 +216,8 @@ const FrameContainer = styled.div` background-color: #fff; ${(props) => - !props.isRoot && props.environment === 'Cupertino' && + !props.isRoot && css` transform: translateX(100%); `}; From b047be175ec142fa7c7694f9a9fdbb4364f11e85 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 20:53:49 +0900 Subject: [PATCH 05/19] feat: update android animation --- example/src/App.tsx | 1 - src/navigator/Navigator.tsx | 19 ++++++++++++++++--- src/navigator/components/Card.tsx | 31 ++++++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 84deea8df..5a9a2e94f 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -16,7 +16,6 @@ function App() { return ( { bridge.router.close() }}> diff --git a/src/navigator/Navigator.tsx b/src/navigator/Navigator.tsx index abeb1829c..dbddb1bdb 100644 --- a/src/navigator/Navigator.tsx +++ b/src/navigator/Navigator.tsx @@ -16,7 +16,9 @@ import { Card } from './components' import { NavigatorOptionsProvider, useNavigatorOptions } from './contexts' import { Environment } from '../types' -const DEFAULT_ANIMATION_DURATION = 350 +const DEFAULT_CUPERTINO_ANIMATION_DURATION = 350 +const DEFAULT_WEB_ANIMATION_DURATION = 270 +const DEFAULT_ANDROID_ANIMATION_DURATION = 270 /** * Navigator가 이미 초기화되었는지 확인 @@ -31,7 +33,7 @@ interface NavigatorProps { environment?: Environment /** - * 애니메이션 지속시간 (기본값: 350) + * 애니메이션 지속시간 */ animationDuration?: number @@ -55,7 +57,18 @@ const Navigator: React.FC = (props) => { { + switch (props.environment ?? 'Web') { + case 'Cupertino': + return DEFAULT_CUPERTINO_ANIMATION_DURATION + case 'Android': + return DEFAULT_ANDROID_ANIMATION_DURATION + case 'Web': + return DEFAULT_WEB_ANIMATION_DURATION + } + })(), }}> {props.children} diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index c5698fb4c..bfc94d0d9 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -147,6 +147,7 @@ const Card: React.FC = (props) => { animationDuration={navigatorOptions.animationDuration} navbarVisible={!!screenInstanceOption?.navbar.visible} isRoot={props.isRoot} + isTop={props.isTop} enterActive={props.enterActive} enterDone={props.enterDone} exitActive={props.exitActive} @@ -241,6 +242,7 @@ interface ContainerProps { environment: Environment animationDuration: number isRoot: boolean + isTop: boolean enterActive: boolean enterDone: boolean exitActive: boolean @@ -302,16 +304,39 @@ const Container = styled.div` ${(props) => (props.environment === 'Android' || props.environment === 'Web') && - !props.isRoot && css` opacity: 0; - transform: translateY(3rem); - transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration / 2}ms; + transform: translateY(10rem); + transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration / 1.5}ms, + box-shadow ${props.animationDuration / 1.5}ms; + + ${props.isRoot && + css` + opacity: 1; + transform: translateY(0); + `} ${(props.enterActive || props.enterDone) && css` opacity: 1; transform: translateY(0); + box-shadow: 0 0 10rem 0 rgba(0, 0, 0, 0.3); + `} + + ${props.enterDone && + css` + box-shadow: none; + `} + + ${!props.isTop && + css` + transform: translateY(-2rem); + `} + + ${(props.exitActive || props.exitActive) && + css` + opacity: 0; + transform: translateY(10rem); `} `} From 15647d0fbf8a313bfeeb756ef4ea04ab859a89d5 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 23:11:50 +0900 Subject: [PATCH 06/19] feat: add will-change --- src/navigator/components/Card.tsx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index bfc94d0d9..81a38d56e 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -191,15 +191,15 @@ interface DimProps { const Dim = styled.div` width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0); - transition: background-color ${(props) => props.animationDuration}ms, - transform ${(props) => props.animationDuration}ms; ${(props) => props.environment === 'Cupertino' && !props.isTop && css` + background-color: rgba(0, 0, 0, 0); transform: translateX(-5rem); + transition: background-color ${props.animationDuration}ms, transform ${props.animationDuration}ms; + will-change: background-color, transform; `} ` @@ -211,16 +211,20 @@ interface FrameContainerProps { const FrameContainer = styled.div` width: 100%; height: 100%; - transition: transform ${(props) => props.animationDuration}ms; - transform: translateX(0); overflow-y: scroll; background-color: #fff; ${(props) => props.environment === 'Cupertino' && - !props.isRoot && css` - transform: translateX(100%); + transform: translateX(0); + transition: transform ${props.animationDuration}ms; + will-change: transform; + + ${!props.isRoot && + css` + transform: translateX(100%); + `} `}; ` @@ -305,10 +309,12 @@ const Container = styled.div` ${(props) => (props.environment === 'Android' || props.environment === 'Web') && css` + box-shadow: rgba(0, 0, 0, 0); opacity: 0; transform: translateY(10rem); transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration / 1.5}ms, box-shadow ${props.animationDuration / 1.5}ms; + will-change: transform, opacity, box-shadow; ${props.isRoot && css` @@ -325,7 +331,7 @@ const Container = styled.div` ${props.enterDone && css` - box-shadow: none; + box-shadow: rgba(0, 0, 0, 0); `} ${!props.isTop && From 1c38c136f25ae0b188447318a8a0c73421897103 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 17 Sep 2020 23:17:45 +0900 Subject: [PATCH 07/19] fix: iOS transition bug --- src/navigator/components/Card.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index 81a38d56e..072a12533 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -194,12 +194,15 @@ const Dim = styled.div` ${(props) => props.environment === 'Cupertino' && - !props.isTop && css` background-color: rgba(0, 0, 0, 0); - transform: translateX(-5rem); transition: background-color ${props.animationDuration}ms, transform ${props.animationDuration}ms; will-change: background-color, transform; + + ${!props.isTop && + css` + transform: translateX(-5rem); + `} `} ` From 8f1d1451c732e5016d6ba39610c94f7932818356 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2020 00:10:15 +0900 Subject: [PATCH 08/19] feat: android motion update --- src/navigator/components/Card.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index 072a12533..bc4afec5c 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -315,8 +315,9 @@ const Container = styled.div` box-shadow: rgba(0, 0, 0, 0); opacity: 0; transform: translateY(10rem); - transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration / 1.5}ms, - box-shadow ${props.animationDuration / 1.5}ms; + transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration}ms, + box-shadow ${props.animationDuration}ms; + transition-timing-function: cubic-bezier(0.22, 0.67, 0.39, 0.83); will-change: transform, opacity, box-shadow; ${props.isRoot && @@ -329,17 +330,18 @@ const Container = styled.div` css` opacity: 1; transform: translateY(0); - box-shadow: 0 0 10rem 0 rgba(0, 0, 0, 0.3); + box-shadow: 0 0 0 20rem rgba(0, 0, 0, 0.2); `} ${props.enterDone && css` - box-shadow: rgba(0, 0, 0, 0); + box-shadow: 0 0 0 20rem rgba(0, 0, 0, 0); `} ${!props.isTop && css` transform: translateY(-2rem); + transition-timing-function: cubic-bezier(0.29, 0.55, 0.36, 0.69); `} ${(props.exitActive || props.exitActive) && From 1ce6767e55643eb1bc7096319cd08d1491771402 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2020 00:23:30 +0900 Subject: [PATCH 09/19] feat: opt-out box-shadow --- src/navigator/components/Card.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/navigator/components/Card.tsx b/src/navigator/components/Card.tsx index bc4afec5c..8a478c992 100644 --- a/src/navigator/components/Card.tsx +++ b/src/navigator/components/Card.tsx @@ -312,13 +312,11 @@ const Container = styled.div` ${(props) => (props.environment === 'Android' || props.environment === 'Web') && css` - box-shadow: rgba(0, 0, 0, 0); opacity: 0; transform: translateY(10rem); - transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration}ms, - box-shadow ${props.animationDuration}ms; + transition: transform ${props.animationDuration}ms, opacity ${props.animationDuration}ms; transition-timing-function: cubic-bezier(0.22, 0.67, 0.39, 0.83); - will-change: transform, opacity, box-shadow; + will-change: transform, opacity; ${props.isRoot && css` @@ -330,12 +328,6 @@ const Container = styled.div` css` opacity: 1; transform: translateY(0); - box-shadow: 0 0 0 20rem rgba(0, 0, 0, 0.2); - `} - - ${props.enterDone && - css` - box-shadow: 0 0 0 20rem rgba(0, 0, 0, 0); `} ${!props.isTop && From 63513e2bac1c26d1dd4af70b349642efe423c5f0 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2020 18:27:18 +0900 Subject: [PATCH 10/19] fix: update option name --- example/src/components/Home.tsx | 4 ++-- src/navigator/ScreenHelmet.tsx | 18 +++++++++----- src/navigator/atoms/ScreenInstanceOptions.ts | 7 +++--- src/navigator/components/Navbar.tsx | 25 +++++++++++--------- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/example/src/components/Home.tsx b/example/src/components/Home.tsx index fbfc65919..0a21522e9 100644 --- a/example/src/components/Home.tsx +++ b/example/src/components/Home.tsx @@ -13,7 +13,7 @@ const Home: React.FC = () => { @@ -22,7 +22,7 @@ const Home: React.FC = () => { } - right={ + appendRight={ diff --git a/src/navigator/ScreenHelmet.tsx b/src/navigator/ScreenHelmet.tsx index c528c649e..9a2cbe56d 100644 --- a/src/navigator/ScreenHelmet.tsx +++ b/src/navigator/ScreenHelmet.tsx @@ -12,18 +12,23 @@ interface ScreenHelmetProps { * 네비게이션의 왼쪽에 요소를 추가 * (이전 버튼 오른쪽에 표시됩니다) */ - left?: React.ReactNode + appendLeft?: React.ReactNode /** * 네비게이션의 오른쪽 요소를 추가 * (닫기 버튼 왼쪽에 표시됩니다) */ - right?: React.ReactNode + appendRight?: React.ReactNode /** * 이전 버튼을 사용자화합니다 */ - back?: React.ReactNode + customBackButton?: React.ReactNode + + /** + * 이전 버튼을 사용자화합니다 + */ + customCloseButton?: React.ReactNode } const ScreenHelmet: React.FC = (props) => { const screen = useScreenInstanceOptions() @@ -32,9 +37,10 @@ const ScreenHelmet: React.FC = (props) => { screen.setNavbar({ visible: true, title: props.title ?? null, - left: props.left ?? null, - right: props.right ?? null, - back: props.back ?? null, + appendLeft: props.appendLeft ?? null, + appendRight: props.appendRight ?? null, + customBackButton: props.customBackButton ?? null, + customCloseButton: props.customCloseButton ?? null, }) }, [props]) diff --git a/src/navigator/atoms/ScreenInstanceOptions.ts b/src/navigator/atoms/ScreenInstanceOptions.ts index ee7afb2d5..007b3cc25 100644 --- a/src/navigator/atoms/ScreenInstanceOptions.ts +++ b/src/navigator/atoms/ScreenInstanceOptions.ts @@ -4,9 +4,10 @@ import { atom } from 'recoil' export interface NavbarOptions { visible: boolean title: React.ReactNode | null - left: React.ReactNode | null - right: React.ReactNode | null - back: React.ReactNode | null + appendLeft: React.ReactNode | null + appendRight: React.ReactNode | null + customBackButton: React.ReactNode | null + customCloseButton: React.ReactNode | null } export const AtomScreenInstanceOptions = atom<{ diff --git a/src/navigator/components/Navbar.tsx b/src/navigator/components/Navbar.tsx index 0f9b49cbb..e51097d5c 100644 --- a/src/navigator/components/Navbar.tsx +++ b/src/navigator/components/Navbar.tsx @@ -32,30 +32,33 @@ const Navbar: React.FC = (props) => {
{screenInstanceOption?.navbar.title}
)} - {(!props.isRoot || screenInstanceOption?.navbar.left) && ( + {(!props.isRoot || screenInstanceOption?.navbar.appendLeft) && ( {!props.isRoot && - (screenInstanceOption?.navbar.back ? ( -
{screenInstanceOption.navbar.back}
+ (screenInstanceOption?.navbar.customBackButton ? ( +
{screenInstanceOption.navbar.customBackButton}
) : ( ))} - {screenInstanceOption?.navbar.left} + {screenInstanceOption?.navbar.appendLeft}
)} {(props.environment === 'Android' || props.environment === 'Web') && (
{screenInstanceOption?.navbar.title}
)} - {(props.isRoot || screenInstanceOption?.navbar.right) && ( + {(props.isRoot || screenInstanceOption?.navbar.appendRight) && ( - {screenInstanceOption?.navbar.right} - {props.isRoot && ( - - - - )} + {screenInstanceOption?.navbar.appendRight} + {props.isRoot && + (screenInstanceOption.navbar.customCloseButton ? ( +
{screenInstanceOption.navbar.customCloseButton}
+ ) : ( + + + + ))}
)}
From 07197dfe2e7daf683cdfc79a3162bebb9be5d59d Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2020 18:29:21 +0900 Subject: [PATCH 11/19] docs: update props name --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4efcc582a..b5e263147 100644 --- a/README.md +++ b/README.md @@ -76,16 +76,15 @@ const Home: React.FC = () => { return (
+ } + appendLeft={
left
} - right={ + appendRight={
right
} - center={ -
- } /> ) From a5ab5602bb108939a80f0d7b75148d614dcc467a Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2020 18:50:17 +0900 Subject: [PATCH 12/19] feat: pop(depth, data) -> pop(depth).send(data) --- example/src/components/Home.tsx | 5 +++-- example/src/components/Page2.tsx | 4 +++- src/navigator/useNavigator.ts | 18 +++++++++++------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/example/src/components/Home.tsx b/example/src/components/Home.tsx index 0a21522e9..b0a329bcb 100644 --- a/example/src/components/Home.tsx +++ b/example/src/components/Home.tsx @@ -5,8 +5,9 @@ import styled from '@emotion/styled' const Home: React.FC = () => { const navigator = useNavigator() - const onPage2Click = () => { - navigator.push('/page2') + const onPage2Click = async () => { + const data = await navigator.push('/page2') + window.alert(JSON.stringify(data)) } return ( diff --git a/example/src/components/Page2.tsx b/example/src/components/Page2.tsx index 04ad9ebcb..97ccc76a1 100644 --- a/example/src/components/Page2.tsx +++ b/example/src/components/Page2.tsx @@ -6,7 +6,9 @@ const Page2: React.FC = () => { const navigator = useNavigator() const onPopClick = () => { - navigator.pop(1) + navigator.pop().send({ + hello: 'world', + }) } const onPage3Click = () => { navigator.push('/page3') diff --git a/src/navigator/useNavigator.ts b/src/navigator/useNavigator.ts index 9f9d52bee..e52dd3e1a 100644 --- a/src/navigator/useNavigator.ts +++ b/src/navigator/useNavigator.ts @@ -40,16 +40,20 @@ export function useNavigator() { history.replace(pathname + '?' + search) }, - pop(depth: number, data?: T) { - const targetScreenInstance = screenInstances.find((_, index) => index === screenInstancePointer - depth) - - if (targetScreenInstance) { - screenInstancePromises[targetScreenInstance.id]?.(data ?? null) - } - + pop(depth = 1) { for (let i = 0; i < depth; i++) { history.goBack() } + + return { + send(data: T) { + const targetScreenInstance = screenInstances.find((_, index) => index === screenInstancePointer - depth) + + if (targetScreenInstance) { + screenInstancePromises[targetScreenInstance.id]?.(data ?? null) + } + }, + } }, } } From ed093ca235b51ac0aae3397e3377220b70796818 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 18 Sep 2020 18:59:58 +0900 Subject: [PATCH 13/19] refactor: parsing querystring --- README.md | 15 ++++++++----- src/navigator/Link.tsx | 12 ++++++----- src/navigator/useNavigator.ts | 40 ++++++++++++++++++----------------- src/utils/appendSearch.ts | 15 +++++++++++++ 4 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 src/utils/appendSearch.ts diff --git a/README.md b/README.md index b5e263147..8d9588dee 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ const Home: React.FC = () => { import { useNavigator } from '@daangn/karrotframe' const Home: React.FC = () => { - const { push } = useNavigator() + const { push, pop, replace } = useNavigator() return (
) } ``` +| Props | 타입 | 역할 | 기본값 | +| ------------- | ------------- | ------------- | ------------- | +| `title` | `React.ReactNode` | 타이틀 부분에 출력할 요소 | `undefined` | +| `appendLeft` | `React.ReactNode` | 왼쪽에 요소를 추가 (이전 버튼 오른쪽에 표시됩니다) | `undefined` | +| `appendRight` | `React.ReactNode` | 오른쪽에 요소를 추가 (닫기 버튼 왼쪽에 표시됩니다) | `undefined` | +| `customBackButton` | `React.ReactNode` | 이전 버튼을 사용자화합니다 | `undefined` | +| `customCloseButton` | `React.ReactNode` | 닫기 버튼을 사용자화합니다 | `undefined` | -다음과 같이 좌측, 우측에 Element를 추가하고, 가운데 타이틀을 덮어씌울수 있습니다. +### 1-d. `Link` +특정 path로 이동할 수 있는 링크를 생성하는 컴포넌트입니다. ```tsx -import { ScreenHelmet } from '@daangn/karrotframe' +import { Link } from '@daangn/karrotframe' const Home: React.FC = () => { return (
-
- } - appendLeft={ -
left
- } - appendRight={ -
right
- } - /> + 글 목록 ) } ``` +| Props | 타입 | 역할 | 기본값 | +| ------------- | ------------- | ------------- | ------------- | +| `to` | string | 이동 할 path | required | +| `replace` | boolean | path 이동을 replace로 처리할 지 여부 | `undefined` | +| `className` | string | className | `undefined` | -### URL 파라미터 받기 -`useLocation`, `useParams`, `useRouteMatch`를 활용할 수 있습니다 +### 1-e. `useLocation`, `useParams`, `useRouteMatch` +react-router-dom에 존재하는 `useLocation`, `useParams`, `useRouteMatch`를 그대로 사용할 수 있습니다 ```tsx import { useLocation, useParams, useRouteMatch } from '@daangn/karrotframe' const Post: React.FC = () => { + /** + * 현재 location 정보 + */ const location = useLocation() + /** + * path parameter로 들어온 값 + */ const params = useParams() + /** + * 현재 위치와 특정 path regex를 비교해 파싱된 값을 반환합니다. + * (매치하지 않는다면 null 반환) + */ const match = useRouteMatch({ path: '/:post_id', }) @@ -110,64 +168,82 @@ const Post: React.FC = () => { } ``` -### 화면 전환 -화면 전환은 `Link` 또는 `useNavigator` 를 통해 수행할 수 있습니다. +### 1-f. `useNavigator` +화면 전환을 수행합니다. ```tsx -import { Link } from '@daangn/karrotframe' +import { useNavigator } from '@daangn/karrotframe' -const Home: React.FC = () => { - return ( -
- 글 목록 -
- ) -} -``` +const Posts: React.FC = () => { + const { push, pop, replace } = useNavigator() -또는 + const goPost = (postId: string) => () => { + // 특정 path로 이동합니다 + push(`/posts/${postId}`) + } -```tsx -import { useNavigator } from '@daangn/karrotframe' + const goBack = () => { + // 한단계 뒤로 갑니다 + pop() + + // depth argument를 통해 여러단계를 pop 할 수 있습니다 + pop(1) + + // .send() 함수를 통해 해당 화면으로 데이터를 전송할 수 있습니다 + pop(1).send({ + hello: 'world', + }) + } + + useEffect(() => { + if (!user) { + // 특정 path로 이동합니다 (replace) + // 애니메이션 없이 이동하므로, redirect behavior에 적절합니다 + replace('/login') + } + }) -const Home: React.FC = () => { - const { push, pop, replace } = useNavigator() return (
- +
+ ) + })} + {/* ... */} + ) } ``` -### 화면 간 데이터 전송 -`useNavigator`의 `pop().send()`과 `await push()`를 통해 화면간 데이터 전송을 할 수 있습니다. +추가적으로, `useNavigator`의 `pop().send()`과 `await push()`를 통해 화면간 데이터 전송을 할 수 있습니다. -`pop()` 함수 내 `depth` argument를 2 이상으로 부여할 시 여러 화면을 뛰어넘어서 전송도 가능합니다. +> `pop()` 함수 내 `depth` argument를 2 이상으로 부여할 시 여러 화면을 뛰어넘어서 전송도 가능합니다. ```tsx import { useNavigator } from '@daangn/karrotframe' -const Home: React.FC = () => { +const Posts: React.FC = () => { const { push } = useNavigator() + + const writePost = () => { + // 다음 화면에서 전송할 데이터를 기다립니다 + const data = await push('/posts/write') + console.log(data) + // { + // hello: 'world', + // } + } return (