@fluentui-react-native/menu 1.4.13 → 1.4.15

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.
Files changed (134) hide show
  1. package/CHANGELOG.json +48 -6
  2. package/CHANGELOG.md +20 -2
  3. package/lib/MenuItem/MenuItem.d.ts.map +1 -1
  4. package/lib/MenuItem/MenuItem.js +9 -2
  5. package/lib/MenuItem/MenuItem.js.map +1 -1
  6. package/lib/MenuItem/MenuItem.styling.d.ts.map +1 -1
  7. package/lib/MenuItem/MenuItem.styling.js +13 -0
  8. package/lib/MenuItem/MenuItem.styling.js.map +1 -1
  9. package/lib/MenuItem/MenuItem.types.d.ts +9 -1
  10. package/lib/MenuItem/MenuItem.types.d.ts.map +1 -1
  11. package/lib/MenuItem/MenuItem.types.js.map +1 -1
  12. package/lib/MenuItem/MenuItemTokens.d.ts.map +1 -1
  13. package/lib/MenuItem/MenuItemTokens.js +5 -0
  14. package/lib/MenuItem/MenuItemTokens.js.map +1 -1
  15. package/lib/MenuItem/MenuItemTokens.macos.d.ts.map +1 -1
  16. package/lib/MenuItem/MenuItemTokens.macos.js +5 -0
  17. package/lib/MenuItem/MenuItemTokens.macos.js.map +1 -1
  18. package/lib/MenuItem/MenuItemTokens.win32.d.ts.map +1 -1
  19. package/lib/MenuItem/MenuItemTokens.win32.js +6 -0
  20. package/lib/MenuItem/MenuItemTokens.win32.js.map +1 -1
  21. package/lib/MenuItem/useMenuItem.d.ts.map +1 -1
  22. package/lib/MenuItem/useMenuItem.js +2 -2
  23. package/lib/MenuItem/useMenuItem.js.map +1 -1
  24. package/lib/MenuItemCheckbox/MenuItemCheckbox.d.ts.map +1 -1
  25. package/lib/MenuItemCheckbox/MenuItemCheckbox.js +9 -2
  26. package/lib/MenuItemCheckbox/MenuItemCheckbox.js.map +1 -1
  27. package/lib/MenuItemCheckbox/MenuItemCheckbox.styling.d.ts.map +1 -1
  28. package/lib/MenuItemCheckbox/MenuItemCheckbox.styling.js +13 -0
  29. package/lib/MenuItemCheckbox/MenuItemCheckbox.styling.js.map +1 -1
  30. package/lib/MenuItemCheckbox/MenuItemCheckbox.types.d.ts +9 -1
  31. package/lib/MenuItemCheckbox/MenuItemCheckbox.types.d.ts.map +1 -1
  32. package/lib/MenuItemCheckbox/MenuItemCheckbox.types.js.map +1 -1
  33. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.d.ts.map +1 -1
  34. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.js +5 -0
  35. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.js.map +1 -1
  36. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.macos.d.ts.map +1 -1
  37. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.macos.js +5 -0
  38. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.macos.js.map +1 -1
  39. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.win32.d.ts.map +1 -1
  40. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.win32.js +6 -0
  41. package/lib/MenuItemCheckbox/MenuItemCheckboxTokens.win32.js.map +1 -1
  42. package/lib/MenuItemCheckbox/useMenuItemCheckbox.d.ts.map +1 -1
  43. package/lib/MenuItemCheckbox/useMenuItemCheckbox.js +2 -2
  44. package/lib/MenuItemCheckbox/useMenuItemCheckbox.js.map +1 -1
  45. package/lib/MenuList/MenuList.types.d.ts +4 -0
  46. package/lib/MenuList/MenuList.types.d.ts.map +1 -1
  47. package/lib/MenuList/useMenuListContextValue.js +1 -1
  48. package/lib/MenuList/useMenuListContextValue.js.map +1 -1
  49. package/lib/MenuPopover/useMenuPopover.d.ts.map +1 -1
  50. package/lib/MenuPopover/useMenuPopover.js +5 -14
  51. package/lib/MenuPopover/useMenuPopover.js.map +1 -1
  52. package/lib/context/menuContext.d.ts.map +1 -1
  53. package/lib/context/menuContext.js +1 -0
  54. package/lib/context/menuContext.js.map +1 -1
  55. package/lib/context/menuListContext.d.ts +1 -0
  56. package/lib/context/menuListContext.d.ts.map +1 -1
  57. package/lib/context/menuListContext.js +1 -0
  58. package/lib/context/menuListContext.js.map +1 -1
  59. package/lib-commonjs/MenuItem/MenuItem.d.ts.map +1 -1
  60. package/lib-commonjs/MenuItem/MenuItem.js +8 -1
  61. package/lib-commonjs/MenuItem/MenuItem.js.map +1 -1
  62. package/lib-commonjs/MenuItem/MenuItem.styling.d.ts.map +1 -1
  63. package/lib-commonjs/MenuItem/MenuItem.styling.js +13 -0
  64. package/lib-commonjs/MenuItem/MenuItem.styling.js.map +1 -1
  65. package/lib-commonjs/MenuItem/MenuItem.types.d.ts +9 -1
  66. package/lib-commonjs/MenuItem/MenuItem.types.d.ts.map +1 -1
  67. package/lib-commonjs/MenuItem/MenuItem.types.js.map +1 -1
  68. package/lib-commonjs/MenuItem/MenuItemTokens.d.ts.map +1 -1
  69. package/lib-commonjs/MenuItem/MenuItemTokens.js +5 -0
  70. package/lib-commonjs/MenuItem/MenuItemTokens.js.map +1 -1
  71. package/lib-commonjs/MenuItem/MenuItemTokens.macos.d.ts.map +1 -1
  72. package/lib-commonjs/MenuItem/MenuItemTokens.macos.js +5 -0
  73. package/lib-commonjs/MenuItem/MenuItemTokens.macos.js.map +1 -1
  74. package/lib-commonjs/MenuItem/MenuItemTokens.win32.d.ts.map +1 -1
  75. package/lib-commonjs/MenuItem/MenuItemTokens.win32.js +6 -0
  76. package/lib-commonjs/MenuItem/MenuItemTokens.win32.js.map +1 -1
  77. package/lib-commonjs/MenuItem/useMenuItem.d.ts.map +1 -1
  78. package/lib-commonjs/MenuItem/useMenuItem.js +2 -2
  79. package/lib-commonjs/MenuItem/useMenuItem.js.map +1 -1
  80. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.d.ts.map +1 -1
  81. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.js +8 -1
  82. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.js.map +1 -1
  83. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.styling.d.ts.map +1 -1
  84. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.styling.js +13 -0
  85. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.styling.js.map +1 -1
  86. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.types.d.ts +9 -1
  87. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.types.d.ts.map +1 -1
  88. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckbox.types.js.map +1 -1
  89. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.d.ts.map +1 -1
  90. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.js +5 -0
  91. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.js.map +1 -1
  92. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.macos.d.ts.map +1 -1
  93. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.macos.js +5 -0
  94. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.macos.js.map +1 -1
  95. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.win32.d.ts.map +1 -1
  96. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.win32.js +6 -0
  97. package/lib-commonjs/MenuItemCheckbox/MenuItemCheckboxTokens.win32.js.map +1 -1
  98. package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.d.ts.map +1 -1
  99. package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.js +2 -2
  100. package/lib-commonjs/MenuItemCheckbox/useMenuItemCheckbox.js.map +1 -1
  101. package/lib-commonjs/MenuList/MenuList.types.d.ts +4 -0
  102. package/lib-commonjs/MenuList/MenuList.types.d.ts.map +1 -1
  103. package/lib-commonjs/MenuList/useMenuListContextValue.js +1 -1
  104. package/lib-commonjs/MenuList/useMenuListContextValue.js.map +1 -1
  105. package/lib-commonjs/MenuPopover/useMenuPopover.d.ts.map +1 -1
  106. package/lib-commonjs/MenuPopover/useMenuPopover.js +5 -14
  107. package/lib-commonjs/MenuPopover/useMenuPopover.js.map +1 -1
  108. package/lib-commonjs/context/menuContext.d.ts.map +1 -1
  109. package/lib-commonjs/context/menuContext.js +1 -0
  110. package/lib-commonjs/context/menuContext.js.map +1 -1
  111. package/lib-commonjs/context/menuListContext.d.ts +1 -0
  112. package/lib-commonjs/context/menuListContext.d.ts.map +1 -1
  113. package/lib-commonjs/context/menuListContext.js +1 -0
  114. package/lib-commonjs/context/menuListContext.js.map +1 -1
  115. package/package.json +3 -2
  116. package/src/MenuItem/MenuItem.styling.ts +22 -0
  117. package/src/MenuItem/MenuItem.tsx +12 -2
  118. package/src/MenuItem/MenuItem.types.ts +14 -1
  119. package/src/MenuItem/MenuItemTokens.macos.ts +5 -0
  120. package/src/MenuItem/MenuItemTokens.ts +5 -0
  121. package/src/MenuItem/MenuItemTokens.win32.ts +6 -0
  122. package/src/MenuItem/useMenuItem.ts +2 -1
  123. package/src/MenuItemCheckbox/MenuItemCheckbox.styling.ts +22 -0
  124. package/src/MenuItemCheckbox/MenuItemCheckbox.tsx +12 -2
  125. package/src/MenuItemCheckbox/MenuItemCheckbox.types.ts +10 -2
  126. package/src/MenuItemCheckbox/MenuItemCheckboxTokens.macos.ts +5 -0
  127. package/src/MenuItemCheckbox/MenuItemCheckboxTokens.ts +5 -0
  128. package/src/MenuItemCheckbox/MenuItemCheckboxTokens.win32.ts +6 -0
  129. package/src/MenuItemCheckbox/useMenuItemCheckbox.ts +2 -1
  130. package/src/MenuList/MenuList.types.ts +5 -0
  131. package/src/MenuList/useMenuListContextValue.ts +1 -1
  132. package/src/MenuPopover/useMenuPopover.ts +5 -14
  133. package/src/context/menuContext.ts +1 -0
  134. package/src/context/menuListContext.ts +2 -0
@@ -7,6 +7,7 @@ exports.MenuListContext = React.createContext({
7
7
  isCheckedControlled: false,
8
8
  checked: {},
9
9
  hasCheckmarks: false,
10
+ hasIcons: false,
10
11
  hasTooltips: false,
11
12
  onCheckedChange: function () { return false; },
12
13
  onArrowClose: function () { return false; },
@@ -1 +1 @@
1
- {"version":3,"file":"menuListContext.js","sourceRoot":"","sources":["../../src/context/menuListContext.ts"],"names":[],"mappings":";;;;AAAA,mDAA+B;AAYlB,QAAA,eAAe,GAAG,KAAK,CAAC,aAAa,CAAuB;IACvE,mBAAmB,EAAE,KAAK;IAC1B,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,KAAK;IACpB,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;IAC5B,YAAY,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;IACzB,YAAY,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;IACzB,eAAe,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;CAC7B,CAAC,CAAC;AAEU,QAAA,gBAAgB,GAAG,uBAAe,CAAC,QAAQ,CAAC;AAClD,IAAM,kBAAkB,GAAG,cAAM,OAAA,KAAK,CAAC,UAAU,CAAC,uBAAe,CAAC,EAAjC,CAAiC,CAAC;AAA7D,QAAA,kBAAkB,sBAA2C"}
1
+ {"version":3,"file":"menuListContext.js","sourceRoot":"","sources":["../../src/context/menuListContext.ts"],"names":[],"mappings":";;;;AAAA,mDAA+B;AAalB,QAAA,eAAe,GAAG,KAAK,CAAC,aAAa,CAAuB;IACvE,mBAAmB,EAAE,KAAK;IAC1B,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,KAAK;IACpB,QAAQ,EAAE,KAAK;IACf,WAAW,EAAE,KAAK;IAClB,eAAe,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;IAC5B,YAAY,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;IACzB,YAAY,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;IACzB,eAAe,EAAE,cAAM,OAAA,KAAK,EAAL,CAAK;CAC7B,CAAC,CAAC;AAEU,QAAA,gBAAgB,GAAG,uBAAe,CAAC,QAAQ,CAAC;AAClD,IAAM,kBAAkB,GAAG,cAAM,OAAA,KAAK,CAAC,UAAU,CAAC,uBAAe,CAAC,EAAjC,CAAiC,CAAC;AAA7D,QAAA,kBAAkB,sBAA2C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluentui-react-native/menu",
3
- "version": "1.4.13",
3
+ "version": "1.4.15",
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",
@@ -26,6 +26,7 @@
26
26
  "@fluentui-react-native/callout": ">=0.23.4 <1.0.0",
27
27
  "@fluentui-react-native/focus-zone": ">=0.11.33 <1.0.0",
28
28
  "@fluentui-react-native/framework": "0.9.3",
29
+ "@fluentui-react-native/icon": "^0.17.16",
29
30
  "@fluentui-react-native/interactive-hooks": ">=0.22.21 <1.0.0",
30
31
  "@fluentui-react-native/text": ">=0.19.23 <1.0.0",
31
32
  "@fluentui-react-native/theme-tokens": ">=0.24.3 <1.0.0",
@@ -34,7 +35,7 @@
34
35
  "tslib": "^2.3.1"
35
36
  },
36
37
  "devDependencies": {
37
- "@fluentui-react-native/button": ">=0.32.36 <1.0.0",
38
+ "@fluentui-react-native/button": ">=0.32.37 <1.0.0",
38
39
  "@fluentui-react-native/eslint-config-rules": "^0.1.1",
39
40
  "@fluentui-react-native/scripts": "^0.1.1",
40
41
  "@fluentui-react-native/test-tools": ">=0.1.1 <1.0.0",
@@ -47,6 +47,28 @@ export const stylingSettings: UseStylingOptions<MenuItemProps, MenuItemSlotProps
47
47
  },
48
48
  ['color', ...fontStyles.keys],
49
49
  ),
50
+ iconPlaceholder: buildProps(
51
+ (tokens: MenuItemTokens) => ({
52
+ style: {
53
+ minHeight: tokens.iconSize,
54
+ minWidth: tokens.iconSize,
55
+ alignItems: 'center',
56
+ justifyContent: 'center',
57
+ marginEnd: tokens.gap,
58
+ },
59
+ }),
60
+ ['checkmarkSize', 'gap'],
61
+ ),
62
+ imgIcon: buildProps(
63
+ (tokens: MenuItemTokens) => ({
64
+ style: { tintColor: tokens.iconColor, height: tokens.iconSize, width: tokens.iconSize },
65
+ }),
66
+ ['gap', 'iconColor', 'iconSize'],
67
+ ),
68
+ fontOrSvgIcon: buildProps(
69
+ (tokens: MenuItemTokens) => ({ color: tokens.iconColor, size: tokens.iconSize }),
70
+ ['gap', 'iconColor', 'iconSize'],
71
+ ),
50
72
  submenuIndicator: buildProps(
51
73
  (tokens: MenuItemTokens) => {
52
74
  return {
@@ -1,9 +1,10 @@
1
1
  /** @jsx withSlots */
2
2
  import React from 'react';
3
- import { I18nManager, Pressable, View } from 'react-native';
3
+ import { I18nManager, Image, Pressable, View } from 'react-native';
4
4
 
5
5
  import type { UseSlots } from '@fluentui-react-native/framework';
6
6
  import { compose, memoize, mergeProps, withSlots } from '@fluentui-react-native/framework';
7
+ import { IconV1 as Icon } from '@fluentui-react-native/icon';
7
8
  import { TextV1 as Text } from '@fluentui-react-native/text';
8
9
  import { SvgXml } from 'react-native-svg';
9
10
 
@@ -19,6 +20,9 @@ export const MenuItem = compose<MenuItemType>({
19
20
  root: Pressable,
20
21
  checkmark: View,
21
22
  content: Text,
23
+ iconPlaceholder: View,
24
+ imgIcon: Image,
25
+ fontOrSvgIcon: Icon,
22
26
  submenuIndicator: SvgXml,
23
27
  },
24
28
  useRender: (userProps: MenuItemProps, useSlots: UseSlots<MenuItemType>) => {
@@ -26,7 +30,7 @@ export const MenuItem = compose<MenuItemType>({
26
30
  const Slots = useSlots(userProps, (layer): boolean => menuItem.state[layer] || userProps[layer]);
27
31
 
28
32
  return (final: MenuItemProps, children: React.ReactNode) => {
29
- const { accessibilityLabel, tooltip, ...mergedProps } = mergeProps(menuItem.props, final);
33
+ const { accessibilityLabel, icon, tooltip, ...mergedProps } = mergeProps(menuItem.props, final);
30
34
  const chevronXml = I18nManager.isRTL
31
35
  ? `
32
36
  <svg>
@@ -44,6 +48,12 @@ export const MenuItem = compose<MenuItemType>({
44
48
  return (
45
49
  <Slots.root {...mergedProps} accessibilityLabel={label}>
46
50
  {menuItem.state.hasCheckmarks && <Slots.checkmark />}
51
+ {(icon || menuItem.state.hasIcons) && (
52
+ <Slots.iconPlaceholder>
53
+ {icon && icon.source && <Slots.imgIcon {...icon} />}
54
+ {icon && (icon.svgSource || icon.fontSource) && <Slots.fontOrSvgIcon {...icon} />}
55
+ </Slots.iconPlaceholder>
56
+ )}
47
57
  {children && <Slots.content tooltip={tooltipResult}>{children}</Slots.content>}
48
58
  {menuItem.state.hasSubmenu && <Slots.submenuIndicator xml={chevronXml} />}
49
59
  </Slots.root>
@@ -1,7 +1,8 @@
1
1
  import type * as React from 'react';
2
- import type { ColorValue } from 'react-native';
2
+ import type { ColorValue, ImageProps } from 'react-native';
3
3
 
4
4
  import type { IViewProps } from '@fluentui-react-native/adapters';
5
+ import type { IconPropsV1 as IconProps } from '@fluentui-react-native/icon';
5
6
  import type { IFocusable, InteractionEvent, PressablePropsExtended, PressableState } from '@fluentui-react-native/interactive-hooks';
6
7
  import type { TextProps } from '@fluentui-react-native/text';
7
8
  import type { FontTokens, IBorderTokens, IColorTokens, LayoutTokens } from '@fluentui-react-native/tokens';
@@ -20,6 +21,9 @@ export interface MenuItemTokens extends LayoutTokens, FontTokens, IBorderTokens,
20
21
  */
21
22
  gap?: number;
22
23
 
24
+ iconColor?: ColorValue;
25
+ iconSize?: number;
26
+
23
27
  /**
24
28
  * Color of the indicator that shows that an item has a submenu
25
29
  */
@@ -50,6 +54,11 @@ export interface MenuItemProps extends Omit<PressablePropsExtended, 'onPress'> {
50
54
  */
51
55
  componentRef?: React.RefObject<IFocusable>;
52
56
 
57
+ /*
58
+ * Source URL or name of the icon to show on the Button.
59
+ */
60
+ icon?: IconProps | ImageProps;
61
+
53
62
  /**
54
63
  * A callback to call on button click event
55
64
  */
@@ -63,6 +72,7 @@ export interface MenuItemProps extends Omit<PressablePropsExtended, 'onPress'> {
63
72
 
64
73
  export interface MenuItemState extends PressableState {
65
74
  hasCheckmarks?: boolean;
75
+ hasIcons?: boolean;
66
76
 
67
77
  /**
68
78
  * If the menu item is a trigger for a submenu
@@ -84,6 +94,9 @@ export interface MenuItemSlotProps {
84
94
  root: React.PropsWithRef<PressablePropsExtended>;
85
95
  content?: TextProps;
86
96
  checkmark?: React.PropsWithRef<IViewProps>;
97
+ iconPlaceholder?: React.PropsWithRef<IViewProps>;
98
+ imgIcon?: ImageProps;
99
+ fontOrSvgIcon?: IconProps;
87
100
  submenuIndicator?: XmlProps;
88
101
  }
89
102
 
@@ -13,6 +13,8 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
13
13
  fontSize: 13, // aligning with NSMenu font size
14
14
  fontWeight: globalTokens.font.weight.regular as FontWeightValue,
15
15
  gap: globalTokens.size40,
16
+ iconColor: t.colors.neutralForeground1,
17
+ iconSize: 16,
16
18
  paddingHorizontal: 5, // hardcoded for now to match NSMenu
17
19
  paddingVertical: 3, // hardcoded for now to match NSMenu
18
20
  submenuIndicatorColor: t.colors.neutralForeground1,
@@ -21,13 +23,16 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
21
23
  focused: {
22
24
  backgroundColor: t.colors.menuItemBackgroundHovered,
23
25
  color: t.colors.menuItemTextHovered,
26
+ iconColor: t.colors.menuItemTextHovered,
24
27
  },
25
28
  pressed: {
26
29
  backgroundColor: t.colors.menuItemBackgroundPressed,
27
30
  color: t.colors.menuItemTextHovered,
31
+ iconColor: t.colors.menuItemTextHovered,
28
32
  },
29
33
  disabled: {
30
34
  backgroundColor: t.colors.menuBackground,
31
35
  color: t.colors.disabledText,
36
+ iconColor: t.colors.disabledText,
32
37
  },
33
38
  });
@@ -13,6 +13,8 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
13
13
  fontSize: globalTokens.font.size300,
14
14
  fontWeight: globalTokens.font.weight.regular as FontWeightValue,
15
15
  gap: globalTokens.size40,
16
+ iconColor: t.colors.neutralForeground2,
17
+ iconSize: 16,
16
18
  minHeight: 32,
17
19
  minWidth: 128,
18
20
  maxWidth: 300,
@@ -23,16 +25,19 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
23
25
  hovered: {
24
26
  backgroundColor: t.colors.neutralBackground1Hover,
25
27
  color: t.colors.neutralForeground2Hover,
28
+ iconColor: t.colors.neutralForeground2Hover,
26
29
  submenuIndicatorColor: t.colors.neutralForeground2Hover,
27
30
  },
28
31
  pressed: {
29
32
  backgroundColor: t.colors.neutralBackground1Pressed,
30
33
  color: t.colors.neutralForeground2Pressed,
34
+ iconColor: t.colors.neutralForeground2Pressed,
31
35
  submenuIndicatorColor: t.colors.neutralForeground2Pressed,
32
36
  },
33
37
  disabled: {
34
38
  backgroundColor: t.colors.neutralBackground1,
35
39
  color: t.colors.neutralForegroundDisabled,
40
+ iconColor: t.colors.neutralForegroundDisabled,
36
41
  submenuIndicatorColor: t.colors.neutralForegroundDisabled,
37
42
  },
38
43
  });
@@ -13,6 +13,8 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
13
13
  fontSize: globalTokens.font.size200,
14
14
  fontWeight: globalTokens.font.weight.regular as FontWeightValue,
15
15
  gap: globalTokens.size40,
16
+ iconColor: t.colors.neutralForeground1,
17
+ iconSize: 16,
16
18
  minHeight: 24,
17
19
  minWidth: 128,
18
20
  maxWidth: 300,
@@ -24,21 +26,25 @@ export const defaultMenuItemTokens: TokenSettings<MenuItemTokens, Theme> = (t: T
24
26
  hovered: {
25
27
  backgroundColor: t.colors.neutralBackground1Hover,
26
28
  color: t.colors.neutralForeground1Hover,
29
+ iconColor: t.colors.neutralForeground1Hover,
27
30
  submenuIndicatorColor: t.colors.neutralForeground1Hover,
28
31
  },
29
32
  pressed: {
30
33
  backgroundColor: t.colors.neutralBackground1Pressed,
31
34
  color: t.colors.neutralForeground1Pressed,
35
+ iconColor: t.colors.neutralForeground1Pressed,
32
36
  submenuIndicatorColor: t.colors.neutralForeground1Pressed,
33
37
  },
34
38
  disabled: {
35
39
  backgroundColor: t.colors.neutralBackground1,
36
40
  color: t.colors.neutralForegroundDisabled,
41
+ iconColor: t.colors.neutralForegroundDisabled,
37
42
  submenuIndicatorColor: t.colors.neutralForegroundDisabled,
38
43
  },
39
44
  focused: {
40
45
  backgroundColor: t.colors.neutralBackground1Hover,
41
46
  color: t.colors.neutralForeground1Hover,
47
+ iconColor: t.colors.neutralForeground1Hover,
42
48
  submenuIndicatorColor: t.colors.neutralForeground1Hover,
43
49
  },
44
50
  });
@@ -19,7 +19,7 @@ export const useMenuItem = (props: MenuItemProps): MenuItemInfo => {
19
19
  const defaultComponentRef = React.useRef(null);
20
20
  const { onClick, accessibilityState, componentRef = defaultComponentRef, disabled, persistOnClick, ...rest } = props;
21
21
  const { isSubmenu, persistOnItemClick, setOpen } = useMenuContext();
22
- const { hasCheckmarks, hasTooltips, onArrowClose } = useMenuListContext();
22
+ const { hasCheckmarks, hasIcons, hasTooltips, onArrowClose } = useMenuListContext();
23
23
  const isTrigger = useMenuTriggerContext();
24
24
  const shouldPersist = persistOnClick ?? persistOnItemClick;
25
25
 
@@ -84,6 +84,7 @@ export const useMenuItem = (props: MenuItemProps): MenuItemInfo => {
84
84
  state: {
85
85
  ...pressable.state,
86
86
  hasSubmenu,
87
+ hasIcons,
87
88
  hasCheckmarks,
88
89
  hasTooltips,
89
90
  },
@@ -46,5 +46,27 @@ export const stylingSettings: UseStylingOptions<MenuItemCheckboxProps, MenuItemC
46
46
  }),
47
47
  ['color', ...fontStyles.keys],
48
48
  ),
49
+ iconPlaceholder: buildProps(
50
+ (tokens: MenuItemCheckboxTokens) => ({
51
+ style: {
52
+ minHeight: tokens.iconSize,
53
+ minWidth: tokens.iconSize,
54
+ alignItems: 'center',
55
+ justifyContent: 'center',
56
+ marginEnd: tokens.gap,
57
+ },
58
+ }),
59
+ ['checkmarkSize', 'gap'],
60
+ ),
61
+ imgIcon: buildProps(
62
+ (tokens: MenuItemCheckboxTokens) => ({
63
+ style: { tintColor: tokens.iconColor, height: tokens.iconSize, width: tokens.iconSize },
64
+ }),
65
+ ['gap', 'iconColor', 'iconSize'],
66
+ ),
67
+ fontOrSvgIcon: buildProps(
68
+ (tokens: MenuItemCheckboxTokens) => ({ color: tokens.iconColor, size: tokens.iconSize }),
69
+ ['gap', 'iconColor', 'iconSize'],
70
+ ),
49
71
  },
50
72
  };
@@ -1,9 +1,10 @@
1
1
  /** @jsx withSlots */
2
2
  import React from 'react';
3
- import { Pressable } from 'react-native';
3
+ import { Image, Pressable, View } from 'react-native';
4
4
 
5
5
  import type { Slots, UseSlots } from '@fluentui-react-native/framework';
6
6
  import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework';
7
+ import { IconV1 as Icon } from '@fluentui-react-native/icon';
7
8
  import { TextV1 as Text } from '@fluentui-react-native/text';
8
9
  import { SvgXml } from 'react-native-svg';
9
10
 
@@ -25,6 +26,9 @@ export const MenuItemCheckbox = compose<MenuItemCheckboxType>({
25
26
  root: Pressable,
26
27
  checkmark: SvgXml,
27
28
  content: Text,
29
+ iconPlaceholder: View,
30
+ imgIcon: Image,
31
+ fontOrSvgIcon: Icon,
28
32
  },
29
33
  useRender: (userProps: MenuItemCheckboxProps, useSlots: UseSlots<MenuItemCheckboxType>) => {
30
34
  const menuItem = useMenuItemCheckbox(userProps);
@@ -39,7 +43,7 @@ export const menuItemFinalRender = (
39
43
  Slots: Slots<MenuItemCheckboxSlotProps>,
40
44
  ): React.FunctionComponent<MenuItemCheckboxProps> => {
41
45
  return (final: MenuItemCheckboxProps, children: React.ReactNode) => {
42
- const { accessibilityLabel, tooltip, ...mergedProps } = mergeProps(menuItem.props, final);
46
+ const { accessibilityLabel, icon, tooltip, ...mergedProps } = mergeProps(menuItem.props, final);
43
47
  const checkmarkXml = `
44
48
  <svg>
45
49
  <path fill='currentColor' d='M9.85355 3.14645C10.0488 3.34171 10.0488 3.65829 9.85355 3.85355L5.35355 8.35355C5.15829 8.54882 4.84171 8.54882 4.64645 8.35355L2.64645 6.35355C2.45118 6.15829 2.45118 5.84171 2.64645 5.64645C2.84171 5.45118 3.15829 5.45118 3.35355 5.64645L5 7.29289L9.14645 3.14645C9.34171 2.95118 9.65829 2.95118 9.85355 3.14645Z' />
@@ -51,6 +55,12 @@ export const menuItemFinalRender = (
51
55
  return (
52
56
  <Slots.root {...mergedProps} accessibilityLabel={label}>
53
57
  <Slots.checkmark xml={checkmarkXml} />
58
+ {(icon || menuItem.state.hasIcons) && (
59
+ <Slots.iconPlaceholder>
60
+ {icon && icon.source && <Slots.imgIcon {...icon} />}
61
+ {icon && (icon.svgSource || icon.fontSource) && <Slots.fontOrSvgIcon {...icon} />}
62
+ </Slots.iconPlaceholder>
63
+ )}
54
64
  {children && <Slots.content tooltip={tooltipResult}>{children}</Slots.content>}
55
65
  </Slots.root>
56
66
  );
@@ -1,6 +1,8 @@
1
1
  import type * as React from 'react';
2
- import type { ColorValue } from 'react-native';
2
+ import type { ColorValue, ImageProps } from 'react-native';
3
3
 
4
+ import type { IViewProps } from '@fluentui-react-native/adapters';
5
+ import type { IconPropsV1 as IconProps } from '@fluentui-react-native/icon';
4
6
  import type { PressablePropsExtended, PressableState } from '@fluentui-react-native/interactive-hooks';
5
7
  import type { TextProps } from '@fluentui-react-native/text';
6
8
  import type { XmlProps } from 'react-native-svg';
@@ -26,6 +28,9 @@ export interface MenuItemCheckboxTokens
26
28
  */
27
29
  checkmarkVisibility?: number;
28
30
 
31
+ iconColor?: ColorValue;
32
+ iconSize?: number;
33
+
29
34
  /**
30
35
  * States of the item control
31
36
  */
@@ -44,13 +49,16 @@ export interface MenuItemCheckboxProps extends MenuItemProps {
44
49
  }
45
50
  export interface MenuItemCheckboxInfo {
46
51
  props: MenuItemCheckboxProps & React.ComponentPropsWithRef<any>;
47
- state: PressableState & { hasTooltips: boolean };
52
+ state: PressableState & { hasIcons: boolean; hasTooltips: boolean };
48
53
  }
49
54
 
50
55
  export interface MenuItemCheckboxSlotProps {
51
56
  root: React.PropsWithRef<PressablePropsExtended>;
52
57
  checkmark?: XmlProps;
53
58
  content?: TextProps;
59
+ iconPlaceholder?: React.PropsWithRef<IViewProps>;
60
+ imgIcon?: ImageProps;
61
+ fontOrSvgIcon?: IconProps;
54
62
  }
55
63
 
56
64
  export interface MenuItemCheckboxType {
@@ -15,11 +15,14 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
15
15
  fontSize: globalTokens.font.size300,
16
16
  fontWeight: globalTokens.font.weight.regular as FontWeightValue,
17
17
  gap: globalTokens.size40,
18
+ iconColor: t.colors.neutralForeground2,
19
+ iconSize: 16,
18
20
  paddingHorizontal: 5,
19
21
  paddingVertical: 3,
20
22
  focused: {
21
23
  backgroundColor: t.colors.brandBackground,
22
24
  color: t.colors.brandedContent,
25
+ iconColor: t.colors.brandedContent,
23
26
  checked: {
24
27
  checkmarkColor: t.colors.neutralForeground2Hover,
25
28
  checkmarkVisibility: 1,
@@ -28,6 +31,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
28
31
  pressed: {
29
32
  backgroundColor: t.colors.brandBackgroundPressed,
30
33
  color: t.colors.brandedPressedContent,
34
+ iconColor: t.colors.brandedPressedContent,
31
35
  checked: {
32
36
  checkmarkColor: t.colors.brandedPressedContent,
33
37
  checkmarkVisibility: 1,
@@ -36,6 +40,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
36
40
  disabled: {
37
41
  backgroundColor: t.colors.transparentBackground,
38
42
  color: t.colors.brandForeground1Disabled,
43
+ iconColor: t.colors.brandForeground1Disabled,
39
44
  checked: {
40
45
  checkmarkColor: t.colors.brandForeground1Disabled,
41
46
  checkmarkVisibility: 1,
@@ -15,6 +15,8 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
15
15
  fontSize: globalTokens.font.size300,
16
16
  fontWeight: globalTokens.font.weight.regular as FontWeightValue,
17
17
  gap: globalTokens.size40,
18
+ iconColor: t.colors.neutralForeground2,
19
+ iconSize: 16,
18
20
  minHeight: 32,
19
21
  minWidth: 160,
20
22
  maxWidth: 300,
@@ -22,6 +24,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
22
24
  hovered: {
23
25
  backgroundColor: t.colors.neutralBackground1Hover,
24
26
  color: t.colors.neutralForeground2Hover,
27
+ iconColor: t.colors.neutralForeground2Hover,
25
28
  checked: {
26
29
  checkmarkColor: t.colors.neutralForeground2Hover,
27
30
  checkmarkVisibility: 1,
@@ -30,6 +33,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
30
33
  pressed: {
31
34
  backgroundColor: t.colors.neutralBackground1Pressed,
32
35
  color: t.colors.neutralForeground2Pressed,
36
+ iconColor: t.colors.neutralForeground2Pressed,
33
37
  checked: {
34
38
  checkmarkColor: t.colors.neutralForeground2Pressed,
35
39
  checkmarkVisibility: 1,
@@ -38,6 +42,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
38
42
  disabled: {
39
43
  backgroundColor: t.colors.neutralBackground1,
40
44
  color: t.colors.neutralForegroundDisabled,
45
+ iconColor: t.colors.neutralForegroundDisabled,
41
46
  checked: {
42
47
  checkmarkColor: t.colors.neutralForegroundDisabled,
43
48
  checkmarkVisibility: 1,
@@ -15,6 +15,8 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
15
15
  fontSize: globalTokens.font.size200,
16
16
  fontWeight: globalTokens.font.weight.regular as FontWeightValue,
17
17
  gap: globalTokens.size40,
18
+ iconColor: t.colors.neutralForeground1,
19
+ iconSize: 16,
18
20
  minHeight: 24,
19
21
  minWidth: 160,
20
22
  maxWidth: 300,
@@ -23,6 +25,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
23
25
  hovered: {
24
26
  backgroundColor: t.colors.neutralBackground1Hover,
25
27
  color: t.colors.neutralForeground1Hover,
28
+ iconColor: t.colors.neutralForeground1Hover,
26
29
  checked: {
27
30
  checkmarkColor: t.colors.neutralForeground1Hover,
28
31
  checkmarkVisibility: 1,
@@ -31,6 +34,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
31
34
  pressed: {
32
35
  backgroundColor: t.colors.neutralBackground1Pressed,
33
36
  color: t.colors.neutralForeground1Pressed,
37
+ iconColor: t.colors.neutralForeground1Pressed,
34
38
  checked: {
35
39
  checkmarkColor: t.colors.neutralForeground1Pressed,
36
40
  checkmarkVisibility: 1,
@@ -39,6 +43,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
39
43
  disabled: {
40
44
  backgroundColor: t.colors.neutralBackground1,
41
45
  color: t.colors.neutralForegroundDisabled,
46
+ iconColor: t.colors.neutralForegroundDisabled,
42
47
  checked: {
43
48
  checkmarkColor: t.colors.neutralForegroundDisabled,
44
49
  checkmarkVisibility: 1,
@@ -47,6 +52,7 @@ export const defaultMenuItemCheckboxTokens: TokenSettings<MenuItemCheckboxTokens
47
52
  focused: {
48
53
  backgroundColor: t.colors.neutralBackground1Hover,
49
54
  color: t.colors.neutralForeground1Hover,
55
+ iconColor: t.colors.neutralForeground1Hover,
50
56
  checked: {
51
57
  checkmarkColor: t.colors.neutralForeground1Hover,
52
58
  checkmarkVisibility: 1,
@@ -65,7 +65,7 @@ export const useMenuCheckboxInteraction = (
65
65
 
66
66
  const isSubmenu = useMenuContext().isSubmenu;
67
67
 
68
- const { checked, hasTooltips, onArrowClose } = useMenuListContext();
68
+ const { checked, hasIcons, hasTooltips, onArrowClose } = useMenuListContext();
69
69
  const isChecked = checked?.[name];
70
70
 
71
71
  // Ensure focus is placed on checkbox after click
@@ -116,6 +116,7 @@ export const useMenuCheckboxInteraction = (
116
116
  ...pressable.state,
117
117
  checked: isChecked,
118
118
  disabled,
119
+ hasIcons,
119
120
  hasTooltips,
120
121
  };
121
122
 
@@ -32,6 +32,11 @@ export interface MenuListProps extends Omit<IViewProps, 'onPress'> {
32
32
  */
33
33
  hasCheckmarks?: boolean;
34
34
 
35
+ /**
36
+ * States that menu items can contain icons and reserves space for item alignment
37
+ */
38
+ hasIcons?: boolean;
39
+
35
40
  /**
36
41
  * States that menu items all have tooltips with its text by default.
37
42
  *
@@ -2,5 +2,5 @@ import type { MenuListState } from './MenuList.types';
2
2
  import type { MenuListContextValue } from '../context/menuListContext';
3
3
 
4
4
  export const useMenuListContextValue = (state: MenuListState): MenuListContextValue => {
5
- return { hasCheckmarks: state.props.hasCheckmarks, hasTooltips: state.props.hasTooltips, ...state };
5
+ return { hasCheckmarks: state.props.hasCheckmarks, hasIcons: state.props.hasIcons, hasTooltips: state.props.hasTooltips, ...state };
6
6
  };
@@ -7,6 +7,7 @@ import type { MenuPopoverProps, MenuPopoverState } from './MenuPopover.types';
7
7
  import { useMenuContext } from '../context/menuContext';
8
8
 
9
9
  const controlledDismissBehaviors = ['preventDismissOnKeyDown', 'preventDismissOnClickOutside'] as DismissBehaviors[];
10
+ const stopPropagationKeys = ['ArrowUp', 'ArrowDown', 'Tab', 'Home', 'End', 'Escape'] as const;
10
11
 
11
12
  export const useMenuPopover = (props: MenuPopoverProps): MenuPopoverState => {
12
13
  const context = useMenuContext();
@@ -61,13 +62,8 @@ export const useMenuPopover = (props: MenuPopoverProps): MenuPopoverState => {
61
62
 
62
63
  // Mark key events that move selection as handled.
63
64
  // These key events are handled on the native side.
64
- switch (e.nativeEvent.key) {
65
- case 'ArrowUp':
66
- case 'ArrowDown':
67
- case 'Tab':
68
- case 'Home':
69
- case 'End':
70
- e.stopPropagation();
65
+ if (stopPropagationKeys.includes(e.nativeEvent.key)) {
66
+ e.stopPropagation();
71
67
  }
72
68
  },
73
69
  [onKeyDownProp],
@@ -79,13 +75,8 @@ export const useMenuPopover = (props: MenuPopoverProps): MenuPopoverState => {
79
75
 
80
76
  // Mark key events that move selection as handled.
81
77
  // These key events are handled on the native side.
82
- switch (e.nativeEvent.key) {
83
- case 'ArrowUp':
84
- case 'ArrowDown':
85
- case 'Tab':
86
- case 'Home':
87
- case 'End':
88
- e.stopPropagation();
78
+ if (stopPropagationKeys.includes(e.nativeEvent.key)) {
79
+ e.stopPropagation();
89
80
  }
90
81
  },
91
82
  [onKeyUpProp],
@@ -17,6 +17,7 @@ export const MenuContext = React.createContext<MenuContextValue>({
17
17
  checked: [],
18
18
  defaultChecked: [],
19
19
  hasCheckmarks: false,
20
+ hasIcons: false,
20
21
  hasTooltips: false,
21
22
  isSubmenu: false,
22
23
  open: false,
@@ -7,6 +7,7 @@ import type { MenuListState } from '../MenuList/MenuList.types';
7
7
  */
8
8
  export type MenuListContextValue = Omit<MenuListState, 'props'> & {
9
9
  hasCheckmarks: boolean;
10
+ hasIcons: boolean;
10
11
  hasTooltips: boolean;
11
12
  };
12
13
 
@@ -14,6 +15,7 @@ export const MenuListContext = React.createContext<MenuListContextValue>({
14
15
  isCheckedControlled: false,
15
16
  checked: {},
16
17
  hasCheckmarks: false,
18
+ hasIcons: false,
17
19
  hasTooltips: false,
18
20
  onCheckedChange: () => false,
19
21
  onArrowClose: () => false,