@@ -16,84 +16,119 @@ interface DropdownProps {
16
16
anchor ?: { x : string ; y : string } ;
17
17
gutter ?: number ;
18
18
modal ?: boolean ;
19
+ portal ?: boolean ;
20
+ preserveTabOrder ?: boolean ;
19
21
focusLoop ?: boolean ;
20
22
menuId : string ;
23
+ mountByState ?: boolean ;
24
+ unmountOnHide ?: boolean ;
25
+ finalFocus ?: React . RefObject < HTMLElement > ;
21
26
}
22
27
28
+ type MenuProps = Omit <
29
+ DropdownProps ,
30
+ 'trigger' | 'isOpen' | 'setIsOpen' | 'focusLoop' | 'mountByState'
31
+ > ;
32
+
23
33
const DropdownPopup : React . FC < DropdownProps > = ( {
24
- keyPrefix,
25
34
trigger,
26
- items,
27
35
isOpen,
28
36
setIsOpen,
29
- menuId,
30
- modal,
31
- gutter = 8 ,
32
- sameWidth,
33
- className,
34
37
focusLoop,
35
- iconClassName ,
36
- itemClassName ,
38
+ mountByState ,
39
+ ... props
37
40
} ) => {
38
41
const menu = Ariakit . useMenuStore ( { open : isOpen , setOpen : setIsOpen , focusLoop } ) ;
39
-
42
+ if ( mountByState ) {
43
+ return (
44
+ < Ariakit . MenuProvider store = { menu } >
45
+ { trigger }
46
+ { isOpen && < Menu { ...props } /> }
47
+ </ Ariakit . MenuProvider >
48
+ ) ;
49
+ }
40
50
return (
41
51
< Ariakit . MenuProvider store = { menu } >
42
52
{ trigger }
43
- < Ariakit . Menu
44
- id = { menuId }
45
- className = { cn ( 'popover-ui z-50' , className ) }
46
- gutter = { gutter }
47
- modal = { modal }
48
- sameWidth = { sameWidth }
49
- >
50
- { items
51
- . filter ( ( item ) => item . show !== false )
52
- . map ( ( item , index ) => {
53
- if ( item . separate === true ) {
54
- return < Ariakit . MenuSeparator key = { index } className = "my-1 h-px bg-white/10" /> ;
55
- }
56
- return (
57
- < Ariakit . MenuItem
58
- key = { `${ keyPrefix ?? '' } ${ index } ` }
59
- id = { item . id }
60
- className = { cn (
61
- 'group flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-3.5 text-sm text-text-primary outline-none transition-colors duration-200 hover:bg-surface-hover focus:bg-surface-hover md:px-2.5 md:py-2' ,
62
- itemClassName ,
63
- ) }
64
- disabled = { item . disabled }
65
- render = { item . render }
66
- ref = { item . ref }
67
- hideOnClick = { item . hideOnClick }
68
- onClick = { ( event ) => {
69
- event . preventDefault ( ) ;
70
- if ( item . onClick ) {
71
- item . onClick ( event ) ;
72
- }
73
- if ( item . hideOnClick === false ) {
74
- return ;
75
- }
76
- menu . hide ( ) ;
77
- } }
78
- >
79
- { item . icon != null && (
80
- < span className = { cn ( 'mr-2 size-4' , iconClassName ) } aria-hidden = "true" >
81
- { item . icon }
82
- </ span >
83
- ) }
84
- { item . label }
85
- { item . kbd != null && (
86
- // eslint-disable-next-line i18next/no-literal-string
87
- < kbd className = "ml-auto hidden font-sans text-xs text-black/50 group-hover:inline group-focus:inline dark:text-white/50" >
88
- ⌘{ item . kbd }
89
- </ kbd >
90
- ) }
91
- </ Ariakit . MenuItem >
92
- ) ;
93
- } ) }
94
- </ Ariakit . Menu >
53
+ < Menu { ...props } />
95
54
</ Ariakit . MenuProvider >
96
55
) ;
97
56
} ;
98
57
58
+ const Menu : React . FC < MenuProps > = ( {
59
+ items,
60
+ menuId,
61
+ keyPrefix,
62
+ className,
63
+ iconClassName,
64
+ itemClassName,
65
+ modal,
66
+ portal,
67
+ sameWidth,
68
+ gutter = 8 ,
69
+ finalFocus,
70
+ unmountOnHide,
71
+ preserveTabOrder,
72
+ } ) => {
73
+ const menu = Ariakit . useMenuContext ( ) ;
74
+ return (
75
+ < Ariakit . Menu
76
+ id = { menuId }
77
+ modal = { modal }
78
+ gutter = { gutter }
79
+ portal = { portal }
80
+ sameWidth = { sameWidth }
81
+ finalFocus = { finalFocus }
82
+ unmountOnHide = { unmountOnHide }
83
+ preserveTabOrder = { preserveTabOrder }
84
+ className = { cn ( 'popover-ui z-50' , className ) }
85
+ >
86
+ { items
87
+ . filter ( ( item ) => item . show !== false )
88
+ . map ( ( item , index ) => {
89
+ if ( item . separate === true ) {
90
+ return < Ariakit . MenuSeparator key = { index } className = "my-1 h-px bg-white/10" /> ;
91
+ }
92
+ return (
93
+ < Ariakit . MenuItem
94
+ key = { `${ keyPrefix ?? '' } ${ index } -${ item . id ?? '' } ` }
95
+ id = { item . id }
96
+ className = { cn (
97
+ 'group flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-3.5 text-sm text-text-primary outline-none transition-colors duration-200 hover:bg-surface-hover focus:bg-surface-hover md:px-2.5 md:py-2' ,
98
+ itemClassName ,
99
+ ) }
100
+ disabled = { item . disabled }
101
+ render = { item . render }
102
+ ref = { item . ref }
103
+ hideOnClick = { item . hideOnClick }
104
+ onClick = { ( event ) => {
105
+ event . preventDefault ( ) ;
106
+ if ( item . onClick ) {
107
+ item . onClick ( event ) ;
108
+ }
109
+ if ( item . hideOnClick === false ) {
110
+ return ;
111
+ }
112
+ menu ?. hide ( ) ;
113
+ } }
114
+ >
115
+ { item . icon != null && (
116
+ < span className = { cn ( 'mr-2 size-4' , iconClassName ) } aria-hidden = "true" >
117
+ { item . icon }
118
+ </ span >
119
+ ) }
120
+ { item . label }
121
+ { item . kbd != null && (
122
+ // eslint-disable-next-line i18next/no-literal-string
123
+ < kbd className = "ml-auto hidden font-sans text-xs text-black/50 group-hover:inline group-focus:inline dark:text-white/50" >
124
+ ⌘{ item . kbd }
125
+ </ kbd >
126
+ ) }
127
+ </ Ariakit . MenuItem >
128
+ ) ;
129
+ } ) }
130
+ </ Ariakit . Menu >
131
+ ) ;
132
+ } ;
133
+
99
134
export default DropdownPopup ;
0 commit comments