From 505cf7e235a88548942ca8918f5208bf0e310e21 Mon Sep 17 00:00:00 2001 From: jiyeon Date: Thu, 12 Oct 2023 23:22:47 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/dropdown/src/components/Dropdown.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dropdown/src/components/Dropdown.tsx b/packages/dropdown/src/components/Dropdown.tsx index fc44f1d..939ffcf 100644 --- a/packages/dropdown/src/components/Dropdown.tsx +++ b/packages/dropdown/src/components/Dropdown.tsx @@ -18,7 +18,7 @@ export const Dropdown = (props: DropdownProps) => { const dropdownRef = useRef(null); const [triggerWidth, setTriggerWidth] = useState(0); const [triggerHeight, setTriggerHeight] = useState(0); - const { children, width = 200, placement = 'buttom', ...otherProps } = props; + const { children, width = 200, placement = 'bottom', ...restProps } = props; const gap = Number(props.gap) || 0; const providerValue = { placement, @@ -42,7 +42,7 @@ export const Dropdown = (props: DropdownProps) => { }); return ( -
+
{children}
From 43984ae392356a330e460c15fbd3d119dc7b2051 Mon Sep 17 00:00:00 2001 From: jiyeon Date: Thu, 12 Oct 2023 23:42:20 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8E=A8=20style:=20apply=20eslint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/dropdown/src/components/Dropdown.tsx | 6 +++--- packages/dropdown/src/components/DropdownDivider.tsx | 4 +--- packages/dropdown/src/components/DropdownMenu.tsx | 12 +++++------- .../dropdown/src/components/DropdownMenuItem.tsx | 4 ++-- packages/dropdown/src/components/DropdownSubMenu.tsx | 2 +- .../dropdown/src/components/DropdownSubMenuItem.tsx | 4 ++-- packages/dropdown/src/components/DropdownTrigger.tsx | 2 +- packages/dropdown/src/hooks/useCloseKeyDown.ts | 2 +- packages/dropdown/src/style.ts | 8 +++----- 9 files changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/dropdown/src/components/Dropdown.tsx b/packages/dropdown/src/components/Dropdown.tsx index 939ffcf..366782e 100644 --- a/packages/dropdown/src/components/Dropdown.tsx +++ b/packages/dropdown/src/components/Dropdown.tsx @@ -1,18 +1,18 @@ /** @jsxImportSource @emotion/react */ -import type { DropdownProps } from '../types'; -import { dropdownWrapperStyle } from '../style'; import { useState, useRef } from 'react'; +import { useOutsideClick } from '@jdesignlab/react-utils'; import { Divider } from './DropdownDivider'; import { Menu } from './DropdownMenu'; import { Trigger } from './DropdownTrigger'; import { MenuItem } from './DropdownMenuItem'; import { SubMenu } from './DropdownSubMenu'; import { SubMenuItem } from './DropdownSubMenuItem'; +import { dropdownWrapperStyle } from '../style'; import { DropdownContext } from '../context'; -import { useOutsideClick } from '@jdesignlab/react-utils'; import { useToggleOpen } from '../hooks/useToggleOpen'; import { DROPDOWN_ROLE_QUERY, DROPDOWN_MENU_OPEN_CLASS_NAME } from '../constants'; +import type { DropdownProps } from '../types'; export const Dropdown = (props: DropdownProps) => { const dropdownRef = useRef(null); diff --git a/packages/dropdown/src/components/DropdownDivider.tsx b/packages/dropdown/src/components/DropdownDivider.tsx index 05706fb..385f504 100644 --- a/packages/dropdown/src/components/DropdownDivider.tsx +++ b/packages/dropdown/src/components/DropdownDivider.tsx @@ -2,8 +2,6 @@ import { dropdownDividerStyle } from '../style'; -export const Divider = () => { - return
; -}; +export const Divider = () =>
; Divider.displayName = 'Dropdown.Divider' \ No newline at end of file diff --git a/packages/dropdown/src/components/DropdownMenu.tsx b/packages/dropdown/src/components/DropdownMenu.tsx index e425b0b..61502f5 100644 --- a/packages/dropdown/src/components/DropdownMenu.tsx +++ b/packages/dropdown/src/components/DropdownMenu.tsx @@ -1,9 +1,9 @@ /** @jsxImportSource @emotion/react */ -import { dropdownLocationStyle } from '../style'; import React, { useContext, useRef } from 'react'; +import { dropdownLocationStyle } from '../style'; import { DropdownContext } from '../context'; -import type { DropdownMenuProps } from '../types'; import useCloseKeyDown from '../hooks/useCloseKeyDown'; +import type { DropdownMenuProps } from '../types'; export const Menu = React.memo((props: DropdownMenuProps) => { const { children, ...otherProps } = props; @@ -11,12 +11,11 @@ export const Menu = React.memo((props: DropdownMenuProps) => { const keyDownHandle = useCloseKeyDown(); const menuRef = useRef(null); - const MenuBox = () => { - return ( + const MenuBox = () => (
    { {children}
); - }; - return <>{}; + return <>; }); Menu.displayName = 'Dropdown.Menu'; diff --git a/packages/dropdown/src/components/DropdownMenuItem.tsx b/packages/dropdown/src/components/DropdownMenuItem.tsx index e4f8fcc..30996dd 100644 --- a/packages/dropdown/src/components/DropdownMenuItem.tsx +++ b/packages/dropdown/src/components/DropdownMenuItem.tsx @@ -2,10 +2,10 @@ import { useRef, useState } from 'react'; import { DropdownSubContext } from '../context'; import { dropdownItemStyle } from '../style'; -import type { DropdownMenuItemProps } from '../types'; import { useKeyboardHandler } from '../hooks/useKeyboardHandler'; import { useSelectItem } from '../hooks/useSelectItem'; import { NOT_DISABLED_DROPDOWN_MENU_QUERY } from '../constants'; +import type { DropdownMenuItemProps } from '../types'; export const MenuItem = (props: DropdownMenuItemProps) => { const menuItemRef = useRef(null); @@ -40,7 +40,7 @@ export const MenuItem = (props: DropdownMenuItemProps) => { role="menuitem" tabIndex={disabled ? -1 : 0} className="menu_item" - aria-disabled={disabled ? true : false} + aria-disabled={!!disabled} css={{ ...dropdownItemStyle(disabled) }} onMouseOver={toggleSubOpen} onMouseLeave={toggleSubOpen} diff --git a/packages/dropdown/src/components/DropdownSubMenu.tsx b/packages/dropdown/src/components/DropdownSubMenu.tsx index bcb8dc0..2de3648 100644 --- a/packages/dropdown/src/components/DropdownSubMenu.tsx +++ b/packages/dropdown/src/components/DropdownSubMenu.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; -import { dropdownSubLocationStyle, dropdownMenuStyle } from '../style'; import React, { useState, useEffect, useContext, useRef, createContext, useCallback } from 'react'; +import { dropdownSubLocationStyle, dropdownMenuStyle } from '../style'; import { DropdownContext, DropdownSubContext } from '../context'; import type { DropdownSubMenuProps } from '../types'; diff --git a/packages/dropdown/src/components/DropdownSubMenuItem.tsx b/packages/dropdown/src/components/DropdownSubMenuItem.tsx index 117c4fb..ca2160b 100644 --- a/packages/dropdown/src/components/DropdownSubMenuItem.tsx +++ b/packages/dropdown/src/components/DropdownSubMenuItem.tsx @@ -1,10 +1,10 @@ /** @jsxImportSource @emotion/react */ import { useContext, useRef, useState } from 'react'; import { dropdownItemStyle } from '../style'; -import type { DropdownSubMenuItemProps } from '../types'; import { useKeyboardHandler } from '../hooks/useKeyboardHandler'; import { useSelectItem } from '../hooks/useSelectItem'; import { DROPDOWN_MENU_OPEN_CLASS_NAME, NOT_DISABLED_DROPDOWN_SUB_ITEM_QUERY } from '../constants'; +import type { DropdownSubMenuItemProps } from '../types'; export const SubMenuItem = (props: DropdownSubMenuItemProps) => { const menuItemRef = useRef(null); @@ -28,7 +28,7 @@ export const SubMenuItem = (props: DropdownSubMenuItemProps) => { tabIndex={disabled ? -1 : 0} className={`sub_item ${disabled ? 'disabled' : ''}`} onClick={e => useSelectItem(e, onClick)} - aria-disabled={disabled ? true : false} + aria-disabled={!!disabled} css={{ ...dropdownItemStyle(disabled) }} onKeyDown={e => onKeyDown(e)} {...otherProps} diff --git a/packages/dropdown/src/components/DropdownTrigger.tsx b/packages/dropdown/src/components/DropdownTrigger.tsx index 0df894d..2d3d4f8 100644 --- a/packages/dropdown/src/components/DropdownTrigger.tsx +++ b/packages/dropdown/src/components/DropdownTrigger.tsx @@ -1,9 +1,9 @@ import { useEffect, useContext, useRef } from 'react'; import { DropdownContext } from '../context'; -import type { DropdownTriggerProps } from '../types'; import useOpenKeyDown from '../hooks/useOpenKeyDown'; import { useToggleOpen } from '../hooks/useToggleOpen'; import { DROPDOWN_ROLE_QUERY, DROPDOWN_MENU_WRAPPER_CLASS } from '../constants'; +import type { DropdownTriggerProps } from '../types'; export const Trigger = (props: DropdownTriggerProps) => { const { children, ...otherProps } = props; diff --git a/packages/dropdown/src/hooks/useCloseKeyDown.ts b/packages/dropdown/src/hooks/useCloseKeyDown.ts index 6aa3332..379c10e 100644 --- a/packages/dropdown/src/hooks/useCloseKeyDown.ts +++ b/packages/dropdown/src/hooks/useCloseKeyDown.ts @@ -8,7 +8,7 @@ const useCloseKeyDown = () => (event: React.KeyboardEvent) => { case 'Esc': event.preventDefault(); useToggleOpen(el); - return; + } }; diff --git a/packages/dropdown/src/style.ts b/packages/dropdown/src/style.ts index 6520d4e..25cc55b 100644 --- a/packages/dropdown/src/style.ts +++ b/packages/dropdown/src/style.ts @@ -1,7 +1,7 @@ import { css, keyframes } from '@emotion/react'; -import type { ColorToken } from '@jdesignlab/theme'; import { getColorByToken, hexToRgba } from '@jdesignlab/theme'; import React, { useCallback } from 'react'; +import type { ColorToken } from '@jdesignlab/theme'; export const dropdownWrapperStyle = css({ position: 'relative' @@ -46,8 +46,7 @@ export const dropdownMenuStyle = css({ // animation: `${shadowDrop} 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both` }); -export const dropdownItemStyle = (disabled: boolean) => { - return css({ +export const dropdownItemStyle = (disabled: boolean) => css({ position: 'relative', padding: '5px', borderRadius: '4px', @@ -59,7 +58,6 @@ export const dropdownItemStyle = (disabled: boolean) => { cursor: `${disabled ? 'not-allowed' : 'pointer'}` } }); -}; export const dropdownDividerStyle = css({ height: 0, @@ -116,7 +114,7 @@ export const dropdownLocationStyle = ( return css({ ...defaultStyle, ...moveLeft, ...centerY }); case 'bottom': return css({ ...defaultStyle, ...moveBottom, ...centerX }); - default: //top + default: // top return css({ ...defaultStyle, ...moveBottom, ...centerX }); } }; From 93a05820c2606e13d3105945fa25b4a57024285a Mon Sep 17 00:00:00 2001 From: jiyeon Date: Thu, 26 Oct 2023 00:13:22 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8E=A8=20style:=20apply=20eslint=20co?= =?UTF-8?q?nventions=20to=20Dropdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/dropdown/src/components/Dropdown.tsx | 40 +++++---- .../src/components/DropdownDivider.tsx | 6 +- .../dropdown/src/components/DropdownMenu.tsx | 42 +++++----- .../src/components/DropdownMenuItem.tsx | 28 ++++--- .../src/components/DropdownSubMenu.tsx | 13 ++- .../src/components/DropdownSubMenuItem.tsx | 17 ++-- .../src/components/DropdownTrigger.tsx | 28 +++---- packages/dropdown/src/context.ts | 11 +-- .../dropdown/src/hooks/useArrowKeyDown.ts | 84 ------------------- .../dropdown/src/hooks/useCloseKeyDown.ts | 15 ---- packages/dropdown/src/hooks/useOpenKeyDown.ts | 25 ------ packages/dropdown/src/hooks/useSelectItem.ts | 9 -- packages/dropdown/src/hooks/useToggleOpen.ts | 6 -- packages/dropdown/src/style.ts | 32 ++----- packages/dropdown/src/types/context.ts | 17 ++++ packages/dropdown/src/types/index.ts | 1 + packages/dropdown/src/utils/closeKeyDown.ts | 16 ++++ .../keyboardHandler.ts} | 11 +-- packages/dropdown/src/utils/openKeyDown.ts | 26 ++++++ packages/dropdown/src/utils/selectItem.ts | 9 ++ packages/dropdown/src/utils/toggleOpen.ts | 10 +++ packages/popover/src/context.ts | 2 +- 22 files changed, 188 insertions(+), 260 deletions(-) delete mode 100644 packages/dropdown/src/hooks/useArrowKeyDown.ts delete mode 100644 packages/dropdown/src/hooks/useCloseKeyDown.ts delete mode 100644 packages/dropdown/src/hooks/useOpenKeyDown.ts delete mode 100644 packages/dropdown/src/hooks/useSelectItem.ts delete mode 100644 packages/dropdown/src/hooks/useToggleOpen.ts create mode 100644 packages/dropdown/src/types/context.ts create mode 100644 packages/dropdown/src/utils/closeKeyDown.ts rename packages/dropdown/src/{hooks/useKeyboardHandler.ts => utils/keyboardHandler.ts} (65%) create mode 100644 packages/dropdown/src/utils/openKeyDown.ts create mode 100644 packages/dropdown/src/utils/selectItem.ts create mode 100644 packages/dropdown/src/utils/toggleOpen.ts diff --git a/packages/dropdown/src/components/Dropdown.tsx b/packages/dropdown/src/components/Dropdown.tsx index 366782e..7d729de 100644 --- a/packages/dropdown/src/components/Dropdown.tsx +++ b/packages/dropdown/src/components/Dropdown.tsx @@ -1,16 +1,16 @@ /** @jsxImportSource @emotion/react */ -import { useState, useRef } from 'react'; +import { useState, useRef, useMemo } from 'react'; import { useOutsideClick } from '@jdesignlab/react-utils'; import { Divider } from './DropdownDivider'; import { Menu } from './DropdownMenu'; -import { Trigger } from './DropdownTrigger'; +import { DropdownTrigger } from './DropdownTrigger'; import { MenuItem } from './DropdownMenuItem'; import { SubMenu } from './DropdownSubMenu'; import { SubMenuItem } from './DropdownSubMenuItem'; -import { dropdownWrapperStyle } from '../style'; +import * as Style from '../style'; import { DropdownContext } from '../context'; -import { useToggleOpen } from '../hooks/useToggleOpen'; +import { toggleOpen } from '../utils/toggleOpen'; import { DROPDOWN_ROLE_QUERY, DROPDOWN_MENU_OPEN_CLASS_NAME } from '../constants'; import type { DropdownProps } from '../types'; @@ -18,17 +18,21 @@ export const Dropdown = (props: DropdownProps) => { const dropdownRef = useRef(null); const [triggerWidth, setTriggerWidth] = useState(0); const [triggerHeight, setTriggerHeight] = useState(0); - const { children, width = 200, placement = 'bottom', ...restProps } = props; - const gap = Number(props.gap) || 0; - const providerValue = { - placement, - width, - triggerWidth, - setTriggerWidth, - triggerHeight, - setTriggerHeight, - gap - }; + const { children, width = 200, placement = 'bottom', gap: gapProp, ...restProps } = props; + const gap = Number(gapProp) || 0; + const providerValue = useMemo( + () => ({ + placement, + width, + triggerWidth, + setTriggerWidth, + triggerHeight, + setTriggerHeight, + gap + }), + [gap, placement, triggerHeight, triggerWidth, width] + ); + useOutsideClick({ ref: dropdownRef, handler: () => { @@ -36,13 +40,13 @@ export const Dropdown = (props: DropdownProps) => { ? (dropdownRef.current.querySelector(DROPDOWN_ROLE_QUERY) as HTMLElement) : null; if (dropdownMenu && dropdownMenu.classList.contains(DROPDOWN_MENU_OPEN_CLASS_NAME)) { - useToggleOpen(dropdownMenu); + toggleOpen(dropdownMenu); } } }); return ( -
+
{children}
@@ -51,7 +55,7 @@ export const Dropdown = (props: DropdownProps) => { Dropdown.displayName = 'Dropdown'; -Dropdown.Trigger = Trigger; +Dropdown.Trigger = DropdownTrigger; Dropdown.Menu = Menu; Dropdown.MenuItem = MenuItem; Dropdown.SubMenu = SubMenu; diff --git a/packages/dropdown/src/components/DropdownDivider.tsx b/packages/dropdown/src/components/DropdownDivider.tsx index 385f504..8de315b 100644 --- a/packages/dropdown/src/components/DropdownDivider.tsx +++ b/packages/dropdown/src/components/DropdownDivider.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource @emotion/react */ -import { dropdownDividerStyle } from '../style'; +import * as Style from '../style'; -export const Divider = () =>
; +export const Divider = () =>
; -Divider.displayName = 'Dropdown.Divider' \ No newline at end of file +Divider.displayName = 'DropdownDivider'; diff --git a/packages/dropdown/src/components/DropdownMenu.tsx b/packages/dropdown/src/components/DropdownMenu.tsx index 61502f5..a3ea5a2 100644 --- a/packages/dropdown/src/components/DropdownMenu.tsx +++ b/packages/dropdown/src/components/DropdownMenu.tsx @@ -1,34 +1,32 @@ /** @jsxImportSource @emotion/react */ + import React, { useContext, useRef } from 'react'; -import { dropdownLocationStyle } from '../style'; +import * as Style from '../style'; + import { DropdownContext } from '../context'; -import useCloseKeyDown from '../hooks/useCloseKeyDown'; +import closeKeyDown from '../utils/closeKeyDown'; import type { DropdownMenuProps } from '../types'; export const Menu = React.memo((props: DropdownMenuProps) => { const { children, ...otherProps } = props; const { width, triggerWidth, triggerHeight, gap, placement } = useContext(DropdownContext); - const keyDownHandle = useCloseKeyDown(); const menuRef = useRef(null); - const MenuBox = () => ( -
    { - keyDownHandle(e); - }} - css={dropdownLocationStyle(menuRef, width, triggerWidth, triggerHeight, gap, placement)} - {...otherProps} - > - {children} -
- ); - return <>; + return ( +
    closeKeyDown(e)} + css={Style.createLocation(menuRef, width, triggerWidth, triggerHeight, gap, placement)} + {...otherProps} + > + {children} +
+ ); }); -Menu.displayName = 'Dropdown.Menu'; +Menu.displayName = 'DropdownMenu'; diff --git a/packages/dropdown/src/components/DropdownMenuItem.tsx b/packages/dropdown/src/components/DropdownMenuItem.tsx index 30996dd..fb8e93e 100644 --- a/packages/dropdown/src/components/DropdownMenuItem.tsx +++ b/packages/dropdown/src/components/DropdownMenuItem.tsx @@ -1,16 +1,15 @@ /** @jsxImportSource @emotion/react */ -import { useRef, useState } from 'react'; +import { useMemo, useRef, useState } from 'react'; import { DropdownSubContext } from '../context'; -import { dropdownItemStyle } from '../style'; -import { useKeyboardHandler } from '../hooks/useKeyboardHandler'; -import { useSelectItem } from '../hooks/useSelectItem'; +import * as Style from '../style'; import { NOT_DISABLED_DROPDOWN_MENU_QUERY } from '../constants'; +import { selectItem } from '../utils/selectItem'; +import { keyboardHandler } from '../utils/keyboardHandler'; import type { DropdownMenuItemProps } from '../types'; export const MenuItem = (props: DropdownMenuItemProps) => { const menuItemRef = useRef(null); - const { children, hasSub, onClick, ...otherProps } = props; - const disabled = props.disabled === undefined ? false : props.disabled; + const { children, hasSub, onClick, disabled, ...restProps } = props; const [subOpen, setSubOpen] = useState(false); const toggleSubOpen = () => { @@ -22,9 +21,9 @@ export const MenuItem = (props: DropdownMenuItemProps) => { const onKeyDown = (event: React.KeyboardEvent) => { if (!menuItemRef.current) return; if (event.key !== 'Tab') { - subOpen && setSubOpen(false); + setSubOpen(false); } - useKeyboardHandler({ + keyboardHandler({ event, parentScope: '.menu', selectorOfList: NOT_DISABLED_DROPDOWN_MENU_QUERY, @@ -33,20 +32,23 @@ export const MenuItem = (props: DropdownMenuItemProps) => { }); }; + const providerValue = useMemo(() => ({ subOpen, setSubOpen }), [subOpen]); + return ( - +
  • useSelectItem(e, onClick)} onKeyDown={e => onKeyDown(e)} - {...otherProps} + onClick={e => selectItem(e, onClick)} + onFocus={toggleSubOpen} + {...restProps} > {children}
  • @@ -54,4 +56,4 @@ export const MenuItem = (props: DropdownMenuItemProps) => { ); }; -MenuItem.displayName = 'Dropdown.MenuItem'; +MenuItem.displayName = 'DropdownMenuItem'; diff --git a/packages/dropdown/src/components/DropdownSubMenu.tsx b/packages/dropdown/src/components/DropdownSubMenu.tsx index 2de3648..213518b 100644 --- a/packages/dropdown/src/components/DropdownSubMenu.tsx +++ b/packages/dropdown/src/components/DropdownSubMenu.tsx @@ -1,30 +1,29 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; -import React, { useState, useEffect, useContext, useRef, createContext, useCallback } from 'react'; -import { dropdownSubLocationStyle, dropdownMenuStyle } from '../style'; +import { useState, useEffect, useContext, useRef } from 'react'; +import * as Style from '../style'; import { DropdownContext, DropdownSubContext } from '../context'; import type { DropdownSubMenuProps } from '../types'; export const SubMenu = (props: DropdownSubMenuProps) => { - const { children, ...otherProps } = props; + const { children, ...restProps } = props; const { width } = useContext(DropdownContext); const { subOpen } = useContext(DropdownSubContext); const [menuStyle, setMenuStyle] = useState>(); const menuRef = useRef(null); useEffect(() => { - setMenuStyle(css({ ...dropdownSubLocationStyle(menuRef, width) }, { ...dropdownMenuStyle })); - }, [open]); + setMenuStyle(css({ ...Style.createDropdownSubLocation(menuRef, width) }, { ...Style.dropdownMenu })); + }, [width]); return (
      {children}
    diff --git a/packages/dropdown/src/components/DropdownSubMenuItem.tsx b/packages/dropdown/src/components/DropdownSubMenuItem.tsx index ca2160b..35ffb26 100644 --- a/packages/dropdown/src/components/DropdownSubMenuItem.tsx +++ b/packages/dropdown/src/components/DropdownSubMenuItem.tsx @@ -1,20 +1,19 @@ /** @jsxImportSource @emotion/react */ -import { useContext, useRef, useState } from 'react'; -import { dropdownItemStyle } from '../style'; -import { useKeyboardHandler } from '../hooks/useKeyboardHandler'; -import { useSelectItem } from '../hooks/useSelectItem'; +import { useRef } from 'react'; +import * as Style from '../style'; import { DROPDOWN_MENU_OPEN_CLASS_NAME, NOT_DISABLED_DROPDOWN_SUB_ITEM_QUERY } from '../constants'; +import { keyboardHandler } from '../utils/keyboardHandler'; +import { selectItem } from '../utils/selectItem'; import type { DropdownSubMenuItemProps } from '../types'; export const SubMenuItem = (props: DropdownSubMenuItemProps) => { const menuItemRef = useRef(null); - const { children, onClick, ...otherProps } = props; - const disabled = props.disabled === undefined ? false : props.disabled; + const { children, onClick, disabled, ...otherProps } = props; const onKeyDown = (event: React.KeyboardEvent) => { if (!menuItemRef.current) return; - useKeyboardHandler({ + keyboardHandler({ event, parentScope: DROPDOWN_MENU_OPEN_CLASS_NAME, selectorOfList: NOT_DISABLED_DROPDOWN_SUB_ITEM_QUERY @@ -27,9 +26,9 @@ export const SubMenuItem = (props: DropdownSubMenuItemProps) => { role="menuitem" tabIndex={disabled ? -1 : 0} className={`sub_item ${disabled ? 'disabled' : ''}`} - onClick={e => useSelectItem(e, onClick)} + onClick={e => selectItem(e, onClick)} aria-disabled={!!disabled} - css={{ ...dropdownItemStyle(disabled) }} + css={{ ...Style.createDropdownItem(!!disabled) }} onKeyDown={e => onKeyDown(e)} {...otherProps} > diff --git a/packages/dropdown/src/components/DropdownTrigger.tsx b/packages/dropdown/src/components/DropdownTrigger.tsx index 2d3d4f8..b16103e 100644 --- a/packages/dropdown/src/components/DropdownTrigger.tsx +++ b/packages/dropdown/src/components/DropdownTrigger.tsx @@ -1,12 +1,11 @@ import { useEffect, useContext, useRef } from 'react'; import { DropdownContext } from '../context'; -import useOpenKeyDown from '../hooks/useOpenKeyDown'; -import { useToggleOpen } from '../hooks/useToggleOpen'; -import { DROPDOWN_ROLE_QUERY, DROPDOWN_MENU_WRAPPER_CLASS } from '../constants'; +import { toggleOpen } from '../utils/toggleOpen'; +import { openKeyDown } from '../utils/openKeyDown'; import type { DropdownTriggerProps } from '../types'; -export const Trigger = (props: DropdownTriggerProps) => { - const { children, ...otherProps } = props; +export const DropdownTrigger = (props: DropdownTriggerProps) => { + const { children, ...restProps } = props; const { setTriggerWidth, setTriggerHeight } = useContext(DropdownContext); const triggerRef = useRef(null); @@ -16,29 +15,28 @@ export const Trigger = (props: DropdownTriggerProps) => { setTriggerWidth(parseInt(style.width, 10)); setTriggerHeight(parseInt(style.height, 10)); } - }, [triggerRef]); + }, [setTriggerHeight, setTriggerWidth, triggerRef]); - const onClickHandle = () => { + const onTriggerClick = () => { if (triggerRef?.current) { - const dropdownMenu = triggerRef.current - .closest(DROPDOWN_MENU_WRAPPER_CLASS) - ?.querySelector(DROPDOWN_ROLE_QUERY) as HTMLElement; - useToggleOpen(dropdownMenu); + toggleOpen(triggerRef.current); } }; return (
    { - useOpenKeyDown(e); + openKeyDown(e); }} - {...otherProps} + {...restProps} > {children}
    ); }; -Trigger.displayName = 'Dropdown.Trigger'; +DropdownTrigger.displayName = 'Dropdown.Trigger'; diff --git a/packages/dropdown/src/context.ts b/packages/dropdown/src/context.ts index 2e585ee..da663ac 100644 --- a/packages/dropdown/src/context.ts +++ b/packages/dropdown/src/context.ts @@ -1,16 +1,17 @@ import { createContext } from 'react'; +import { DropdownContextProps, DropdownSubContextProps } from './types'; -export const DropdownContext = createContext({ +export const DropdownContext = createContext({ width: 0, placement: 'top', triggerWidth: 0, - setTriggerWidth: (w: any) => {}, + setTriggerWidth: () => {}, triggerHeight: 0, - setTriggerHeight: (h: any) => {}, + setTriggerHeight: () => {}, gap: 0 }); -export const DropdownSubContext = createContext({ +export const DropdownSubContext = createContext({ subOpen: false, - setSubOpen: (value: boolean) => {} + setSubOpen: () => {} }); diff --git a/packages/dropdown/src/hooks/useArrowKeyDown.ts b/packages/dropdown/src/hooks/useArrowKeyDown.ts deleted file mode 100644 index 6dab4de..0000000 --- a/packages/dropdown/src/hooks/useArrowKeyDown.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type { Dispatch, SetStateAction } from 'react'; -import React, { useContext, useCallback, RefObject } from 'react'; - -const useArrowKeyDown = () => { - // const handleKeyDown = useCallback( - // ( - // event: React.KeyboardEvent, - // menuItemRef: RefObject, - // setSubOpen?: Dispatch> - // ) => { - // if (!menuItemRef.current) return; - // const MENU_DROPDOWN_SELECTOR = '.menu'; - // const MENU_ITEM_SELECTOR = '.menu_item:not([disabled])'; - // const SUB_MENU_ITEM_SELECTOR = '.sub_item:not([disabled])'; - // const onArrowDown = (currIndex: number, itemEls: NodeListOf) => { - // if (itemEls) { - // if (currIndex < itemEls.length - 1) { - // itemEls[currIndex + 1].focus(); - // } else { - // itemEls[0].focus(); - // } - // } - // }; - // const onArrowUp = (currIndex: number, itemEls: NodeListOf) => { - // if (itemEls) { - // if (currIndex > 0) { - // itemEls[currIndex - 1].focus(); - // } else { - // itemEls[itemEls?.length - 1].focus(); - // } - // } - // }; - // const menuItemEls = menuItemRef.current - // .closest(MENU_DROPDOWN_SELECTOR) - // ?.querySelectorAll(MENU_ITEM_SELECTOR); - // if (menuItemEls) { - // const currItemIndex = Array.from(menuItemEls).indexOf(event.currentTarget); - // const currSubItemEls = menuItemEls[currItemIndex]?.querySelectorAll(SUB_MENU_ITEM_SELECTOR); - // const targetEl = event.target as HTMLElement; - // const isSubItem = targetEl.classList.contains('sub_item'); - // if (isSubItem) { - // const currSubIndex = currSubItemEls ? Array.from(currSubItemEls).indexOf(targetEl) : 0; - // switch (event.key) { - // case 'ArrowDown': - // event.preventDefault(); - // onArrowDown(currSubIndex, currSubItemEls); - // return; - // case 'ArrowUp': - // event.preventDefault(); - // onArrowUp(currSubIndex, currSubItemEls); - // return; - // case 'ArrowLeft': - // event.preventDefault(); - // setSubOpen && setSubOpen(false); - // menuItemEls[currItemIndex]?.focus(); - // return; - // } - // } else { - // switch (event.key) { - // case 'ArrowDown': - // event.preventDefault(); - // onArrowDown(currItemIndex, menuItemEls); - // return; - // case 'ArrowUp': - // event.preventDefault(); - // onArrowUp(currItemIndex, menuItemEls); - // return; - // case 'ArrowRight': - // event.preventDefault(); - // if (currSubItemEls) { - // setSubOpen && setSubOpen(true); - // currSubItemEls[0]?.focus(); - // } - // return; - // } - // } - // } - // }, - // [] - // ); - // return handleKeyDown; -}; - -export default useArrowKeyDown; diff --git a/packages/dropdown/src/hooks/useCloseKeyDown.ts b/packages/dropdown/src/hooks/useCloseKeyDown.ts deleted file mode 100644 index 379c10e..0000000 --- a/packages/dropdown/src/hooks/useCloseKeyDown.ts +++ /dev/null @@ -1,15 +0,0 @@ -import React, { useContext, useCallback, RefObject } from 'react'; -import { useToggleOpen } from './useToggleOpen'; - -const useCloseKeyDown = () => (event: React.KeyboardEvent) => { - const el = event.currentTarget; - switch (event.key) { - case 'Escape': - case 'Esc': - event.preventDefault(); - useToggleOpen(el); - - } -}; - -export default useCloseKeyDown; diff --git a/packages/dropdown/src/hooks/useOpenKeyDown.ts b/packages/dropdown/src/hooks/useOpenKeyDown.ts deleted file mode 100644 index ead0e1e..0000000 --- a/packages/dropdown/src/hooks/useOpenKeyDown.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Dispatch, SetStateAction } from 'react'; -import React, { useContext, useCallback, RefObject } from 'react'; -import { useToggleOpen } from './useToggleOpen'; - -const MENU_WRAPPER_SELECTOR = '.menu_wrapper'; -const MENU_ITEM_SELECTOR = '.menu_item:not([disabled])'; - -const useOpenKeyDown = (event: React.KeyboardEvent) => { - const el = event.currentTarget; - switch (event.key) { - case 'ArrowUp': - case 'ArrowDown': - case 'ArrowLeft': - case 'ArrowRight': - case 'Space': - case ' ': - case 'Enter': - event.preventDefault(); - useToggleOpen(el); - const firstItem = el.closest(MENU_WRAPPER_SELECTOR)?.querySelector(MENU_ITEM_SELECTOR); - firstItem?.focus(); - } -}; - -export default useOpenKeyDown; diff --git a/packages/dropdown/src/hooks/useSelectItem.ts b/packages/dropdown/src/hooks/useSelectItem.ts deleted file mode 100644 index 34ba86a..0000000 --- a/packages/dropdown/src/hooks/useSelectItem.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useToggleOpen } from './useToggleOpen'; - -export const useSelectItem = ( - event: React.MouseEvent | React.KeyboardEvent, - onClick?: () => void -) => { - if (onClick) onClick(); - useToggleOpen(event.currentTarget); -}; diff --git a/packages/dropdown/src/hooks/useToggleOpen.ts b/packages/dropdown/src/hooks/useToggleOpen.ts deleted file mode 100644 index 61d3ee2..0000000 --- a/packages/dropdown/src/hooks/useToggleOpen.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const useToggleOpen = (dropdownMenu: HTMLElement) => { - if (dropdownMenu) { - dropdownMenu.classList.toggle('menu_close'); - dropdownMenu.classList.toggle('menu_open'); - } -}; diff --git a/packages/dropdown/src/style.ts b/packages/dropdown/src/style.ts index 25cc55b..e372bf7 100644 --- a/packages/dropdown/src/style.ts +++ b/packages/dropdown/src/style.ts @@ -1,9 +1,6 @@ -import { css, keyframes } from '@emotion/react'; -import { getColorByToken, hexToRgba } from '@jdesignlab/theme'; -import React, { useCallback } from 'react'; -import type { ColorToken } from '@jdesignlab/theme'; +import { css } from '@emotion/react'; -export const dropdownWrapperStyle = css({ +export const dropdownWrapper = css({ position: 'relative' }); @@ -15,16 +12,7 @@ export const dropdownOverlayStyle = css({ height: '100vh' }); -const shadowDrop = keyframes` -0% { - box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); -} -100% { - box-shadow: 10.5px 10.5px 15px -4.5px rgba(100,100,100,0.2); -} -`; -export const dropdownMenuStyle = css({ - // ul +export const dropdownMenu = css({ position: 'absolute', zIndex: 100, minWidth: '220px', @@ -32,7 +20,7 @@ export const dropdownMenuStyle = css({ background: 'rgba(255,255,255,0.5)', backdropFilter: 'blur(20px)', display: 'flex', - flexDirection: 'column', // TODO
      ?? + flexDirection: 'column', borderRadius: '2px', padding: '5px', boxShadow: @@ -43,10 +31,10 @@ export const dropdownMenuStyle = css({ '&.menu_close': { display: 'none' } - // animation: `${shadowDrop} 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both` }); -export const dropdownItemStyle = (disabled: boolean) => css({ +export const createDropdownItem = (disabled: boolean) => + css({ position: 'relative', padding: '5px', borderRadius: '4px', @@ -59,14 +47,14 @@ export const dropdownItemStyle = (disabled: boolean) => css({ } }); -export const dropdownDividerStyle = css({ +export const dropdownDivider = css({ height: 0, width: '100%', margin: '5px 0', borderTop: '#e0e0e0 solid 0.5px' }); -export const dropdownLocationStyle = ( +export const createLocation = ( labelRef: React.RefObject, width: number, triggerWidth: number, @@ -82,7 +70,6 @@ export const dropdownLocationStyle = ( background: 'rgba(255,255,255,0.5)', backdropFilter: 'blur(20px)', display: 'flex', - // border: 'solid lightgray 1px', borderRadius: '2px', padding: '5px', boxShadow: @@ -119,8 +106,7 @@ export const dropdownLocationStyle = ( } }; -export const dropdownSubLocationStyle = (labelRef: React.RefObject, width: number) => { - const menu = labelRef.current?.closest('[role="menu"]'); +export const createDropdownSubLocation = (labelRef: React.RefObject, width: number) => { const rightSub = { left: `${width}px`, transform: 'translate(12px, -30px)' diff --git a/packages/dropdown/src/types/context.ts b/packages/dropdown/src/types/context.ts new file mode 100644 index 0000000..9dec8b6 --- /dev/null +++ b/packages/dropdown/src/types/context.ts @@ -0,0 +1,17 @@ +import { Dispatch, SetStateAction } from 'react'; +import { DropdownAnchor } from './base'; + +export interface DropdownContextProps { + width: number; + placement: DropdownAnchor; + triggerWidth: number; + setTriggerWidth: Dispatch>; + triggerHeight: number; + setTriggerHeight: Dispatch>; + gap: number; +} + +export interface DropdownSubContextProps { + subOpen: boolean; + setSubOpen: Dispatch>; +} diff --git a/packages/dropdown/src/types/index.ts b/packages/dropdown/src/types/index.ts index 76ca478..a57a096 100644 --- a/packages/dropdown/src/types/index.ts +++ b/packages/dropdown/src/types/index.ts @@ -1,2 +1,3 @@ export * from './base'; export * from './props'; +export * from './context'; diff --git a/packages/dropdown/src/utils/closeKeyDown.ts b/packages/dropdown/src/utils/closeKeyDown.ts new file mode 100644 index 0000000..e49bbd4 --- /dev/null +++ b/packages/dropdown/src/utils/closeKeyDown.ts @@ -0,0 +1,16 @@ +import { toggleOpen } from './toggleOpen'; + +const closeKeyDown = (event: React.KeyboardEvent) => { + const el = event.currentTarget; + switch (event.key) { + case 'Escape': + case 'Esc': + event.preventDefault(); + toggleOpen(el); + break; + default: + break; + } +}; + +export default closeKeyDown; diff --git a/packages/dropdown/src/hooks/useKeyboardHandler.ts b/packages/dropdown/src/utils/keyboardHandler.ts similarity index 65% rename from packages/dropdown/src/hooks/useKeyboardHandler.ts rename to packages/dropdown/src/utils/keyboardHandler.ts index 35a2c47..791c5f3 100644 --- a/packages/dropdown/src/hooks/useKeyboardHandler.ts +++ b/packages/dropdown/src/utils/keyboardHandler.ts @@ -1,8 +1,8 @@ -import type { Dispatch, SetStateAction } from 'react'; -import { spaceKeyToggleHandler, arrowKeyNavigationHandler } from '@jdesignlab/react-utils'; -import { useSelectItem } from './useSelectItem'; +import { arrowKeyNavigationHandler, spaceKeyToggleHandler } from '@jdesignlab/react-utils'; +import { Dispatch, SetStateAction } from 'react'; +import { selectItem } from './selectItem'; -export const useKeyboardHandler = (props: { +export const keyboardHandler = (props: { event: React.KeyboardEvent; parentScope: string; selectorOfList: string; @@ -12,8 +12,9 @@ export const useKeyboardHandler = (props: { }) => { const { event, parentScope, selectorOfList, setState, onClick } = props; if (event.key === 'Enter' && onClick) { - useSelectItem(event, onClick); + selectItem(event, onClick); } + spaceKeyToggleHandler({ event, setState }); arrowKeyNavigationHandler({ event, parentScope, selectorOfList }); }; diff --git a/packages/dropdown/src/utils/openKeyDown.ts b/packages/dropdown/src/utils/openKeyDown.ts new file mode 100644 index 0000000..68a8a1d --- /dev/null +++ b/packages/dropdown/src/utils/openKeyDown.ts @@ -0,0 +1,26 @@ +import { toggleOpen } from './toggleOpen'; +import { NOT_DISABLED_DROPDOWN_MENU_QUERY, DROPDOWN_MENU_WRAPPER_CLASS } from '../constants'; + +export const openKeyDown = (event: React.KeyboardEvent) => { + const el = event.currentTarget; + switch (event.key) { + case 'ArrowUp': + case 'ArrowDown': + case 'ArrowLeft': + case 'ArrowRight': + case 'Space': + case ' ': + case 'Enter': + { + event.preventDefault(); + toggleOpen(el); + const firstItem = el + .closest(DROPDOWN_MENU_WRAPPER_CLASS) + ?.querySelector(NOT_DISABLED_DROPDOWN_MENU_QUERY); + firstItem?.focus(); + } + break; + default: + break; + } +}; diff --git a/packages/dropdown/src/utils/selectItem.ts b/packages/dropdown/src/utils/selectItem.ts new file mode 100644 index 0000000..bb518aa --- /dev/null +++ b/packages/dropdown/src/utils/selectItem.ts @@ -0,0 +1,9 @@ +import { toggleOpen } from './toggleOpen'; + +export const selectItem = ( + event: React.MouseEvent | React.KeyboardEvent, + onClick?: () => void +) => { + onClick?.(); + toggleOpen(event.currentTarget); +}; diff --git a/packages/dropdown/src/utils/toggleOpen.ts b/packages/dropdown/src/utils/toggleOpen.ts new file mode 100644 index 0000000..4a45c5e --- /dev/null +++ b/packages/dropdown/src/utils/toggleOpen.ts @@ -0,0 +1,10 @@ +import { DROPDOWN_MENU_WRAPPER_CLASS, DROPDOWN_ROLE_QUERY } from '../constants'; + +export const toggleOpen = (el: HTMLElement) => { + const dropdownMenu = el + .closest(DROPDOWN_MENU_WRAPPER_CLASS) + ?.querySelector(DROPDOWN_ROLE_QUERY) as HTMLDivElement; + + dropdownMenu?.classList.toggle('menu_close'); + dropdownMenu?.classList.toggle('menu_open'); +}; diff --git a/packages/popover/src/context.ts b/packages/popover/src/context.ts index db82d24..f92fff1 100644 --- a/packages/popover/src/context.ts +++ b/packages/popover/src/context.ts @@ -1,4 +1,4 @@ -import { createContext, RefObject, useState } from 'react'; +import { createContext } from 'react'; import { DEFAULT_OPTIONS } from './constants'; import type { ReturnContext } from './types';