@@ -6,15 +6,38 @@ import React, {useCallback, useMemo} from 'react'
6
6
import { AnchoredOverlay } from './AnchoredOverlay'
7
7
import { useProvidedStateOrCreate } from './hooks/useProvidedStateOrCreate'
8
8
import { OverlayProps } from './Overlay'
9
- export interface ActionMenuProps extends Partial < Omit < GroupedListProps , keyof ListPropsBase > > , ListPropsBase {
9
+ import { useProvidedRefOrCreate } from './hooks'
10
+
11
+ interface ActionMenuPropsWithAnchor {
10
12
/**
11
13
* A custom function component used to render the anchor element.
12
14
* Will receive the `anchoredContent` prop as `children` prop.
13
15
* Uses a `Button` by default.
14
16
*/
15
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
- renderAnchor ?: ( props : any ) => JSX . Element
17
+ renderAnchor ?: < T extends React . HTMLAttributes < HTMLElement > > ( props : T ) => JSX . Element
18
+
19
+ /**
20
+ * An override to the internal renderAnchor ref that will be spread on to the renderAnchor,
21
+ * When renderAnchor is defined, this prop will be spread on to the rendAnchor
22
+ * component that is passed in.
23
+ */
24
+ anchorRef ?: React . RefObject < HTMLElement >
25
+ }
26
+
27
+ interface ActionMenuPropsWithoutAnchor {
28
+ /**
29
+ * A custom function component used to render the anchor element.
30
+ * When renderAnchor is null, an anchorRef is required.
31
+ */
32
+ renderAnchor : null
17
33
34
+ /**
35
+ * An override to the internal renderAnchor ref. When renderAnchor is null this can be
36
+ * used to make an anchor that is detached from ActionMenu.
37
+ */
38
+ anchorRef : React . RefObject < HTMLElement >
39
+ }
40
+ interface ActionMenuBaseProps extends Partial < Omit < GroupedListProps , keyof ListPropsBase > > , ListPropsBase {
18
41
/**
19
42
* Content that is passed into the renderAnchor component, which is a button by default.
20
43
*/
@@ -41,13 +64,16 @@ export interface ActionMenuProps extends Partial<Omit<GroupedListProps, keyof Li
41
64
overlayProps ?: Partial < OverlayProps >
42
65
}
43
66
67
+ export type ActionMenuProps = ActionMenuBaseProps & ( ActionMenuPropsWithAnchor | ActionMenuPropsWithoutAnchor )
68
+
44
69
const ActionMenuItem = ( props : ItemProps ) => < Item role = "menuitem" { ...props } />
45
70
46
71
ActionMenuItem . displayName = 'ActionMenu.Item'
47
72
48
73
const ActionMenuBase = ( {
49
74
anchorContent,
50
75
renderAnchor = < T extends ButtonProps > ( props : T ) => < Button { ...props } /> ,
76
+ anchorRef : externalAnchorRef ,
51
77
onAction,
52
78
open,
53
79
setOpen,
@@ -56,19 +82,22 @@ const ActionMenuBase = ({
56
82
...listProps
57
83
} : ActionMenuProps ) : JSX . Element => {
58
84
const [ combinedOpenState , setCombinedOpenState ] = useProvidedStateOrCreate ( open , setOpen , false )
85
+ const anchorRef = useProvidedRefOrCreate ( externalAnchorRef )
59
86
const onOpen = useCallback ( ( ) => setCombinedOpenState ( true ) , [ setCombinedOpenState ] )
60
87
const onClose = useCallback ( ( ) => setCombinedOpenState ( false ) , [ setCombinedOpenState ] )
61
88
62
- const renderMenuAnchor = useCallback (
63
- < T extends React . HTMLAttributes < HTMLElement > > ( props : T ) => {
89
+ const renderMenuAnchor = useMemo ( ( ) => {
90
+ if ( renderAnchor === null ) {
91
+ return null
92
+ }
93
+ return < T extends React . HTMLAttributes < HTMLElement > > ( props : T ) => {
64
94
return renderAnchor ( {
65
95
'aria-label' : 'menu' ,
66
96
children : anchorContent ,
67
97
...props
68
98
} )
69
- } ,
70
- [ anchorContent , renderAnchor ]
71
- )
99
+ }
100
+ } , [ anchorContent , renderAnchor ] )
72
101
73
102
const itemsToRender = useMemo ( ( ) => {
74
103
return items . map ( item => {
@@ -89,6 +118,7 @@ const ActionMenuBase = ({
89
118
return (
90
119
< AnchoredOverlay
91
120
renderAnchor = { renderMenuAnchor }
121
+ anchorRef = { anchorRef }
92
122
open = { combinedOpenState }
93
123
onOpen = { onOpen }
94
124
onClose = { onClose }
0 commit comments