From 4a5b42fd0c5516f59a6e060ab4117fa899e52acd Mon Sep 17 00:00:00 2001 From: danivelop <55433950+danivelop@users.noreply.github.com> Date: Tue, 2 Feb 2021 17:44:19 +0900 Subject: [PATCH 1/4] =?UTF-8?q?overlay=EC=97=90=20container=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80=20(#191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * overlay에 container속성 추가 * container props가 추가된 부분도 스토리북에 반영 --- src/components/Overlay/Overlay.stories.tsx | 5 +- src/components/Overlay/Overlay.tsx | 80 ++++++++++++++++------ src/components/Overlay/Overlay.types.ts | 4 ++ 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/components/Overlay/Overlay.stories.tsx b/src/components/Overlay/Overlay.stories.tsx index b3f46ed71f..b663fcc3c3 100644 --- a/src/components/Overlay/Overlay.stories.tsx +++ b/src/components/Overlay/Overlay.stories.tsx @@ -53,6 +53,7 @@ export default { const Container = styled.div` width: 600px; height: 500px; + position: relative; overflow: scroll; border: 1px solid ${props => props.foundation?.theme?.['bg-black-dark']}; ` @@ -93,9 +94,10 @@ const ScrollContent = styled.div` const Template = (props) => { const targetRef = useRef() + const containerRef = useRef() return ( - + target @@ -103,6 +105,7 @@ const Template = (props) => { diff --git a/src/components/Overlay/Overlay.tsx b/src/components/Overlay/Overlay.tsx index 9d14ba4eae..9e96ca43cb 100644 --- a/src/components/Overlay/Overlay.tsx +++ b/src/components/Overlay/Overlay.tsx @@ -33,12 +33,16 @@ const rootElement = document.getElementById!('root') || document.getElementById!('__next') as HTMLElement -function getOverlayPosition({ target }: GetOverlayPositionProps): React.CSSProperties { +function getOverlayPosition({ container, target }: GetOverlayPositionProps): React.CSSProperties { if (target) { const { top: targetTop, left: targetLeft } = target.getBoundingClientRect() - const top = targetTop - target.clientTop - const left = targetLeft - target.clientLeft + const top = container ? + targetTop - target.clientTop - container.getBoundingClientRect().top + container.scrollTop : + targetTop - target.clientTop + const left = container ? + targetLeft - target.clientLeft - container.getBoundingClientRect().left + container.scrollLeft : + targetLeft - target.clientLeft return { top, left } } @@ -46,6 +50,7 @@ function getOverlayPosition({ target }: GetOverlayPositionProps): React.CSSPrope } function getOverlayTranslation({ + container, target, overlay, placement, @@ -54,12 +59,13 @@ function getOverlayTranslation({ keepInContainer, }: GetOverlayTranslatationProps): React.CSSProperties { if (target) { + const containerElement = container || rootElement as HTMLElement const { width: rootWidth, height: rootHeight, top: rootTop, left: rootLeft, - } = rootElement.getBoundingClientRect() + } = containerElement.getBoundingClientRect() const { width: targetWidth, height: targetHeight, top: targetTop, left: targetLeft } = target.getBoundingClientRect() const { width: overlayWidth, height: overlayHeight } = overlay.getBoundingClientRect() @@ -134,6 +140,7 @@ function getOverlayTranslation({ } function getOverlayStyle({ + container, target, overlay, placement, @@ -142,8 +149,16 @@ function getOverlayStyle({ keepInContainer, }: GetOverlayStyleProps): React.CSSProperties { if (target) { - const overlayPositionStyle = getOverlayPosition({ target }) - const overlayTranslateStyle = getOverlayTranslation({ target, overlay, placement, marginX, marginY, keepInContainer }) + const overlayPositionStyle = getOverlayPosition({ container, target }) + const overlayTranslateStyle = getOverlayTranslation({ + container, + target, + overlay, + placement, + marginX, + marginY, + keepInContainer, + }) const combinedStyle = { ...overlayPositionStyle, @@ -167,6 +182,7 @@ function Overlay( style, containerClassName = '', containerStyle, + container, target, placement = OverlayPosition.LeftCenter, marginX = 0, @@ -181,7 +197,7 @@ function Overlay( const [overlayStyle, setOverlayStyle] = useState() const [isHidden, setIsHidden] = useState(true) const overlayRef = useRef(null) - const [containerRef, setContainerRef] = useState(null) + const containerRef = useRef(null) const mergedRef = useMergeRefs(overlayRef, forwardedRef) const handleBlockMouseWheel = useCallback((event: HTMLElementEventMap['wheel']) => { @@ -205,14 +221,9 @@ function Overlay( } }, [onHide]) - const overlay = useMemo(() => ( - - + const overlay = useMemo(() => { + if (container) { + return ( { children } - - - ), [ + ) + } + return ( + + + + { children } + + + + ) + }, [ as, className, style, containerClassName, containerStyle, + container, isHidden, overlayStyle, children, @@ -248,11 +285,12 @@ function Overlay( useEventHandler(document, 'click', handleHideOverlay, show) useEventHandler(document, 'keyup', handleKeydown, show) useEventHandler(target, 'click', handleClickTarget, show) - useEventHandler(containerRef, 'wheel', handleBlockMouseWheel, show) + useEventHandler(containerRef.current, 'wheel', handleBlockMouseWheel, show) useEffect(() => { if (show) { const tempOverlayStyle = getOverlayStyle({ + container, target, overlay: overlayRef.current as HTMLElement, placement, @@ -269,11 +307,11 @@ function Overlay( } } return noop - }, [show, marginX, marginY, placement, target, keepInContainer]) + }, [show, container, marginX, marginY, placement, target, keepInContainer]) if (!show) return null - return ReactDOM.createPortal(overlay, rootElement as HTMLElement) + return ReactDOM.createPortal(overlay, container || rootElement as HTMLElement) } export default forwardRef(Overlay) diff --git a/src/components/Overlay/Overlay.types.ts b/src/components/Overlay/Overlay.types.ts index ab4ca7cdd5..a957e9cade 100644 --- a/src/components/Overlay/Overlay.types.ts +++ b/src/components/Overlay/Overlay.types.ts @@ -8,6 +8,7 @@ export default interface OverlayProps extends UIComponentProps, ChildrenComponen show?: boolean containerClassName?: string containerStyle?: React.CSSProperties + container?: HTMLElement | null target?: HTMLElement | null placement?: OverlayPosition marginX?: number @@ -17,6 +18,7 @@ export default interface OverlayProps extends UIComponentProps, ChildrenComponen } export interface GetOverlayStyleProps { + container?: HTMLElement | null target?: HTMLElement | null overlay: HTMLElement placement: OverlayPosition @@ -26,10 +28,12 @@ export interface GetOverlayStyleProps { } export interface GetOverlayPositionProps { + container?: HTMLElement | null target: HTMLElement } export interface GetOverlayTranslatationProps { + container?: HTMLElement | null target: HTMLElement overlay: HTMLElement placement: OverlayPosition From cb43a9082632ae901f127c155c16bab36d71fac1 Mon Sep 17 00:00:00 2001 From: Leo Jeong Date: Wed, 3 Feb 2021 14:40:48 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Navigation=20Area=20key=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EB=A7=88=EC=9D=B4=EB=84=88=20=EB=B2=84=EA=B7=B8?= =?UTF-8?q?=20=ED=94=BD=EC=8A=A4=20(#227)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Switch/Switch.stories.tsx | 1 + .../NavigationContent/NavigationContent.tsx | 6 +-- src/layout/Navigations/Navigations.tsx | 50 +++++++++---------- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/components/Switch/Switch.stories.tsx b/src/components/Switch/Switch.stories.tsx index bd41d93ec7..da6122b271 100644 --- a/src/components/Switch/Switch.stories.tsx +++ b/src/components/Switch/Switch.stories.tsx @@ -8,6 +8,7 @@ import Switch from './Switch' export default { title: getTitle(base), + component: Switch, argTypes: { onClick: { action: 'onClick' }, }, diff --git a/src/layout/Navigations/NavigationContent/NavigationContent.tsx b/src/layout/Navigations/NavigationContent/NavigationContent.tsx index d97e804bbd..a5fa4f35ea 100644 --- a/src/layout/Navigations/NavigationContent/NavigationContent.tsx +++ b/src/layout/Navigations/NavigationContent/NavigationContent.tsx @@ -1,5 +1,5 @@ /* External dependencies */ -import React, { useLayoutEffect, useMemo } from 'react' +import React, { useMemo } from 'react' import { noop } from 'lodash-es' /* Internal dependencies */ @@ -36,10 +36,6 @@ function NavigationContent({ optionIndex, ...otherProps }: NavigationContentProps) { - useLayoutEffect(() => { - - }, []) - const clazzName = useMemo(() => ( mergeClassNames(className, ((withScroll && scrollClassName) || undefined)) ), [className, scrollClassName, withScroll]) diff --git a/src/layout/Navigations/Navigations.tsx b/src/layout/Navigations/Navigations.tsx index 940fab8633..468abe4c58 100644 --- a/src/layout/Navigations/Navigations.tsx +++ b/src/layout/Navigations/Navigations.tsx @@ -16,7 +16,6 @@ import { toNumber, noop, } from 'lodash-es' -import { v4 as uuid } from 'uuid' /* Internal dependencies */ import useLayoutDispatch from '../../hooks/useLayoutDispatch' @@ -187,34 +186,31 @@ forwardedRef: React.Ref, }, [navOptions]) const NavigationAreasComponent = useMemo(() => { - const childrens = React.Children.toArray(children) - navigationRefs.current = [] - return ( - childrens.map((navChildren, index) => { - // TODO: instanceof 등으로 NavigationContent가 안 내려오면 warning을 띄워주는 것이 좋을 듯 - if (!navOptions[index] || !React.isValidElement(navChildren)) { return null } - - return ( - { - set(navigationRefs.current, [index, 'target'], element) - set(navigationRefs.current, [index, 'minWidth'], navOptions[index].minWidth) - set(navigationRefs.current, [index, 'maxWidth'], navOptions[index].maxWidth) - }} - disableResize={navOptions[index].disableResize} - hidable={navOptions[index].hidable} - optionIndex={index} - onMouseDown={handleMouseDown} - onMouseMove={handleMouseMove} - > - { navChildren } - - ) - }) - ) + return React.Children.map(children, (navChildren, index) => { + // TODO: instanceof 등으로 NavigationContent가 안 내려오면 warning을 띄워주는 것이 좋을 듯 + if (!navOptions[index] || !React.isValidElement(navChildren)) { return null } + + return ( + { + set(navigationRefs.current, [index, 'target'], element) + set(navigationRefs.current, [index, 'minWidth'], navOptions[index].minWidth) + set(navigationRefs.current, [index, 'maxWidth'], navOptions[index].maxWidth) + }} + disableResize={navOptions[index].disableResize} + hidable={navOptions[index].hidable} + optionIndex={index} + onMouseDown={handleMouseDown} + onMouseMove={handleMouseMove} + > + { navChildren } + + ) + }) }, [children, handleMouseDown, handleMouseMove, navOptions]) return ( From bda20f91579a47d5747e6c42e68ef02d76375cfd Mon Sep 17 00:00:00 2001 From: danivelop <55433950+danivelop@users.noreply.github.com> Date: Wed, 3 Feb 2021 14:41:04 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EB=B2=84=EC=A0=84=EC=88=98=EC=A0=95=20v0.3?= =?UTF-8?q?.1=20(#225)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9d7561297..0dae0cd17b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@channel.io/design-system", - "version": "0.3.0", + "version": "0.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e7e9fa0eb1..1a9eeb60b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@channel.io/design-system", - "version": "0.3.0", + "version": "0.3.1", "description": "Design System by Channel", "repository": { "type": "git", From 6781cc6e422769ed2b237af972e3e6ec1b943aa0 Mon Sep 17 00:00:00 2001 From: MongWook Song Date: Thu, 4 Feb 2021 19:41:23 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Header=EA=B3=BC=20Navigation=EC=9D=B4=20?= =?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8D=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95=20(#226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * HeaderContent 대신 ContentHeader과 CoverableRoute를 각각 props로 받게 변경 * Navigation 구조에서 child.prop로 가져오던 부분을 context로 변경함 * NavigationArea가 남아 있던 버그 수정 - key 값이 남아있는 듯 함 * 레오 리뷰 반영 * React.Children.map 에 null escape 조건 추가 --- src/contexts/NavigationContext.ts | 19 +++++++ src/index.ts | 1 - src/layout/Client/utils/LayoutReducer.ts | 49 +++++++++++++++-- src/layout/HeaderArea/HeaderArea.styled.ts | 4 +- src/layout/HeaderArea/HeaderArea.tsx | 8 +-- src/layout/HeaderContent/HeaderContent.tsx | 13 ----- .../HeaderContent/HeaderContent.types.ts | 7 --- src/layout/HeaderContent/index.ts | 10 ---- src/layout/Main/Main.tsx | 12 ++--- .../NavigationArea/NavigationArea.tsx | 32 ++++++----- .../NavigationContent/NavigationContent.tsx | 45 +++++++++++++--- src/layout/Navigations/Navigations.tsx | 54 +++++-------------- .../LayoutPlayGround.stories.tsx | 35 ++++++------ 13 files changed, 160 insertions(+), 129 deletions(-) create mode 100644 src/contexts/NavigationContext.ts delete mode 100644 src/layout/HeaderContent/HeaderContent.tsx delete mode 100644 src/layout/HeaderContent/HeaderContent.types.ts delete mode 100644 src/layout/HeaderContent/index.ts diff --git a/src/contexts/NavigationContext.ts b/src/contexts/NavigationContext.ts new file mode 100644 index 0000000000..cbef182ba7 --- /dev/null +++ b/src/contexts/NavigationContext.ts @@ -0,0 +1,19 @@ +/* External dependencies */ +import { createContext } from 'react' +import { noop } from 'lodash-es' + +export interface NavigationContextProps { + optionIndex: number + showChevron: boolean + allowMouseMove: boolean + isHoveringOnPresenter: boolean + onClickClose: () => void +} + +export const NavigationContext = createContext({ + optionIndex: -1, + showChevron: false, + allowMouseMove: false, + isHoveringOnPresenter: false, + onClickClose: noop, +}) diff --git a/src/index.ts b/src/index.ts index 23b2922d21..5b7b354b0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,7 +28,6 @@ export * from './components/Header' export * from './layout/GNB' export * from './layout/GlobalHeader' export * from './layout/Client' -export * from './layout/HeaderContent' export * from './layout/Main' export * from './layout/Navigations' diff --git a/src/layout/Client/utils/LayoutReducer.ts b/src/layout/Client/utils/LayoutReducer.ts index 8e57c067a3..99cbdaac48 100644 --- a/src/layout/Client/utils/LayoutReducer.ts +++ b/src/layout/Client/utils/LayoutReducer.ts @@ -1,3 +1,5 @@ +import { insertItem, removeItem } from '../../../utils/utils' + export interface NavigationState { initialWidth: number maxWidth: number @@ -10,12 +12,14 @@ export interface LayoutState { sideWidth: number | null showSideView: boolean showNavigation: boolean + navOptions: NavigationState[] } export const defaultState: LayoutState = { sideWidth: null, showSideView: false, showNavigation: true, + navOptions: [], } export enum ActionType { @@ -23,8 +27,9 @@ export enum ActionType { OPEN_SIDE_VIEW, CLOSE_SIDE_VIEW, SET_SHOW_NAVIGATION, - ADD_NAVIGATION, - CLEAR_NAVIGATIONS, + ADD_NAV_OPTION, + REMOVE_NAV_OPTION, + CLEAR_NAV_OPTION, } interface SetSideWidthAction { @@ -45,11 +50,28 @@ interface SetShowNavigationAction { payload: boolean } +interface AddNavOptionAction { + type: ActionType.ADD_NAV_OPTION + payload: { index: number, option: NavigationState } +} + +interface RemoveNavOptionAction { + type: ActionType.REMOVE_NAV_OPTION + payload: { index: number } +} + +interface ClearNavOptionAction { + type: ActionType.CLEAR_NAV_OPTION +} + export type LayoutAction = ( SetSideWidthAction | OpenSideViewAction | CloseSideViewAction | - SetShowNavigationAction + SetShowNavigationAction | + AddNavOptionAction | + RemoveNavOptionAction | + ClearNavOptionAction ) function LayoutReducer(state: LayoutState = defaultState, action: LayoutAction): LayoutState { @@ -82,6 +104,27 @@ function LayoutReducer(state: LayoutState = defaultState, action: LayoutAction): } } + case ActionType.ADD_NAV_OPTION: { + return { + ...state, + navOptions: insertItem(state.navOptions, action.payload.index, action.payload.option), + } + } + + case ActionType.REMOVE_NAV_OPTION: { + return { + ...state, + navOptions: removeItem(state.navOptions, action.payload.index), + } + } + + case ActionType.CLEAR_NAV_OPTION: { + return { + ...state, + navOptions: [], + } + } + default: return state } diff --git a/src/layout/HeaderArea/HeaderArea.styled.ts b/src/layout/HeaderArea/HeaderArea.styled.ts index 4ae0f792dc..87f01658c6 100644 --- a/src/layout/HeaderArea/HeaderArea.styled.ts +++ b/src/layout/HeaderArea/HeaderArea.styled.ts @@ -18,12 +18,12 @@ export const HeaderWrapper = styled.div.attrs(({ showSideView, sideWidth }: Head grid-column: 1 / 3; ` -export const LeftHeader = styled.div` +export const ContentHeader = styled.div` grid-row: 1 / 2; grid-column: 1 / 2; ` -export const RightHeader = styled.div` +export const CoverableHeader = styled.div` grid-row: 1 / 2; grid-column: 2 / 3; ` diff --git a/src/layout/HeaderArea/HeaderArea.tsx b/src/layout/HeaderArea/HeaderArea.tsx index 79c8633351..aee65f11b4 100644 --- a/src/layout/HeaderArea/HeaderArea.tsx +++ b/src/layout/HeaderArea/HeaderArea.tsx @@ -3,7 +3,7 @@ import React, { forwardRef } from 'react' /* Internal dependencies */ import useLayoutState from '../../hooks/useLayoutState' -import { HeaderWrapper, LeftHeader, RightHeader } from './HeaderArea.styled' +import { HeaderWrapper, ContentHeader, CoverableHeader } from './HeaderArea.styled' import HeaderAreaProps from './HeaderArea.types' export const HEADER_AREA_TEST_ID = 'ch-design-system-header-area' @@ -12,7 +12,7 @@ function HeaderArea( { style, className, - mainHeader, + contentHeader, coverableHeader, testId = HEADER_AREA_TEST_ID, hasHeader, @@ -34,8 +34,8 @@ function HeaderArea( showSideView={showSideView} {...otherProps} > - { mainHeader } - { coverableHeader } + { contentHeader } + { coverableHeader } ) } diff --git a/src/layout/HeaderContent/HeaderContent.tsx b/src/layout/HeaderContent/HeaderContent.tsx deleted file mode 100644 index d92c03cd40..0000000000 --- a/src/layout/HeaderContent/HeaderContent.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* Internal dependencies */ -import HeaderContentProps from './HeaderContent.types' - -function HeaderContent({ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - mainHeader, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - coverableHeader, -}: HeaderContentProps) { - return null -} - -export default HeaderContent diff --git a/src/layout/HeaderContent/HeaderContent.types.ts b/src/layout/HeaderContent/HeaderContent.types.ts deleted file mode 100644 index 0fe57621e9..0000000000 --- a/src/layout/HeaderContent/HeaderContent.types.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* Internal dependencies */ -import { UIComponentProps } from '../../types/ComponentProps' - -export default interface HeaderContent extends UIComponentProps { - mainHeader?: React.ReactElement - coverableHeader?: React.ReactElement -} diff --git a/src/layout/HeaderContent/index.ts b/src/layout/HeaderContent/index.ts deleted file mode 100644 index b767b4143c..0000000000 --- a/src/layout/HeaderContent/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import HeaderContent from './HeaderContent' -import type HeaderContentProps from './HeaderContent.types' - -export type { - HeaderContentProps, -} - -export { - HeaderContent, -} diff --git a/src/layout/Main/Main.tsx b/src/layout/Main/Main.tsx index 3fdf02e270..28a35a8746 100644 --- a/src/layout/Main/Main.tsx +++ b/src/layout/Main/Main.tsx @@ -1,6 +1,6 @@ /* External dependencies */ import React, { forwardRef, useCallback, useRef } from 'react' -import { clamp, get, isNil } from 'lodash-es' +import { clamp, isNil } from 'lodash-es' import { window } from 'ssr-window' /* Internal dependencies */ @@ -18,7 +18,8 @@ import MainProps from './Main.types' function Main( { content, - header, + contentHeader, + coverableHeader, sidePanel, sideView, navigationRef, @@ -34,11 +35,8 @@ function Main( const sideInitialWidth = useRef(0) const initialPosition = useRef(0) - const mainHeader = get(header, 'props.mainHeader', null) - const coverableHeader = get(header, 'props.coverableHeader', null) - const hasSide = !isNil(sidePanel) || showSideView - const hasHeader = !isNil(mainHeader || coverableHeader) + const hasHeader = !isNil(contentHeader || coverableHeader) const handleResizerMouseDown = useCallback((e: MouseEvent) => { contentInitialWidth.current = contentRef.current!.clientWidth @@ -79,7 +77,7 @@ function Main( > , ) { const dispatch = useLayoutDispatch() - const { showNavigation } = useLayoutState() + const { showNavigation, navOptions } = useLayoutState() + const hidable = useMemo(() => navOptions[optionIndex]?.hidable || false, [navOptions, optionIndex]) + const disableResize = useMemo(() => navOptions[optionIndex]?.disableResize || false, [navOptions, optionIndex]) const show = useMemo(() => (hidable ? showNavigation : undefined), [hidable, showNavigation]) const containerRef = useRef(null) @@ -73,11 +74,7 @@ function NavigationArea( if (!allowMouseMove) return onMouseMove(event) }) - }, [ - disableResize, - allowMouseMove, - onMouseMove, - ]) + }, [disableResize, allowMouseMove, onMouseMove]) useEventHandler(resizeBarRef, 'mousedown', handleMouseDown) useEventHandler(document, 'mouseup', handleMouseUp) @@ -121,14 +118,13 @@ function NavigationArea( } }, [handleDecideHover, show]) - const ContentComponent = useMemo(() => ( - React.cloneElement(children, { - showChevron, - allowMouseMove, - isHoveringOnPresenter, - onClickClose: handleClickClose, - }) - ), [allowMouseMove, children, handleClickClose, isHoveringOnPresenter, showChevron]) + const navigationContextValues: NavigationContextProps = useMemo(() => ({ + optionIndex, + showChevron, + allowMouseMove, + isHoveringOnPresenter, + onClickClose: handleClickClose, + }), [allowMouseMove, handleClickClose, isHoveringOnPresenter, optionIndex, showChevron]) return ( - { ContentComponent } + + { children } + { + if (optionIndex === 0) { + dispatch({ type: ActionType.CLEAR_NAV_OPTION }) + } + + dispatch({ + type: ActionType.ADD_NAV_OPTION, + payload: { index: optionIndex, option: layoutOption }, + }) + + if (!isNil(showNavigation)) { + dispatch({ + type: ActionType.SET_SHOW_NAVIGATION, + payload: showNavigation, + }) + } + }, [dispatch, layoutOption, optionIndex, showNavigation]) + const clazzName = useMemo(() => ( mergeClassNames(className, ((withScroll && scrollClassName) || undefined)) ), [className, scrollClassName, withScroll]) diff --git a/src/layout/Navigations/Navigations.tsx b/src/layout/Navigations/Navigations.tsx index 468abe4c58..778e78f335 100644 --- a/src/layout/Navigations/Navigations.tsx +++ b/src/layout/Navigations/Navigations.tsx @@ -6,10 +6,8 @@ import React, { useLayoutEffect, useMemo, useRef, - useState, } from 'react' import { - isArray, isNil, set, size, @@ -18,13 +16,10 @@ import { } from 'lodash-es' /* Internal dependencies */ -import useLayoutDispatch from '../../hooks/useLayoutDispatch' -import { insertItem } from '../../utils/utils' -import { ActionType, NavigationState } from '../Client/utils/LayoutReducer' +import useLayoutState from '../../hooks/useLayoutState' import NavigationsProps, { NavigationRefsProps } from './Navigations.types' import { NavigationsWrapper } from './Navigations.styled' import { NavigationArea } from './NavigationArea' -import NavigationContentProps from './NavigationContent/NavigationContent.types' // TODO-LAYOUT: z-index 명확화 하기 const MAX_NAV_Z_INDEX = 100 @@ -40,9 +35,7 @@ function Navigations({ }: NavigationsProps, forwardedRef: React.Ref, ) { - const layoutDispatch = useLayoutDispatch() - - const [navOptions, setNavOptions] = useState>([]) + const { navOptions } = useLayoutState() const navigationRefs = useRef>([]) const currentIndex = useRef(0) @@ -156,27 +149,14 @@ forwardedRef: React.Ref, handleMouseMoveOutside, }), [handleMouseDownOutside, handleMouseMoveOutside]) - useLayoutEffect(() => { - setNavOptions([]) - - const childrens = React.Children.toArray(children) - childrens.forEach((child, index) => { - if (!React.isValidElement(child)) { return } - - setNavOptions(options => (insertItem(options, index, child.props.layoutOption))) - - if (child.props.showNavigation) { - layoutDispatch({ - type: ActionType.SET_SHOW_NAVIGATION, - payload: child.props.showNavigation, - }) - } - }) - }, [children, layoutDispatch]) + const setNavigationRef = useCallback((element: HTMLDivElement, index: number) => { + set(navigationRefs.current, [index, 'target'], element) + set(navigationRefs.current, [index, 'minWidth'], navOptions[index]?.minWidth) + set(navigationRefs.current, [index, 'maxWidth'], navOptions[index]?.maxWidth) + }, [navOptions]) + // NOTE: LAYOUTEFFECT를 사용하지 않으면 initial 값이 없을때 순간 깜빡임이 발생한다 useLayoutEffect(() => { - if (!isArray(navOptions)) { return } - for (const index in navigationRefs.current) { if (!isNil(navigationRefs.current[index].target) && navOptions[index]) { navigationRefs.current[index].target.style.width = `${navOptions[index].initialWidth}px` @@ -187,22 +167,16 @@ forwardedRef: React.Ref, const NavigationAreasComponent = useMemo(() => { navigationRefs.current = [] - + // NOTE: React.Children.toArray 와 React.Children.map 의 차이점은 + // toArray의 경우에는 null child는 무시한 array를 만든다 + // Map으로 하면 null child 도 iterate을 수행한다 return React.Children.map(children, (navChildren, index) => { - // TODO: instanceof 등으로 NavigationContent가 안 내려오면 warning을 띄워주는 것이 좋을 듯 - if (!navOptions[index] || !React.isValidElement(navChildren)) { return null } - + if (!navChildren) { return null } return ( { - set(navigationRefs.current, [index, 'target'], element) - set(navigationRefs.current, [index, 'minWidth'], navOptions[index].minWidth) - set(navigationRefs.current, [index, 'maxWidth'], navOptions[index].maxWidth) - }} - disableResize={navOptions[index].disableResize} - hidable={navOptions[index].hidable} + ref={(element: HTMLDivElement) => setNavigationRef(element, index)} optionIndex={index} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} @@ -211,7 +185,7 @@ forwardedRef: React.Ref, ) }) - }, [children, handleMouseDown, handleMouseMove, navOptions]) + }, [children, handleMouseDown, handleMouseMove, setNavigationRef]) return ( diff --git a/src/layout/stories/LayoutPlayground/LayoutPlayGround.stories.tsx b/src/layout/stories/LayoutPlayground/LayoutPlayGround.stories.tsx index dff4bd3515..ca4b7a5d83 100644 --- a/src/layout/stories/LayoutPlayground/LayoutPlayGround.stories.tsx +++ b/src/layout/stories/LayoutPlayground/LayoutPlayGround.stories.tsx @@ -11,7 +11,6 @@ import { Header } from '../../../components/Header' import { ListItem } from '../../../components/List/ListItem' import Client from '../../Client/Client' import { Main } from '../../Main' -import { HeaderContent } from '../../HeaderContent' import GNB from '../../GNB/GNB' import Navigations, { NavigationHandles } from '../../Navigations/Navigations' import { SidePanelChildProps } from '../../SidePanelArea/SidePanelArea.types' @@ -89,7 +88,7 @@ const Template = ({ onChangeWidth }) => { /* LayoutState Prop */ showNavigation layoutOption={{ - initialWidth: 200, + initialWidth: 250, maxWidth: 600, minWidth: 180, hidable: true, @@ -106,6 +105,7 @@ const Template = ({ onChangeWidth }) => { header={Element1Header} withScroll /* LayoutState Prop */ + showNavigation layoutOption={{ initialWidth: 200, maxWidth: 600, @@ -172,30 +172,30 @@ const Template = ({ onChangeWidth }) => { const ContentRoute = useMemo(() => (), []) - const HeaderRoute = useMemo(() => { + const ContentHeaderRoute = useMemo(() => { switch (route) { case RouteKeys.TeamChat: - return ( - TeamChat Header} - coverableHeader={
Search
} - /> - ) + return (
TeamChat Header
) case RouteKeys.UserChat: - return ( - UserChat Header} - coverableHeader={
Search
} - /> - ) + return (
UserChat Header
) case RouteKeys.Statistic: - return (
Statistic Header
) + return (
Statistic Heassder
) case RouteKeys.Setting: default: return null } }, [Div, route]) + const CoverableHeaderRoute = useMemo(() => { + switch (route) { + case RouteKeys.TeamChat: + case RouteKeys.UserChat: + return (
Search
) + default: + return null + } + }, [Div, route]) + const SidePanelRoute = useMemo(() => { switch (route) { case RouteKeys.TeamChat: @@ -228,7 +228,8 @@ const Template = ({ onChangeWidth }) => {