-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split top level component into more granular sub-components:
- trigger button - submenu trigger item - popover Update types accordingly
- Loading branch information
Showing
5 changed files
with
264 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
packages/components/src/dropdown-menu-v2/menu-popover.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import * as Ariakit from '@ariakit/react'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
useContext, | ||
useMemo, | ||
forwardRef, | ||
useCallback, | ||
} from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import type { WordPressComponentProps } from '../context'; | ||
import type { DropdownMenuPopoverProps } from './types'; | ||
import * as Styled from './styles'; | ||
import { DropdownMenuContext } from './context'; | ||
|
||
export const DropdownMenuPopover = forwardRef< | ||
HTMLDivElement, | ||
WordPressComponentProps< DropdownMenuPopoverProps, 'div', false > | ||
>( function DropdownMenuPopover( | ||
{ gutter, children, shift, modal = true, ...otherProps }, | ||
ref | ||
) { | ||
const dropdownMenuContext = useContext( DropdownMenuContext ); | ||
|
||
if ( ! dropdownMenuContext?.store ) { | ||
throw new Error( | ||
'DropdownMenu.Popover can only be rendered inside a DropdownMenu component' | ||
); | ||
} | ||
|
||
// Extract the side from the applied placement — useful for animations. | ||
// Using `currentPlacement` instead of `placement` to make sure that we | ||
// use the final computed placement (including "flips" etc). | ||
const appliedPlacementSide = Ariakit.useStoreState( | ||
dropdownMenuContext.store, | ||
'currentPlacement' | ||
).split( '-' )[ 0 ]; | ||
|
||
const hideOnEscape = useCallback( | ||
( event: React.KeyboardEvent< Element > ) => { | ||
// Pressing Escape can cause unexpected consequences (ie. exiting | ||
// full screen mode on MacOs, close parent modals...). | ||
event.preventDefault(); | ||
// Returning `true` causes the menu to hide. | ||
return true; | ||
}, | ||
[] | ||
); | ||
|
||
const computedDirection = Ariakit.useStoreState( | ||
dropdownMenuContext.store, | ||
'rtl' | ||
) | ||
? 'rtl' | ||
: 'ltr'; | ||
|
||
const wrapperProps = useMemo( | ||
() => ( { | ||
dir: computedDirection, | ||
style: { | ||
direction: | ||
computedDirection as React.CSSProperties[ 'direction' ], | ||
}, | ||
} ), | ||
[ computedDirection ] | ||
); | ||
|
||
return ( | ||
<Ariakit.Menu | ||
{ ...otherProps } | ||
ref={ ref } | ||
modal={ modal } | ||
store={ dropdownMenuContext.store } | ||
// Root menu has an 8px distance from its trigger, | ||
// otherwise 0 (which causes the submenu to slightly overlap) | ||
gutter={ gutter ?? ( dropdownMenuContext.store.parent ? 0 : 8 ) } | ||
// Align nested menu by the same (but opposite) amount | ||
// as the menu container's padding. | ||
shift={ shift ?? ( dropdownMenuContext.store.parent ? -4 : 0 ) } | ||
hideOnHoverOutside={ false } | ||
data-side={ appliedPlacementSide } | ||
wrapperProps={ wrapperProps } | ||
hideOnEscape={ hideOnEscape } | ||
unmountOnHide | ||
render={ ( renderProps ) => ( | ||
// Two wrappers are needed for the entry animation, where the menu | ||
// container scales with a different factor than its contents. | ||
// The {...renderProps} are passed to the inner wrapper, so that the | ||
// menu element is the direct parent of the menu item elements. | ||
<Styled.MenuPopoverOuterWrapper | ||
variant={ dropdownMenuContext.variant } | ||
> | ||
<Styled.MenuPopoverInnerWrapper { ...renderProps } /> | ||
</Styled.MenuPopoverOuterWrapper> | ||
) } | ||
> | ||
{ children } | ||
</Ariakit.Menu> | ||
); | ||
} ); |
58 changes: 58 additions & 0 deletions
58
packages/components/src/dropdown-menu-v2/submenu-trigger-item.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import * as Ariakit from '@ariakit/react'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { forwardRef, useContext } from '@wordpress/element'; | ||
import { chevronRightSmall } from '@wordpress/icons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import type { WordPressComponentProps } from '../context'; | ||
import type { DropdownMenuSubmenuTriggerItemProps } from './types'; | ||
import { DropdownMenuContext } from './context'; | ||
import { DropdownMenuItem } from './item'; | ||
import * as Styled from './styles'; | ||
|
||
export const DropdownMenuSubmenuTriggerItem = forwardRef< | ||
HTMLDivElement, | ||
WordPressComponentProps< DropdownMenuSubmenuTriggerItemProps, 'div', false > | ||
>( function DropdownMenuSubmenuTriggerItem( { suffix, ...otherProps }, ref ) { | ||
const dropdownMenuContext = useContext( DropdownMenuContext ); | ||
|
||
if ( ! dropdownMenuContext?.store.parent ) { | ||
throw new Error( | ||
'DropdownMenu.SubmenuTriggerItem can only be rendered inside a nested DropdownMenu component' | ||
); | ||
} | ||
|
||
return ( | ||
<Ariakit.MenuButton | ||
ref={ ref } | ||
accessibleWhenDisabled | ||
store={ dropdownMenuContext.store } | ||
render={ | ||
<DropdownMenuItem | ||
{ ...otherProps } | ||
// The menu item needs to register and be part of the parent menu | ||
store={ dropdownMenuContext.store.parent } | ||
suffix={ | ||
<> | ||
{ suffix } | ||
<Styled.SubmenuChevronIcon | ||
aria-hidden="true" | ||
icon={ chevronRightSmall } | ||
size={ 24 } | ||
preserveAspectRatio="xMidYMid slice" | ||
/> | ||
</> | ||
} | ||
/> | ||
} | ||
/> | ||
); | ||
} ); |
Oops, something went wrong.