@fluentui-react-native/menu 0.10.0 → 0.12.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/CHANGELOG.json +64 -1
- package/CHANGELOG.md +29 -2
- package/lib/Menu/useMenu.d.ts.map +1 -1
- package/lib/Menu/useMenu.js +5 -1
- package/lib/Menu/useMenu.js.map +1 -1
- package/lib/MenuItem/MenuItem.d.ts.map +1 -1
- package/lib/MenuItem/MenuItem.js +4 -2
- package/lib/MenuItem/MenuItem.js.map +1 -1
- package/lib/MenuItem/MenuItem.styling.d.ts.map +1 -1
- package/lib/MenuItem/MenuItem.styling.js +6 -0
- package/lib/MenuItem/MenuItem.styling.js.map +1 -1
- package/lib/MenuItem/MenuItem.types.d.ts +2 -0
- package/lib/MenuItem/MenuItem.types.d.ts.map +1 -1
- package/lib/MenuItem/MenuItemTokens.d.ts.map +1 -1
- package/lib/MenuItem/MenuItemTokens.js +2 -0
- package/lib/MenuItem/MenuItemTokens.js.map +1 -1
- package/lib/MenuItem/MenuItemTokens.win32.d.ts.map +1 -1
- package/lib/MenuItem/MenuItemTokens.win32.js +2 -0
- package/lib/MenuItem/MenuItemTokens.win32.js.map +1 -1
- package/lib/MenuItem/useMenuItem.d.ts.map +1 -1
- package/lib/MenuItem/useMenuItem.js +4 -2
- package/lib/MenuItem/useMenuItem.js.map +1 -1
- package/lib/MenuItemCheckbox/useMenuItemCheckbox.js +1 -1
- package/lib/MenuItemCheckbox/useMenuItemCheckbox.js.map +1 -1
- package/lib/MenuPopover/MenuPopover.d.ts +1 -2
- package/lib/MenuPopover/MenuPopover.d.ts.map +1 -1
- package/lib/MenuPopover/MenuPopover.js +1 -1
- package/lib/MenuPopover/MenuPopover.js.map +1 -1
- package/lib/MenuPopover/MenuPopover.types.d.ts +5 -4
- package/lib/MenuPopover/MenuPopover.types.d.ts.map +1 -1
- package/lib/MenuPopover/useMenuPopover.d.ts.map +1 -1
- package/lib/MenuPopover/useMenuPopover.js +13 -2
- package/lib/MenuPopover/useMenuPopover.js.map +1 -1
- package/lib/MenuTrigger/MenuTrigger.d.ts.map +1 -1
- package/lib/MenuTrigger/MenuTrigger.js +27 -4
- package/lib/MenuTrigger/MenuTrigger.js.map +1 -1
- package/lib/MenuTrigger/MenuTrigger.types.d.ts +14 -2
- package/lib/MenuTrigger/MenuTrigger.types.d.ts.map +1 -1
- package/lib/MenuTrigger/MenuTrigger.types.js.map +1 -1
- package/lib/MenuTrigger/useMenuTrigger.d.ts +2 -11
- package/lib/MenuTrigger/useMenuTrigger.d.ts.map +1 -1
- package/lib/MenuTrigger/useMenuTrigger.js +29 -1
- package/lib/MenuTrigger/useMenuTrigger.js.map +1 -1
- package/lib/context/menuTriggerContext.d.ts +9 -0
- package/lib/context/menuTriggerContext.d.ts.map +1 -0
- package/lib/context/menuTriggerContext.js +9 -0
- package/lib/context/menuTriggerContext.js.map +1 -0
- package/lib-commonjs/Menu/useMenu.d.ts.map +1 -1
- package/lib-commonjs/Menu/useMenu.js +5 -1
- package/lib-commonjs/Menu/useMenu.js.map +1 -1
- package/lib-commonjs/MenuItem/MenuItem.d.ts.map +1 -1
- package/lib-commonjs/MenuItem/MenuItem.js +3 -1
- package/lib-commonjs/MenuItem/MenuItem.js.map +1 -1
- package/lib-commonjs/MenuItem/MenuItem.styling.d.ts.map +1 -1
- package/lib-commonjs/MenuItem/MenuItem.styling.js +6 -0
- package/lib-commonjs/MenuItem/MenuItem.styling.js.map +1 -1
- package/lib-commonjs/MenuItem/MenuItem.types.d.ts +2 -0
- package/lib-commonjs/MenuItem/MenuItem.types.d.ts.map +1 -1
- package/lib-commonjs/MenuItem/MenuItemTokens.d.ts.map +1 -1
- package/lib-commonjs/MenuItem/MenuItemTokens.js +2 -0
- package/lib-commonjs/MenuItem/MenuItemTokens.js.map +1 -1
- package/lib-commonjs/MenuItem/MenuItemTokens.win32.d.ts.map +1 -1
- package/lib-commonjs/MenuItem/MenuItemTokens.win32.js +2 -0
- package/lib-commonjs/MenuItem/MenuItemTokens.win32.js.map +1 -1
- package/lib-commonjs/MenuItem/useMenuItem.d.ts.map +1 -1
- package/lib-commonjs/MenuItem/useMenuItem.js +4 -2
- package/lib-commonjs/MenuItem/useMenuItem.js.map +1 -1
- package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.js +1 -1
- package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.js.map +1 -1
- package/lib-commonjs/MenuPopover/MenuPopover.d.ts +1 -2
- package/lib-commonjs/MenuPopover/MenuPopover.d.ts.map +1 -1
- package/lib-commonjs/MenuPopover/MenuPopover.js +1 -1
- package/lib-commonjs/MenuPopover/MenuPopover.js.map +1 -1
- package/lib-commonjs/MenuPopover/MenuPopover.types.d.ts +5 -4
- package/lib-commonjs/MenuPopover/MenuPopover.types.d.ts.map +1 -1
- package/lib-commonjs/MenuPopover/useMenuPopover.d.ts.map +1 -1
- package/lib-commonjs/MenuPopover/useMenuPopover.js +12 -1
- package/lib-commonjs/MenuPopover/useMenuPopover.js.map +1 -1
- package/lib-commonjs/MenuTrigger/MenuTrigger.d.ts.map +1 -1
- package/lib-commonjs/MenuTrigger/MenuTrigger.js +25 -3
- package/lib-commonjs/MenuTrigger/MenuTrigger.js.map +1 -1
- package/lib-commonjs/MenuTrigger/MenuTrigger.types.d.ts +14 -2
- package/lib-commonjs/MenuTrigger/MenuTrigger.types.d.ts.map +1 -1
- package/lib-commonjs/MenuTrigger/MenuTrigger.types.js.map +1 -1
- package/lib-commonjs/MenuTrigger/useMenuTrigger.d.ts +2 -11
- package/lib-commonjs/MenuTrigger/useMenuTrigger.d.ts.map +1 -1
- package/lib-commonjs/MenuTrigger/useMenuTrigger.js +30 -1
- package/lib-commonjs/MenuTrigger/useMenuTrigger.js.map +1 -1
- package/lib-commonjs/context/menuTriggerContext.d.ts +9 -0
- package/lib-commonjs/context/menuTriggerContext.d.ts.map +1 -0
- package/lib-commonjs/context/menuTriggerContext.js +14 -0
- package/lib-commonjs/context/menuTriggerContext.js.map +1 -0
- package/package.json +3 -3
- package/src/Menu/useMenu.ts +6 -0
- package/src/MenuItem/MenuItem.styling.ts +7 -0
- package/src/MenuItem/MenuItem.tsx +10 -5
- package/src/MenuItem/MenuItem.types.ts +2 -0
- package/src/MenuItem/MenuItemTokens.ts +2 -0
- package/src/MenuItem/MenuItemTokens.win32.ts +2 -0
- package/src/MenuItem/useMenuItem.ts +4 -2
- package/src/MenuItemCheckbox/useMenuItemCheckbox.ts +1 -1
- package/src/MenuPopover/MenuPopover.tsx +2 -0
- package/src/MenuPopover/MenuPopover.types.ts +5 -3
- package/src/MenuPopover/useMenuPopover.ts +17 -3
- package/src/MenuTrigger/MenuTrigger.tsx +32 -5
- package/src/MenuTrigger/MenuTrigger.types.ts +17 -3
- package/src/MenuTrigger/useMenuTrigger.ts +38 -4
- package/src/context/menuTriggerContext.ts +10 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* This context keeps track of whether a trigger component is for a submenu.
|
|
4
|
+
* This allows the trigger to show a submenu indicator.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MenuTriggerContext: React.Context<boolean>;
|
|
7
|
+
export declare const MenuTriggerProvider: React.Provider<boolean>;
|
|
8
|
+
export declare const useMenuTriggerContext: () => boolean;
|
|
9
|
+
//# sourceMappingURL=menuTriggerContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menuTriggerContext.d.ts","sourceRoot":"","sources":["../../src/context/menuTriggerContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;GAGG;AACH,eAAO,MAAM,kBAAkB,wBAAsC,CAAC;AAEtE,eAAO,MAAM,mBAAmB,yBAA8B,CAAC;AAC/D,eAAO,MAAM,qBAAqB,eAA6C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMenuTriggerContext = exports.MenuTriggerProvider = exports.MenuTriggerContext = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var react_1 = (0, tslib_1.__importDefault)(require("react"));
|
|
6
|
+
/**
|
|
7
|
+
* This context keeps track of whether a trigger component is for a submenu.
|
|
8
|
+
* This allows the trigger to show a submenu indicator.
|
|
9
|
+
*/
|
|
10
|
+
exports.MenuTriggerContext = react_1.default.createContext(false);
|
|
11
|
+
exports.MenuTriggerProvider = exports.MenuTriggerContext.Provider;
|
|
12
|
+
var useMenuTriggerContext = function () { return react_1.default.useContext(exports.MenuTriggerContext); };
|
|
13
|
+
exports.useMenuTriggerContext = useMenuTriggerContext;
|
|
14
|
+
//# sourceMappingURL=menuTriggerContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menuTriggerContext.js","sourceRoot":"","sources":["../../src/context/menuTriggerContext.ts"],"names":[],"mappings":";;;;AAAA,6DAA0B;AAE1B;;;GAGG;AACU,QAAA,kBAAkB,GAAG,eAAK,CAAC,aAAa,CAAU,KAAK,CAAC,CAAC;AAEzD,QAAA,mBAAmB,GAAG,0BAAkB,CAAC,QAAQ,CAAC;AACxD,IAAM,qBAAqB,GAAG,cAAM,OAAA,eAAK,CAAC,UAAU,CAAC,0BAAkB,CAAC,EAApC,CAAoC,CAAC;AAAnE,QAAA,qBAAqB,yBAA8C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluentui-react-native/menu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "A cross-platform Menu component using the Fluent Design System",
|
|
5
5
|
"main": "lib-commonjs/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@fluentui-react-native/adapters": ">=0.8.5 <1.0.0",
|
|
26
|
-
"@fluentui-react-native/callout": ">=0.20.
|
|
26
|
+
"@fluentui-react-native/callout": ">=0.20.5 <1.0.0",
|
|
27
27
|
"@fluentui-react-native/experimental-text": ">=0.9.0 <1.0.0",
|
|
28
28
|
"@fluentui-react-native/framework": "0.7.30",
|
|
29
|
-
"@fluentui-react-native/interactive-hooks": ">=0.
|
|
29
|
+
"@fluentui-react-native/interactive-hooks": ">=0.16.1 <1.0.0",
|
|
30
30
|
"@fluentui-react-native/theme-tokens": ">=0.18.0 <1.0.0",
|
|
31
31
|
"@fluentui-react-native/tokens": ">=0.14.0 <1.0.0",
|
|
32
32
|
"@fluentui-react-native/use-styling": ">=0.8.3 <1.0.0",
|
package/src/Menu/useMenu.ts
CHANGED
|
@@ -10,7 +10,13 @@ export const useMenu = (props: MenuProps): MenuState => {
|
|
|
10
10
|
const isControlled = typeof props.open !== 'undefined';
|
|
11
11
|
const [open, setOpen] = useMenuOpenState(isControlled, props);
|
|
12
12
|
|
|
13
|
+
// Default behaviot for submenu is to open on hover
|
|
14
|
+
// the ...props line below will override this behavior for a submenu
|
|
15
|
+
// or apply openOnHover if passed into a root Menu.
|
|
16
|
+
const openOnHover = isSubmenu;
|
|
17
|
+
|
|
13
18
|
return {
|
|
19
|
+
openOnHover,
|
|
14
20
|
...props,
|
|
15
21
|
open,
|
|
16
22
|
setOpen,
|
|
@@ -47,6 +47,13 @@ export const stylingSettings: UseStylingOptions<MenuItemProps, MenuItemSlotProps
|
|
|
47
47
|
(tokens: MenuItemTokens) => {
|
|
48
48
|
return {
|
|
49
49
|
color: tokens.color,
|
|
50
|
+
height: 16,
|
|
51
|
+
width: 16,
|
|
52
|
+
viewBox:
|
|
53
|
+
'0 0 ' +
|
|
54
|
+
(tokens.submenuIndicatorSize - tokens.submenuIndicatorPadding * 2) +
|
|
55
|
+
' ' +
|
|
56
|
+
(tokens.submenuIndicatorSize - tokens.submenuIndicatorPadding * 2),
|
|
50
57
|
};
|
|
51
58
|
},
|
|
52
59
|
['color'],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @jsx withSlots */
|
|
2
|
-
import { View } from 'react-native';
|
|
2
|
+
import { I18nManager, View } from 'react-native';
|
|
3
3
|
import { SvgXml } from 'react-native-svg';
|
|
4
4
|
import { compose, mergeProps, UseSlots, withSlots } from '@fluentui-react-native/framework';
|
|
5
5
|
import { Text } from '@fluentui-react-native/experimental-text';
|
|
@@ -22,10 +22,15 @@ export const MenuItem = compose<MenuItemType>({
|
|
|
22
22
|
|
|
23
23
|
return (final: MenuItemProps) => {
|
|
24
24
|
const mergedProps = mergeProps(menuItem.props, final);
|
|
25
|
-
const chevronXml =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const chevronXml = I18nManager.isRTL
|
|
26
|
+
? `
|
|
27
|
+
<svg>
|
|
28
|
+
<path fill='currentColor' d='M7.35355 2.14645C7.54882 2.34171 7.54882 2.65829 7.35355 2.85355L4.20711 6L7.35355 9.14645C7.54882 9.34171 7.54882 9.65829 7.35355 9.85355C7.15829 10.0488 6.84171 10.0488 6.64645 9.85355L3.14645 6.35355C2.95118 6.15829 2.95118 5.84171 3.14645 5.64645L6.64645 2.14645C6.84171 1.95118 7.15829 1.95118 7.35355 2.14645Z' />
|
|
29
|
+
</svg>`
|
|
30
|
+
: `
|
|
31
|
+
<svg>
|
|
32
|
+
<path fill='currentColor' d='M4.64645 2.14645C4.45118 2.34171 4.45118 2.65829 4.64645 2.85355L7.79289 6L4.64645 9.14645C4.45118 9.34171 4.45118 9.65829 4.64645 9.85355C4.84171 10.0488 5.15829 10.0488 5.35355 9.85355L8.85355 6.35355C9.04882 6.15829 9.04882 5.84171 8.85355 5.64645L5.35355 2.14645C5.15829 1.95118 4.84171 1.95118 4.64645 2.14645Z' />
|
|
33
|
+
</svg>`;
|
|
29
34
|
|
|
30
35
|
return (
|
|
31
36
|
<Slots.root {...mergedProps}>
|
|
@@ -10,6 +10,8 @@ export const menuItemName = 'MenuItem';
|
|
|
10
10
|
|
|
11
11
|
export interface MenuItemTokens extends LayoutTokens, FontTokens, IBorderTokens, IColorTokens {
|
|
12
12
|
checkmarkSize?: number;
|
|
13
|
+
submenuIndicatorPadding?: number;
|
|
14
|
+
submenuIndicatorSize?: number;
|
|
13
15
|
gap?: number;
|
|
14
16
|
|
|
15
17
|
disabled?: MenuItemTokens;
|
|
@@ -7,6 +7,8 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
|
|
|
7
7
|
backgroundColor: t.colors.neutralBackground1,
|
|
8
8
|
borderRadius: globalTokens.corner.radius.medium,
|
|
9
9
|
checkmarkSize: 16,
|
|
10
|
+
submenuIndicatorPadding: globalTokens.spacing.none,
|
|
11
|
+
submenuIndicatorSize: 16,
|
|
10
12
|
color: t.colors.neutralForeground2,
|
|
11
13
|
fontFamily: t.typography.families.primary,
|
|
12
14
|
fontSize: globalTokens.font.size[300],
|
|
@@ -7,6 +7,8 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
|
|
|
7
7
|
backgroundColor: t.colors.neutralBackground1,
|
|
8
8
|
borderRadius: globalTokens.corner.radius.none,
|
|
9
9
|
checkmarkSize: 16,
|
|
10
|
+
submenuIndicatorPadding: globalTokens.spacing.xxs,
|
|
11
|
+
submenuIndicatorSize: 16,
|
|
10
12
|
color: t.colors.neutralForeground1,
|
|
11
13
|
fontFamily: t.typography.families.primary,
|
|
12
14
|
fontSize: globalTokens.font.size[200],
|
|
@@ -5,6 +5,7 @@ import { memoize } from '@fluentui-react-native/framework';
|
|
|
5
5
|
import { useAsPressable, useKeyProps } from '@fluentui-react-native/interactive-hooks';
|
|
6
6
|
import { useMenuContext } from '../context/menuContext';
|
|
7
7
|
import { useMenuListContext } from '../context/menuListContext';
|
|
8
|
+
import { useMenuTriggerContext } from '../context/menuTriggerContext';
|
|
8
9
|
|
|
9
10
|
export const useMenuItem = (props: MenuItemProps): MenuItemState => {
|
|
10
11
|
// attach the pressable state handlers
|
|
@@ -12,7 +13,8 @@ export const useMenuItem = (props: MenuItemProps): MenuItemState => {
|
|
|
12
13
|
const { onClick, accessibilityState, componentRef = defaultComponentRef, disabled, ...rest } = props;
|
|
13
14
|
const pressable = useAsPressable({ ...rest, disabled, onPress: onClick });
|
|
14
15
|
const onKeyProps = useKeyProps(onClick, ' ', 'Enter');
|
|
15
|
-
const
|
|
16
|
+
const isTrigger = useMenuTriggerContext();
|
|
17
|
+
const hasSubmenu = useMenuContext().isSubmenu && isTrigger;
|
|
16
18
|
const hasCheckmarks = useMenuListContext().hasCheckmarks;
|
|
17
19
|
|
|
18
20
|
return {
|
|
@@ -21,7 +23,7 @@ export const useMenuItem = (props: MenuItemProps): MenuItemState => {
|
|
|
21
23
|
accessible: true,
|
|
22
24
|
accessibilityRole: 'menuitem',
|
|
23
25
|
onAccessibilityTap: props.onAccessibilityTap || props.onClick,
|
|
24
|
-
accessibilityLabel: props.accessibilityLabel,
|
|
26
|
+
accessibilityLabel: props.accessibilityLabel || props.content,
|
|
25
27
|
accessibilityState: getAccessibilityState(disabled, accessibilityState),
|
|
26
28
|
enableFocusRing: true,
|
|
27
29
|
focusable: !disabled,
|
|
@@ -93,7 +93,7 @@ export const useMenuCheckboxInteraction = (
|
|
|
93
93
|
...pressable.props,
|
|
94
94
|
accessible: true,
|
|
95
95
|
accessibilityActions: accessibilityActionsProp,
|
|
96
|
-
accessibilityLabel: props.accessibilityLabel,
|
|
96
|
+
accessibilityLabel: props.accessibilityLabel || props.content,
|
|
97
97
|
accessibilityRole: 'menuitem',
|
|
98
98
|
accessibilityState: getAccessibilityState(disabled, state.checked, accessibilityState),
|
|
99
99
|
enableFocusRing: true,
|
|
@@ -11,12 +11,14 @@ export const MenuPopover = stagedComponent((props: MenuPopoverProps) => {
|
|
|
11
11
|
return (_rest: MenuPopoverProps, children: React.ReactNode) => {
|
|
12
12
|
return (
|
|
13
13
|
<Callout
|
|
14
|
+
accessibilityRole={state.accessibilityRole}
|
|
14
15
|
borderWidth={1}
|
|
15
16
|
borderColor={theme.colors.neutralStrokeAccessible}
|
|
16
17
|
target={state.triggerRef}
|
|
17
18
|
onDismiss={state.onDismiss}
|
|
18
19
|
dismissBehaviors={state.dismissBehaviors}
|
|
19
20
|
setInitialFocus={state.setInitialFocus}
|
|
21
|
+
directionalHint={state.directionalHint}
|
|
20
22
|
doNotTakePointerCapture={state.doNotTakePointerCapture}
|
|
21
23
|
>
|
|
22
24
|
{children}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { DirectionalHint, DismissBehaviors, ICalloutProps } from '@fluentui-react-native/callout';
|
|
2
|
+
import { AccessibilityRole } from 'react-native';
|
|
3
3
|
|
|
4
4
|
export const menuPopoverName = 'MenuPopover';
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export type MenuPopoverProps = ICalloutProps;
|
|
7
7
|
|
|
8
8
|
export interface MenuPopoverState {
|
|
9
|
+
accessibilityRole: AccessibilityRole;
|
|
10
|
+
directionalHint?: DirectionalHint;
|
|
9
11
|
dismissBehaviors: DismissBehaviors[];
|
|
10
12
|
doNotTakePointerCapture: boolean;
|
|
11
13
|
onDismiss: () => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Platform } from 'react-native';
|
|
3
|
-
import { DismissBehaviors } from '@fluentui-react-native/callout';
|
|
2
|
+
import { I18nManager, Platform } from 'react-native';
|
|
3
|
+
import { DirectionalHint, DismissBehaviors } from '@fluentui-react-native/callout';
|
|
4
4
|
import { useMenuContext } from '../context/menuContext';
|
|
5
5
|
import { MenuPopoverProps, MenuPopoverState } from './MenuPopover.types';
|
|
6
6
|
|
|
@@ -13,11 +13,25 @@ export const useMenuPopover = (_props: MenuPopoverProps): MenuPopoverState => {
|
|
|
13
13
|
const dismissBehaviors = context.isControlled
|
|
14
14
|
? (['preventDismissOnKeyDown', 'preventDismissOnClickOutside'] as DismissBehaviors[])
|
|
15
15
|
: undefined;
|
|
16
|
+
const directionalHint = getDirectionalHint(context.isSubmenu, I18nManager.isRTL);
|
|
16
17
|
|
|
17
18
|
// Initial focus behavior differs per platform, Windows platforms move focus
|
|
18
19
|
// automatically onto first element of Callout
|
|
19
20
|
const setInitialFocus = Platform.OS === ('win32' as any) || Platform.OS === 'windows';
|
|
20
21
|
const doNotTakePointerCapture = context.openOnHover;
|
|
22
|
+
const accessibilityRole = 'menu';
|
|
21
23
|
|
|
22
|
-
return { triggerRef, onDismiss, dismissBehaviors, doNotTakePointerCapture, setInitialFocus };
|
|
24
|
+
return { accessibilityRole, triggerRef, onDismiss, directionalHint, dismissBehaviors, doNotTakePointerCapture, setInitialFocus };
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getDirectionalHint = (isSubmenu: boolean, isRtl: boolean): DirectionalHint | undefined => {
|
|
28
|
+
if (!isSubmenu) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (isRtl) {
|
|
33
|
+
return 'leftTopEdge';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 'rightTopEdge';
|
|
23
37
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { stagedComponent } from '@fluentui-react-native/framework';
|
|
3
|
-
import { menuTriggerName, MenuTriggerProps } from './MenuTrigger.types';
|
|
2
|
+
import { memoize, stagedComponent } from '@fluentui-react-native/framework';
|
|
3
|
+
import { menuTriggerName, MenuTriggerProps, MenuTriggerState } from './MenuTrigger.types';
|
|
4
4
|
import { useMenuTrigger } from './useMenuTrigger';
|
|
5
|
+
import { AccessibilityActionEvent } from 'react-native';
|
|
6
|
+
import { MenuTriggerProvider } from '../context/menuTriggerContext';
|
|
5
7
|
|
|
6
8
|
export const MenuTrigger = stagedComponent((props: MenuTriggerProps) => {
|
|
7
|
-
const
|
|
9
|
+
const menuTrigger = useMenuTrigger(props);
|
|
8
10
|
|
|
9
11
|
return (_rest: MenuTriggerProps, children: React.ReactNode) => {
|
|
10
12
|
const childrenArray = React.Children.toArray(children) as React.ReactElement[];
|
|
@@ -15,12 +17,37 @@ export const MenuTrigger = stagedComponent((props: MenuTriggerProps) => {
|
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
// In order to properly support accessibility without erasing props set on the
|
|
21
|
+
// child component which may affect accessibility, we need to modify the
|
|
22
|
+
// state in the inner render so we can access the child component and its props.
|
|
18
23
|
const child = childrenArray[0];
|
|
19
|
-
const
|
|
24
|
+
const revisedState = getRevisedState(menuTrigger, child.props);
|
|
25
|
+
const revised = React.cloneElement(child, revisedState);
|
|
20
26
|
|
|
21
|
-
return
|
|
27
|
+
return <MenuTriggerProvider value={menuTrigger.hasSubmenu}>{revised}</MenuTriggerProvider>;
|
|
22
28
|
};
|
|
23
29
|
});
|
|
24
30
|
MenuTrigger.displayName = menuTriggerName;
|
|
25
31
|
|
|
32
|
+
const getRevisedState = memoize(getRevisedStateWorker);
|
|
33
|
+
function getRevisedStateWorker(state: MenuTriggerState, props: any): MenuTriggerState {
|
|
34
|
+
const revisedState = { ...state };
|
|
35
|
+
if (props.accessibilityState) {
|
|
36
|
+
revisedState.props.accessibilityState = { ...state.props.accessibilityState, ...props.accessibilityState };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (props.accessibilityActions) {
|
|
40
|
+
revisedState.props.accessibilityActions = { ...state.props.accessibilityActions, ...props.accessibilityActions };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (props.onAccessibilityAction) {
|
|
44
|
+
revisedState.props.onAccessibilityAction = (e: AccessibilityActionEvent) => {
|
|
45
|
+
state.props.onAccessibilityAction(e);
|
|
46
|
+
props.onAccessibilityAction(e);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return revisedState;
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
export default MenuTrigger;
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { InteractionEvent, IWithPressableOptions } from '@fluentui-react-native/interactive-hooks';
|
|
2
|
+
import { ViewProps } from 'react-native';
|
|
2
3
|
|
|
3
4
|
export const menuTriggerName = 'MenuTrigger';
|
|
4
5
|
|
|
5
|
-
export interface MenuTriggerProps extends Omit<
|
|
6
|
+
export interface MenuTriggerProps extends Omit<IWithPressableOptions<ViewProps>, 'onPress'> {
|
|
7
|
+
/**
|
|
8
|
+
* A RefObject to refer to the trigger component.
|
|
9
|
+
*/
|
|
10
|
+
componentRef?: React.RefObject<React.Component>;
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* A callback to call on button click event
|
|
14
|
+
*/
|
|
15
|
+
onClick?: (e: InteractionEvent) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MenuTriggerState {
|
|
19
|
+
props: MenuTriggerProps;
|
|
20
|
+
hasSubmenu: boolean;
|
|
21
|
+
}
|
|
@@ -1,15 +1,36 @@
|
|
|
1
1
|
import { useMenuContext } from '../context/menuContext';
|
|
2
2
|
import { InteractionEvent } from '@fluentui-react-native/interactive-hooks';
|
|
3
|
-
import { MenuTriggerProps } from './MenuTrigger.types';
|
|
4
|
-
import { Platform } from 'react-native';
|
|
3
|
+
import { MenuTriggerProps, MenuTriggerState } from './MenuTrigger.types';
|
|
4
|
+
import { AccessibilityActionEvent, AccessibilityActionName, Platform } from 'react-native';
|
|
5
|
+
import React from 'react';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
const accessibilityActions =
|
|
8
|
+
Platform.OS === ('win32' as any) ? [{ name: 'Expand' as AccessibilityActionName }, { name: 'Collapse' as AccessibilityActionName }] : [];
|
|
9
|
+
|
|
10
|
+
export const useMenuTrigger = (_props: MenuTriggerProps): MenuTriggerState => {
|
|
7
11
|
const context = useMenuContext();
|
|
8
12
|
|
|
9
13
|
const setOpen = context.setOpen;
|
|
10
14
|
const open = context.open;
|
|
11
15
|
const openOnHover = context.openOnHover;
|
|
12
16
|
const triggerRef = context.triggerRef;
|
|
17
|
+
const accessibilityState = context.open ? { expanded: true } : { expanded: false };
|
|
18
|
+
const onAccessibilityAction = React.useCallback(
|
|
19
|
+
(e: AccessibilityActionEvent) => {
|
|
20
|
+
if (Platform.OS === ('win32' as any)) {
|
|
21
|
+
switch (e.nativeEvent.actionName) {
|
|
22
|
+
case 'Expand':
|
|
23
|
+
setOpen(e, true /* isOpen */);
|
|
24
|
+
break;
|
|
25
|
+
|
|
26
|
+
case 'Collapse':
|
|
27
|
+
setOpen(e, false /* isOpen */);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
[setOpen],
|
|
33
|
+
);
|
|
13
34
|
|
|
14
35
|
const delayHover = Platform.select({
|
|
15
36
|
macos: 100,
|
|
@@ -32,5 +53,18 @@ export const useMenuTrigger = (_props: MenuTriggerProps) => {
|
|
|
32
53
|
setOpen(e, !open);
|
|
33
54
|
};
|
|
34
55
|
|
|
35
|
-
return {
|
|
56
|
+
return {
|
|
57
|
+
props: {
|
|
58
|
+
onClick,
|
|
59
|
+
onHoverIn,
|
|
60
|
+
onHoverOut: Platform.OS === ('win32' as any) && onHoverOut,
|
|
61
|
+
componentRef: triggerRef,
|
|
62
|
+
delayHoverIn: delayHover,
|
|
63
|
+
delayHoverOut: Platform.OS === ('win32' as any) && delayHover,
|
|
64
|
+
accessibilityState,
|
|
65
|
+
accessibilityActions,
|
|
66
|
+
onAccessibilityAction,
|
|
67
|
+
},
|
|
68
|
+
hasSubmenu: context.isSubmenu,
|
|
69
|
+
};
|
|
36
70
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This context keeps track of whether a trigger component is for a submenu.
|
|
5
|
+
* This allows the trigger to show a submenu indicator.
|
|
6
|
+
*/
|
|
7
|
+
export const MenuTriggerContext = React.createContext<boolean>(false);
|
|
8
|
+
|
|
9
|
+
export const MenuTriggerProvider = MenuTriggerContext.Provider;
|
|
10
|
+
export const useMenuTriggerContext = () => React.useContext(MenuTriggerContext);
|