@react-spectrum/menu 3.16.1-nightly.4321 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ar-AE.main.js +8 -0
- package/dist/ar-AE.main.js.map +1 -0
- package/dist/ar-AE.mjs +10 -0
- package/dist/ar-AE.module.js +10 -0
- package/dist/ar-AE.module.js.map +1 -0
- package/dist/bg-BG.main.js +8 -0
- package/dist/bg-BG.main.js.map +1 -0
- package/dist/bg-BG.mjs +10 -0
- package/dist/bg-BG.module.js +10 -0
- package/dist/bg-BG.module.js.map +1 -0
- package/dist/cs-CZ.main.js +8 -0
- package/dist/cs-CZ.main.js.map +1 -0
- package/dist/cs-CZ.mjs +10 -0
- package/dist/cs-CZ.module.js +10 -0
- package/dist/cs-CZ.module.js.map +1 -0
- package/dist/da-DK.main.js +8 -0
- package/dist/da-DK.main.js.map +1 -0
- package/dist/da-DK.mjs +10 -0
- package/dist/da-DK.module.js +10 -0
- package/dist/da-DK.module.js.map +1 -0
- package/dist/de-DE.main.js +8 -0
- package/dist/de-DE.main.js.map +1 -0
- package/dist/de-DE.mjs +10 -0
- package/dist/de-DE.module.js +10 -0
- package/dist/de-DE.module.js.map +1 -0
- package/dist/el-GR.main.js +8 -0
- package/dist/el-GR.main.js.map +1 -0
- package/dist/el-GR.mjs +10 -0
- package/dist/el-GR.module.js +10 -0
- package/dist/el-GR.module.js.map +1 -0
- package/dist/en-US.main.js +8 -0
- package/dist/en-US.main.js.map +1 -0
- package/dist/en-US.mjs +10 -0
- package/dist/en-US.module.js +10 -0
- package/dist/en-US.module.js.map +1 -0
- package/dist/es-ES.main.js +8 -0
- package/dist/es-ES.main.js.map +1 -0
- package/dist/es-ES.mjs +10 -0
- package/dist/es-ES.module.js +10 -0
- package/dist/es-ES.module.js.map +1 -0
- package/dist/et-EE.main.js +8 -0
- package/dist/et-EE.main.js.map +1 -0
- package/dist/et-EE.mjs +10 -0
- package/dist/et-EE.module.js +10 -0
- package/dist/et-EE.module.js.map +1 -0
- package/dist/fi-FI.main.js +8 -0
- package/dist/fi-FI.main.js.map +1 -0
- package/dist/fi-FI.mjs +10 -0
- package/dist/fi-FI.module.js +10 -0
- package/dist/fi-FI.module.js.map +1 -0
- package/dist/fr-FR.main.js +8 -0
- package/dist/fr-FR.main.js.map +1 -0
- package/dist/fr-FR.mjs +10 -0
- package/dist/fr-FR.module.js +10 -0
- package/dist/fr-FR.module.js.map +1 -0
- package/dist/he-IL.main.js +8 -0
- package/dist/he-IL.main.js.map +1 -0
- package/dist/he-IL.mjs +10 -0
- package/dist/he-IL.module.js +10 -0
- package/dist/he-IL.module.js.map +1 -0
- package/dist/hr-HR.main.js +8 -0
- package/dist/hr-HR.main.js.map +1 -0
- package/dist/hr-HR.mjs +10 -0
- package/dist/hr-HR.module.js +10 -0
- package/dist/hr-HR.module.js.map +1 -0
- package/dist/hu-HU.main.js +8 -0
- package/dist/hu-HU.main.js.map +1 -0
- package/dist/hu-HU.mjs +10 -0
- package/dist/hu-HU.module.js +10 -0
- package/dist/hu-HU.module.js.map +1 -0
- package/dist/import.mjs +458 -332
- package/dist/it-IT.main.js +8 -0
- package/dist/it-IT.main.js.map +1 -0
- package/dist/it-IT.mjs +10 -0
- package/dist/it-IT.module.js +10 -0
- package/dist/it-IT.module.js.map +1 -0
- package/dist/ja-JP.main.js +8 -0
- package/dist/ja-JP.main.js.map +1 -0
- package/dist/ja-JP.mjs +10 -0
- package/dist/ja-JP.module.js +10 -0
- package/dist/ja-JP.module.js.map +1 -0
- package/dist/ko-KR.main.js +8 -0
- package/dist/ko-KR.main.js.map +1 -0
- package/dist/ko-KR.mjs +10 -0
- package/dist/ko-KR.module.js +10 -0
- package/dist/ko-KR.module.js.map +1 -0
- package/dist/lt-LT.main.js +8 -0
- package/dist/lt-LT.main.js.map +1 -0
- package/dist/lt-LT.mjs +10 -0
- package/dist/lt-LT.module.js +10 -0
- package/dist/lt-LT.module.js.map +1 -0
- package/dist/lv-LV.main.js +8 -0
- package/dist/lv-LV.main.js.map +1 -0
- package/dist/lv-LV.mjs +10 -0
- package/dist/lv-LV.module.js +10 -0
- package/dist/lv-LV.module.js.map +1 -0
- package/dist/main.css +545 -1
- package/dist/main.css.map +1 -0
- package/dist/main.js +452 -325
- package/dist/main.js.map +1 -1
- package/dist/module.js +458 -332
- package/dist/module.js.map +1 -1
- package/dist/nb-NO.main.js +8 -0
- package/dist/nb-NO.main.js.map +1 -0
- package/dist/nb-NO.mjs +10 -0
- package/dist/nb-NO.module.js +10 -0
- package/dist/nb-NO.module.js.map +1 -0
- package/dist/nl-NL.main.js +8 -0
- package/dist/nl-NL.main.js.map +1 -0
- package/dist/nl-NL.mjs +10 -0
- package/dist/nl-NL.module.js +10 -0
- package/dist/nl-NL.module.js.map +1 -0
- package/dist/pl-PL.main.js +8 -0
- package/dist/pl-PL.main.js.map +1 -0
- package/dist/pl-PL.mjs +10 -0
- package/dist/pl-PL.module.js +10 -0
- package/dist/pl-PL.module.js.map +1 -0
- package/dist/pt-BR.main.js +8 -0
- package/dist/pt-BR.main.js.map +1 -0
- package/dist/pt-BR.mjs +10 -0
- package/dist/pt-BR.module.js +10 -0
- package/dist/pt-BR.module.js.map +1 -0
- package/dist/pt-PT.main.js +8 -0
- package/dist/pt-PT.main.js.map +1 -0
- package/dist/pt-PT.mjs +10 -0
- package/dist/pt-PT.module.js +10 -0
- package/dist/pt-PT.module.js.map +1 -0
- package/dist/ro-RO.main.js +8 -0
- package/dist/ro-RO.main.js.map +1 -0
- package/dist/ro-RO.mjs +10 -0
- package/dist/ro-RO.module.js +10 -0
- package/dist/ro-RO.module.js.map +1 -0
- package/dist/ru-RU.main.js +8 -0
- package/dist/ru-RU.main.js.map +1 -0
- package/dist/ru-RU.mjs +10 -0
- package/dist/ru-RU.module.js +10 -0
- package/dist/ru-RU.module.js.map +1 -0
- package/dist/sk-SK.main.js +8 -0
- package/dist/sk-SK.main.js.map +1 -0
- package/dist/sk-SK.mjs +10 -0
- package/dist/sk-SK.module.js +10 -0
- package/dist/sk-SK.module.js.map +1 -0
- package/dist/sl-SI.main.js +8 -0
- package/dist/sl-SI.main.js.map +1 -0
- package/dist/sl-SI.mjs +10 -0
- package/dist/sl-SI.module.js +10 -0
- package/dist/sl-SI.module.js.map +1 -0
- package/dist/sr-SP.main.js +8 -0
- package/dist/sr-SP.main.js.map +1 -0
- package/dist/sr-SP.mjs +10 -0
- package/dist/sr-SP.module.js +10 -0
- package/dist/sr-SP.module.js.map +1 -0
- package/dist/sv-SE.main.js +8 -0
- package/dist/sv-SE.main.js.map +1 -0
- package/dist/sv-SE.mjs +10 -0
- package/dist/sv-SE.module.js +10 -0
- package/dist/sv-SE.module.js.map +1 -0
- package/dist/tr-TR.main.js +8 -0
- package/dist/tr-TR.main.js.map +1 -0
- package/dist/tr-TR.mjs +10 -0
- package/dist/tr-TR.module.js +10 -0
- package/dist/tr-TR.module.js.map +1 -0
- package/dist/types.d.ts +13 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/uk-UA.main.js +8 -0
- package/dist/uk-UA.main.js.map +1 -0
- package/dist/uk-UA.mjs +10 -0
- package/dist/uk-UA.module.js +10 -0
- package/dist/uk-UA.module.js.map +1 -0
- package/dist/zh-CN.main.js +8 -0
- package/dist/zh-CN.main.js.map +1 -0
- package/dist/zh-CN.mjs +10 -0
- package/dist/zh-CN.module.js +10 -0
- package/dist/zh-CN.module.js.map +1 -0
- package/dist/zh-TW.main.js +8 -0
- package/dist/zh-TW.main.js.map +1 -0
- package/dist/zh-TW.mjs +10 -0
- package/dist/zh-TW.module.js +10 -0
- package/dist/zh-TW.module.js.map +1 -0
- package/package.json +24 -24
- package/src/ActionMenu.tsx +1 -1
- package/src/ContextualHelpTrigger.tsx +127 -57
- package/src/Menu.tsx +87 -12
- package/src/MenuItem.tsx +27 -22
- package/src/MenuTrigger.tsx +3 -3
- package/src/SubmenuTrigger.tsx +167 -0
- package/src/context.ts +21 -13
- package/src/index.ts +2 -0
|
@@ -10,15 +10,20 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {classNames, SlotProvider, useIsMobileDevice} from '@react-spectrum/utils';
|
|
14
|
-
import {
|
|
13
|
+
import {classNames, SlotProvider, unwrapDOMRef, useIsMobileDevice} from '@react-spectrum/utils';
|
|
14
|
+
import {FocusScope} from '@react-aria/focus';
|
|
15
|
+
import {getInteractionModality} from '@react-aria/interactions';
|
|
15
16
|
import helpStyles from '@adobe/spectrum-css-temp/components/contextualhelp/vars.css';
|
|
16
17
|
import {ItemProps, Key} from '@react-types/shared';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import
|
|
18
|
+
import {Popover} from '@react-spectrum/overlays';
|
|
19
|
+
import React, {JSX, ReactElement, useRef, useState} from 'react';
|
|
20
|
+
import ReactDOM from 'react-dom';
|
|
20
21
|
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
|
|
21
|
-
import {
|
|
22
|
+
import {SubmenuTriggerContext, useMenuStateContext} from './context';
|
|
23
|
+
import {TrayHeaderWrapper} from './Menu';
|
|
24
|
+
import {UNSTABLE_useSubmenuTrigger} from '@react-aria/menu';
|
|
25
|
+
import {UNSTABLE_useSubmenuTriggerState} from '@react-stately/menu';
|
|
26
|
+
import {useLayoutEffect} from '@react-aria/utils';
|
|
22
27
|
|
|
23
28
|
interface MenuDialogTriggerProps {
|
|
24
29
|
/** Whether the menu item is currently unavailable. */
|
|
@@ -34,23 +39,40 @@ interface InternalMenuDialogTriggerProps extends MenuDialogTriggerProps {
|
|
|
34
39
|
export interface SpectrumMenuDialogTriggerProps extends MenuDialogTriggerProps {}
|
|
35
40
|
|
|
36
41
|
function ContextualHelpTrigger(props: InternalMenuDialogTriggerProps): ReactElement {
|
|
37
|
-
let {isUnavailable} = props;
|
|
42
|
+
let {isUnavailable = false, targetKey} = props;
|
|
38
43
|
|
|
39
44
|
let triggerRef = useRef<HTMLLIElement>(null);
|
|
40
45
|
let popoverRef = useRef(null);
|
|
41
|
-
let {
|
|
42
|
-
let
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
let {popoverContainerRef, trayContainerRef, rootMenuTriggerState, menu: parentMenuRef, state} = useMenuStateContext();
|
|
47
|
+
let triggerNode = state.collection.getItem(targetKey);
|
|
48
|
+
let submenuTriggerState = UNSTABLE_useSubmenuTriggerState({triggerKey: targetKey}, {...rootMenuTriggerState, ...state});
|
|
49
|
+
let submenuRef = unwrapDOMRef(popoverRef);
|
|
50
|
+
let {submenuTriggerProps, popoverProps} = UNSTABLE_useSubmenuTrigger({
|
|
51
|
+
node: triggerNode,
|
|
52
|
+
parentMenuRef,
|
|
53
|
+
submenuRef,
|
|
54
|
+
type: 'dialog',
|
|
55
|
+
isDisabled: !isUnavailable
|
|
56
|
+
}, submenuTriggerState, triggerRef);
|
|
49
57
|
let isMobile = useIsMobileDevice();
|
|
50
58
|
let slots = {};
|
|
51
59
|
if (isUnavailable) {
|
|
52
60
|
slots = {
|
|
53
|
-
dialog: {
|
|
61
|
+
dialog: {
|
|
62
|
+
UNSAFE_className: classNames(
|
|
63
|
+
helpStyles,
|
|
64
|
+
'react-spectrum-ContextualHelp-dialog',
|
|
65
|
+
{
|
|
66
|
+
'react-spectrum-ContextualHelp-dialog--isMobile': isMobile
|
|
67
|
+
},
|
|
68
|
+
classNames(
|
|
69
|
+
styles,
|
|
70
|
+
{
|
|
71
|
+
'spectrum-Menu-subdialog': !isMobile
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
},
|
|
54
76
|
content: {UNSAFE_className: helpStyles['react-spectrum-ContextualHelp-content']},
|
|
55
77
|
footer: {UNSAFE_className: helpStyles['react-spectrum-ContextualHelp-footer']}
|
|
56
78
|
};
|
|
@@ -58,62 +80,110 @@ function ContextualHelpTrigger(props: InternalMenuDialogTriggerProps): ReactElem
|
|
|
58
80
|
let [trigger] = React.Children.toArray(props.children);
|
|
59
81
|
let [, content] = props.children as [ReactElement, ReactElement];
|
|
60
82
|
|
|
61
|
-
let onExit = () => {
|
|
62
|
-
// if focus was already moved back to a menu item, don't need to do anything
|
|
63
|
-
if (!menu.current.contains(document.activeElement)) {
|
|
64
|
-
// need to return focus to the trigger because hitting Esc causes focus to go to the subdialog, which is then unmounted
|
|
65
|
-
// this leads to blur never being fired nor focus on the body
|
|
66
|
-
triggerRef.current.focus();
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
83
|
let onBlurWithin = (e) => {
|
|
70
|
-
if (e.relatedTarget && popoverRef.current && !popoverRef
|
|
71
|
-
if (
|
|
72
|
-
|
|
84
|
+
if (e.relatedTarget && popoverRef.current && (!popoverRef?.current?.UNSAFE_getDOMNode()?.contains(e.relatedTarget) && !(e.relatedTarget === triggerRef.current && getInteractionModality() === 'pointer'))) {
|
|
85
|
+
if (submenuTriggerState.isOpen) {
|
|
86
|
+
submenuTriggerState.close();
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
};
|
|
90
|
+
|
|
91
|
+
let overlay;
|
|
92
|
+
let tray;
|
|
93
|
+
let onBackButtonPress = () => {
|
|
94
|
+
submenuTriggerState.close();
|
|
95
|
+
if (parentMenuRef.current && !parentMenuRef.current.contains(document.activeElement)) {
|
|
96
|
+
parentMenuRef.current.focus();
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
let [offset, setOffset] = useState(0);
|
|
100
|
+
useLayoutEffect(() => {
|
|
101
|
+
if (parentMenuRef.current) {
|
|
102
|
+
let offset = window?.getComputedStyle(parentMenuRef?.current)?.getPropertyValue('--spectrum-submenu-offset-distance');
|
|
103
|
+
if (offset !== '') {
|
|
104
|
+
setOffset(-1 * parseInt(offset, 10));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, [parentMenuRef]);
|
|
108
|
+
|
|
109
|
+
if (isMobile) {
|
|
110
|
+
delete submenuTriggerProps.onBlur;
|
|
111
|
+
delete submenuTriggerProps.onHoverChange;
|
|
112
|
+
if (trayContainerRef.current && submenuTriggerState.isOpen) {
|
|
113
|
+
let subDialogKeyDown = (e: KeyboardEvent) => {
|
|
114
|
+
switch (e.key) {
|
|
115
|
+
case 'Escape':
|
|
116
|
+
e.stopPropagation();
|
|
117
|
+
onBackButtonPress();
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
tray = (
|
|
123
|
+
<TrayHeaderWrapper
|
|
124
|
+
isSubmenu
|
|
125
|
+
parentMenuTreeState={state}
|
|
126
|
+
rootMenuTriggerState={rootMenuTriggerState}
|
|
127
|
+
wrapperKeyDown={subDialogKeyDown}
|
|
128
|
+
onBackButtonPress={onBackButtonPress}>
|
|
129
|
+
{content}
|
|
130
|
+
</TrayHeaderWrapper>
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
overlay = ReactDOM.createPortal(tray, trayContainerRef.current);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
let onDismissButtonPress = () => {
|
|
137
|
+
submenuTriggerState.close();
|
|
138
|
+
parentMenuRef.current?.focus();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
overlay = (
|
|
142
|
+
<Popover
|
|
143
|
+
{...popoverProps}
|
|
144
|
+
UNSAFE_style={{clipPath: 'unset', overflow: 'visible', filter: 'unset', borderWidth: '0px'}}
|
|
145
|
+
UNSAFE_className={classNames(styles, 'spectrum-Submenu-popover')}
|
|
146
|
+
onDismissButtonPress={onDismissButtonPress}
|
|
147
|
+
onBlurWithin={onBlurWithin}
|
|
148
|
+
container={popoverContainerRef.current}
|
|
149
|
+
state={submenuTriggerState}
|
|
150
|
+
ref={popoverRef}
|
|
151
|
+
triggerRef={triggerRef}
|
|
152
|
+
placement="end top"
|
|
153
|
+
containerPadding={0}
|
|
154
|
+
crossOffset={offset}
|
|
155
|
+
offset={offset}
|
|
156
|
+
hideArrow
|
|
157
|
+
enableBothDismissButtons>
|
|
158
|
+
<FocusScope restoreFocus>
|
|
159
|
+
{content}
|
|
160
|
+
</FocusScope>
|
|
161
|
+
</Popover>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
76
165
|
return (
|
|
77
166
|
<>
|
|
78
|
-
<
|
|
167
|
+
<SubmenuTriggerContext.Provider value={{isUnavailable, triggerRef, ...submenuTriggerProps}}>{trigger}</SubmenuTriggerContext.Provider>
|
|
79
168
|
<SlotProvider slots={slots}>
|
|
80
|
-
{
|
|
81
|
-
isMobile ? (
|
|
82
|
-
<Modal state={state} isDismissable>
|
|
83
|
-
<DismissButton onDismiss={state.close} />
|
|
84
|
-
{content}
|
|
85
|
-
<DismissButton onDismiss={state.close} />
|
|
86
|
-
</Modal>
|
|
87
|
-
) : (
|
|
88
|
-
<Popover
|
|
89
|
-
UNSAFE_style={{clipPath: 'unset', overflow: 'visible', filter: 'unset', borderWidth: '0px'}}
|
|
90
|
-
onExit={onExit}
|
|
91
|
-
onBlurWithin={onBlurWithin}
|
|
92
|
-
container={container.current}
|
|
93
|
-
state={state}
|
|
94
|
-
ref={popoverRef}
|
|
95
|
-
triggerRef={triggerRef}
|
|
96
|
-
placement="end top"
|
|
97
|
-
offset={-10}
|
|
98
|
-
hideArrow
|
|
99
|
-
isNonModal
|
|
100
|
-
enableBothDismissButtons
|
|
101
|
-
disableFocusManagement>
|
|
102
|
-
{content}
|
|
103
|
-
</Popover>
|
|
104
|
-
)
|
|
105
|
-
}
|
|
169
|
+
{submenuTriggerState.isOpen && overlay}
|
|
106
170
|
</SlotProvider>
|
|
107
171
|
</>
|
|
108
172
|
);
|
|
109
173
|
}
|
|
110
174
|
|
|
111
175
|
ContextualHelpTrigger.getCollectionNode = function* getCollectionNode<T>(props: ItemProps<T>) {
|
|
112
|
-
let [
|
|
176
|
+
let childArray: ReactElement[] = [];
|
|
177
|
+
React.Children.forEach(props.children, child => {
|
|
178
|
+
if (React.isValidElement(child)) {
|
|
179
|
+
childArray.push(child);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
let [trigger] = childArray;
|
|
113
183
|
let [, content] = props.children as [ReactElement, ReactElement];
|
|
114
184
|
|
|
115
185
|
yield {
|
|
116
|
-
element: React.cloneElement(trigger, {...trigger.props, hasChildItems: true}),
|
|
186
|
+
element: React.cloneElement(trigger, {...trigger.props, hasChildItems: true, isTrigger: true}),
|
|
117
187
|
wrapper: (element) => (
|
|
118
188
|
<ContextualHelpTrigger key={element.key} targetKey={element.key} {...props}>
|
|
119
189
|
{element}
|
|
@@ -123,5 +193,5 @@ ContextualHelpTrigger.getCollectionNode = function* getCollectionNode<T>(props:
|
|
|
123
193
|
};
|
|
124
194
|
};
|
|
125
195
|
|
|
126
|
-
let _Item = ContextualHelpTrigger as (props: SpectrumMenuDialogTriggerProps) =>
|
|
196
|
+
let _Item = ContextualHelpTrigger as (props: SpectrumMenuDialogTriggerProps) => JSX.Element;
|
|
127
197
|
export {_Item as ContextualHelpTrigger};
|
package/src/Menu.tsx
CHANGED
|
@@ -10,40 +10,67 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import {ActionButton} from '@react-spectrum/button';
|
|
14
|
+
import ArrowDownSmall from '@spectrum-icons/ui/ArrowDownSmall';
|
|
13
15
|
import {classNames, useDOMRef, useIsMobileDevice, useStyleProps} from '@react-spectrum/utils';
|
|
14
16
|
import {DOMRef} from '@react-types/shared';
|
|
15
17
|
import {FocusScope} from '@react-aria/focus';
|
|
16
|
-
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
import intlMessages from '../intl/*.json';
|
|
20
|
+
import {MenuContext, MenuStateContext, useMenuStateContext} from './context';
|
|
17
21
|
import {MenuItem} from './MenuItem';
|
|
18
22
|
import {MenuSection} from './MenuSection';
|
|
19
|
-
import {mergeProps, useSyncRef} from '@react-aria/utils';
|
|
20
|
-
import React, {ReactElement, useContext, useRef} from 'react';
|
|
23
|
+
import {mergeProps, useSlotId, useSyncRef} from '@react-aria/utils';
|
|
24
|
+
import React, {ReactElement, useContext, useEffect, useRef, useState} from 'react';
|
|
21
25
|
import {SpectrumMenuProps} from '@react-types/menu';
|
|
22
26
|
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
|
|
27
|
+
import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
23
28
|
import {useMenu} from '@react-aria/menu';
|
|
24
29
|
import {useTreeState} from '@react-stately/tree';
|
|
25
30
|
|
|
26
31
|
function Menu<T extends object>(props: SpectrumMenuProps<T>, ref: DOMRef<HTMLDivElement>) {
|
|
32
|
+
let isSubmenu = true;
|
|
27
33
|
let contextProps = useContext(MenuContext);
|
|
34
|
+
let parentMenuContext = useMenuStateContext();
|
|
35
|
+
let {rootMenuTriggerState, state: parentMenuTreeState} = parentMenuContext || {rootMenuTriggerState: contextProps.state};
|
|
36
|
+
if (!parentMenuContext) {
|
|
37
|
+
isSubmenu = false;
|
|
38
|
+
}
|
|
28
39
|
let completeProps = {
|
|
29
40
|
...mergeProps(contextProps, props)
|
|
30
41
|
};
|
|
31
|
-
|
|
32
42
|
let domRef = useDOMRef(ref);
|
|
33
|
-
let
|
|
43
|
+
let popoverContainerRef = useRef(null);
|
|
44
|
+
let trayContainerRef = useRef(null);
|
|
34
45
|
let state = useTreeState(completeProps);
|
|
46
|
+
let submenuRef = useRef<HTMLDivElement>(null);
|
|
35
47
|
let {menuProps} = useMenu(completeProps, state, domRef);
|
|
36
48
|
let {styleProps} = useStyleProps(completeProps);
|
|
37
49
|
useSyncRef(contextProps, domRef);
|
|
38
|
-
let
|
|
50
|
+
let [leftOffset, setLeftOffset] = useState({left: 0});
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (popoverContainerRef.current) {
|
|
53
|
+
let {left} = popoverContainerRef.current.getBoundingClientRect();
|
|
54
|
+
setLeftOffset({left: -1 * left});
|
|
55
|
+
}
|
|
56
|
+
}, []);
|
|
39
57
|
|
|
58
|
+
let menuLevel = contextProps.submenuLevel ?? -1;
|
|
59
|
+
let hasOpenSubmenu = state.collection.getItem(rootMenuTriggerState?.UNSTABLE_expandedKeysStack[menuLevel + 1]) != null;
|
|
60
|
+
// TODO: add slide transition
|
|
40
61
|
return (
|
|
41
|
-
<MenuStateContext.Provider value={{
|
|
42
|
-
<
|
|
43
|
-
|
|
62
|
+
<MenuStateContext.Provider value={{popoverContainerRef, trayContainerRef, menu: domRef, submenu: submenuRef, rootMenuTriggerState, state}}>
|
|
63
|
+
<div style={{height: hasOpenSubmenu ? '100%' : undefined}} ref={trayContainerRef} />
|
|
64
|
+
<FocusScope>
|
|
65
|
+
<TrayHeaderWrapper
|
|
66
|
+
onBackButtonPress={contextProps.onBackButtonPress}
|
|
67
|
+
hasOpenSubmenu={hasOpenSubmenu}
|
|
68
|
+
isSubmenu={isSubmenu}
|
|
69
|
+
parentMenuTreeState={parentMenuTreeState}
|
|
70
|
+
rootMenuTriggerState={rootMenuTriggerState}>
|
|
44
71
|
<div
|
|
45
72
|
{...menuProps}
|
|
46
|
-
{
|
|
73
|
+
style={mergeProps(styleProps.style, menuProps.style)}
|
|
47
74
|
ref={domRef}
|
|
48
75
|
className={
|
|
49
76
|
classNames(
|
|
@@ -78,13 +105,61 @@ function Menu<T extends object>(props: SpectrumMenuProps<T>, ref: DOMRef<HTMLDiv
|
|
|
78
105
|
return menuItem;
|
|
79
106
|
})}
|
|
80
107
|
</div>
|
|
81
|
-
</
|
|
82
|
-
<div ref={
|
|
108
|
+
</TrayHeaderWrapper>
|
|
109
|
+
{rootMenuTriggerState?.isOpen && <div ref={popoverContainerRef} style={{width: '100vw', position: 'absolute', top: -5, ...leftOffset}} /> }
|
|
83
110
|
</FocusScope>
|
|
84
111
|
</MenuStateContext.Provider>
|
|
85
112
|
);
|
|
86
113
|
}
|
|
87
114
|
|
|
115
|
+
export function TrayHeaderWrapper(props) {
|
|
116
|
+
let {children, isSubmenu, hasOpenSubmenu, parentMenuTreeState, rootMenuTriggerState, onBackButtonPress, wrapperKeyDown} = props;
|
|
117
|
+
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/menu');
|
|
118
|
+
let backButtonText = parentMenuTreeState?.collection.getItem(rootMenuTriggerState?.UNSTABLE_expandedKeysStack.slice(-1)[0])?.textValue;
|
|
119
|
+
let backButtonLabel = stringFormatter.format('backButton', {
|
|
120
|
+
prevMenuButton: backButtonText
|
|
121
|
+
});
|
|
122
|
+
let headingId = useSlotId();
|
|
123
|
+
let isMobile = useIsMobileDevice();
|
|
124
|
+
let {direction} = useLocale();
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<>
|
|
128
|
+
<div
|
|
129
|
+
role={headingId ? 'dialog' : undefined}
|
|
130
|
+
aria-labelledby={headingId}
|
|
131
|
+
aria-hidden={isMobile && hasOpenSubmenu}
|
|
132
|
+
data-testid="menu-wrapper"
|
|
133
|
+
className={
|
|
134
|
+
classNames(
|
|
135
|
+
styles,
|
|
136
|
+
'spectrum-Menu-wrapper',
|
|
137
|
+
{
|
|
138
|
+
'spectrum-Menu-wrapper--isMobile': isMobile,
|
|
139
|
+
'is-expanded': hasOpenSubmenu
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
}>
|
|
143
|
+
<div role="presentation" className={classNames(styles, 'spectrum-Submenu-wrapper')} onKeyDown={wrapperKeyDown}>
|
|
144
|
+
{isMobile && isSubmenu && !hasOpenSubmenu && (
|
|
145
|
+
<div className={classNames(styles, 'spectrum-Submenu-headingWrapper')}>
|
|
146
|
+
<ActionButton
|
|
147
|
+
aria-label={backButtonLabel}
|
|
148
|
+
isQuiet
|
|
149
|
+
onPress={onBackButtonPress}>
|
|
150
|
+
{/* We don't have a ArrowLeftSmall so make due with ArrowDownSmall and transforms */}
|
|
151
|
+
{direction === 'rtl' ? <ArrowDownSmall UNSAFE_style={{rotate: '270deg'}} /> : <ArrowDownSmall UNSAFE_style={{rotate: '90deg'}} />}
|
|
152
|
+
</ActionButton>
|
|
153
|
+
<h1 id={headingId} className={classNames(styles, 'spectrum-Submenu-heading')}>{backButtonText}</h1>
|
|
154
|
+
</div>
|
|
155
|
+
)}
|
|
156
|
+
{children}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
88
163
|
/**
|
|
89
164
|
* Menus display a list of actions or options that a user can choose.
|
|
90
165
|
*/
|
package/src/MenuItem.tsx
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import CheckmarkMedium from '@spectrum-icons/ui/CheckmarkMedium';
|
|
14
|
+
import ChevronLeft from '@spectrum-icons/workflow/ChevronLeft';
|
|
15
|
+
import ChevronRight from '@spectrum-icons/workflow/ChevronRight';
|
|
14
16
|
import {classNames, ClearSlots, SlotProvider} from '@react-spectrum/utils';
|
|
15
17
|
import {DOMAttributes, Key, Node} from '@react-types/shared';
|
|
16
18
|
import {FocusRing} from '@react-aria/focus';
|
|
@@ -23,8 +25,8 @@ import React, {useMemo, useRef} from 'react';
|
|
|
23
25
|
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
|
|
24
26
|
import {Text} from '@react-spectrum/text';
|
|
25
27
|
import {TreeState} from '@react-stately/tree';
|
|
26
|
-
import {useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
27
|
-
import {useMenuContext,
|
|
28
|
+
import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
|
|
29
|
+
import {useMenuContext, useSubmenuTriggerContext} from './context';
|
|
28
30
|
import {useMenuItem} from '@react-aria/menu';
|
|
29
31
|
|
|
30
32
|
interface MenuItemProps<T> {
|
|
@@ -42,33 +44,32 @@ export function MenuItem<T>(props: MenuItemProps<T>) {
|
|
|
42
44
|
isVirtualized,
|
|
43
45
|
onAction
|
|
44
46
|
} = props;
|
|
45
|
-
let stringFormatter = useLocalizedStringFormatter(intlMessages);
|
|
46
|
-
let menuDialogContext = useMenuDialogContext();
|
|
47
|
-
let {triggerRef} = menuDialogContext || {};
|
|
48
|
-
let isMenuDialogTrigger = !!menuDialogContext;
|
|
49
|
-
let isUnavailable = false;
|
|
50
|
-
|
|
51
|
-
if (isMenuDialogTrigger) {
|
|
52
|
-
isUnavailable = menuDialogContext.isUnavailable;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let ElementType: React.ElementType = item.props.href ? 'a' : 'div';
|
|
56
|
-
|
|
57
47
|
let {
|
|
58
48
|
closeOnSelect
|
|
59
49
|
} = useMenuContext();
|
|
60
|
-
|
|
61
50
|
let {
|
|
62
51
|
rendered,
|
|
63
52
|
key
|
|
64
53
|
} = item;
|
|
65
54
|
|
|
66
|
-
let
|
|
67
|
-
let
|
|
55
|
+
let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/menu');
|
|
56
|
+
let {direction} = useLocale();
|
|
68
57
|
|
|
58
|
+
let submenuTriggerContext = useSubmenuTriggerContext();
|
|
59
|
+
let {triggerRef, ...submenuTriggerProps} = submenuTriggerContext || {};
|
|
60
|
+
let isSubmenuTrigger = !!submenuTriggerContext;
|
|
61
|
+
let isUnavailable;
|
|
62
|
+
let ElementType: React.ElementType = item.props.href ? 'a' : 'div';
|
|
63
|
+
|
|
64
|
+
if (isSubmenuTrigger) {
|
|
65
|
+
isUnavailable = submenuTriggerContext.isUnavailable;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let isDisabled = state.disabledKeys.has(key);
|
|
69
|
+
let isSelectable = !isSubmenuTrigger && state.selectionManager.selectionMode !== 'none';
|
|
70
|
+
let isSelected = isSelectable && state.selectionManager.isSelected(key);
|
|
69
71
|
let itemref = useRef<any>(null);
|
|
70
72
|
let ref = useObjectRef(useMemo(() => mergeRefs(itemref, triggerRef), [itemref, triggerRef]));
|
|
71
|
-
|
|
72
73
|
let {
|
|
73
74
|
menuItemProps,
|
|
74
75
|
labelProps,
|
|
@@ -83,7 +84,7 @@ export function MenuItem<T>(props: MenuItemProps<T>) {
|
|
|
83
84
|
closeOnSelect,
|
|
84
85
|
isVirtualized,
|
|
85
86
|
onAction,
|
|
86
|
-
|
|
87
|
+
...submenuTriggerProps
|
|
87
88
|
},
|
|
88
89
|
state,
|
|
89
90
|
ref
|
|
@@ -110,8 +111,8 @@ export function MenuItem<T>(props: MenuItemProps<T>) {
|
|
|
110
111
|
{
|
|
111
112
|
'is-disabled': isDisabled,
|
|
112
113
|
'is-selected': isSelected,
|
|
113
|
-
'is-selectable':
|
|
114
|
-
'is-open':
|
|
114
|
+
'is-selectable': isSelectable,
|
|
115
|
+
'is-open': submenuTriggerProps.isOpen
|
|
115
116
|
}
|
|
116
117
|
)}>
|
|
117
118
|
<Grid
|
|
@@ -128,7 +129,8 @@ export function MenuItem<T>(props: MenuItemProps<T>) {
|
|
|
128
129
|
end: {UNSAFE_className: styles['spectrum-Menu-end'], ...endProps},
|
|
129
130
|
icon: {UNSAFE_className: styles['spectrum-Menu-icon'], size: 'S'},
|
|
130
131
|
description: {UNSAFE_className: styles['spectrum-Menu-description'], ...descriptionProps},
|
|
131
|
-
keyboard: {UNSAFE_className: styles['spectrum-Menu-keyboard'], ...keyboardShortcutProps}
|
|
132
|
+
keyboard: {UNSAFE_className: styles['spectrum-Menu-keyboard'], ...keyboardShortcutProps},
|
|
133
|
+
chevron: {UNSAFE_className: styles['spectrum-Menu-chevron'], size: 'S'}
|
|
132
134
|
}}>
|
|
133
135
|
{contents}
|
|
134
136
|
{isSelected &&
|
|
@@ -144,6 +146,9 @@ export function MenuItem<T>(props: MenuItemProps<T>) {
|
|
|
144
146
|
{
|
|
145
147
|
isUnavailable && <InfoOutline slot="end" size="XS" alignSelf="center" aria-label={stringFormatter.format('unavailable')} />
|
|
146
148
|
}
|
|
149
|
+
{
|
|
150
|
+
isUnavailable == null && isSubmenuTrigger && (direction === 'rtl' ? <ChevronLeft slot="chevron" /> : <ChevronRight slot="chevron" />)
|
|
151
|
+
}
|
|
147
152
|
</SlotProvider>
|
|
148
153
|
</ClearSlots>
|
|
149
154
|
</Grid>
|
package/src/MenuTrigger.tsx
CHANGED
|
@@ -58,7 +58,6 @@ function MenuTrigger(props: SpectrumMenuTriggerProps, ref: DOMRef<HTMLElement>)
|
|
|
58
58
|
let isMobile = useIsMobileDevice();
|
|
59
59
|
let menuContext = {
|
|
60
60
|
...menuProps,
|
|
61
|
-
state,
|
|
62
61
|
ref: menuRef,
|
|
63
62
|
onClose: state.close,
|
|
64
63
|
closeOnSelect,
|
|
@@ -67,14 +66,15 @@ function MenuTrigger(props: SpectrumMenuTriggerProps, ref: DOMRef<HTMLElement>)
|
|
|
67
66
|
width: '100%',
|
|
68
67
|
maxHeight: 'inherit'
|
|
69
68
|
} : undefined,
|
|
70
|
-
UNSAFE_className: classNames(styles, {'spectrum-Menu-popover': !isMobile})
|
|
69
|
+
UNSAFE_className: classNames(styles, {'spectrum-Menu-popover': !isMobile}),
|
|
70
|
+
state
|
|
71
71
|
};
|
|
72
72
|
|
|
73
73
|
// On small screen devices, the menu is rendered in a tray, otherwise a popover.
|
|
74
74
|
let overlay;
|
|
75
75
|
if (isMobile) {
|
|
76
76
|
overlay = (
|
|
77
|
-
<Tray state={state}>
|
|
77
|
+
<Tray state={state} isFixedHeight>
|
|
78
78
|
{menu}
|
|
79
79
|
</Tray>
|
|
80
80
|
);
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {classNames, useIsMobileDevice} from '@react-spectrum/utils';
|
|
14
|
+
import {Key} from '@react-types/shared';
|
|
15
|
+
import {MenuContext, SubmenuTriggerContext, useMenuStateContext} from './context';
|
|
16
|
+
import {mergeProps, useLayoutEffect} from '@react-aria/utils';
|
|
17
|
+
import {Popover} from '@react-spectrum/overlays';
|
|
18
|
+
import React, {ReactElement, useRef, useState} from 'react';
|
|
19
|
+
import ReactDOM from 'react-dom';
|
|
20
|
+
import styles from '@adobe/spectrum-css-temp/components/menu/vars.css';
|
|
21
|
+
import {UNSTABLE_useSubmenuTrigger} from '@react-aria/menu';
|
|
22
|
+
import {UNSTABLE_useSubmenuTriggerState} from '@react-stately/menu';
|
|
23
|
+
import {useLocale} from '@react-aria/i18n';
|
|
24
|
+
|
|
25
|
+
interface SubmenuTriggerProps {
|
|
26
|
+
/**
|
|
27
|
+
* The contents of the SubmenuTrigger - a Item and a Menu.
|
|
28
|
+
*/
|
|
29
|
+
children: ReactElement[],
|
|
30
|
+
targetKey: Key
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SpectrumSubmenuTriggerProps extends Omit<SubmenuTriggerProps, 'targetKey'> {}
|
|
34
|
+
|
|
35
|
+
function SubmenuTrigger(props: SubmenuTriggerProps) {
|
|
36
|
+
let triggerRef = useRef<HTMLDivElement>();
|
|
37
|
+
let {
|
|
38
|
+
children,
|
|
39
|
+
targetKey
|
|
40
|
+
} = props;
|
|
41
|
+
|
|
42
|
+
let [menuTrigger, menu] = React.Children.toArray(children);
|
|
43
|
+
let {popoverContainerRef, trayContainerRef, menu: parentMenuRef, submenu: menuRef, rootMenuTriggerState, state} = useMenuStateContext();
|
|
44
|
+
let triggerNode = state.collection.getItem(targetKey);
|
|
45
|
+
let submenuTriggerState = UNSTABLE_useSubmenuTriggerState({triggerKey: targetKey}, rootMenuTriggerState);
|
|
46
|
+
let {submenuTriggerProps, submenuProps, popoverProps} = UNSTABLE_useSubmenuTrigger({
|
|
47
|
+
node: triggerNode,
|
|
48
|
+
parentMenuRef,
|
|
49
|
+
submenuRef: menuRef
|
|
50
|
+
}, submenuTriggerState, triggerRef);
|
|
51
|
+
let isMobile = useIsMobileDevice();
|
|
52
|
+
let onBackButtonPress = () => {
|
|
53
|
+
submenuTriggerState.close();
|
|
54
|
+
if (parentMenuRef.current && !parentMenuRef.current.contains(document.activeElement)) {
|
|
55
|
+
parentMenuRef.current.focus();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
let {direction} = useLocale();
|
|
60
|
+
let mobileSubmenuKeyDown = (e: KeyboardEvent) => {
|
|
61
|
+
switch (e.key) {
|
|
62
|
+
case 'ArrowLeft':
|
|
63
|
+
if (direction === 'ltr') {
|
|
64
|
+
triggerRef.current.focus();
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
case 'ArrowRight':
|
|
68
|
+
if (direction === 'rtl') {
|
|
69
|
+
triggerRef.current.focus();
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let overlay;
|
|
76
|
+
let [offset, setOffset] = useState(0);
|
|
77
|
+
useLayoutEffect(() => {
|
|
78
|
+
if (parentMenuRef.current) {
|
|
79
|
+
let offset = window?.getComputedStyle(parentMenuRef?.current)?.getPropertyValue('--spectrum-submenu-offset-distance');
|
|
80
|
+
if (offset !== '') {
|
|
81
|
+
setOffset(-1 * parseInt(offset, 10));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, [parentMenuRef]);
|
|
85
|
+
|
|
86
|
+
if (isMobile) {
|
|
87
|
+
delete submenuTriggerProps.onBlur;
|
|
88
|
+
delete submenuTriggerProps.onHoverChange;
|
|
89
|
+
submenuProps.autoFocus ??= true;
|
|
90
|
+
if (trayContainerRef.current && submenuTriggerState.isOpen) {
|
|
91
|
+
overlay = ReactDOM.createPortal(menu, trayContainerRef.current);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
let onDismissButtonPress = () => {
|
|
95
|
+
submenuTriggerState.close();
|
|
96
|
+
parentMenuRef.current?.focus();
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
overlay = (
|
|
100
|
+
<Popover
|
|
101
|
+
{...popoverProps}
|
|
102
|
+
onDismissButtonPress={onDismissButtonPress}
|
|
103
|
+
UNSAFE_className={classNames(styles, 'spectrum-Submenu-popover')}
|
|
104
|
+
container={popoverContainerRef.current}
|
|
105
|
+
containerPadding={0}
|
|
106
|
+
crossOffset={offset}
|
|
107
|
+
offset={offset}
|
|
108
|
+
enableBothDismissButtons
|
|
109
|
+
UNSAFE_style={{clipPath: 'unset', overflow: 'visible', borderWidth: '0px'}}
|
|
110
|
+
state={submenuTriggerState}
|
|
111
|
+
triggerRef={triggerRef}
|
|
112
|
+
scrollRef={menuRef}
|
|
113
|
+
placement="end top"
|
|
114
|
+
hideArrow>
|
|
115
|
+
{menu}
|
|
116
|
+
</Popover>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let menuContext = {
|
|
121
|
+
...mergeProps(submenuProps, {
|
|
122
|
+
ref: menuRef,
|
|
123
|
+
UNSAFE_style: isMobile ? {
|
|
124
|
+
width: '100%',
|
|
125
|
+
maxHeight: 'inherit'
|
|
126
|
+
} : undefined,
|
|
127
|
+
UNSAFE_className: classNames(styles, {'spectrum-Menu-popover': !isMobile}),
|
|
128
|
+
...(isMobile && {
|
|
129
|
+
onBackButtonPress,
|
|
130
|
+
onKeyDown: mobileSubmenuKeyDown
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<>
|
|
137
|
+
<SubmenuTriggerContext.Provider value={{triggerRef, ...submenuTriggerProps}}>{menuTrigger}</SubmenuTriggerContext.Provider>
|
|
138
|
+
<MenuContext.Provider value={menuContext}>
|
|
139
|
+
{overlay}
|
|
140
|
+
</MenuContext.Provider>
|
|
141
|
+
</>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
SubmenuTrigger.getCollectionNode = function* (props: SpectrumSubmenuTriggerProps) {
|
|
146
|
+
let childArray: ReactElement[] = [];
|
|
147
|
+
React.Children.forEach(props.children, child => {
|
|
148
|
+
if (React.isValidElement(child)) {
|
|
149
|
+
childArray.push(child);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
let [trigger] = childArray;
|
|
153
|
+
let [, content] = props.children as [ReactElement, ReactElement];
|
|
154
|
+
|
|
155
|
+
yield {
|
|
156
|
+
element: React.cloneElement(trigger, {...trigger.props, hasChildItems: true, isTrigger: true}),
|
|
157
|
+
wrapper: (element) => (
|
|
158
|
+
<SubmenuTrigger key={element.key} targetKey={element.key} {...props}>
|
|
159
|
+
{element}
|
|
160
|
+
{content}
|
|
161
|
+
</SubmenuTrigger>
|
|
162
|
+
)
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
let _SubmenuTrigger = SubmenuTrigger as (props: SpectrumSubmenuTriggerProps) => JSX.Element;
|
|
167
|
+
export {_SubmenuTrigger as SubmenuTrigger};
|