1- import React from 'react'
1+ import React , { useState } from 'react'
22import produce , { Draft } from 'immer'
3- import getEventPath , { handleRefs , getDirection } from './utils'
3+ import { usePopper } from 'react-popper'
4+ import { Placement } from '@popperjs/core'
5+ import { Options } from '@popperjs/core/lib/modifiers/offset'
46
7+ import getEventPath , { handleRefs , getDirection } from './utils'
58export interface MenuItem {
69 id : string
710 label : string
@@ -35,8 +38,6 @@ interface ClosePathAction {
3538
3639type Action = ToggleAction | OpenPathAction | ClosePathAction
3740
38- type Placement = 'top' | 'bottom' | 'start' | 'end'
39-
4041/**
4142 * @ignore
4243 */
@@ -45,7 +46,7 @@ interface NestedMenuState {
4546 isOpen : boolean
4647 currentPath : string [ ]
4748 currentPathItems : MenuItem [ ]
48- placement : Placement
49+ placement ? : Placement
4950}
5051
5152/**
@@ -83,6 +84,7 @@ interface NestedMenuProps {
8384 isOpen ?: boolean
8485 defaultOpenPath ?: string [ ]
8586 placement ?: Placement
87+ offset ?: Options [ 'offset' ]
8688}
8789
8890// interface HitAreaProps {
@@ -98,7 +100,8 @@ export const useNestedMenu = ({
98100 items = [ ] ,
99101 isOpen = false ,
100102 defaultOpenPath = [ ] ,
101- placement = 'end' ,
103+ placement,
104+ offset,
102105} : NestedMenuProps ) => {
103106 const [ state , dispatch ] = React . useReducer ( reducer , {
104107 items,
@@ -171,15 +174,30 @@ export const useNestedMenu = ({
171174 } )
172175
173176 const menuRefs = React . useRef < { [ key : string ] : HTMLElement } > ( { } )
174-
175- const getMenuProps = ( item ?: MenuItem ) => ( {
176- key : item ?. id || 'root' ,
177- ref : handleRefs ( ( itemNode ) => {
178- if ( itemNode ) {
179- menuRefs . current [ item ?. id || 'root' ] = itemNode
177+ const [ popperElement , setPopperElement ] = useState < HTMLElement | null > ( null )
178+
179+ const getMenuProps = ( item ?: MenuItem ) => {
180+ if ( item ) {
181+ return {
182+ key : item . id ,
183+ ref : handleRefs ( ( itemNode ) => {
184+ if ( itemNode ) {
185+ menuRefs . current [ item . id ] = itemNode
186+ }
187+ } ) ,
188+ style : getMenuOffsetStyles ( item ) ,
180189 }
181- } ) ,
182- } )
190+ } else {
191+ return {
192+ key : 'root' ,
193+ ref : handleRefs ( ( itemNode ) => {
194+ setPopperElement ( itemNode )
195+ } ) ,
196+ style : styles . popper ,
197+ ...attributes . popper ,
198+ }
199+ }
200+ }
183201
184202 const itemRefs = React . useRef < { [ key : string ] : HTMLElement } > ( { } )
185203
@@ -192,63 +210,16 @@ export const useNestedMenu = ({
192210 } ) ,
193211 } )
194212
195- const getMenuOffsetStyles = ( currentItem ?: MenuItem ) => {
196- const item = currentItem ? itemRefs . current [ currentItem . id ] : null
197- const button = toggleButtonRef . current as HTMLElement
213+ const getMenuOffsetStyles = ( currentItem ?: MenuItem ) : React . CSSProperties => {
214+ if ( ! currentItem ) return { }
215+ const item = itemRefs . current [ currentItem . id ]
198216
199217 const dir = getDirection ( )
200- const rootXEnd =
201- dir === 'ltr'
202- ? button . getBoundingClientRect ( ) . right
203- : window . innerWidth - button . getBoundingClientRect ( ) . left
204-
205- let vertical : string = 'top'
206- let horizontal : string = dir === 'ltr' ? 'left' : 'right'
207- let verticalValue = item ? 0 : button . getBoundingClientRect ( ) . top
208- let horizontalValue = item ? item . getBoundingClientRect ( ) . width : rootXEnd
209-
210- if ( dir === 'ltr' ) {
211- if ( placement === 'top' ) {
212- vertical = item ? 'top' : 'bottom'
213- verticalValue = item ? 0 : window . innerHeight - button . getBoundingClientRect ( ) . top
214- horizontalValue = item
215- ? item . getBoundingClientRect ( ) . width
216- : button . getBoundingClientRect ( ) . left
217- } else if ( placement === 'bottom' ) {
218- verticalValue = item ? 0 : button . getBoundingClientRect ( ) . bottom
219- horizontalValue = item
220- ? item . getBoundingClientRect ( ) . width
221- : button . getBoundingClientRect ( ) . left
222- } else if ( placement === 'start' ) {
223- horizontal = item ? 'left' : 'right'
224- horizontalValue = item
225- ? item . getBoundingClientRect ( ) . width
226- : window . innerWidth - button . getBoundingClientRect ( ) . left
227- }
228- } else {
229- if ( placement === 'top' ) {
230- vertical = item ? 'top' : 'bottom'
231- horizontal = 'right'
232- verticalValue = item ? 0 : window . innerHeight - button . getBoundingClientRect ( ) . top
233- horizontalValue = item
234- ? item . getBoundingClientRect ( ) . width
235- : window . innerWidth - button . getBoundingClientRect ( ) . right
236- } else if ( placement === 'bottom' ) {
237- verticalValue = item ? 0 : button . getBoundingClientRect ( ) . bottom
238- horizontalValue = item
239- ? item . getBoundingClientRect ( ) . width
240- : window . innerWidth - button . getBoundingClientRect ( ) . right
241- } else if ( placement === 'start' ) {
242- horizontal = item ? 'right' : 'left'
243- horizontalValue = item
244- ? item . getBoundingClientRect ( ) . width
245- : button . getBoundingClientRect ( ) . right
246- }
247- }
248218
249219 return {
250- [ vertical ] : verticalValue ,
251- [ horizontal ] : horizontalValue ,
220+ position : 'absolute' ,
221+ top : 0 ,
222+ [ dir === 'ltr' ? 'left' : 'right' ] : item . clientWidth ,
252223 }
253224 }
254225
@@ -294,6 +265,22 @@ export const useNestedMenu = ({
294265 const anchorRef = React . useRef < HTMLElement > ( )
295266 const menuRef = React . useRef < HTMLElement > ( )
296267
268+ const { styles, attributes } = usePopper ( toggleButtonRef . current , popperElement , {
269+ placement : 'right-start' ,
270+ modifiers : [
271+ {
272+ name : 'offset' ,
273+ options : {
274+ offset : offset ,
275+ } ,
276+ } ,
277+ {
278+ name : 'flip' ,
279+ enabled : false ,
280+ } ,
281+ ] ,
282+ } )
283+
297284 return {
298285 getToggleButtonProps,
299286 getMenuProps,
0 commit comments