1
1
import PropTypes from 'prop-types' ;
2
2
import React , { useContext , useRef } from 'react' ;
3
3
import useCallbackRef from '@restart/hooks/useCallbackRef' ;
4
- import DropdownContext from './DropdownContext' ;
5
- import usePopper , { UsePopperOptions , Placement , Offset } from './usePopper' ;
4
+ import DropdownContext , { DropdownContextValue } from './DropdownContext' ;
5
+ import usePopper , {
6
+ UsePopperOptions ,
7
+ Placement ,
8
+ Offset ,
9
+ UsePopperState ,
10
+ } from './usePopper' ;
6
11
import useRootClose , { RootCloseOptions } from './useRootClose' ;
7
12
import mergeOptionsWithPopperConfig from './mergeOptionsWithPopperConfig' ;
8
13
9
14
export interface UseDropdownMenuOptions {
10
15
flip ?: boolean ;
11
16
show ?: boolean ;
17
+ fixed ?: boolean ;
12
18
alignEnd ?: boolean ;
13
19
usePopper ?: boolean ;
14
20
offset ?: Offset ;
15
21
rootCloseEvent ?: RootCloseOptions [ 'clickTrigger' ] ;
16
22
popperConfig ?: Omit < UsePopperOptions , 'enabled' | 'placement' > ;
17
23
}
18
24
19
- export interface UseDropdownMenuValue {
25
+ export type UserDropdownMenuProps = Record < string , any > & {
26
+ ref : React . RefCallback < HTMLElement > ;
27
+ style ?: React . CSSProperties ;
28
+ 'aria-labelledby' ?: string ;
29
+ } ;
30
+
31
+ export type UserDropdownMenuArrowProps = Record < string , any > & {
32
+ ref : React . RefCallback < HTMLElement > ;
33
+ style : React . CSSProperties ;
34
+ } ;
35
+
36
+ export interface UseDropdownMenuMetadata {
20
37
show : boolean ;
21
38
alignEnd ?: boolean ;
22
39
hasShown : boolean ;
23
- close : ( e : Event ) => void ;
24
- update : ( ) => void ;
25
- forceUpdate : ( ) => void ;
26
- props : Record < string , any > & {
27
- ref : React . RefCallback < HTMLElement > ;
28
- style ?: React . CSSProperties ;
29
- 'aria-labelledby' ?: string ;
30
- } ;
31
- arrowProps : Record < string , any > & {
32
- ref : React . RefCallback < HTMLElement > ;
33
- style : React . CSSProperties ;
34
- } ;
40
+ toggle ?: DropdownContextValue [ 'toggle' ] ;
41
+ popper : UsePopperState | null ;
42
+ arrowProps : Partial < UserDropdownMenuArrowProps > ;
35
43
}
36
44
37
45
const noop : any = ( ) => { } ;
@@ -57,11 +65,12 @@ export function useDropdownMenu(options: UseDropdownMenuOptions = {}) {
57
65
flip,
58
66
offset,
59
67
rootCloseEvent,
68
+ fixed = false ,
60
69
popperConfig = { } ,
61
70
usePopper : shouldUsePopper = ! ! context ,
62
71
} = options ;
63
72
64
- const show = context ?. show == null ? options . show : context . show ;
73
+ const show = context ?. show == null ? ! ! options . show : context . show ;
65
74
const alignEnd =
66
75
context ?. alignEnd == null ? options . alignEnd : context . alignEnd ;
67
76
@@ -80,7 +89,7 @@ export function useDropdownMenu(options: UseDropdownMenuOptions = {}) {
80
89
else if ( drop === 'right' ) placement = alignEnd ? 'right-end' : 'right-start' ;
81
90
else if ( drop === 'left' ) placement = alignEnd ? 'left-end' : 'left-start' ;
82
91
83
- const { styles , attributes , ... popper } = usePopper (
92
+ const popper = usePopper (
84
93
toggleElement ,
85
94
menuElement ,
86
95
mergeOptionsWithPopperConfig ( {
@@ -89,50 +98,40 @@ export function useDropdownMenu(options: UseDropdownMenuOptions = {}) {
89
98
enableEvents : show ,
90
99
offset,
91
100
flip,
101
+ fixed,
92
102
arrowElement,
93
103
popperConfig,
94
104
} ) ,
95
105
) ;
96
106
97
- let menu : Partial < UseDropdownMenuValue > ;
98
-
99
- const menuProps = {
107
+ const menuProps : UserDropdownMenuProps = {
100
108
ref : setMenu || noop ,
101
109
'aria-labelledby' : toggleElement ?. id ,
110
+ ...popper . attributes . popper ,
111
+ style : popper . styles . popper as any ,
102
112
} ;
103
113
104
- const childArgs = {
114
+ const metadata : UseDropdownMenuMetadata = {
105
115
show,
106
116
alignEnd,
107
117
hasShown : hasShownRef . current ,
108
- close : handleClose ,
118
+ toggle : context ?. toggle ,
119
+ popper : shouldUsePopper ? popper : null ,
120
+ arrowProps : shouldUsePopper
121
+ ? {
122
+ ref : attachArrowRef ,
123
+ ...popper . attributes . arrow ,
124
+ style : popper . styles . arrow as any ,
125
+ }
126
+ : { } ,
109
127
} ;
110
128
111
- if ( ! shouldUsePopper ) {
112
- menu = { ...childArgs , props : menuProps } ;
113
- } else {
114
- menu = {
115
- ...popper ,
116
- ...childArgs ,
117
- props : {
118
- ...menuProps ,
119
- ...attributes . popper ,
120
- style : styles . popper as any ,
121
- } ,
122
- arrowProps : {
123
- ref : attachArrowRef ,
124
- ...attributes . arrow ,
125
- style : styles . arrow as any ,
126
- } ,
127
- } ;
128
- }
129
-
130
129
useRootClose ( menuElement , handleClose , {
131
130
clickTrigger : rootCloseEvent ,
132
- disabled : ! ( menu && show ) ,
131
+ disabled : ! show ,
133
132
} ) ;
134
133
135
- return menu as UseDropdownMenuValue ;
134
+ return [ menuProps , metadata ] as const ;
136
135
}
137
136
138
137
const propTypes = {
@@ -199,7 +198,10 @@ const defaultProps = {
199
198
} ;
200
199
201
200
export interface DropdownMenuProps extends UseDropdownMenuOptions {
202
- children : ( args : UseDropdownMenuValue ) => React . ReactNode ;
201
+ children : (
202
+ props : UserDropdownMenuProps ,
203
+ meta : UseDropdownMenuMetadata ,
204
+ ) => React . ReactNode ;
203
205
}
204
206
205
207
/**
@@ -209,9 +211,9 @@ export interface DropdownMenuProps extends UseDropdownMenuOptions {
209
211
* @memberOf Dropdown
210
212
*/
211
213
function DropdownMenu ( { children, ...options } : DropdownMenuProps ) {
212
- const args = useDropdownMenu ( options ) ;
214
+ const [ props , meta ] = useDropdownMenu ( options ) ;
213
215
214
- return < > { args . hasShown ? children ( args ) : null } </ > ;
216
+ return < > { meta . hasShown ? children ( props , meta ) : null } </ > ;
215
217
}
216
218
217
219
DropdownMenu . displayName = 'ReactOverlaysDropdownMenu' ;
0 commit comments