1
1
import * as React from 'react' ;
2
2
3
3
import {
4
+ Composite ,
5
+ CompositeItem ,
4
6
FloatingList ,
5
7
FloatingTree ,
6
8
flip ,
@@ -31,13 +33,15 @@ import {MenuDivider} from './MenuDivider';
31
33
import { MenuItem } from './MenuItem' ;
32
34
import { MenuTrigger } from './MenuTrigger' ;
33
35
import type { MenuProps } from './types' ;
36
+ import { isComponentType } from './utils' ;
34
37
35
38
import './Menu.scss' ;
36
39
37
40
const b = block ( 'menu2' ) ;
38
41
39
42
function MenuComponent ( {
40
43
trigger,
44
+ inline = false ,
41
45
defaultOpen,
42
46
open,
43
47
onOpenChange,
@@ -110,13 +114,53 @@ function MenuComponent({
110
114
? trigger ( anchorProps , anchorRef )
111
115
: null ;
112
116
117
+ const getItemPropsInline = React . useCallback (
118
+ ( userProps ?: React . HTMLAttributes < HTMLElement > ) => {
119
+ const handleItemPointerEnter = ( event : React . PointerEvent < HTMLElement > ) => {
120
+ userProps ?. onPointerEnter ?.( event ) ;
121
+
122
+ const element = event . currentTarget ;
123
+ const index = [
124
+ ...( element . closest ( '[role="menu"]' ) ?. querySelectorAll ( '[role="menuitem"]' ) ??
125
+ [ ] ) ,
126
+ ] . indexOf ( element ) ;
127
+
128
+ if (
129
+ ! ( element as HTMLButtonElement ) . disabled &&
130
+ ! element . ariaDisabled &&
131
+ index >= 0
132
+ ) {
133
+ element . focus ( ) ;
134
+ setActiveIndex ( index ) ;
135
+ } else {
136
+ setActiveIndex ( null ) ;
137
+ }
138
+ } ;
139
+
140
+ const handleItemPointerLeave = ( event : React . PointerEvent < HTMLElement > ) => {
141
+ userProps ?. onPointerLeave ?.( event ) ;
142
+ setActiveIndex ( null ) ;
143
+ } ;
144
+
145
+ return {
146
+ // Clear attribute set by Floating UI Composite (we don't use it)
147
+ 'data-active' : undefined ,
148
+ ...userProps ,
149
+ onPointerEnter : handleItemPointerEnter ,
150
+ onPointerLeave : handleItemPointerLeave ,
151
+ } ;
152
+ } ,
153
+ [ ] ,
154
+ ) ;
155
+
113
156
const contextValue = React . useMemo (
114
157
( ) => ( {
158
+ inline : parentMenu ?. inline ?? inline ,
115
159
size : parentMenu ?. size ?? size ,
116
160
activeIndex,
117
- getItemProps,
161
+ getItemProps : inline ? getItemPropsInline : getItemProps ,
118
162
} ) ,
119
- [ parentMenu , size , activeIndex , getItemProps ] ,
163
+ [ parentMenu , inline , size , activeIndex , getItemPropsInline , getItemProps ] ,
120
164
) ;
121
165
122
166
React . useEffect ( ( ) => {
@@ -161,6 +205,44 @@ function MenuComponent({
161
205
}
162
206
} , [ trigger ] ) ;
163
207
208
+ if ( inline ) {
209
+ const preparedChildren = React . Children . toArray ( children ) . map ( ( child , index ) => {
210
+ if ( ! React . isValidElement ( child ) || ! isComponentType ( child , 'Menu.Item' ) ) {
211
+ return child ;
212
+ }
213
+
214
+ return (
215
+ < CompositeItem
216
+ key = { index }
217
+ render = { ( props ) => React . cloneElement ( child , { ...child . props , ...props } ) }
218
+ />
219
+ ) ;
220
+ } ) ;
221
+
222
+ return (
223
+ < MenuContext . Provider value = { contextValue } >
224
+ < Composite
225
+ render = {
226
+ < div
227
+ role = "menu"
228
+ className = { b ( null , className ) }
229
+ style = { style }
230
+ data-qa = { qa }
231
+ />
232
+ }
233
+ orientation = "vertical"
234
+ loop = { false }
235
+ rtl = { isRTL }
236
+ // @ts -expect-error
237
+ activeIndex = { activeIndex }
238
+ onNavigate = { setActiveIndex }
239
+ >
240
+ { preparedChildren }
241
+ </ Composite >
242
+ </ MenuContext . Provider >
243
+ ) ;
244
+ }
245
+
164
246
return (
165
247
< React . Fragment >
166
248
{ anchorNode }
@@ -189,7 +271,7 @@ function MenuComponent({
189
271
export function Menu ( props : MenuProps ) {
190
272
const parentId = useFloatingParentNodeId ( ) ;
191
273
192
- if ( parentId === null ) {
274
+ if ( ! props . inline && parentId === null ) {
193
275
return (
194
276
< FloatingTree >
195
277
< MenuComponent { ...props } />
@@ -200,6 +282,8 @@ export function Menu(props: MenuProps) {
200
282
return < MenuComponent { ...props } /> ;
201
283
}
202
284
285
+ Menu . displayName = 'Menu' ;
286
+
203
287
Menu . Trigger = MenuTrigger ;
204
288
Menu . Item = MenuItem ;
205
289
Menu . Divider = MenuDivider ;
0 commit comments