@fluentui/react-menu 0.0.0-nightly-20250917-0405.1 → 0.0.0-nightly-20250919-0407.1

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.md CHANGED
@@ -1,30 +1,30 @@
1
1
  # Change Log - @fluentui/react-menu
2
2
 
3
- This log was last generated on Wed, 17 Sep 2025 04:20:23 GMT and should not be manually modified.
3
+ This log was last generated on Fri, 19 Sep 2025 04:22:07 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
- ## [0.0.0-nightly-20250917-0405.1](https://github.com/microsoft/fluentui/tree/@fluentui/react-menu_v0.0.0-nightly-20250917-0405.1)
7
+ ## [0.0.0-nightly-20250919-0407.1](https://github.com/microsoft/fluentui/tree/@fluentui/react-menu_v0.0.0-nightly-20250919-0407.1)
8
8
 
9
- Wed, 17 Sep 2025 04:20:23 GMT
10
- [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-menu_v9.19.6..@fluentui/react-menu_v0.0.0-nightly-20250917-0405.1)
9
+ Fri, 19 Sep 2025 04:22:07 GMT
10
+ [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-menu_v9.19.6..@fluentui/react-menu_v0.0.0-nightly-20250919-0407.1)
11
11
 
12
12
  ### Changes
13
13
 
14
14
  - Release nightly v9 ([commit](https://github.com/microsoft/fluentui/commit/not available) by fluentui-internal@service.microsoft.com)
15
- - Bump @fluentui/keyboard-keys to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
16
- - Bump @fluentui/react-aria to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
17
- - Bump @fluentui/react-context-selector to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
18
- - Bump @fluentui/react-portal to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
19
- - Bump @fluentui/react-positioning to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
20
- - Bump @fluentui/react-shared-contexts to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
21
- - Bump @fluentui/react-tabster to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
22
- - Bump @fluentui/react-theme to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
23
- - Bump @fluentui/react-utilities to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
24
- - Bump @fluentui/react-jsx-runtime to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
25
- - Bump @fluentui/react-provider to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
26
- - Bump @fluentui/react-conformance to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
27
- - Bump @fluentui/react-conformance-griffel to v0.0.0-nightly-20250917-0405.1 ([commit](https://github.com/microsoft/fluentui/commit/df94c5bae27f3968e4a673d2886c404947a4f661) by beachball)
15
+ - Bump @fluentui/keyboard-keys to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
16
+ - Bump @fluentui/react-aria to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
17
+ - Bump @fluentui/react-context-selector to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
18
+ - Bump @fluentui/react-portal to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
19
+ - Bump @fluentui/react-positioning to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
20
+ - Bump @fluentui/react-shared-contexts to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
21
+ - Bump @fluentui/react-tabster to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
22
+ - Bump @fluentui/react-theme to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
23
+ - Bump @fluentui/react-utilities to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
24
+ - Bump @fluentui/react-jsx-runtime to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
25
+ - Bump @fluentui/react-provider to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
26
+ - Bump @fluentui/react-conformance to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
27
+ - Bump @fluentui/react-conformance-griffel to v0.0.0-nightly-20250919-0407.1 ([commit](https://github.com/microsoft/fluentui/commit/07786044b0ea4144dbeef6d59c61d0e814375a47) by beachball)
28
28
 
29
29
  ## [9.19.6](https://github.com/microsoft/fluentui/tree/@fluentui/react-menu_v9.19.6)
30
30
 
@@ -9,6 +9,7 @@ import { useMenuContext_unstable } from '../../contexts/menuContext';
9
9
  import { useARIAButtonProps } from '@fluentui/react-aria';
10
10
  import { Enter, Space } from '@fluentui/keyboard-keys';
11
11
  import { useIsInMenuSplitGroup, useMenuSplitGroupContext_unstable } from '../../contexts/menuSplitGroupContext';
12
+ import { useValidateNesting } from '../../utils/useValidateNesting';
12
13
  const ChevronRightIcon = bundleIcon(ChevronRightFilled, ChevronRightRegular);
13
14
  const ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);
14
15
  /**
@@ -28,6 +29,7 @@ const ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);
28
29
  const { dir } = useFluent();
29
30
  const innerRef = React.useRef(null);
30
31
  const dismissedWithKeyboardRef = React.useRef(false);
32
+ const validateNestingRef = useValidateNesting(getValidateNestingComponentName(props.role));
31
33
  const state = {
32
34
  hasSubmenu,
33
35
  disabled,
@@ -46,7 +48,7 @@ const ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);
46
48
  ...rest,
47
49
  disabled: false,
48
50
  disabledFocusable: disabled,
49
- ref: useMergedRefs(ref, innerRef),
51
+ ref: useMergedRefs(ref, innerRef, validateNestingRef),
50
52
  onKeyDown: useEventCallback((event)=>{
51
53
  var _props_onKeyDown;
52
54
  (_props_onKeyDown = props.onKeyDown) === null || _props_onKeyDown === void 0 ? void 0 : _props_onKeyDown.call(props, event);
@@ -138,3 +140,12 @@ const useIconAndCheckmarkAlignment = (options)=>{
138
140
  hasCheckmarks: hasCheckmarks && !isSplitItemTrigger
139
141
  };
140
142
  };
143
+ const getValidateNestingComponentName = (role)=>{
144
+ switch(role){
145
+ case 'menuitemcheckbox':
146
+ return 'MenuItemCheckbox';
147
+ case 'menuitemradio':
148
+ return 'MenuItemRadio';
149
+ }
150
+ return 'MenuItem';
151
+ };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MenuItem/useMenuItem.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n useEventCallback,\n useMergedRefs,\n getIntrinsicElementProps,\n slot,\n useIsomorphicLayoutEffect,\n} from '@fluentui/react-utilities';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useCharacterSearch } from './useCharacterSearch';\nimport { useMenuTriggerContext_unstable } from '../../contexts/menuTriggerContext';\nimport {\n ChevronRightFilled,\n ChevronRightRegular,\n ChevronLeftFilled,\n ChevronLeftRegular,\n bundleIcon,\n} from '@fluentui/react-icons';\nimport { useMenuListContext_unstable } from '../../contexts/menuListContext';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport type { MenuItemProps, MenuItemState } from './MenuItem.types';\nimport {\n ARIAButtonElement,\n ARIAButtonElementIntersection,\n ARIAButtonProps,\n useARIAButtonProps,\n} from '@fluentui/react-aria';\nimport { Enter, Space } from '@fluentui/keyboard-keys';\nimport { useIsInMenuSplitGroup, useMenuSplitGroupContext_unstable } from '../../contexts/menuSplitGroupContext';\n\nconst ChevronRightIcon = bundleIcon(ChevronRightFilled, ChevronRightRegular);\nconst ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref<ARIAButtonElement<'div'>>): MenuItemState => {\n const isSubmenuTrigger = useMenuTriggerContext_unstable();\n const persistOnClickContext = useMenuContext_unstable(context => context.persistOnItemClick);\n const {\n as = 'div',\n disabled = false,\n hasSubmenu = isSubmenuTrigger,\n persistOnClick = persistOnClickContext,\n content: _content, // `content` is a slot and it's type clashes with the HTMLElement `content` attribute\n ...rest\n } = props;\n const { hasIcons, hasCheckmarks } = useIconAndCheckmarkAlignment({ hasSubmenu });\n const setOpen = useMenuContext_unstable(context => context.setOpen);\n useNotifySplitItemMultiline({ multiline: !!props.subText, hasSubmenu });\n\n const { dir } = useFluent();\n const innerRef = React.useRef<ARIAButtonElementIntersection<'div'>>(null);\n const dismissedWithKeyboardRef = React.useRef(false);\n\n const state: MenuItemState = {\n hasSubmenu,\n disabled,\n persistOnClick,\n components: {\n root: 'div',\n icon: 'span',\n checkmark: 'span',\n submenuIndicator: 'span',\n content: 'span',\n secondaryContent: 'span',\n subText: 'span',\n },\n root: slot.always(\n getIntrinsicElementProps(\n as,\n useARIAButtonProps<'div', ARIAButtonProps<'div'>>(as, {\n role: 'menuitem',\n ...rest,\n disabled: false,\n disabledFocusable: disabled,\n ref: useMergedRefs(ref, innerRef) as React.Ref<ARIAButtonElementIntersection<'div'>>,\n onKeyDown: useEventCallback(event => {\n props.onKeyDown?.(event);\n if (!event.isDefaultPrevented() && (event.key === Space || event.key === Enter)) {\n dismissedWithKeyboardRef.current = true;\n }\n }),\n onMouseMove: useEventCallback(event => {\n if (event.currentTarget.ownerDocument.activeElement !== event.currentTarget) {\n innerRef.current?.focus();\n }\n\n props.onMouseMove?.(event);\n }),\n onClick: useEventCallback(event => {\n if (!hasSubmenu && !persistOnClick) {\n setOpen(event, {\n open: false,\n keyboard: dismissedWithKeyboardRef.current,\n bubble: true,\n type: 'menuItemClick',\n event,\n });\n dismissedWithKeyboardRef.current = false;\n }\n\n props.onClick?.(event);\n }),\n }),\n ),\n { elementType: 'div' },\n ),\n icon: slot.optional(props.icon, { renderByDefault: hasIcons, elementType: 'span' }),\n checkmark: slot.optional(props.checkmark, {\n renderByDefault: hasCheckmarks,\n elementType: 'span',\n }),\n submenuIndicator: slot.optional(props.submenuIndicator, {\n renderByDefault: hasSubmenu,\n defaultProps: {\n children: dir === 'ltr' ? <ChevronRightIcon /> : <ChevronLeftIcon />,\n },\n elementType: 'span',\n }),\n content: slot.optional(props.content, {\n renderByDefault: !!props.children,\n defaultProps: { children: props.children },\n elementType: 'span',\n }),\n secondaryContent: slot.optional(props.secondaryContent, { elementType: 'span' }),\n subText: slot.optional(props.subText, { elementType: 'span' }),\n };\n useCharacterSearch(state, innerRef);\n return state;\n};\n\n/**\n * MenuSplitGroup needs to apply extra styles when its main item is in multiline layout mode\n * Notify the parent MenuSplitGroup so that it can handle this case\n */\nconst useNotifySplitItemMultiline = (options: { hasSubmenu: boolean; multiline: boolean }) => {\n const { hasSubmenu, multiline } = options;\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n const { setMultiline } = useMenuSplitGroupContext_unstable();\n\n useIsomorphicLayoutEffect(() => {\n if (!isSplitItemTrigger) {\n setMultiline(multiline);\n }\n }, [setMultiline, multiline, isSplitItemTrigger]);\n};\n\nconst useIconAndCheckmarkAlignment = (options: { hasSubmenu: boolean }) => {\n const { hasSubmenu } = options;\n const hasIcons = useMenuListContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuListContext_unstable(context => context.hasCheckmarks);\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n return {\n hasIcons: hasIcons && !isSplitItemTrigger,\n hasCheckmarks: hasCheckmarks && !isSplitItemTrigger,\n };\n};\n"],"names":["React","useEventCallback","useMergedRefs","getIntrinsicElementProps","slot","useIsomorphicLayoutEffect","useFluent_unstable","useFluent","useCharacterSearch","useMenuTriggerContext_unstable","ChevronRightFilled","ChevronRightRegular","ChevronLeftFilled","ChevronLeftRegular","bundleIcon","useMenuListContext_unstable","useMenuContext_unstable","useARIAButtonProps","Enter","Space","useIsInMenuSplitGroup","useMenuSplitGroupContext_unstable","ChevronRightIcon","ChevronLeftIcon","useMenuItem_unstable","props","ref","isSubmenuTrigger","persistOnClickContext","context","persistOnItemClick","as","disabled","hasSubmenu","persistOnClick","content","_content","rest","hasIcons","hasCheckmarks","useIconAndCheckmarkAlignment","setOpen","useNotifySplitItemMultiline","multiline","subText","dir","innerRef","useRef","dismissedWithKeyboardRef","state","components","root","icon","checkmark","submenuIndicator","secondaryContent","always","role","disabledFocusable","onKeyDown","event","isDefaultPrevented","key","current","onMouseMove","currentTarget","ownerDocument","activeElement","focus","onClick","open","keyboard","bubble","type","elementType","optional","renderByDefault","defaultProps","children","options","isSplitItemTrigger","setMultiline"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/components/MenuItem/useMenuItem.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n useEventCallback,\n useMergedRefs,\n getIntrinsicElementProps,\n slot,\n useIsomorphicLayoutEffect,\n} from '@fluentui/react-utilities';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useCharacterSearch } from './useCharacterSearch';\nimport { useMenuTriggerContext_unstable } from '../../contexts/menuTriggerContext';\nimport {\n ChevronRightFilled,\n ChevronRightRegular,\n ChevronLeftFilled,\n ChevronLeftRegular,\n bundleIcon,\n} from '@fluentui/react-icons';\nimport { useMenuListContext_unstable } from '../../contexts/menuListContext';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport type { MenuItemProps, MenuItemState } from './MenuItem.types';\nimport {\n ARIAButtonElement,\n ARIAButtonElementIntersection,\n ARIAButtonProps,\n useARIAButtonProps,\n} from '@fluentui/react-aria';\nimport { Enter, Space } from '@fluentui/keyboard-keys';\nimport { useIsInMenuSplitGroup, useMenuSplitGroupContext_unstable } from '../../contexts/menuSplitGroupContext';\nimport { useValidateNesting } from '../../utils/useValidateNesting';\n\nconst ChevronRightIcon = bundleIcon(ChevronRightFilled, ChevronRightRegular);\nconst ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref<ARIAButtonElement<'div'>>): MenuItemState => {\n const isSubmenuTrigger = useMenuTriggerContext_unstable();\n const persistOnClickContext = useMenuContext_unstable(context => context.persistOnItemClick);\n const {\n as = 'div',\n disabled = false,\n hasSubmenu = isSubmenuTrigger,\n persistOnClick = persistOnClickContext,\n content: _content, // `content` is a slot and it's type clashes with the HTMLElement `content` attribute\n ...rest\n } = props;\n const { hasIcons, hasCheckmarks } = useIconAndCheckmarkAlignment({ hasSubmenu });\n const setOpen = useMenuContext_unstable(context => context.setOpen);\n useNotifySplitItemMultiline({ multiline: !!props.subText, hasSubmenu });\n\n const { dir } = useFluent();\n const innerRef = React.useRef<ARIAButtonElementIntersection<'div'>>(null);\n const dismissedWithKeyboardRef = React.useRef(false);\n\n const validateNestingRef = useValidateNesting(getValidateNestingComponentName(props.role));\n\n const state: MenuItemState = {\n hasSubmenu,\n disabled,\n persistOnClick,\n components: {\n root: 'div',\n icon: 'span',\n checkmark: 'span',\n submenuIndicator: 'span',\n content: 'span',\n secondaryContent: 'span',\n subText: 'span',\n },\n root: slot.always(\n getIntrinsicElementProps(\n as,\n useARIAButtonProps<'div', ARIAButtonProps<'div'>>(as, {\n role: 'menuitem',\n ...rest,\n disabled: false,\n disabledFocusable: disabled,\n ref: useMergedRefs(ref, innerRef, validateNestingRef) as React.Ref<ARIAButtonElementIntersection<'div'>>,\n onKeyDown: useEventCallback(event => {\n props.onKeyDown?.(event);\n if (!event.isDefaultPrevented() && (event.key === Space || event.key === Enter)) {\n dismissedWithKeyboardRef.current = true;\n }\n }),\n onMouseMove: useEventCallback(event => {\n if (event.currentTarget.ownerDocument.activeElement !== event.currentTarget) {\n innerRef.current?.focus();\n }\n\n props.onMouseMove?.(event);\n }),\n onClick: useEventCallback(event => {\n if (!hasSubmenu && !persistOnClick) {\n setOpen(event, {\n open: false,\n keyboard: dismissedWithKeyboardRef.current,\n bubble: true,\n type: 'menuItemClick',\n event,\n });\n dismissedWithKeyboardRef.current = false;\n }\n\n props.onClick?.(event);\n }),\n }),\n ),\n { elementType: 'div' },\n ),\n icon: slot.optional(props.icon, { renderByDefault: hasIcons, elementType: 'span' }),\n checkmark: slot.optional(props.checkmark, {\n renderByDefault: hasCheckmarks,\n elementType: 'span',\n }),\n submenuIndicator: slot.optional(props.submenuIndicator, {\n renderByDefault: hasSubmenu,\n defaultProps: {\n children: dir === 'ltr' ? <ChevronRightIcon /> : <ChevronLeftIcon />,\n },\n elementType: 'span',\n }),\n content: slot.optional(props.content, {\n renderByDefault: !!props.children,\n defaultProps: { children: props.children },\n elementType: 'span',\n }),\n secondaryContent: slot.optional(props.secondaryContent, { elementType: 'span' }),\n subText: slot.optional(props.subText, { elementType: 'span' }),\n };\n useCharacterSearch(state, innerRef);\n return state;\n};\n\n/**\n * MenuSplitGroup needs to apply extra styles when its main item is in multiline layout mode\n * Notify the parent MenuSplitGroup so that it can handle this case\n */\nconst useNotifySplitItemMultiline = (options: { hasSubmenu: boolean; multiline: boolean }) => {\n const { hasSubmenu, multiline } = options;\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n const { setMultiline } = useMenuSplitGroupContext_unstable();\n\n useIsomorphicLayoutEffect(() => {\n if (!isSplitItemTrigger) {\n setMultiline(multiline);\n }\n }, [setMultiline, multiline, isSplitItemTrigger]);\n};\n\nconst useIconAndCheckmarkAlignment = (options: { hasSubmenu: boolean }) => {\n const { hasSubmenu } = options;\n const hasIcons = useMenuListContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuListContext_unstable(context => context.hasCheckmarks);\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n return {\n hasIcons: hasIcons && !isSplitItemTrigger,\n hasCheckmarks: hasCheckmarks && !isSplitItemTrigger,\n };\n};\n\nconst getValidateNestingComponentName = (role?: string) => {\n switch (role) {\n case 'menuitemcheckbox':\n return 'MenuItemCheckbox';\n case 'menuitemradio':\n return 'MenuItemRadio';\n }\n return 'MenuItem';\n};\n"],"names":["React","useEventCallback","useMergedRefs","getIntrinsicElementProps","slot","useIsomorphicLayoutEffect","useFluent_unstable","useFluent","useCharacterSearch","useMenuTriggerContext_unstable","ChevronRightFilled","ChevronRightRegular","ChevronLeftFilled","ChevronLeftRegular","bundleIcon","useMenuListContext_unstable","useMenuContext_unstable","useARIAButtonProps","Enter","Space","useIsInMenuSplitGroup","useMenuSplitGroupContext_unstable","useValidateNesting","ChevronRightIcon","ChevronLeftIcon","useMenuItem_unstable","props","ref","isSubmenuTrigger","persistOnClickContext","context","persistOnItemClick","as","disabled","hasSubmenu","persistOnClick","content","_content","rest","hasIcons","hasCheckmarks","useIconAndCheckmarkAlignment","setOpen","useNotifySplitItemMultiline","multiline","subText","dir","innerRef","useRef","dismissedWithKeyboardRef","validateNestingRef","getValidateNestingComponentName","role","state","components","root","icon","checkmark","submenuIndicator","secondaryContent","always","disabledFocusable","onKeyDown","event","isDefaultPrevented","key","current","onMouseMove","currentTarget","ownerDocument","activeElement","focus","onClick","open","keyboard","bubble","type","elementType","optional","renderByDefault","defaultProps","children","options","isSplitItemTrigger","setMultiline"],"mappings":"AAAA,YAAYA,WAAW,QAAQ;AAC/B,SACEC,gBAAgB,EAChBC,aAAa,EACbC,wBAAwB,EACxBC,IAAI,EACJC,yBAAyB,QACpB,4BAA4B;AACnC,SAASC,sBAAsBC,SAAS,QAAQ,kCAAkC;AAClF,SAASC,kBAAkB,QAAQ,uBAAuB;AAC1D,SAASC,8BAA8B,QAAQ,oCAAoC;AACnF,SACEC,kBAAkB,EAClBC,mBAAmB,EACnBC,iBAAiB,EACjBC,kBAAkB,EAClBC,UAAU,QACL,wBAAwB;AAC/B,SAASC,2BAA2B,QAAQ,iCAAiC;AAC7E,SAASC,uBAAuB,QAAQ,6BAA6B;AAErE,SAIEC,kBAAkB,QACb,uBAAuB;AAC9B,SAASC,KAAK,EAAEC,KAAK,QAAQ,0BAA0B;AACvD,SAASC,qBAAqB,EAAEC,iCAAiC,QAAQ,uCAAuC;AAChH,SAASC,kBAAkB,QAAQ,iCAAiC;AAEpE,MAAMC,mBAAmBT,WAAWJ,oBAAoBC;AACxD,MAAMa,kBAAkBV,WAAWF,mBAAmBC;AAEtD;;CAEC,GACD,OAAO,MAAMY,uBAAuB,CAACC,OAAsBC;IACzD,MAAMC,mBAAmBnB;IACzB,MAAMoB,wBAAwBb,wBAAwBc,CAAAA,UAAWA,QAAQC,kBAAkB;IAC3F,MAAM,EACJC,KAAK,KAAK,EACVC,WAAW,KAAK,EAChBC,aAAaN,gBAAgB,EAC7BO,iBAAiBN,qBAAqB,EACtCO,SAASC,QAAQ,EACjB,GAAGC,MACJ,GAAGZ;IACJ,MAAM,EAAEa,QAAQ,EAAEC,aAAa,EAAE,GAAGC,6BAA6B;QAAEP;IAAW;IAC9E,MAAMQ,UAAU1B,wBAAwBc,CAAAA,UAAWA,QAAQY,OAAO;IAClEC,4BAA4B;QAAEC,WAAW,CAAC,CAAClB,MAAMmB,OAAO;QAAEX;IAAW;IAErE,MAAM,EAAEY,GAAG,EAAE,GAAGvC;IAChB,MAAMwC,WAAW/C,MAAMgD,MAAM,CAAuC;IACpE,MAAMC,2BAA2BjD,MAAMgD,MAAM,CAAC;IAE9C,MAAME,qBAAqB5B,mBAAmB6B,gCAAgCzB,MAAM0B,IAAI;IAExF,MAAMC,QAAuB;QAC3BnB;QACAD;QACAE;QACAmB,YAAY;YACVC,MAAM;YACNC,MAAM;YACNC,WAAW;YACXC,kBAAkB;YAClBtB,SAAS;YACTuB,kBAAkB;YAClBd,SAAS;QACX;QACAU,MAAMnD,KAAKwD,MAAM,CACfzD,yBACE6B,IACAf,mBAAkDe,IAAI;YACpDoB,MAAM;YACN,GAAGd,IAAI;YACPL,UAAU;YACV4B,mBAAmB5B;YACnBN,KAAKzB,cAAcyB,KAAKoB,UAAUG;YAClCY,WAAW7D,iBAAiB8D,CAAAA;oBAC1BrC;iBAAAA,mBAAAA,MAAMoC,SAAS,cAAfpC,uCAAAA,sBAAAA,OAAkBqC;gBAClB,IAAI,CAACA,MAAMC,kBAAkB,MAAOD,CAAAA,MAAME,GAAG,KAAK9C,SAAS4C,MAAME,GAAG,KAAK/C,KAAI,GAAI;oBAC/E+B,yBAAyBiB,OAAO,GAAG;gBACrC;YACF;YACAC,aAAalE,iBAAiB8D,CAAAA;oBAK5BrC;gBAJA,IAAIqC,MAAMK,aAAa,CAACC,aAAa,CAACC,aAAa,KAAKP,MAAMK,aAAa,EAAE;wBAC3ErB;qBAAAA,oBAAAA,SAASmB,OAAO,cAAhBnB,wCAAAA,kBAAkBwB,KAAK;gBACzB;iBAEA7C,qBAAAA,MAAMyC,WAAW,cAAjBzC,yCAAAA,wBAAAA,OAAoBqC;YACtB;YACAS,SAASvE,iBAAiB8D,CAAAA;oBAYxBrC;gBAXA,IAAI,CAACQ,cAAc,CAACC,gBAAgB;oBAClCO,QAAQqB,OAAO;wBACbU,MAAM;wBACNC,UAAUzB,yBAAyBiB,OAAO;wBAC1CS,QAAQ;wBACRC,MAAM;wBACNb;oBACF;oBACAd,yBAAyBiB,OAAO,GAAG;gBACrC;iBAEAxC,iBAAAA,MAAM8C,OAAO,cAAb9C,qCAAAA,oBAAAA,OAAgBqC;YAClB;QACF,KAEF;YAAEc,aAAa;QAAM;QAEvBrB,MAAMpD,KAAK0E,QAAQ,CAACpD,MAAM8B,IAAI,EAAE;YAAEuB,iBAAiBxC;YAAUsC,aAAa;QAAO;QACjFpB,WAAWrD,KAAK0E,QAAQ,CAACpD,MAAM+B,SAAS,EAAE;YACxCsB,iBAAiBvC;YACjBqC,aAAa;QACf;QACAnB,kBAAkBtD,KAAK0E,QAAQ,CAACpD,MAAMgC,gBAAgB,EAAE;YACtDqB,iBAAiB7C;YACjB8C,cAAc;gBACZC,UAAUnC,QAAQ,sBAAQ,oBAACvB,wCAAsB,oBAACC;YACpD;YACAqD,aAAa;QACf;QACAzC,SAAShC,KAAK0E,QAAQ,CAACpD,MAAMU,OAAO,EAAE;YACpC2C,iBAAiB,CAAC,CAACrD,MAAMuD,QAAQ;YACjCD,cAAc;gBAAEC,UAAUvD,MAAMuD,QAAQ;YAAC;YACzCJ,aAAa;QACf;QACAlB,kBAAkBvD,KAAK0E,QAAQ,CAACpD,MAAMiC,gBAAgB,EAAE;YAAEkB,aAAa;QAAO;QAC9EhC,SAASzC,KAAK0E,QAAQ,CAACpD,MAAMmB,OAAO,EAAE;YAAEgC,aAAa;QAAO;IAC9D;IACArE,mBAAmB6C,OAAON;IAC1B,OAAOM;AACT,EAAE;AAEF;;;CAGC,GACD,MAAMV,8BAA8B,CAACuC;IACnC,MAAM,EAAEhD,UAAU,EAAEU,SAAS,EAAE,GAAGsC;IAClC,MAAMC,qBAAqB/D,2BAA2Bc;IAEtD,MAAM,EAAEkD,YAAY,EAAE,GAAG/D;IAEzBhB,0BAA0B;QACxB,IAAI,CAAC8E,oBAAoB;YACvBC,aAAaxC;QACf;IACF,GAAG;QAACwC;QAAcxC;QAAWuC;KAAmB;AAClD;AAEA,MAAM1C,+BAA+B,CAACyC;IACpC,MAAM,EAAEhD,UAAU,EAAE,GAAGgD;IACvB,MAAM3C,WAAWxB,4BAA4Be,CAAAA,UAAWA,QAAQS,QAAQ;IACxE,MAAMC,gBAAgBzB,4BAA4Be,CAAAA,UAAWA,QAAQU,aAAa;IAClF,MAAM2C,qBAAqB/D,2BAA2Bc;IAEtD,OAAO;QACLK,UAAUA,YAAY,CAAC4C;QACvB3C,eAAeA,iBAAiB,CAAC2C;IACnC;AACF;AAEA,MAAMhC,kCAAkC,CAACC;IACvC,OAAQA;QACN,KAAK;YACH,OAAO;QACT,KAAK;YACH,OAAO;IACX;IACA,OAAO;AACT"}
@@ -5,6 +5,7 @@ import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts
5
5
  import { useHasParentContext } from '@fluentui/react-context-selector';
6
6
  import { useMenuContext_unstable } from '../../contexts/menuContext';
7
7
  import { MenuContext } from '../../contexts/menuContext';
8
+ import { useValidateNesting } from '../../utils/useValidateNesting';
8
9
  /**
9
10
  * Returns the props and state required to render the component
10
11
  */ export const useMenuList_unstable = (props, ref)=>{
@@ -21,6 +22,7 @@ import { MenuContext } from '../../contexts/menuContext';
21
22
  console.warn('You are using both MenuList and Menu props, we recommend you to use Menu props when available');
22
23
  }
23
24
  const innerRef = React.useRef(null);
25
+ const validateNestingRef = useValidateNesting('MenuList');
24
26
  React.useEffect(()=>{
25
27
  const element = innerRef.current;
26
28
  if (hasMenuContext && targetDocument && element) {
@@ -130,7 +132,7 @@ import { MenuContext } from '../../contexts/menuContext';
130
132
  // FIXME:
131
133
  // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`
132
134
  // but since it would be a breaking change to fix it, we are casting ref to it's proper type
133
- ref: useMergedRefs(ref, innerRef),
135
+ ref: useMergedRefs(ref, innerRef, validateNestingRef),
134
136
  role: 'menu',
135
137
  'aria-labelledby': menuContext.triggerId,
136
138
  ...focusAttributes,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MenuList/useMenuList.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n useMergedRefs,\n useEventCallback,\n useControllableState,\n getIntrinsicElementProps,\n slot,\n} from '@fluentui/react-utilities';\nimport {\n useArrowNavigationGroup,\n useFocusFinders,\n TabsterMoveFocusEventName,\n type TabsterMoveFocusEvent,\n} from '@fluentui/react-tabster';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useHasParentContext } from '@fluentui/react-context-selector';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport { MenuContext } from '../../contexts/menuContext';\nimport type { MenuListProps, MenuListState } from './MenuList.types';\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuList_unstable = (props: MenuListProps, ref: React.Ref<HTMLElement>): MenuListState => {\n const { findAllFocusable } = useFocusFinders();\n const { targetDocument } = useFluent();\n const menuContext = useMenuContextSelectors();\n const hasMenuContext = useHasParentContext(MenuContext);\n const focusAttributes = useArrowNavigationGroup({ circular: true });\n\n if (usingPropsAndMenuContext(props, menuContext, hasMenuContext)) {\n // TODO throw warnings in development safely\n // eslint-disable-next-line no-console\n console.warn('You are using both MenuList and Menu props, we recommend you to use Menu props when available');\n }\n\n const innerRef = React.useRef<HTMLElement>(null);\n\n React.useEffect(() => {\n const element = innerRef.current;\n\n if (hasMenuContext && targetDocument && element) {\n const onTabsterMoveFocus = (e: TabsterMoveFocusEvent) => {\n const nextElement = e.detail.next;\n\n if (nextElement && element.contains(targetDocument.activeElement) && !element.contains(nextElement)) {\n // Preventing Tabster from handling Tab press, useMenuPopover will handle it.\n e.preventDefault();\n }\n };\n\n targetDocument.addEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n\n return () => {\n targetDocument.removeEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n };\n }\n }, [innerRef, targetDocument, hasMenuContext]);\n\n const setFocusByFirstCharacter = React.useCallback(\n (e: React.KeyboardEvent<HTMLElement>, itemEl: HTMLElement) => {\n // TODO use some kind of children registration to reduce dependency on DOM roles\n const acceptedRoles = ['menuitem', 'menuitemcheckbox', 'menuitemradio'];\n if (!innerRef.current) {\n return;\n }\n\n const menuItems = findAllFocusable(\n innerRef.current,\n (el: HTMLElement) => el.hasAttribute('role') && acceptedRoles.indexOf(el.getAttribute('role')!) !== -1,\n );\n\n let startIndex = menuItems.indexOf(itemEl) + 1;\n if (startIndex === menuItems.length) {\n startIndex = 0;\n }\n\n const firstChars = menuItems.map(menuItem => menuItem.textContent?.charAt(0).toLowerCase());\n const char = e.key.toLowerCase();\n\n const getIndexFirstChars = (start: number, firstChar: string) => {\n for (let i = start; i < firstChars.length; i++) {\n if (char === firstChars[i]) {\n return i;\n }\n }\n return -1;\n };\n\n // Check remaining slots in the menu\n let index = getIndexFirstChars(startIndex, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n menuItems[index].focus();\n }\n },\n [findAllFocusable],\n );\n\n const [checkedValues, setCheckedValues] = useControllableState({\n state: props.checkedValues ?? (hasMenuContext ? menuContext.checkedValues : undefined),\n defaultState: props.defaultCheckedValues,\n initialState: {},\n });\n\n const handleCheckedValueChange =\n props.onCheckedValueChange ?? (hasMenuContext ? menuContext.onCheckedValueChange : undefined);\n\n const toggleCheckbox = useEventCallback(\n (e: React.MouseEvent | React.KeyboardEvent, name: string, value: string, checked: boolean) => {\n const checkedItems = checkedValues?.[name] || [];\n const newCheckedItems = [...checkedItems];\n if (checked) {\n newCheckedItems.splice(newCheckedItems.indexOf(value), 1);\n } else {\n newCheckedItems.push(value);\n }\n\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n },\n );\n\n const selectRadio = useEventCallback((e: React.MouseEvent | React.KeyboardEvent, name: string, value: string) => {\n const newCheckedItems = [value];\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n });\n\n return {\n components: {\n root: 'div',\n },\n root: slot.always(\n getIntrinsicElementProps('div', {\n // FIXME:\n // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`\n // but since it would be a breaking change to fix it, we are casting ref to it's proper type\n ref: useMergedRefs(ref, innerRef) as React.Ref<HTMLDivElement>,\n role: 'menu',\n 'aria-labelledby': menuContext.triggerId,\n ...focusAttributes,\n ...props,\n }),\n { elementType: 'div' },\n ),\n hasIcons: menuContext.hasIcons || false,\n hasCheckmarks: menuContext.hasCheckmarks || false,\n checkedValues,\n hasMenuContext,\n setFocusByFirstCharacter,\n selectRadio,\n toggleCheckbox,\n };\n};\n\n/**\n * Adds some sugar to fetching multiple context selector values\n */\nconst useMenuContextSelectors = () => {\n const checkedValues = useMenuContext_unstable(context => context.checkedValues);\n const onCheckedValueChange = useMenuContext_unstable(context => context.onCheckedValueChange);\n const triggerId = useMenuContext_unstable(context => context.triggerId);\n const hasIcons = useMenuContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuContext_unstable(context => context.hasCheckmarks);\n\n return {\n checkedValues,\n onCheckedValueChange,\n triggerId,\n hasIcons,\n hasCheckmarks,\n };\n};\n\n/**\n * Helper function to detect if props and MenuContext values are both used\n */\nconst usingPropsAndMenuContext = (\n props: MenuListProps,\n contextValue: ReturnType<typeof useMenuContextSelectors>,\n hasMenuContext: boolean,\n) => {\n let isUsingPropsAndContext = false;\n for (const val in contextValue) {\n if (props[val as keyof Omit<typeof contextValue, 'hasMenuContext' | 'onCheckedValueChange' | 'triggerId'>]) {\n isUsingPropsAndContext = true;\n }\n }\n\n return hasMenuContext && isUsingPropsAndContext;\n};\n"],"names":["React","useMergedRefs","useEventCallback","useControllableState","getIntrinsicElementProps","slot","useArrowNavigationGroup","useFocusFinders","TabsterMoveFocusEventName","useFluent_unstable","useFluent","useHasParentContext","useMenuContext_unstable","MenuContext","useMenuList_unstable","props","ref","findAllFocusable","targetDocument","menuContext","useMenuContextSelectors","hasMenuContext","focusAttributes","circular","usingPropsAndMenuContext","console","warn","innerRef","useRef","useEffect","element","current","onTabsterMoveFocus","e","nextElement","detail","next","contains","activeElement","preventDefault","addEventListener","removeEventListener","setFocusByFirstCharacter","useCallback","itemEl","acceptedRoles","menuItems","el","hasAttribute","indexOf","getAttribute","startIndex","length","firstChars","map","menuItem","textContent","charAt","toLowerCase","char","key","getIndexFirstChars","start","firstChar","i","index","focus","checkedValues","setCheckedValues","state","undefined","defaultState","defaultCheckedValues","initialState","handleCheckedValueChange","onCheckedValueChange","toggleCheckbox","name","value","checked","checkedItems","newCheckedItems","splice","push","s","selectRadio","components","root","always","role","triggerId","elementType","hasIcons","hasCheckmarks","context","contextValue","isUsingPropsAndContext","val"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/components/MenuList/useMenuList.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n useMergedRefs,\n useEventCallback,\n useControllableState,\n getIntrinsicElementProps,\n slot,\n} from '@fluentui/react-utilities';\nimport {\n useArrowNavigationGroup,\n useFocusFinders,\n TabsterMoveFocusEventName,\n type TabsterMoveFocusEvent,\n} from '@fluentui/react-tabster';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useHasParentContext } from '@fluentui/react-context-selector';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport { MenuContext } from '../../contexts/menuContext';\nimport type { MenuListProps, MenuListState } from './MenuList.types';\nimport { useValidateNesting } from '../../utils/useValidateNesting';\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuList_unstable = (props: MenuListProps, ref: React.Ref<HTMLElement>): MenuListState => {\n const { findAllFocusable } = useFocusFinders();\n const { targetDocument } = useFluent();\n const menuContext = useMenuContextSelectors();\n const hasMenuContext = useHasParentContext(MenuContext);\n const focusAttributes = useArrowNavigationGroup({ circular: true });\n\n if (usingPropsAndMenuContext(props, menuContext, hasMenuContext)) {\n // TODO throw warnings in development safely\n // eslint-disable-next-line no-console\n console.warn('You are using both MenuList and Menu props, we recommend you to use Menu props when available');\n }\n\n const innerRef = React.useRef<HTMLElement>(null);\n const validateNestingRef = useValidateNesting('MenuList');\n\n React.useEffect(() => {\n const element = innerRef.current;\n\n if (hasMenuContext && targetDocument && element) {\n const onTabsterMoveFocus = (e: TabsterMoveFocusEvent) => {\n const nextElement = e.detail.next;\n\n if (nextElement && element.contains(targetDocument.activeElement) && !element.contains(nextElement)) {\n // Preventing Tabster from handling Tab press, useMenuPopover will handle it.\n e.preventDefault();\n }\n };\n\n targetDocument.addEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n\n return () => {\n targetDocument.removeEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n };\n }\n }, [innerRef, targetDocument, hasMenuContext]);\n\n const setFocusByFirstCharacter = React.useCallback(\n (e: React.KeyboardEvent<HTMLElement>, itemEl: HTMLElement) => {\n // TODO use some kind of children registration to reduce dependency on DOM roles\n const acceptedRoles = ['menuitem', 'menuitemcheckbox', 'menuitemradio'];\n if (!innerRef.current) {\n return;\n }\n\n const menuItems = findAllFocusable(\n innerRef.current,\n (el: HTMLElement) => el.hasAttribute('role') && acceptedRoles.indexOf(el.getAttribute('role')!) !== -1,\n );\n\n let startIndex = menuItems.indexOf(itemEl) + 1;\n if (startIndex === menuItems.length) {\n startIndex = 0;\n }\n\n const firstChars = menuItems.map(menuItem => menuItem.textContent?.charAt(0).toLowerCase());\n const char = e.key.toLowerCase();\n\n const getIndexFirstChars = (start: number, firstChar: string) => {\n for (let i = start; i < firstChars.length; i++) {\n if (char === firstChars[i]) {\n return i;\n }\n }\n return -1;\n };\n\n // Check remaining slots in the menu\n let index = getIndexFirstChars(startIndex, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n menuItems[index].focus();\n }\n },\n [findAllFocusable],\n );\n\n const [checkedValues, setCheckedValues] = useControllableState({\n state: props.checkedValues ?? (hasMenuContext ? menuContext.checkedValues : undefined),\n defaultState: props.defaultCheckedValues,\n initialState: {},\n });\n\n const handleCheckedValueChange =\n props.onCheckedValueChange ?? (hasMenuContext ? menuContext.onCheckedValueChange : undefined);\n\n const toggleCheckbox = useEventCallback(\n (e: React.MouseEvent | React.KeyboardEvent, name: string, value: string, checked: boolean) => {\n const checkedItems = checkedValues?.[name] || [];\n const newCheckedItems = [...checkedItems];\n if (checked) {\n newCheckedItems.splice(newCheckedItems.indexOf(value), 1);\n } else {\n newCheckedItems.push(value);\n }\n\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n },\n );\n\n const selectRadio = useEventCallback((e: React.MouseEvent | React.KeyboardEvent, name: string, value: string) => {\n const newCheckedItems = [value];\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n });\n\n return {\n components: {\n root: 'div',\n },\n root: slot.always(\n getIntrinsicElementProps('div', {\n // FIXME:\n // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`\n // but since it would be a breaking change to fix it, we are casting ref to it's proper type\n ref: useMergedRefs(ref, innerRef, validateNestingRef) as React.Ref<HTMLDivElement>,\n role: 'menu',\n 'aria-labelledby': menuContext.triggerId,\n ...focusAttributes,\n ...props,\n }),\n { elementType: 'div' },\n ),\n hasIcons: menuContext.hasIcons || false,\n hasCheckmarks: menuContext.hasCheckmarks || false,\n checkedValues,\n hasMenuContext,\n setFocusByFirstCharacter,\n selectRadio,\n toggleCheckbox,\n };\n};\n\n/**\n * Adds some sugar to fetching multiple context selector values\n */\nconst useMenuContextSelectors = () => {\n const checkedValues = useMenuContext_unstable(context => context.checkedValues);\n const onCheckedValueChange = useMenuContext_unstable(context => context.onCheckedValueChange);\n const triggerId = useMenuContext_unstable(context => context.triggerId);\n const hasIcons = useMenuContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuContext_unstable(context => context.hasCheckmarks);\n\n return {\n checkedValues,\n onCheckedValueChange,\n triggerId,\n hasIcons,\n hasCheckmarks,\n };\n};\n\n/**\n * Helper function to detect if props and MenuContext values are both used\n */\nconst usingPropsAndMenuContext = (\n props: MenuListProps,\n contextValue: ReturnType<typeof useMenuContextSelectors>,\n hasMenuContext: boolean,\n) => {\n let isUsingPropsAndContext = false;\n for (const val in contextValue) {\n if (props[val as keyof Omit<typeof contextValue, 'hasMenuContext' | 'onCheckedValueChange' | 'triggerId'>]) {\n isUsingPropsAndContext = true;\n }\n }\n\n return hasMenuContext && isUsingPropsAndContext;\n};\n"],"names":["React","useMergedRefs","useEventCallback","useControllableState","getIntrinsicElementProps","slot","useArrowNavigationGroup","useFocusFinders","TabsterMoveFocusEventName","useFluent_unstable","useFluent","useHasParentContext","useMenuContext_unstable","MenuContext","useValidateNesting","useMenuList_unstable","props","ref","findAllFocusable","targetDocument","menuContext","useMenuContextSelectors","hasMenuContext","focusAttributes","circular","usingPropsAndMenuContext","console","warn","innerRef","useRef","validateNestingRef","useEffect","element","current","onTabsterMoveFocus","e","nextElement","detail","next","contains","activeElement","preventDefault","addEventListener","removeEventListener","setFocusByFirstCharacter","useCallback","itemEl","acceptedRoles","menuItems","el","hasAttribute","indexOf","getAttribute","startIndex","length","firstChars","map","menuItem","textContent","charAt","toLowerCase","char","key","getIndexFirstChars","start","firstChar","i","index","focus","checkedValues","setCheckedValues","state","undefined","defaultState","defaultCheckedValues","initialState","handleCheckedValueChange","onCheckedValueChange","toggleCheckbox","name","value","checked","checkedItems","newCheckedItems","splice","push","s","selectRadio","components","root","always","role","triggerId","elementType","hasIcons","hasCheckmarks","context","contextValue","isUsingPropsAndContext","val"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export { MENU_ENTER_EVENT, dispatchMenuEnterEvent, useOnMenuMouseEnter } from './useOnMenuEnter';
2
2
  export { useIsSubmenu } from './useIsSubmenu';
3
+ export { useValidateNesting } from './useValidateNesting';
3
4
  export { MENU_SAFEZONE_TIMEOUT_EVENT, useOnMenuSafeZoneTimeout } from './useOnMenuSafeZoneTimeout';
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/index.ts"],"sourcesContent":["export { MENU_ENTER_EVENT, dispatchMenuEnterEvent, useOnMenuMouseEnter } from './useOnMenuEnter';\nexport { useIsSubmenu } from './useIsSubmenu';\nexport { MENU_SAFEZONE_TIMEOUT_EVENT, useOnMenuSafeZoneTimeout } from './useOnMenuSafeZoneTimeout';\n"],"names":["MENU_ENTER_EVENT","dispatchMenuEnterEvent","useOnMenuMouseEnter","useIsSubmenu","MENU_SAFEZONE_TIMEOUT_EVENT","useOnMenuSafeZoneTimeout"],"mappings":"AAAA,SAASA,gBAAgB,EAAEC,sBAAsB,EAAEC,mBAAmB,QAAQ,mBAAmB;AACjG,SAASC,YAAY,QAAQ,iBAAiB;AAC9C,SAASC,2BAA2B,EAAEC,wBAAwB,QAAQ,6BAA6B"}
1
+ {"version":3,"sources":["../src/utils/index.ts"],"sourcesContent":["export { MENU_ENTER_EVENT, dispatchMenuEnterEvent, useOnMenuMouseEnter } from './useOnMenuEnter';\nexport { useIsSubmenu } from './useIsSubmenu';\nexport { useValidateNesting } from './useValidateNesting';\nexport { MENU_SAFEZONE_TIMEOUT_EVENT, useOnMenuSafeZoneTimeout } from './useOnMenuSafeZoneTimeout';\n"],"names":["MENU_ENTER_EVENT","dispatchMenuEnterEvent","useOnMenuMouseEnter","useIsSubmenu","useValidateNesting","MENU_SAFEZONE_TIMEOUT_EVENT","useOnMenuSafeZoneTimeout"],"mappings":"AAAA,SAASA,gBAAgB,EAAEC,sBAAsB,EAAEC,mBAAmB,QAAQ,mBAAmB;AACjG,SAASC,YAAY,QAAQ,iBAAiB;AAC9C,SAASC,kBAAkB,QAAQ,uBAAuB;AAC1D,SAASC,2BAA2B,EAAEC,wBAAwB,QAAQ,6BAA6B"}
@@ -0,0 +1,79 @@
1
+ import * as React from 'react';
2
+ import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
3
+ import { useMenuContext_unstable } from '../contexts/menuContext';
4
+ export const useValidateNesting = (componentName)=>{
5
+ 'use no memo';
6
+ const { targetDocument } = useFluent();
7
+ const triggerRef = useMenuContext_unstable((context)=>context.triggerRef);
8
+ const inline = useMenuContext_unstable((context)=>context.inline);
9
+ const ref = React.useRef(null);
10
+ if (process.env.NODE_ENV !== 'production') {
11
+ // This check should run only in development mode
12
+ // It's okay to disable the ESLint rule because we ar checking env variable statically (not at runtime)
13
+ // eslint-disable-next-line react-hooks/rules-of-hooks
14
+ React.useEffect(()=>{
15
+ let ancestor = ref.current;
16
+ let ancestorComponentName = '';
17
+ do {
18
+ var _ancestor_parentElement;
19
+ ancestor = (_ancestor_parentElement = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parentElement) !== null && _ancestor_parentElement !== void 0 ? _ancestor_parentElement : null;
20
+ if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuList')) {
21
+ break;
22
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGrid')) {
23
+ ancestorComponentName = 'MenuGrid';
24
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridItem')) {
25
+ ancestorComponentName = 'MenuGridItem';
26
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridRow')) {
27
+ ancestorComponentName = 'MenuGridRow';
28
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridCell')) {
29
+ ancestorComponentName = 'MenuGridCell';
30
+ }
31
+ if ([
32
+ 'MenuItem',
33
+ 'MenuItemCheckbox',
34
+ 'MenuItemRadio'
35
+ ].includes(componentName)) {
36
+ if ([
37
+ 'MenuGrid',
38
+ 'MenuGridItem',
39
+ 'MenuGridRow',
40
+ 'MenuGridCell'
41
+ ].includes(ancestorComponentName)) {
42
+ throw new Error(`${componentName} is incorrectly nested within ${ancestorComponentName}. You probably want to wrap it in a MenuList instead.`);
43
+ }
44
+ } else if (componentName === 'MenuList') {
45
+ if (ancestorComponentName === 'MenuGridCell') {
46
+ if (inline && getCellOfTrigger(triggerRef.current, targetDocument) === ancestor) {
47
+ break;
48
+ }
49
+ throw new Error(`MenuList is incorrectly nested within MenuGridCell.`);
50
+ } else if ([
51
+ 'MenuGrid',
52
+ 'MenuGridItem',
53
+ 'MenuGridRow'
54
+ ].includes(ancestorComponentName)) {
55
+ throw new Error(`MenuList is incorrectly nested within ${ancestorComponentName}.`);
56
+ }
57
+ }
58
+ }while (ancestor && ancestor !== (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body))
59
+ }, [
60
+ componentName,
61
+ ref,
62
+ triggerRef,
63
+ inline,
64
+ targetDocument
65
+ ]);
66
+ }
67
+ return ref;
68
+ };
69
+ const getCellOfTrigger = (trigger, targetDocument)=>{
70
+ let ancestor = trigger === null || trigger === void 0 ? void 0 : trigger.parentElement;
71
+ while(ancestor && ancestor !== (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body)){
72
+ if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridCell')) {
73
+ return ancestor;
74
+ }
75
+ var _ancestor_parentElement;
76
+ ancestor = (_ancestor_parentElement = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parentElement) !== null && _ancestor_parentElement !== void 0 ? _ancestor_parentElement : null;
77
+ }
78
+ return null;
79
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/useValidateNesting.ts"],"sourcesContent":["import * as React from 'react';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\n\nimport type { MenuContextValue } from '../contexts/menuContext';\nimport { useMenuContext_unstable } from '../contexts/menuContext';\n\ntype NestingComponentName = 'MenuList' | 'MenuItem' | 'MenuItemCheckbox' | 'MenuItemRadio';\n\nexport const useValidateNesting = (componentName: NestingComponentName): React.RefObject<HTMLElement> => {\n 'use no memo';\n\n const { targetDocument } = useFluent();\n const triggerRef = useMenuContext_unstable((context: MenuContextValue) => context.triggerRef);\n const inline = useMenuContext_unstable((context: MenuContextValue) => context.inline);\n const ref = React.useRef<HTMLElement>(null);\n\n if (process.env.NODE_ENV !== 'production') {\n // This check should run only in development mode\n // It's okay to disable the ESLint rule because we ar checking env variable statically (not at runtime)\n // eslint-disable-next-line react-hooks/rules-of-hooks\n React.useEffect(() => {\n let ancestor = ref.current;\n let ancestorComponentName = '';\n do {\n ancestor = ancestor?.parentElement ?? null;\n if (ancestor?.classList.contains('fui-MenuList')) {\n break;\n } else if (ancestor?.classList.contains('fui-MenuGrid')) {\n ancestorComponentName = 'MenuGrid';\n } else if (ancestor?.classList.contains('fui-MenuGridItem')) {\n ancestorComponentName = 'MenuGridItem';\n } else if (ancestor?.classList.contains('fui-MenuGridRow')) {\n ancestorComponentName = 'MenuGridRow';\n } else if (ancestor?.classList.contains('fui-MenuGridCell')) {\n ancestorComponentName = 'MenuGridCell';\n }\n if (['MenuItem', 'MenuItemCheckbox', 'MenuItemRadio'].includes(componentName)) {\n if (['MenuGrid', 'MenuGridItem', 'MenuGridRow', 'MenuGridCell'].includes(ancestorComponentName)) {\n throw new Error(\n `${componentName} is incorrectly nested within ${ancestorComponentName}. You probably want to wrap it in a MenuList instead.`,\n );\n }\n } else if (componentName === 'MenuList') {\n if (ancestorComponentName === 'MenuGridCell') {\n if (inline && getCellOfTrigger(triggerRef.current, targetDocument) === ancestor) {\n break;\n }\n throw new Error(`MenuList is incorrectly nested within MenuGridCell.`);\n } else if (['MenuGrid', 'MenuGridItem', 'MenuGridRow'].includes(ancestorComponentName)) {\n throw new Error(`MenuList is incorrectly nested within ${ancestorComponentName}.`);\n }\n }\n } while (ancestor && ancestor !== targetDocument?.body);\n }, [componentName, ref, triggerRef, inline, targetDocument]);\n }\n return ref;\n};\n\nconst getCellOfTrigger = (trigger: HTMLElement | null, targetDocument?: Document): HTMLElement | null => {\n let ancestor = trigger?.parentElement;\n while (ancestor && ancestor !== targetDocument?.body) {\n if (ancestor?.classList.contains('fui-MenuGridCell')) {\n return ancestor;\n }\n ancestor = ancestor?.parentElement ?? null;\n }\n return null;\n};\n"],"names":["React","useFluent_unstable","useFluent","useMenuContext_unstable","useValidateNesting","componentName","targetDocument","triggerRef","context","inline","ref","useRef","process","env","NODE_ENV","useEffect","ancestor","current","ancestorComponentName","parentElement","classList","contains","includes","Error","getCellOfTrigger","body","trigger"],"mappings":"AAAA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,sBAAsBC,SAAS,QAAQ,kCAAkC;AAGlF,SAASC,uBAAuB,QAAQ,0BAA0B;AAIlE,OAAO,MAAMC,qBAAqB,CAACC;IACjC;IAEA,MAAM,EAAEC,cAAc,EAAE,GAAGJ;IAC3B,MAAMK,aAAaJ,wBAAwB,CAACK,UAA8BA,QAAQD,UAAU;IAC5F,MAAME,SAASN,wBAAwB,CAACK,UAA8BA,QAAQC,MAAM;IACpF,MAAMC,MAAMV,MAAMW,MAAM,CAAc;IAEtC,IAAIC,QAAQC,GAAG,CAACC,QAAQ,KAAK,cAAc;QACzC,iDAAiD;QACjD,uGAAuG;QACvG,sDAAsD;QACtDd,MAAMe,SAAS,CAAC;YACd,IAAIC,WAAWN,IAAIO,OAAO;YAC1B,IAAIC,wBAAwB;YAC5B,GAAG;oBACUF;gBAAXA,WAAWA,CAAAA,0BAAAA,qBAAAA,+BAAAA,SAAUG,aAAa,cAAvBH,qCAAAA,0BAA2B;gBACtC,IAAIA,qBAAAA,+BAAAA,SAAUI,SAAS,CAACC,QAAQ,CAAC,iBAAiB;oBAChD;gBACF,OAAO,IAAIL,qBAAAA,+BAAAA,SAAUI,SAAS,CAACC,QAAQ,CAAC,iBAAiB;oBACvDH,wBAAwB;gBAC1B,OAAO,IAAIF,qBAAAA,+BAAAA,SAAUI,SAAS,CAACC,QAAQ,CAAC,qBAAqB;oBAC3DH,wBAAwB;gBAC1B,OAAO,IAAIF,qBAAAA,+BAAAA,SAAUI,SAAS,CAACC,QAAQ,CAAC,oBAAoB;oBAC1DH,wBAAwB;gBAC1B,OAAO,IAAIF,qBAAAA,+BAAAA,SAAUI,SAAS,CAACC,QAAQ,CAAC,qBAAqB;oBAC3DH,wBAAwB;gBAC1B;gBACA,IAAI;oBAAC;oBAAY;oBAAoB;iBAAgB,CAACI,QAAQ,CAACjB,gBAAgB;oBAC7E,IAAI;wBAAC;wBAAY;wBAAgB;wBAAe;qBAAe,CAACiB,QAAQ,CAACJ,wBAAwB;wBAC/F,MAAM,IAAIK,MACR,GAAGlB,cAAc,8BAA8B,EAAEa,sBAAsB,qDAAqD,CAAC;oBAEjI;gBACF,OAAO,IAAIb,kBAAkB,YAAY;oBACvC,IAAIa,0BAA0B,gBAAgB;wBAC5C,IAAIT,UAAUe,iBAAiBjB,WAAWU,OAAO,EAAEX,oBAAoBU,UAAU;4BAC/E;wBACF;wBACA,MAAM,IAAIO,MAAM,CAAC,mDAAmD,CAAC;oBACvE,OAAO,IAAI;wBAAC;wBAAY;wBAAgB;qBAAc,CAACD,QAAQ,CAACJ,wBAAwB;wBACtF,MAAM,IAAIK,MAAM,CAAC,sCAAsC,EAAEL,sBAAsB,CAAC,CAAC;oBACnF;gBACF;YACF,QAASF,YAAYA,cAAaV,2BAAAA,qCAAAA,eAAgBmB,IAAI,EAAE;QAC1D,GAAG;YAACpB;YAAeK;YAAKH;YAAYE;YAAQH;SAAe;IAC7D;IACA,OAAOI;AACT,EAAE;AAEF,MAAMc,mBAAmB,CAACE,SAA6BpB;IACrD,IAAIU,WAAWU,oBAAAA,8BAAAA,QAASP,aAAa;IACrC,MAAOH,YAAYA,cAAaV,2BAAAA,qCAAAA,eAAgBmB,IAAI,EAAE;QACpD,IAAIT,qBAAAA,+BAAAA,SAAUI,SAAS,CAACC,QAAQ,CAAC,qBAAqB;YACpD,OAAOL;QACT;YACWA;QAAXA,WAAWA,CAAAA,0BAAAA,qBAAAA,+BAAAA,SAAUG,aAAa,cAAvBH,qCAAAA,0BAA2B;IACxC;IACA,OAAO;AACT"}
@@ -20,6 +20,7 @@ const _menuContext = require("../../contexts/menuContext");
20
20
  const _reactaria = require("@fluentui/react-aria");
21
21
  const _keyboardkeys = require("@fluentui/keyboard-keys");
22
22
  const _menuSplitGroupContext = require("../../contexts/menuSplitGroupContext");
23
+ const _useValidateNesting = require("../../utils/useValidateNesting");
23
24
  const ChevronRightIcon = (0, _reacticons.bundleIcon)(_reacticons.ChevronRightFilled, _reacticons.ChevronRightRegular);
24
25
  const ChevronLeftIcon = (0, _reacticons.bundleIcon)(_reacticons.ChevronLeftFilled, _reacticons.ChevronLeftRegular);
25
26
  const useMenuItem_unstable = (props, ref)=>{
@@ -37,6 +38,7 @@ const useMenuItem_unstable = (props, ref)=>{
37
38
  const { dir } = (0, _reactsharedcontexts.useFluent_unstable)();
38
39
  const innerRef = _react.useRef(null);
39
40
  const dismissedWithKeyboardRef = _react.useRef(false);
41
+ const validateNestingRef = (0, _useValidateNesting.useValidateNesting)(getValidateNestingComponentName(props.role));
40
42
  const state = {
41
43
  hasSubmenu,
42
44
  disabled,
@@ -55,7 +57,7 @@ const useMenuItem_unstable = (props, ref)=>{
55
57
  ...rest,
56
58
  disabled: false,
57
59
  disabledFocusable: disabled,
58
- ref: (0, _reactutilities.useMergedRefs)(ref, innerRef),
60
+ ref: (0, _reactutilities.useMergedRefs)(ref, innerRef, validateNestingRef),
59
61
  onKeyDown: (0, _reactutilities.useEventCallback)((event)=>{
60
62
  var _props_onKeyDown;
61
63
  (_props_onKeyDown = props.onKeyDown) === null || _props_onKeyDown === void 0 ? void 0 : _props_onKeyDown.call(props, event);
@@ -147,3 +149,12 @@ const useIconAndCheckmarkAlignment = (options)=>{
147
149
  hasCheckmarks: hasCheckmarks && !isSplitItemTrigger
148
150
  };
149
151
  };
152
+ const getValidateNestingComponentName = (role)=>{
153
+ switch(role){
154
+ case 'menuitemcheckbox':
155
+ return 'MenuItemCheckbox';
156
+ case 'menuitemradio':
157
+ return 'MenuItemRadio';
158
+ }
159
+ return 'MenuItem';
160
+ };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MenuItem/useMenuItem.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n useEventCallback,\n useMergedRefs,\n getIntrinsicElementProps,\n slot,\n useIsomorphicLayoutEffect,\n} from '@fluentui/react-utilities';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useCharacterSearch } from './useCharacterSearch';\nimport { useMenuTriggerContext_unstable } from '../../contexts/menuTriggerContext';\nimport {\n ChevronRightFilled,\n ChevronRightRegular,\n ChevronLeftFilled,\n ChevronLeftRegular,\n bundleIcon,\n} from '@fluentui/react-icons';\nimport { useMenuListContext_unstable } from '../../contexts/menuListContext';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport type { MenuItemProps, MenuItemState } from './MenuItem.types';\nimport {\n ARIAButtonElement,\n ARIAButtonElementIntersection,\n ARIAButtonProps,\n useARIAButtonProps,\n} from '@fluentui/react-aria';\nimport { Enter, Space } from '@fluentui/keyboard-keys';\nimport { useIsInMenuSplitGroup, useMenuSplitGroupContext_unstable } from '../../contexts/menuSplitGroupContext';\n\nconst ChevronRightIcon = bundleIcon(ChevronRightFilled, ChevronRightRegular);\nconst ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref<ARIAButtonElement<'div'>>): MenuItemState => {\n const isSubmenuTrigger = useMenuTriggerContext_unstable();\n const persistOnClickContext = useMenuContext_unstable(context => context.persistOnItemClick);\n const {\n as = 'div',\n disabled = false,\n hasSubmenu = isSubmenuTrigger,\n persistOnClick = persistOnClickContext,\n content: _content, // `content` is a slot and it's type clashes with the HTMLElement `content` attribute\n ...rest\n } = props;\n const { hasIcons, hasCheckmarks } = useIconAndCheckmarkAlignment({ hasSubmenu });\n const setOpen = useMenuContext_unstable(context => context.setOpen);\n useNotifySplitItemMultiline({ multiline: !!props.subText, hasSubmenu });\n\n const { dir } = useFluent();\n const innerRef = React.useRef<ARIAButtonElementIntersection<'div'>>(null);\n const dismissedWithKeyboardRef = React.useRef(false);\n\n const state: MenuItemState = {\n hasSubmenu,\n disabled,\n persistOnClick,\n components: {\n root: 'div',\n icon: 'span',\n checkmark: 'span',\n submenuIndicator: 'span',\n content: 'span',\n secondaryContent: 'span',\n subText: 'span',\n },\n root: slot.always(\n getIntrinsicElementProps(\n as,\n useARIAButtonProps<'div', ARIAButtonProps<'div'>>(as, {\n role: 'menuitem',\n ...rest,\n disabled: false,\n disabledFocusable: disabled,\n ref: useMergedRefs(ref, innerRef) as React.Ref<ARIAButtonElementIntersection<'div'>>,\n onKeyDown: useEventCallback(event => {\n props.onKeyDown?.(event);\n if (!event.isDefaultPrevented() && (event.key === Space || event.key === Enter)) {\n dismissedWithKeyboardRef.current = true;\n }\n }),\n onMouseMove: useEventCallback(event => {\n if (event.currentTarget.ownerDocument.activeElement !== event.currentTarget) {\n innerRef.current?.focus();\n }\n\n props.onMouseMove?.(event);\n }),\n onClick: useEventCallback(event => {\n if (!hasSubmenu && !persistOnClick) {\n setOpen(event, {\n open: false,\n keyboard: dismissedWithKeyboardRef.current,\n bubble: true,\n type: 'menuItemClick',\n event,\n });\n dismissedWithKeyboardRef.current = false;\n }\n\n props.onClick?.(event);\n }),\n }),\n ),\n { elementType: 'div' },\n ),\n icon: slot.optional(props.icon, { renderByDefault: hasIcons, elementType: 'span' }),\n checkmark: slot.optional(props.checkmark, {\n renderByDefault: hasCheckmarks,\n elementType: 'span',\n }),\n submenuIndicator: slot.optional(props.submenuIndicator, {\n renderByDefault: hasSubmenu,\n defaultProps: {\n children: dir === 'ltr' ? <ChevronRightIcon /> : <ChevronLeftIcon />,\n },\n elementType: 'span',\n }),\n content: slot.optional(props.content, {\n renderByDefault: !!props.children,\n defaultProps: { children: props.children },\n elementType: 'span',\n }),\n secondaryContent: slot.optional(props.secondaryContent, { elementType: 'span' }),\n subText: slot.optional(props.subText, { elementType: 'span' }),\n };\n useCharacterSearch(state, innerRef);\n return state;\n};\n\n/**\n * MenuSplitGroup needs to apply extra styles when its main item is in multiline layout mode\n * Notify the parent MenuSplitGroup so that it can handle this case\n */\nconst useNotifySplitItemMultiline = (options: { hasSubmenu: boolean; multiline: boolean }) => {\n const { hasSubmenu, multiline } = options;\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n const { setMultiline } = useMenuSplitGroupContext_unstable();\n\n useIsomorphicLayoutEffect(() => {\n if (!isSplitItemTrigger) {\n setMultiline(multiline);\n }\n }, [setMultiline, multiline, isSplitItemTrigger]);\n};\n\nconst useIconAndCheckmarkAlignment = (options: { hasSubmenu: boolean }) => {\n const { hasSubmenu } = options;\n const hasIcons = useMenuListContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuListContext_unstable(context => context.hasCheckmarks);\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n return {\n hasIcons: hasIcons && !isSplitItemTrigger,\n hasCheckmarks: hasCheckmarks && !isSplitItemTrigger,\n };\n};\n"],"names":["React","useEventCallback","useMergedRefs","getIntrinsicElementProps","slot","useIsomorphicLayoutEffect","useFluent_unstable","useFluent","useCharacterSearch","useMenuTriggerContext_unstable","ChevronRightFilled","ChevronRightRegular","ChevronLeftFilled","ChevronLeftRegular","bundleIcon","useMenuListContext_unstable","useMenuContext_unstable","useARIAButtonProps","Enter","Space","useIsInMenuSplitGroup","useMenuSplitGroupContext_unstable","ChevronRightIcon","ChevronLeftIcon","useMenuItem_unstable","props","ref","isSubmenuTrigger","persistOnClickContext","context","persistOnItemClick","as","disabled","hasSubmenu","persistOnClick","content","_content","rest","hasIcons","hasCheckmarks","useIconAndCheckmarkAlignment","setOpen","useNotifySplitItemMultiline","multiline","subText","dir","innerRef","useRef","dismissedWithKeyboardRef","state","components","root","icon","checkmark","submenuIndicator","secondaryContent","always","role","disabledFocusable","onKeyDown","event","isDefaultPrevented","key","current","onMouseMove","currentTarget","ownerDocument","activeElement","focus","onClick","open","keyboard","bubble","type","elementType","optional","renderByDefault","defaultProps","children","options","isSplitItemTrigger","setMultiline"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/components/MenuItem/useMenuItem.tsx"],"sourcesContent":["import * as React from 'react';\nimport {\n useEventCallback,\n useMergedRefs,\n getIntrinsicElementProps,\n slot,\n useIsomorphicLayoutEffect,\n} from '@fluentui/react-utilities';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useCharacterSearch } from './useCharacterSearch';\nimport { useMenuTriggerContext_unstable } from '../../contexts/menuTriggerContext';\nimport {\n ChevronRightFilled,\n ChevronRightRegular,\n ChevronLeftFilled,\n ChevronLeftRegular,\n bundleIcon,\n} from '@fluentui/react-icons';\nimport { useMenuListContext_unstable } from '../../contexts/menuListContext';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport type { MenuItemProps, MenuItemState } from './MenuItem.types';\nimport {\n ARIAButtonElement,\n ARIAButtonElementIntersection,\n ARIAButtonProps,\n useARIAButtonProps,\n} from '@fluentui/react-aria';\nimport { Enter, Space } from '@fluentui/keyboard-keys';\nimport { useIsInMenuSplitGroup, useMenuSplitGroupContext_unstable } from '../../contexts/menuSplitGroupContext';\nimport { useValidateNesting } from '../../utils/useValidateNesting';\n\nconst ChevronRightIcon = bundleIcon(ChevronRightFilled, ChevronRightRegular);\nconst ChevronLeftIcon = bundleIcon(ChevronLeftFilled, ChevronLeftRegular);\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuItem_unstable = (props: MenuItemProps, ref: React.Ref<ARIAButtonElement<'div'>>): MenuItemState => {\n const isSubmenuTrigger = useMenuTriggerContext_unstable();\n const persistOnClickContext = useMenuContext_unstable(context => context.persistOnItemClick);\n const {\n as = 'div',\n disabled = false,\n hasSubmenu = isSubmenuTrigger,\n persistOnClick = persistOnClickContext,\n content: _content, // `content` is a slot and it's type clashes with the HTMLElement `content` attribute\n ...rest\n } = props;\n const { hasIcons, hasCheckmarks } = useIconAndCheckmarkAlignment({ hasSubmenu });\n const setOpen = useMenuContext_unstable(context => context.setOpen);\n useNotifySplitItemMultiline({ multiline: !!props.subText, hasSubmenu });\n\n const { dir } = useFluent();\n const innerRef = React.useRef<ARIAButtonElementIntersection<'div'>>(null);\n const dismissedWithKeyboardRef = React.useRef(false);\n\n const validateNestingRef = useValidateNesting(getValidateNestingComponentName(props.role));\n\n const state: MenuItemState = {\n hasSubmenu,\n disabled,\n persistOnClick,\n components: {\n root: 'div',\n icon: 'span',\n checkmark: 'span',\n submenuIndicator: 'span',\n content: 'span',\n secondaryContent: 'span',\n subText: 'span',\n },\n root: slot.always(\n getIntrinsicElementProps(\n as,\n useARIAButtonProps<'div', ARIAButtonProps<'div'>>(as, {\n role: 'menuitem',\n ...rest,\n disabled: false,\n disabledFocusable: disabled,\n ref: useMergedRefs(ref, innerRef, validateNestingRef) as React.Ref<ARIAButtonElementIntersection<'div'>>,\n onKeyDown: useEventCallback(event => {\n props.onKeyDown?.(event);\n if (!event.isDefaultPrevented() && (event.key === Space || event.key === Enter)) {\n dismissedWithKeyboardRef.current = true;\n }\n }),\n onMouseMove: useEventCallback(event => {\n if (event.currentTarget.ownerDocument.activeElement !== event.currentTarget) {\n innerRef.current?.focus();\n }\n\n props.onMouseMove?.(event);\n }),\n onClick: useEventCallback(event => {\n if (!hasSubmenu && !persistOnClick) {\n setOpen(event, {\n open: false,\n keyboard: dismissedWithKeyboardRef.current,\n bubble: true,\n type: 'menuItemClick',\n event,\n });\n dismissedWithKeyboardRef.current = false;\n }\n\n props.onClick?.(event);\n }),\n }),\n ),\n { elementType: 'div' },\n ),\n icon: slot.optional(props.icon, { renderByDefault: hasIcons, elementType: 'span' }),\n checkmark: slot.optional(props.checkmark, {\n renderByDefault: hasCheckmarks,\n elementType: 'span',\n }),\n submenuIndicator: slot.optional(props.submenuIndicator, {\n renderByDefault: hasSubmenu,\n defaultProps: {\n children: dir === 'ltr' ? <ChevronRightIcon /> : <ChevronLeftIcon />,\n },\n elementType: 'span',\n }),\n content: slot.optional(props.content, {\n renderByDefault: !!props.children,\n defaultProps: { children: props.children },\n elementType: 'span',\n }),\n secondaryContent: slot.optional(props.secondaryContent, { elementType: 'span' }),\n subText: slot.optional(props.subText, { elementType: 'span' }),\n };\n useCharacterSearch(state, innerRef);\n return state;\n};\n\n/**\n * MenuSplitGroup needs to apply extra styles when its main item is in multiline layout mode\n * Notify the parent MenuSplitGroup so that it can handle this case\n */\nconst useNotifySplitItemMultiline = (options: { hasSubmenu: boolean; multiline: boolean }) => {\n const { hasSubmenu, multiline } = options;\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n const { setMultiline } = useMenuSplitGroupContext_unstable();\n\n useIsomorphicLayoutEffect(() => {\n if (!isSplitItemTrigger) {\n setMultiline(multiline);\n }\n }, [setMultiline, multiline, isSplitItemTrigger]);\n};\n\nconst useIconAndCheckmarkAlignment = (options: { hasSubmenu: boolean }) => {\n const { hasSubmenu } = options;\n const hasIcons = useMenuListContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuListContext_unstable(context => context.hasCheckmarks);\n const isSplitItemTrigger = useIsInMenuSplitGroup() && hasSubmenu;\n\n return {\n hasIcons: hasIcons && !isSplitItemTrigger,\n hasCheckmarks: hasCheckmarks && !isSplitItemTrigger,\n };\n};\n\nconst getValidateNestingComponentName = (role?: string) => {\n switch (role) {\n case 'menuitemcheckbox':\n return 'MenuItemCheckbox';\n case 'menuitemradio':\n return 'MenuItemRadio';\n }\n return 'MenuItem';\n};\n"],"names":["React","useEventCallback","useMergedRefs","getIntrinsicElementProps","slot","useIsomorphicLayoutEffect","useFluent_unstable","useFluent","useCharacterSearch","useMenuTriggerContext_unstable","ChevronRightFilled","ChevronRightRegular","ChevronLeftFilled","ChevronLeftRegular","bundleIcon","useMenuListContext_unstable","useMenuContext_unstable","useARIAButtonProps","Enter","Space","useIsInMenuSplitGroup","useMenuSplitGroupContext_unstable","useValidateNesting","ChevronRightIcon","ChevronLeftIcon","useMenuItem_unstable","props","ref","isSubmenuTrigger","persistOnClickContext","context","persistOnItemClick","as","disabled","hasSubmenu","persistOnClick","content","_content","rest","hasIcons","hasCheckmarks","useIconAndCheckmarkAlignment","setOpen","useNotifySplitItemMultiline","multiline","subText","dir","innerRef","useRef","dismissedWithKeyboardRef","validateNestingRef","getValidateNestingComponentName","role","state","components","root","icon","checkmark","submenuIndicator","secondaryContent","always","disabledFocusable","onKeyDown","event","isDefaultPrevented","key","current","onMouseMove","currentTarget","ownerDocument","activeElement","focus","onClick","open","keyboard","bubble","type","elementType","optional","renderByDefault","defaultProps","children","options","isSplitItemTrigger","setMultiline"],"mappings":"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"}
@@ -15,6 +15,7 @@ const _reacttabster = require("@fluentui/react-tabster");
15
15
  const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
16
16
  const _reactcontextselector = require("@fluentui/react-context-selector");
17
17
  const _menuContext = require("../../contexts/menuContext");
18
+ const _useValidateNesting = require("../../utils/useValidateNesting");
18
19
  const useMenuList_unstable = (props, ref)=>{
19
20
  const { findAllFocusable } = (0, _reacttabster.useFocusFinders)();
20
21
  const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
@@ -29,6 +30,7 @@ const useMenuList_unstable = (props, ref)=>{
29
30
  console.warn('You are using both MenuList and Menu props, we recommend you to use Menu props when available');
30
31
  }
31
32
  const innerRef = _react.useRef(null);
33
+ const validateNestingRef = (0, _useValidateNesting.useValidateNesting)('MenuList');
32
34
  _react.useEffect(()=>{
33
35
  const element = innerRef.current;
34
36
  if (hasMenuContext && targetDocument && element) {
@@ -138,7 +140,7 @@ const useMenuList_unstable = (props, ref)=>{
138
140
  // FIXME:
139
141
  // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`
140
142
  // but since it would be a breaking change to fix it, we are casting ref to it's proper type
141
- ref: (0, _reactutilities.useMergedRefs)(ref, innerRef),
143
+ ref: (0, _reactutilities.useMergedRefs)(ref, innerRef, validateNestingRef),
142
144
  role: 'menu',
143
145
  'aria-labelledby': menuContext.triggerId,
144
146
  ...focusAttributes,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/MenuList/useMenuList.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n useMergedRefs,\n useEventCallback,\n useControllableState,\n getIntrinsicElementProps,\n slot,\n} from '@fluentui/react-utilities';\nimport {\n useArrowNavigationGroup,\n useFocusFinders,\n TabsterMoveFocusEventName,\n type TabsterMoveFocusEvent,\n} from '@fluentui/react-tabster';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useHasParentContext } from '@fluentui/react-context-selector';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport { MenuContext } from '../../contexts/menuContext';\nimport type { MenuListProps, MenuListState } from './MenuList.types';\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuList_unstable = (props: MenuListProps, ref: React.Ref<HTMLElement>): MenuListState => {\n const { findAllFocusable } = useFocusFinders();\n const { targetDocument } = useFluent();\n const menuContext = useMenuContextSelectors();\n const hasMenuContext = useHasParentContext(MenuContext);\n const focusAttributes = useArrowNavigationGroup({ circular: true });\n\n if (usingPropsAndMenuContext(props, menuContext, hasMenuContext)) {\n // TODO throw warnings in development safely\n // eslint-disable-next-line no-console\n console.warn('You are using both MenuList and Menu props, we recommend you to use Menu props when available');\n }\n\n const innerRef = React.useRef<HTMLElement>(null);\n\n React.useEffect(() => {\n const element = innerRef.current;\n\n if (hasMenuContext && targetDocument && element) {\n const onTabsterMoveFocus = (e: TabsterMoveFocusEvent) => {\n const nextElement = e.detail.next;\n\n if (nextElement && element.contains(targetDocument.activeElement) && !element.contains(nextElement)) {\n // Preventing Tabster from handling Tab press, useMenuPopover will handle it.\n e.preventDefault();\n }\n };\n\n targetDocument.addEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n\n return () => {\n targetDocument.removeEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n };\n }\n }, [innerRef, targetDocument, hasMenuContext]);\n\n const setFocusByFirstCharacter = React.useCallback(\n (e: React.KeyboardEvent<HTMLElement>, itemEl: HTMLElement) => {\n // TODO use some kind of children registration to reduce dependency on DOM roles\n const acceptedRoles = ['menuitem', 'menuitemcheckbox', 'menuitemradio'];\n if (!innerRef.current) {\n return;\n }\n\n const menuItems = findAllFocusable(\n innerRef.current,\n (el: HTMLElement) => el.hasAttribute('role') && acceptedRoles.indexOf(el.getAttribute('role')!) !== -1,\n );\n\n let startIndex = menuItems.indexOf(itemEl) + 1;\n if (startIndex === menuItems.length) {\n startIndex = 0;\n }\n\n const firstChars = menuItems.map(menuItem => menuItem.textContent?.charAt(0).toLowerCase());\n const char = e.key.toLowerCase();\n\n const getIndexFirstChars = (start: number, firstChar: string) => {\n for (let i = start; i < firstChars.length; i++) {\n if (char === firstChars[i]) {\n return i;\n }\n }\n return -1;\n };\n\n // Check remaining slots in the menu\n let index = getIndexFirstChars(startIndex, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n menuItems[index].focus();\n }\n },\n [findAllFocusable],\n );\n\n const [checkedValues, setCheckedValues] = useControllableState({\n state: props.checkedValues ?? (hasMenuContext ? menuContext.checkedValues : undefined),\n defaultState: props.defaultCheckedValues,\n initialState: {},\n });\n\n const handleCheckedValueChange =\n props.onCheckedValueChange ?? (hasMenuContext ? menuContext.onCheckedValueChange : undefined);\n\n const toggleCheckbox = useEventCallback(\n (e: React.MouseEvent | React.KeyboardEvent, name: string, value: string, checked: boolean) => {\n const checkedItems = checkedValues?.[name] || [];\n const newCheckedItems = [...checkedItems];\n if (checked) {\n newCheckedItems.splice(newCheckedItems.indexOf(value), 1);\n } else {\n newCheckedItems.push(value);\n }\n\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n },\n );\n\n const selectRadio = useEventCallback((e: React.MouseEvent | React.KeyboardEvent, name: string, value: string) => {\n const newCheckedItems = [value];\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n });\n\n return {\n components: {\n root: 'div',\n },\n root: slot.always(\n getIntrinsicElementProps('div', {\n // FIXME:\n // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`\n // but since it would be a breaking change to fix it, we are casting ref to it's proper type\n ref: useMergedRefs(ref, innerRef) as React.Ref<HTMLDivElement>,\n role: 'menu',\n 'aria-labelledby': menuContext.triggerId,\n ...focusAttributes,\n ...props,\n }),\n { elementType: 'div' },\n ),\n hasIcons: menuContext.hasIcons || false,\n hasCheckmarks: menuContext.hasCheckmarks || false,\n checkedValues,\n hasMenuContext,\n setFocusByFirstCharacter,\n selectRadio,\n toggleCheckbox,\n };\n};\n\n/**\n * Adds some sugar to fetching multiple context selector values\n */\nconst useMenuContextSelectors = () => {\n const checkedValues = useMenuContext_unstable(context => context.checkedValues);\n const onCheckedValueChange = useMenuContext_unstable(context => context.onCheckedValueChange);\n const triggerId = useMenuContext_unstable(context => context.triggerId);\n const hasIcons = useMenuContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuContext_unstable(context => context.hasCheckmarks);\n\n return {\n checkedValues,\n onCheckedValueChange,\n triggerId,\n hasIcons,\n hasCheckmarks,\n };\n};\n\n/**\n * Helper function to detect if props and MenuContext values are both used\n */\nconst usingPropsAndMenuContext = (\n props: MenuListProps,\n contextValue: ReturnType<typeof useMenuContextSelectors>,\n hasMenuContext: boolean,\n) => {\n let isUsingPropsAndContext = false;\n for (const val in contextValue) {\n if (props[val as keyof Omit<typeof contextValue, 'hasMenuContext' | 'onCheckedValueChange' | 'triggerId'>]) {\n isUsingPropsAndContext = true;\n }\n }\n\n return hasMenuContext && isUsingPropsAndContext;\n};\n"],"names":["React","useMergedRefs","useEventCallback","useControllableState","getIntrinsicElementProps","slot","useArrowNavigationGroup","useFocusFinders","TabsterMoveFocusEventName","useFluent_unstable","useFluent","useHasParentContext","useMenuContext_unstable","MenuContext","useMenuList_unstable","props","ref","findAllFocusable","targetDocument","menuContext","useMenuContextSelectors","hasMenuContext","focusAttributes","circular","usingPropsAndMenuContext","console","warn","innerRef","useRef","useEffect","element","current","onTabsterMoveFocus","e","nextElement","detail","next","contains","activeElement","preventDefault","addEventListener","removeEventListener","setFocusByFirstCharacter","useCallback","itemEl","acceptedRoles","menuItems","el","hasAttribute","indexOf","getAttribute","startIndex","length","firstChars","map","menuItem","textContent","charAt","toLowerCase","char","key","getIndexFirstChars","start","firstChar","i","index","focus","checkedValues","setCheckedValues","state","undefined","defaultState","defaultCheckedValues","initialState","handleCheckedValueChange","onCheckedValueChange","toggleCheckbox","name","value","checked","checkedItems","newCheckedItems","splice","push","s","selectRadio","components","root","always","role","triggerId","elementType","hasIcons","hasCheckmarks","context","contextValue","isUsingPropsAndContext","val"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/components/MenuList/useMenuList.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n useMergedRefs,\n useEventCallback,\n useControllableState,\n getIntrinsicElementProps,\n slot,\n} from '@fluentui/react-utilities';\nimport {\n useArrowNavigationGroup,\n useFocusFinders,\n TabsterMoveFocusEventName,\n type TabsterMoveFocusEvent,\n} from '@fluentui/react-tabster';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\nimport { useHasParentContext } from '@fluentui/react-context-selector';\nimport { useMenuContext_unstable } from '../../contexts/menuContext';\nimport { MenuContext } from '../../contexts/menuContext';\nimport type { MenuListProps, MenuListState } from './MenuList.types';\nimport { useValidateNesting } from '../../utils/useValidateNesting';\n\n/**\n * Returns the props and state required to render the component\n */\nexport const useMenuList_unstable = (props: MenuListProps, ref: React.Ref<HTMLElement>): MenuListState => {\n const { findAllFocusable } = useFocusFinders();\n const { targetDocument } = useFluent();\n const menuContext = useMenuContextSelectors();\n const hasMenuContext = useHasParentContext(MenuContext);\n const focusAttributes = useArrowNavigationGroup({ circular: true });\n\n if (usingPropsAndMenuContext(props, menuContext, hasMenuContext)) {\n // TODO throw warnings in development safely\n // eslint-disable-next-line no-console\n console.warn('You are using both MenuList and Menu props, we recommend you to use Menu props when available');\n }\n\n const innerRef = React.useRef<HTMLElement>(null);\n const validateNestingRef = useValidateNesting('MenuList');\n\n React.useEffect(() => {\n const element = innerRef.current;\n\n if (hasMenuContext && targetDocument && element) {\n const onTabsterMoveFocus = (e: TabsterMoveFocusEvent) => {\n const nextElement = e.detail.next;\n\n if (nextElement && element.contains(targetDocument.activeElement) && !element.contains(nextElement)) {\n // Preventing Tabster from handling Tab press, useMenuPopover will handle it.\n e.preventDefault();\n }\n };\n\n targetDocument.addEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n\n return () => {\n targetDocument.removeEventListener(TabsterMoveFocusEventName, onTabsterMoveFocus);\n };\n }\n }, [innerRef, targetDocument, hasMenuContext]);\n\n const setFocusByFirstCharacter = React.useCallback(\n (e: React.KeyboardEvent<HTMLElement>, itemEl: HTMLElement) => {\n // TODO use some kind of children registration to reduce dependency on DOM roles\n const acceptedRoles = ['menuitem', 'menuitemcheckbox', 'menuitemradio'];\n if (!innerRef.current) {\n return;\n }\n\n const menuItems = findAllFocusable(\n innerRef.current,\n (el: HTMLElement) => el.hasAttribute('role') && acceptedRoles.indexOf(el.getAttribute('role')!) !== -1,\n );\n\n let startIndex = menuItems.indexOf(itemEl) + 1;\n if (startIndex === menuItems.length) {\n startIndex = 0;\n }\n\n const firstChars = menuItems.map(menuItem => menuItem.textContent?.charAt(0).toLowerCase());\n const char = e.key.toLowerCase();\n\n const getIndexFirstChars = (start: number, firstChar: string) => {\n for (let i = start; i < firstChars.length; i++) {\n if (char === firstChars[i]) {\n return i;\n }\n }\n return -1;\n };\n\n // Check remaining slots in the menu\n let index = getIndexFirstChars(startIndex, char);\n\n // If not found in remaining slots, check from beginning\n if (index === -1) {\n index = getIndexFirstChars(0, char);\n }\n\n // If match was found...\n if (index > -1) {\n menuItems[index].focus();\n }\n },\n [findAllFocusable],\n );\n\n const [checkedValues, setCheckedValues] = useControllableState({\n state: props.checkedValues ?? (hasMenuContext ? menuContext.checkedValues : undefined),\n defaultState: props.defaultCheckedValues,\n initialState: {},\n });\n\n const handleCheckedValueChange =\n props.onCheckedValueChange ?? (hasMenuContext ? menuContext.onCheckedValueChange : undefined);\n\n const toggleCheckbox = useEventCallback(\n (e: React.MouseEvent | React.KeyboardEvent, name: string, value: string, checked: boolean) => {\n const checkedItems = checkedValues?.[name] || [];\n const newCheckedItems = [...checkedItems];\n if (checked) {\n newCheckedItems.splice(newCheckedItems.indexOf(value), 1);\n } else {\n newCheckedItems.push(value);\n }\n\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n },\n );\n\n const selectRadio = useEventCallback((e: React.MouseEvent | React.KeyboardEvent, name: string, value: string) => {\n const newCheckedItems = [value];\n setCheckedValues(s => ({ ...s, [name]: newCheckedItems }));\n handleCheckedValueChange?.(e, { name, checkedItems: newCheckedItems });\n });\n\n return {\n components: {\n root: 'div',\n },\n root: slot.always(\n getIntrinsicElementProps('div', {\n // FIXME:\n // `ref` is wrongly assigned to be `HTMLElement` instead of `HTMLDivElement`\n // but since it would be a breaking change to fix it, we are casting ref to it's proper type\n ref: useMergedRefs(ref, innerRef, validateNestingRef) as React.Ref<HTMLDivElement>,\n role: 'menu',\n 'aria-labelledby': menuContext.triggerId,\n ...focusAttributes,\n ...props,\n }),\n { elementType: 'div' },\n ),\n hasIcons: menuContext.hasIcons || false,\n hasCheckmarks: menuContext.hasCheckmarks || false,\n checkedValues,\n hasMenuContext,\n setFocusByFirstCharacter,\n selectRadio,\n toggleCheckbox,\n };\n};\n\n/**\n * Adds some sugar to fetching multiple context selector values\n */\nconst useMenuContextSelectors = () => {\n const checkedValues = useMenuContext_unstable(context => context.checkedValues);\n const onCheckedValueChange = useMenuContext_unstable(context => context.onCheckedValueChange);\n const triggerId = useMenuContext_unstable(context => context.triggerId);\n const hasIcons = useMenuContext_unstable(context => context.hasIcons);\n const hasCheckmarks = useMenuContext_unstable(context => context.hasCheckmarks);\n\n return {\n checkedValues,\n onCheckedValueChange,\n triggerId,\n hasIcons,\n hasCheckmarks,\n };\n};\n\n/**\n * Helper function to detect if props and MenuContext values are both used\n */\nconst usingPropsAndMenuContext = (\n props: MenuListProps,\n contextValue: ReturnType<typeof useMenuContextSelectors>,\n hasMenuContext: boolean,\n) => {\n let isUsingPropsAndContext = false;\n for (const val in contextValue) {\n if (props[val as keyof Omit<typeof contextValue, 'hasMenuContext' | 'onCheckedValueChange' | 'triggerId'>]) {\n isUsingPropsAndContext = true;\n }\n }\n\n return hasMenuContext && isUsingPropsAndContext;\n};\n"],"names":["React","useMergedRefs","useEventCallback","useControllableState","getIntrinsicElementProps","slot","useArrowNavigationGroup","useFocusFinders","TabsterMoveFocusEventName","useFluent_unstable","useFluent","useHasParentContext","useMenuContext_unstable","MenuContext","useValidateNesting","useMenuList_unstable","props","ref","findAllFocusable","targetDocument","menuContext","useMenuContextSelectors","hasMenuContext","focusAttributes","circular","usingPropsAndMenuContext","console","warn","innerRef","useRef","validateNestingRef","useEffect","element","current","onTabsterMoveFocus","e","nextElement","detail","next","contains","activeElement","preventDefault","addEventListener","removeEventListener","setFocusByFirstCharacter","useCallback","itemEl","acceptedRoles","menuItems","el","hasAttribute","indexOf","getAttribute","startIndex","length","firstChars","map","menuItem","textContent","charAt","toLowerCase","char","key","getIndexFirstChars","start","firstChar","i","index","focus","checkedValues","setCheckedValues","state","undefined","defaultState","defaultCheckedValues","initialState","handleCheckedValueChange","onCheckedValueChange","toggleCheckbox","name","value","checked","checkedItems","newCheckedItems","splice","push","s","selectRadio","components","root","always","role","triggerId","elementType","hasIcons","hasCheckmarks","context","contextValue","isUsingPropsAndContext","val"],"mappings":"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"}
@@ -26,8 +26,12 @@ _export(exports, {
26
26
  },
27
27
  useOnMenuSafeZoneTimeout: function() {
28
28
  return _useOnMenuSafeZoneTimeout.useOnMenuSafeZoneTimeout;
29
+ },
30
+ useValidateNesting: function() {
31
+ return _useValidateNesting.useValidateNesting;
29
32
  }
30
33
  });
31
34
  const _useOnMenuEnter = require("./useOnMenuEnter");
32
35
  const _useIsSubmenu = require("./useIsSubmenu");
36
+ const _useValidateNesting = require("./useValidateNesting");
33
37
  const _useOnMenuSafeZoneTimeout = require("./useOnMenuSafeZoneTimeout");
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/index.ts"],"sourcesContent":["export { MENU_ENTER_EVENT, dispatchMenuEnterEvent, useOnMenuMouseEnter } from './useOnMenuEnter';\nexport { useIsSubmenu } from './useIsSubmenu';\nexport { MENU_SAFEZONE_TIMEOUT_EVENT, useOnMenuSafeZoneTimeout } from './useOnMenuSafeZoneTimeout';\n"],"names":["MENU_ENTER_EVENT","dispatchMenuEnterEvent","useOnMenuMouseEnter","useIsSubmenu","MENU_SAFEZONE_TIMEOUT_EVENT","useOnMenuSafeZoneTimeout"],"mappings":";;;;;;;;;;;;eAASA,gCAAgB;;;eAEhBI,qDAA2B;;;eAFTH,sCAAsB;;;eACxCE,0BAAY;;;eAD8BD,mCAAmB;;;eAEhCG,kDAAwB;;;gCAFgB,mBAAmB;8BACpE,iBAAiB;0CACwB,6BAA6B"}
1
+ {"version":3,"sources":["../src/utils/index.ts"],"sourcesContent":["export { MENU_ENTER_EVENT, dispatchMenuEnterEvent, useOnMenuMouseEnter } from './useOnMenuEnter';\nexport { useIsSubmenu } from './useIsSubmenu';\nexport { useValidateNesting } from './useValidateNesting';\nexport { MENU_SAFEZONE_TIMEOUT_EVENT, useOnMenuSafeZoneTimeout } from './useOnMenuSafeZoneTimeout';\n"],"names":["MENU_ENTER_EVENT","dispatchMenuEnterEvent","useOnMenuMouseEnter","useIsSubmenu","useValidateNesting","MENU_SAFEZONE_TIMEOUT_EVENT","useOnMenuSafeZoneTimeout"],"mappings":";;;;;;;;;;;;eAASA,gCAAgB;;;eAGhBK,qDAA2B;;;eAHTJ,sCAAsB;;;eACxCE,0BAAY;;;eAD8BD,mCAAmB;;;eAGhCI,kDAAwB;;;eADrDF,sCAAkB;;;gCAFmD,mBAAmB;8BACpE,iBAAiB;oCACX,uBAAuB;0CACY,6BAA6B"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "useValidateNesting", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return useValidateNesting;
9
+ }
10
+ });
11
+ const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
12
+ const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
13
+ const _reactsharedcontexts = require("@fluentui/react-shared-contexts");
14
+ const _menuContext = require("../contexts/menuContext");
15
+ const useValidateNesting = (componentName)=>{
16
+ 'use no memo';
17
+ const { targetDocument } = (0, _reactsharedcontexts.useFluent_unstable)();
18
+ const triggerRef = (0, _menuContext.useMenuContext_unstable)((context)=>context.triggerRef);
19
+ const inline = (0, _menuContext.useMenuContext_unstable)((context)=>context.inline);
20
+ const ref = _react.useRef(null);
21
+ if (process.env.NODE_ENV !== 'production') {
22
+ // This check should run only in development mode
23
+ // It's okay to disable the ESLint rule because we ar checking env variable statically (not at runtime)
24
+ // eslint-disable-next-line react-hooks/rules-of-hooks
25
+ _react.useEffect(()=>{
26
+ let ancestor = ref.current;
27
+ let ancestorComponentName = '';
28
+ do {
29
+ var _ancestor_parentElement;
30
+ ancestor = (_ancestor_parentElement = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parentElement) !== null && _ancestor_parentElement !== void 0 ? _ancestor_parentElement : null;
31
+ if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuList')) {
32
+ break;
33
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGrid')) {
34
+ ancestorComponentName = 'MenuGrid';
35
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridItem')) {
36
+ ancestorComponentName = 'MenuGridItem';
37
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridRow')) {
38
+ ancestorComponentName = 'MenuGridRow';
39
+ } else if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridCell')) {
40
+ ancestorComponentName = 'MenuGridCell';
41
+ }
42
+ if ([
43
+ 'MenuItem',
44
+ 'MenuItemCheckbox',
45
+ 'MenuItemRadio'
46
+ ].includes(componentName)) {
47
+ if ([
48
+ 'MenuGrid',
49
+ 'MenuGridItem',
50
+ 'MenuGridRow',
51
+ 'MenuGridCell'
52
+ ].includes(ancestorComponentName)) {
53
+ throw new Error(`${componentName} is incorrectly nested within ${ancestorComponentName}. You probably want to wrap it in a MenuList instead.`);
54
+ }
55
+ } else if (componentName === 'MenuList') {
56
+ if (ancestorComponentName === 'MenuGridCell') {
57
+ if (inline && getCellOfTrigger(triggerRef.current, targetDocument) === ancestor) {
58
+ break;
59
+ }
60
+ throw new Error(`MenuList is incorrectly nested within MenuGridCell.`);
61
+ } else if ([
62
+ 'MenuGrid',
63
+ 'MenuGridItem',
64
+ 'MenuGridRow'
65
+ ].includes(ancestorComponentName)) {
66
+ throw new Error(`MenuList is incorrectly nested within ${ancestorComponentName}.`);
67
+ }
68
+ }
69
+ }while (ancestor && ancestor !== (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body))
70
+ }, [
71
+ componentName,
72
+ ref,
73
+ triggerRef,
74
+ inline,
75
+ targetDocument
76
+ ]);
77
+ }
78
+ return ref;
79
+ };
80
+ const getCellOfTrigger = (trigger, targetDocument)=>{
81
+ let ancestor = trigger === null || trigger === void 0 ? void 0 : trigger.parentElement;
82
+ while(ancestor && ancestor !== (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body)){
83
+ if (ancestor === null || ancestor === void 0 ? void 0 : ancestor.classList.contains('fui-MenuGridCell')) {
84
+ return ancestor;
85
+ }
86
+ var _ancestor_parentElement;
87
+ ancestor = (_ancestor_parentElement = ancestor === null || ancestor === void 0 ? void 0 : ancestor.parentElement) !== null && _ancestor_parentElement !== void 0 ? _ancestor_parentElement : null;
88
+ }
89
+ return null;
90
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/useValidateNesting.ts"],"sourcesContent":["import * as React from 'react';\nimport { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';\n\nimport type { MenuContextValue } from '../contexts/menuContext';\nimport { useMenuContext_unstable } from '../contexts/menuContext';\n\ntype NestingComponentName = 'MenuList' | 'MenuItem' | 'MenuItemCheckbox' | 'MenuItemRadio';\n\nexport const useValidateNesting = (componentName: NestingComponentName): React.RefObject<HTMLElement> => {\n 'use no memo';\n\n const { targetDocument } = useFluent();\n const triggerRef = useMenuContext_unstable((context: MenuContextValue) => context.triggerRef);\n const inline = useMenuContext_unstable((context: MenuContextValue) => context.inline);\n const ref = React.useRef<HTMLElement>(null);\n\n if (process.env.NODE_ENV !== 'production') {\n // This check should run only in development mode\n // It's okay to disable the ESLint rule because we ar checking env variable statically (not at runtime)\n // eslint-disable-next-line react-hooks/rules-of-hooks\n React.useEffect(() => {\n let ancestor = ref.current;\n let ancestorComponentName = '';\n do {\n ancestor = ancestor?.parentElement ?? null;\n if (ancestor?.classList.contains('fui-MenuList')) {\n break;\n } else if (ancestor?.classList.contains('fui-MenuGrid')) {\n ancestorComponentName = 'MenuGrid';\n } else if (ancestor?.classList.contains('fui-MenuGridItem')) {\n ancestorComponentName = 'MenuGridItem';\n } else if (ancestor?.classList.contains('fui-MenuGridRow')) {\n ancestorComponentName = 'MenuGridRow';\n } else if (ancestor?.classList.contains('fui-MenuGridCell')) {\n ancestorComponentName = 'MenuGridCell';\n }\n if (['MenuItem', 'MenuItemCheckbox', 'MenuItemRadio'].includes(componentName)) {\n if (['MenuGrid', 'MenuGridItem', 'MenuGridRow', 'MenuGridCell'].includes(ancestorComponentName)) {\n throw new Error(\n `${componentName} is incorrectly nested within ${ancestorComponentName}. You probably want to wrap it in a MenuList instead.`,\n );\n }\n } else if (componentName === 'MenuList') {\n if (ancestorComponentName === 'MenuGridCell') {\n if (inline && getCellOfTrigger(triggerRef.current, targetDocument) === ancestor) {\n break;\n }\n throw new Error(`MenuList is incorrectly nested within MenuGridCell.`);\n } else if (['MenuGrid', 'MenuGridItem', 'MenuGridRow'].includes(ancestorComponentName)) {\n throw new Error(`MenuList is incorrectly nested within ${ancestorComponentName}.`);\n }\n }\n } while (ancestor && ancestor !== targetDocument?.body);\n }, [componentName, ref, triggerRef, inline, targetDocument]);\n }\n return ref;\n};\n\nconst getCellOfTrigger = (trigger: HTMLElement | null, targetDocument?: Document): HTMLElement | null => {\n let ancestor = trigger?.parentElement;\n while (ancestor && ancestor !== targetDocument?.body) {\n if (ancestor?.classList.contains('fui-MenuGridCell')) {\n return ancestor;\n }\n ancestor = ancestor?.parentElement ?? null;\n }\n return null;\n};\n"],"names":["React","useFluent_unstable","useFluent","useMenuContext_unstable","useValidateNesting","componentName","targetDocument","triggerRef","context","inline","ref","useRef","process","env","NODE_ENV","useEffect","ancestor","current","ancestorComponentName","parentElement","classList","contains","includes","Error","getCellOfTrigger","body","trigger"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluentui/react-menu",
3
- "version": "0.0.0-nightly-20250917-0405.1",
3
+ "version": "0.0.0-nightly-20250919-0407.1",
4
4
  "description": "Fluent UI menu component",
5
5
  "main": "lib-commonjs/index.js",
6
6
  "module": "lib/index.js",
@@ -12,25 +12,25 @@
12
12
  },
13
13
  "license": "MIT",
14
14
  "devDependencies": {
15
- "@fluentui/react-provider": "0.0.0-nightly-20250917-0405.1",
15
+ "@fluentui/react-provider": "0.0.0-nightly-20250919-0407.1",
16
16
  "@fluentui/eslint-plugin": "*",
17
- "@fluentui/react-conformance": "0.0.0-nightly-20250917-0405.1",
18
- "@fluentui/react-conformance-griffel": "0.0.0-nightly-20250917-0405.1",
17
+ "@fluentui/react-conformance": "0.0.0-nightly-20250919-0407.1",
18
+ "@fluentui/react-conformance-griffel": "0.0.0-nightly-20250919-0407.1",
19
19
  "@fluentui/scripts-api-extractor": "*",
20
20
  "@fluentui/scripts-cypress": "*"
21
21
  },
22
22
  "dependencies": {
23
- "@fluentui/keyboard-keys": "0.0.0-nightly-20250917-0405.1",
24
- "@fluentui/react-aria": "0.0.0-nightly-20250917-0405.1",
25
- "@fluentui/react-context-selector": "0.0.0-nightly-20250917-0405.1",
23
+ "@fluentui/keyboard-keys": "0.0.0-nightly-20250919-0407.1",
24
+ "@fluentui/react-aria": "0.0.0-nightly-20250919-0407.1",
25
+ "@fluentui/react-context-selector": "0.0.0-nightly-20250919-0407.1",
26
26
  "@fluentui/react-icons": "^2.0.245",
27
- "@fluentui/react-portal": "0.0.0-nightly-20250917-0405.1",
28
- "@fluentui/react-positioning": "0.0.0-nightly-20250917-0405.1",
29
- "@fluentui/react-shared-contexts": "0.0.0-nightly-20250917-0405.1",
30
- "@fluentui/react-tabster": "0.0.0-nightly-20250917-0405.1",
31
- "@fluentui/react-theme": "0.0.0-nightly-20250917-0405.1",
32
- "@fluentui/react-utilities": "0.0.0-nightly-20250917-0405.1",
33
- "@fluentui/react-jsx-runtime": "0.0.0-nightly-20250917-0405.1",
27
+ "@fluentui/react-portal": "0.0.0-nightly-20250919-0407.1",
28
+ "@fluentui/react-positioning": "0.0.0-nightly-20250919-0407.1",
29
+ "@fluentui/react-shared-contexts": "0.0.0-nightly-20250919-0407.1",
30
+ "@fluentui/react-tabster": "0.0.0-nightly-20250919-0407.1",
31
+ "@fluentui/react-theme": "0.0.0-nightly-20250919-0407.1",
32
+ "@fluentui/react-utilities": "0.0.0-nightly-20250919-0407.1",
33
+ "@fluentui/react-jsx-runtime": "0.0.0-nightly-20250919-0407.1",
34
34
  "@griffel/react": "^1.5.22",
35
35
  "@swc/helpers": "^0.5.1"
36
36
  },