@fluentui-react-native/menu 0.9.3 → 0.10.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 +72 -1
- package/CHANGELOG.md +22 -2
- package/lib/MenuItemCheckbox/MenuItemCheckbox.d.ts +4 -2
- package/lib/MenuItemCheckbox/MenuItemCheckbox.d.ts.map +1 -1
- package/lib/MenuItemCheckbox/MenuItemCheckbox.js +11 -8
- package/lib/MenuItemCheckbox/MenuItemCheckbox.js.map +1 -1
- package/lib/MenuItemCheckbox/useMenuItemCheckbox.d.ts +10 -0
- package/lib/MenuItemCheckbox/useMenuItemCheckbox.d.ts.map +1 -1
- package/lib/MenuItemCheckbox/useMenuItemCheckbox.js +28 -14
- package/lib/MenuItemCheckbox/useMenuItemCheckbox.js.map +1 -1
- package/lib/MenuItemRadio/MenuItemRadio.d.ts +4 -0
- package/lib/MenuItemRadio/MenuItemRadio.d.ts.map +1 -0
- package/lib/MenuItemRadio/MenuItemRadio.js +12 -0
- package/lib/MenuItemRadio/MenuItemRadio.js.map +1 -0
- package/lib/MenuItemRadio/useMenuItemRadio.d.ts +3 -0
- package/lib/MenuItemRadio/useMenuItemRadio.d.ts.map +1 -0
- package/lib/MenuItemRadio/useMenuItemRadio.js +15 -0
- package/lib/MenuItemRadio/useMenuItemRadio.js.map +1 -0
- package/lib/MenuList/MenuList.types.d.ts +1 -0
- package/lib/MenuList/MenuList.types.d.ts.map +1 -1
- package/lib/MenuList/useMenuList.d.ts.map +1 -1
- package/lib/MenuList/useMenuList.js +13 -3
- package/lib/MenuList/useMenuList.js.map +1 -1
- 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 +2 -0
- package/lib/MenuPopover/MenuPopover.types.d.ts.map +1 -1
- package/lib/MenuPopover/useMenuPopover.d.ts.map +1 -1
- package/lib/MenuPopover/useMenuPopover.js +6 -1
- package/lib/MenuPopover/useMenuPopover.js.map +1 -1
- package/lib/MenuTrigger/useMenuTrigger.d.ts +3 -0
- package/lib/MenuTrigger/useMenuTrigger.d.ts.map +1 -1
- package/lib/MenuTrigger/useMenuTrigger.js +11 -1
- package/lib/MenuTrigger/useMenuTrigger.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.d.ts +4 -2
- package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.d.ts.map +1 -1
- package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.js +12 -8
- package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.js.map +1 -1
- package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.d.ts +10 -0
- package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.d.ts.map +1 -1
- package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.js +31 -16
- package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.js.map +1 -1
- package/lib-commonjs/MenuItemRadio/MenuItemRadio.d.ts +4 -0
- package/lib-commonjs/MenuItemRadio/MenuItemRadio.d.ts.map +1 -0
- package/lib-commonjs/MenuItemRadio/MenuItemRadio.js +15 -0
- package/lib-commonjs/MenuItemRadio/MenuItemRadio.js.map +1 -0
- package/lib-commonjs/MenuItemRadio/useMenuItemRadio.d.ts +3 -0
- package/lib-commonjs/MenuItemRadio/useMenuItemRadio.d.ts.map +1 -0
- package/lib-commonjs/MenuItemRadio/useMenuItemRadio.js +20 -0
- package/lib-commonjs/MenuItemRadio/useMenuItemRadio.js.map +1 -0
- package/lib-commonjs/MenuList/MenuList.types.d.ts +1 -0
- package/lib-commonjs/MenuList/MenuList.types.d.ts.map +1 -1
- package/lib-commonjs/MenuList/useMenuList.d.ts.map +1 -1
- package/lib-commonjs/MenuList/useMenuList.js +13 -3
- package/lib-commonjs/MenuList/useMenuList.js.map +1 -1
- 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 +2 -0
- 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 +6 -1
- package/lib-commonjs/MenuPopover/useMenuPopover.js.map +1 -1
- package/lib-commonjs/MenuTrigger/useMenuTrigger.d.ts +3 -0
- package/lib-commonjs/MenuTrigger/useMenuTrigger.d.ts.map +1 -1
- package/lib-commonjs/MenuTrigger/useMenuTrigger.js +11 -1
- package/lib-commonjs/MenuTrigger/useMenuTrigger.js.map +1 -1
- package/lib-commonjs/index.d.ts +1 -0
- package/lib-commonjs/index.d.ts.map +1 -1
- package/lib-commonjs/index.js +3 -1
- package/lib-commonjs/index.js.map +1 -1
- package/package.json +7 -7
- package/src/MenuItemCheckbox/MenuItemCheckbox.tsx +29 -16
- package/src/MenuItemCheckbox/useMenuItemCheckbox.ts +39 -20
- package/src/MenuItemRadio/MenuItemRadio.tsx +16 -0
- package/src/MenuItemRadio/useMenuItemRadio.ts +21 -0
- package/src/MenuList/MenuList.types.ts +1 -0
- package/src/MenuList/useMenuList.ts +22 -3
- package/src/MenuPopover/MenuPopover.tsx +2 -0
- package/src/MenuPopover/MenuPopover.types.ts +2 -0
- package/src/MenuPopover/useMenuPopover.ts +7 -1
- package/src/MenuTrigger/useMenuTrigger.ts +13 -1
- package/src/index.ts +1 -0
|
@@ -14,7 +14,41 @@ import { useMenuListContext } from '../context/menuListContext';
|
|
|
14
14
|
const defaultAccessibilityActions = [{ name: 'Toggle' }];
|
|
15
15
|
|
|
16
16
|
export const useMenuItemCheckbox = (props: MenuItemCheckboxProps): MenuItemCheckboxState => {
|
|
17
|
-
|
|
17
|
+
const { name } = props;
|
|
18
|
+
const context = useMenuListContext();
|
|
19
|
+
const checked = context.checked?.[name];
|
|
20
|
+
const onCheckedChange = context.onCheckedChange;
|
|
21
|
+
|
|
22
|
+
const toggleChecked = React.useCallback(
|
|
23
|
+
(e: InteractionEvent) => {
|
|
24
|
+
onCheckedChange(e, name, !checked);
|
|
25
|
+
},
|
|
26
|
+
[checked, name, onCheckedChange],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return useMenuCheckboxInteraction(props, toggleChecked);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getAccessibilityState = memoize(getAccessibilityStateWorker);
|
|
33
|
+
function getAccessibilityStateWorker(disabled: boolean, checked: boolean, accessibilityState?: AccessibilityState) {
|
|
34
|
+
if (accessibilityState) {
|
|
35
|
+
return { disabled, checked, ...accessibilityState };
|
|
36
|
+
}
|
|
37
|
+
return { disabled, checked };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create interactivity and accessibility props to be passed into the inner render.
|
|
42
|
+
* This logic is shared between Checkbox and Radio versions of MenuItem.
|
|
43
|
+
*
|
|
44
|
+
* @param props Props passed into the outer compoennt
|
|
45
|
+
* @param toggleCallback Function to be called when item is toggled
|
|
46
|
+
* @returns Props and additional state needed to render the component
|
|
47
|
+
*/
|
|
48
|
+
export const useMenuCheckboxInteraction = (
|
|
49
|
+
props: MenuItemCheckboxProps,
|
|
50
|
+
toggleCallback: (e: InteractionEvent) => void,
|
|
51
|
+
): MenuItemCheckboxState => {
|
|
18
52
|
const defaultComponentRef = React.useRef(null);
|
|
19
53
|
const {
|
|
20
54
|
accessibilityActions,
|
|
@@ -27,32 +61,25 @@ export const useMenuItemCheckbox = (props: MenuItemCheckboxProps): MenuItemCheck
|
|
|
27
61
|
} = props;
|
|
28
62
|
const context = useMenuListContext();
|
|
29
63
|
const checked = context.checked?.[name];
|
|
30
|
-
const onCheckedChange = context.onCheckedChange;
|
|
31
64
|
|
|
32
|
-
const toggleChecked = React.useCallback(
|
|
33
|
-
(e: InteractionEvent) => {
|
|
34
|
-
onCheckedChange(e, name, !checked);
|
|
35
|
-
},
|
|
36
|
-
[checked, name, onCheckedChange],
|
|
37
|
-
);
|
|
38
65
|
// Ensure focus is placed on checkbox after click
|
|
39
|
-
const toggleCheckedWithFocus = useOnPressWithFocus(componentRef,
|
|
66
|
+
const toggleCheckedWithFocus = useOnPressWithFocus(componentRef, toggleCallback);
|
|
40
67
|
|
|
41
68
|
const pressable = useAsPressable({ onPress: toggleCheckedWithFocus, ...rest });
|
|
42
69
|
const buttonRef = useViewCommandFocus(componentRef);
|
|
43
70
|
|
|
44
|
-
const onKeyProps = useKeyProps(
|
|
71
|
+
const onKeyProps = useKeyProps(toggleCallback, ' ');
|
|
45
72
|
const accessibilityActionsProp = accessibilityActions
|
|
46
73
|
? [...defaultAccessibilityActions, ...accessibilityActions]
|
|
47
74
|
: defaultAccessibilityActions;
|
|
48
75
|
const onAccessibilityActionProp = React.useCallback(
|
|
49
76
|
(event: AccessibilityActionEvent) => {
|
|
50
77
|
if (event.nativeEvent.actionName === 'Toggle') {
|
|
51
|
-
|
|
78
|
+
toggleCallback(event);
|
|
52
79
|
}
|
|
53
80
|
onAccessibilityAction && onAccessibilityAction(event);
|
|
54
81
|
},
|
|
55
|
-
[
|
|
82
|
+
[toggleCallback, onAccessibilityAction],
|
|
56
83
|
);
|
|
57
84
|
|
|
58
85
|
const state = {
|
|
@@ -78,11 +105,3 @@ export const useMenuItemCheckbox = (props: MenuItemCheckboxProps): MenuItemCheck
|
|
|
78
105
|
state: state,
|
|
79
106
|
};
|
|
80
107
|
};
|
|
81
|
-
|
|
82
|
-
const getAccessibilityState = memoize(getAccessibilityStateWorker);
|
|
83
|
-
function getAccessibilityStateWorker(disabled: boolean, checked: boolean, accessibilityState?: AccessibilityState) {
|
|
84
|
-
if (accessibilityState) {
|
|
85
|
-
return { disabled, checked, ...accessibilityState };
|
|
86
|
-
}
|
|
87
|
-
return { disabled, checked };
|
|
88
|
-
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { UseSlots } from '@fluentui-react-native/framework';
|
|
2
|
+
import { useMenuItemRadio } from './useMenuItemRadio';
|
|
3
|
+
import { MenuItemCheckbox, menuItemFinalRender } from '../MenuItemCheckbox/MenuItemCheckbox';
|
|
4
|
+
import { MenuItemCheckboxProps, MenuItemCheckboxType } from '../MenuItemCheckbox/MenuItemCheckbox.types';
|
|
5
|
+
|
|
6
|
+
export const menuItemRadioName = 'MenuItemRadio';
|
|
7
|
+
|
|
8
|
+
export const MenuItemRadio = MenuItemCheckbox.compose({
|
|
9
|
+
displayName: menuItemRadioName,
|
|
10
|
+
useRender: (userProps: MenuItemCheckboxProps, useSlots: UseSlots<MenuItemCheckboxType>) => {
|
|
11
|
+
const menuItem = useMenuItemRadio(userProps);
|
|
12
|
+
const Slots = useSlots(userProps, (layer): boolean => menuItem.state[layer]);
|
|
13
|
+
|
|
14
|
+
return menuItemFinalRender(menuItem, Slots);
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { InteractionEvent } from '@fluentui-react-native/interactive-hooks';
|
|
3
|
+
import { useMenuListContext } from '../context/menuListContext';
|
|
4
|
+
import { MenuItemCheckboxProps, MenuItemCheckboxState } from '../MenuItemCheckbox/MenuItemCheckbox.types';
|
|
5
|
+
import { useMenuCheckboxInteraction } from '../MenuItemCheckbox/useMenuItemCheckbox';
|
|
6
|
+
|
|
7
|
+
export const useMenuItemRadio = (props: MenuItemCheckboxProps): MenuItemCheckboxState => {
|
|
8
|
+
const { name } = props;
|
|
9
|
+
const context = useMenuListContext();
|
|
10
|
+
const checked = context.checked?.[name];
|
|
11
|
+
const selectRadio = context.selectRadio;
|
|
12
|
+
|
|
13
|
+
const toggleChecked = React.useCallback(
|
|
14
|
+
(e: InteractionEvent) => {
|
|
15
|
+
selectRadio(e, name, !checked);
|
|
16
|
+
},
|
|
17
|
+
[checked, name, selectRadio],
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
return useMenuCheckboxInteraction(props, toggleChecked);
|
|
21
|
+
};
|
|
@@ -17,6 +17,7 @@ export interface MenuListProps extends Omit<IViewProps, 'onPress'> {
|
|
|
17
17
|
|
|
18
18
|
export interface MenuListState extends MenuListProps {
|
|
19
19
|
isCheckedControlled: boolean;
|
|
20
|
+
selectRadio?: (e: InteractionEvent, name: string, isChecked: boolean) => void;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface MenuListSlotProps {
|
|
@@ -9,20 +9,25 @@ export const useMenuList = (_props: MenuListProps): MenuListState => {
|
|
|
9
9
|
// MenuList v2 needs to be able to be standalone, but this is not in scope for v1
|
|
10
10
|
// Assuming that checked information will come from parent Menu
|
|
11
11
|
const isCheckedControlled = typeof context.checked !== 'undefined';
|
|
12
|
-
const [checked, onCheckedChange] = useMenuCheckedState(isCheckedControlled, context);
|
|
12
|
+
const [checked, onCheckedChange, selectRadio] = useMenuCheckedState(isCheckedControlled, context);
|
|
13
13
|
|
|
14
14
|
return {
|
|
15
15
|
...context,
|
|
16
16
|
isCheckedControlled,
|
|
17
17
|
checked,
|
|
18
18
|
onCheckedChange,
|
|
19
|
+
selectRadio,
|
|
19
20
|
};
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
const useMenuCheckedState = (
|
|
23
24
|
isControlled: boolean,
|
|
24
25
|
props: MenuListProps,
|
|
25
|
-
): [
|
|
26
|
+
): [
|
|
27
|
+
Record<string, boolean>,
|
|
28
|
+
(e: InteractionEvent, name: string, isChecked: boolean) => void,
|
|
29
|
+
(e: InteractionEvent, name: string, isChecked: boolean) => void,
|
|
30
|
+
] => {
|
|
26
31
|
const { defaultChecked, onCheckedChange, checked } = props;
|
|
27
32
|
const initialState = defaultChecked ?? checked ?? {};
|
|
28
33
|
const [checkedInternal, setCheckedInternal] = React.useState<Record<string, boolean>>(initialState);
|
|
@@ -45,5 +50,19 @@ const useMenuCheckedState = (
|
|
|
45
50
|
[isControlled, state, onCheckedChange, setCheckedInternal],
|
|
46
51
|
);
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
const selectRadio = React.useCallback(
|
|
54
|
+
(e: InteractionEvent, name: string, isChecked: boolean) => {
|
|
55
|
+
if (!isControlled) {
|
|
56
|
+
const updatedChecked = { [name]: true };
|
|
57
|
+
setCheckedInternal(updatedChecked);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (onCheckedChange) {
|
|
61
|
+
onCheckedChange(e, name, isChecked);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[isControlled, onCheckedChange, setCheckedInternal],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return [state, setChecked, selectRadio];
|
|
49
68
|
};
|
|
@@ -16,6 +16,8 @@ export const MenuPopover = stagedComponent((props: MenuPopoverProps) => {
|
|
|
16
16
|
target={state.triggerRef}
|
|
17
17
|
onDismiss={state.onDismiss}
|
|
18
18
|
dismissBehaviors={state.dismissBehaviors}
|
|
19
|
+
setInitialFocus={state.setInitialFocus}
|
|
20
|
+
doNotTakePointerCapture={state.doNotTakePointerCapture}
|
|
19
21
|
>
|
|
20
22
|
{children}
|
|
21
23
|
</Callout>
|
|
@@ -7,6 +7,8 @@ export interface MenuPopoverProps extends Omit<IViewProps, 'onPress'> {}
|
|
|
7
7
|
|
|
8
8
|
export interface MenuPopoverState {
|
|
9
9
|
dismissBehaviors: DismissBehaviors[];
|
|
10
|
+
doNotTakePointerCapture: boolean;
|
|
10
11
|
onDismiss: () => void;
|
|
12
|
+
setInitialFocus: boolean;
|
|
11
13
|
triggerRef: React.RefObject<React.Component>;
|
|
12
14
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
2
3
|
import { DismissBehaviors } from '@fluentui-react-native/callout';
|
|
3
4
|
import { useMenuContext } from '../context/menuContext';
|
|
4
5
|
import { MenuPopoverProps, MenuPopoverState } from './MenuPopover.types';
|
|
@@ -13,5 +14,10 @@ export const useMenuPopover = (_props: MenuPopoverProps): MenuPopoverState => {
|
|
|
13
14
|
? (['preventDismissOnKeyDown', 'preventDismissOnClickOutside'] as DismissBehaviors[])
|
|
14
15
|
: undefined;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
// Initial focus behavior differs per platform, Windows platforms move focus
|
|
18
|
+
// automatically onto first element of Callout
|
|
19
|
+
const setInitialFocus = Platform.OS === ('win32' as any) || Platform.OS === 'windows';
|
|
20
|
+
const doNotTakePointerCapture = context.openOnHover;
|
|
21
|
+
|
|
22
|
+
return { triggerRef, onDismiss, dismissBehaviors, doNotTakePointerCapture, setInitialFocus };
|
|
17
23
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMenuContext } from '../context/menuContext';
|
|
2
2
|
import { InteractionEvent } from '@fluentui-react-native/interactive-hooks';
|
|
3
3
|
import { MenuTriggerProps } from './MenuTrigger.types';
|
|
4
|
+
import { Platform } from 'react-native';
|
|
4
5
|
|
|
5
6
|
export const useMenuTrigger = (_props: MenuTriggerProps) => {
|
|
6
7
|
const context = useMenuContext();
|
|
@@ -10,15 +11,26 @@ export const useMenuTrigger = (_props: MenuTriggerProps) => {
|
|
|
10
11
|
const openOnHover = context.openOnHover;
|
|
11
12
|
const triggerRef = context.triggerRef;
|
|
12
13
|
|
|
14
|
+
const delayHover = Platform.select({
|
|
15
|
+
macos: 100,
|
|
16
|
+
default: 500, // win32
|
|
17
|
+
});
|
|
18
|
+
|
|
13
19
|
const onHoverIn = (e: InteractionEvent) => {
|
|
14
20
|
if (openOnHover) {
|
|
15
21
|
setOpen(e, true /* isOpen */);
|
|
16
22
|
}
|
|
17
23
|
};
|
|
18
24
|
|
|
25
|
+
const onHoverOut = (e: InteractionEvent) => {
|
|
26
|
+
if (openOnHover) {
|
|
27
|
+
setOpen(e, false /* isOpen */);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
19
31
|
const onClick = (e: InteractionEvent) => {
|
|
20
32
|
setOpen(e, !open);
|
|
21
33
|
};
|
|
22
34
|
|
|
23
|
-
return { onClick, onHoverIn, componentRef: triggerRef };
|
|
35
|
+
return { onClick, onHoverIn, onHoverOut, componentRef: triggerRef, delayHoverIn: delayHover, delayHoverOut: delayHover };
|
|
24
36
|
};
|
package/src/index.ts
CHANGED
|
@@ -3,5 +3,6 @@ export { MenuTrigger } from './MenuTrigger/MenuTrigger';
|
|
|
3
3
|
export { MenuPopover } from './MenuPopover/MenuPopover';
|
|
4
4
|
export { MenuItem } from './MenuItem/MenuItem';
|
|
5
5
|
export { MenuItemCheckbox } from './MenuItemCheckbox/MenuItemCheckbox';
|
|
6
|
+
export { MenuItemRadio } from './MenuItemRadio/MenuItemRadio';
|
|
6
7
|
export { MenuList } from './MenuList/MenuList';
|
|
7
8
|
export { MenuDivider } from './MenuDivider/MenuDivider';
|