Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
import * as React from 'react';
import { useMenu_unstable } from './useMenu';
import { useMenuContextValues_unstable } from './useMenuContextValues';
import { renderMenu_unstable } from './renderMenu';
/**
* Wrapper component that manages state for a popup MenuList and a MenuTrigger
*/ export const Menu = (props)=>{
const state = useMenu_unstable(props);
const contextValues = useMenuContextValues_unstable(state);
return renderMenu_unstable(state, contextValues);
};
Menu.displayName = 'Menu';
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Menu/Menu.tsx"],"sourcesContent":["import * as React from 'react';\nimport { useMenu_unstable } from './useMenu';\nimport { useMenuContextValues_unstable } from './useMenuContextValues';\nimport { renderMenu_unstable } from './renderMenu';\nimport type { MenuProps } from './Menu.types';\n\n/**\n * Wrapper component that manages state for a popup MenuList and a MenuTrigger\n */\nexport const Menu: React.FC<MenuProps> = props => {\n const state = useMenu_unstable(props);\n const contextValues = useMenuContextValues_unstable(state);\n\n return renderMenu_unstable(state, contextValues);\n};\n\nMenu.displayName = 'Menu';\n"],"names":["React","useMenu_unstable","useMenuContextValues_unstable","renderMenu_unstable","Menu","props","state","contextValues","displayName"],"rangeMappings":";;;;;;;;;;;","mappings":"AAAA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,gBAAgB,QAAQ,YAAY;AAC7C,SAASC,6BAA6B,QAAQ,yBAAyB;AACvE,SAASC,mBAAmB,QAAQ,eAAe;AAGnD;;CAEC,GACD,OAAO,MAAMC,OAA4BC,CAAAA;IACvC,MAAMC,QAAQL,iBAAiBI;IAC/B,MAAME,gBAAgBL,8BAA8BI;IAEpD,OAAOH,oBAAoBG,OAAOC;AACpC,EAAE;AAEFH,KAAKI,WAAW,GAAG"}
+1
View File
@@ -0,0 +1 @@
import * as React from 'react';
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
export { Menu } from './Menu';
export { renderMenu_unstable } from './renderMenu';
export { useMenu_unstable } from './useMenu';
export { useMenuContextValues_unstable } from './useMenuContextValues';
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Menu/index.ts"],"sourcesContent":["export { Menu } from './Menu';\nexport type {\n MenuContextValues,\n MenuOpenChangeData,\n MenuOpenEvent,\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n MenuOpenEvents,\n MenuProps,\n MenuSlots,\n MenuState,\n} from './Menu.types';\nexport { renderMenu_unstable } from './renderMenu';\nexport { useMenu_unstable } from './useMenu';\nexport { useMenuContextValues_unstable } from './useMenuContextValues';\n"],"names":["Menu","renderMenu_unstable","useMenu_unstable","useMenuContextValues_unstable"],"rangeMappings":";;;","mappings":"AAAA,SAASA,IAAI,QAAQ,SAAS;AAW9B,SAASC,mBAAmB,QAAQ,eAAe;AACnD,SAASC,gBAAgB,QAAQ,YAAY;AAC7C,SAASC,6BAA6B,QAAQ,yBAAyB"}
+9
View File
@@ -0,0 +1,9 @@
import * as React from 'react';
import { MenuProvider } from '../../contexts/menuContext';
/**
* Render the final JSX of Menu
*/ export const renderMenu_unstable = (state, contextValues)=>{
return /*#__PURE__*/ React.createElement(MenuProvider, {
value: contextValues.menu
}, state.menuTrigger, state.open && state.menuPopover);
};
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Menu/renderMenu.tsx"],"sourcesContent":["import * as React from 'react';\nimport { MenuProvider } from '../../contexts/menuContext';\nimport type { MenuContextValues, MenuState } from './Menu.types';\n\n/**\n * Render the final JSX of Menu\n */\nexport const renderMenu_unstable = (state: MenuState, contextValues: MenuContextValues) => {\n return (\n <MenuProvider value={contextValues.menu}>\n {state.menuTrigger}\n {state.open && state.menuPopover}\n </MenuProvider>\n );\n};\n"],"names":["React","MenuProvider","renderMenu_unstable","state","contextValues","value","menu","menuTrigger","open","menuPopover"],"rangeMappings":";;;;;;;;","mappings":"AAAA,YAAYA,WAAW,QAAQ;AAC/B,SAASC,YAAY,QAAQ,6BAA6B;AAG1D;;CAEC,GACD,OAAO,MAAMC,sBAAsB,CAACC,OAAkBC;IACpD,qBACE,oBAACH;QAAaI,OAAOD,cAAcE,IAAI;OACpCH,MAAMI,WAAW,EACjBJ,MAAMK,IAAI,IAAIL,MAAMM,WAAW;AAGtC,EAAE"}
+264
View File
@@ -0,0 +1,264 @@
import * as React from 'react';
import { usePositioningMouseTarget, usePositioning, resolvePositioningShorthand } from '@fluentui/react-positioning';
import { useControllableState, useId, useOnClickOutside, useEventCallback, useOnScrollOutside, elementContains, useTimeout, useFirstMount } from '@fluentui/react-utilities';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
import { useFocusFinders } from '@fluentui/react-tabster';
import { useMenuContext_unstable } from '../../contexts/menuContext';
import { MENU_ENTER_EVENT, useOnMenuMouseEnter } from '../../utils/index';
import { useIsSubmenu } from '../../utils/useIsSubmenu';
// If it's not possible to position the submenu in smaller viewports, try
// and fallback to this order of positions
const submenuFallbackPositions = [
'after',
'after-bottom',
'before-top',
'before',
'before-bottom',
'above'
];
/**
* Create the state required to render Menu.
*
* The returned state can be modified with hooks such as useMenuStyles,
* before being passed to renderMenu_unstable.
*
* @param props - props from this instance of Menu
*/ export const useMenu_unstable = (props)=>{
const isSubmenu = useIsSubmenu();
const { hoverDelay = 500, inline = false, hasCheckmarks = false, hasIcons = false, closeOnScroll = false, openOnContext = false, persistOnItemClick = false, openOnHover = isSubmenu, defaultCheckedValues, mountNode = null } = props;
const triggerId = useId('menu');
const [contextTarget, setContextTarget] = usePositioningMouseTarget();
const positioningState = {
position: isSubmenu ? 'after' : 'below',
align: isSubmenu ? 'top' : 'start',
target: props.openOnContext ? contextTarget : undefined,
fallbackPositions: isSubmenu ? submenuFallbackPositions : undefined,
...resolvePositioningShorthand(props.positioning)
};
const children = React.Children.toArray(props.children);
if (process.env.NODE_ENV !== 'production') {
if (children.length === 0) {
// eslint-disable-next-line no-console
console.warn('Menu must contain at least one child');
}
if (children.length > 2) {
// eslint-disable-next-line no-console
console.warn('Menu must contain at most two children');
}
}
let menuTrigger = undefined;
let menuPopover = undefined;
if (children.length === 2) {
menuTrigger = children[0];
menuPopover = children[1];
} else if (children.length === 1) {
menuPopover = children[0];
}
const { targetRef: triggerRef, containerRef: menuPopoverRef } = usePositioning(positioningState);
// TODO Better way to narrow types ?
const [open, setOpen] = useMenuOpenState({
hoverDelay,
isSubmenu,
setContextTarget,
closeOnScroll,
menuPopoverRef,
triggerRef,
open: props.open,
defaultOpen: props.defaultOpen,
onOpenChange: props.onOpenChange,
openOnContext
});
const [checkedValues, onCheckedValueChange] = useMenuSelectableState({
checkedValues: props.checkedValues,
defaultCheckedValues,
onCheckedValueChange: props.onCheckedValueChange
});
return {
inline,
hoverDelay,
triggerId,
isSubmenu,
openOnHover,
contextTarget,
setContextTarget,
hasCheckmarks,
hasIcons,
closeOnScroll,
menuTrigger,
menuPopover,
mountNode,
triggerRef,
menuPopoverRef,
components: {},
openOnContext,
open,
setOpen,
checkedValues,
onCheckedValueChange,
persistOnItemClick
};
};
/**
* Adds appropriate state values and handlers for selectable items
* i.e checkboxes and radios
*/ const useMenuSelectableState = (props)=>{
const [checkedValues, setCheckedValues] = useControllableState({
state: props.checkedValues,
defaultState: props.defaultCheckedValues,
initialState: {}
});
const onCheckedValueChange = useEventCallback((e, { name, checkedItems })=>{
var _props_onCheckedValueChange;
(_props_onCheckedValueChange = props.onCheckedValueChange) === null || _props_onCheckedValueChange === void 0 ? void 0 : _props_onCheckedValueChange.call(props, e, {
name,
checkedItems
});
setCheckedValues((currentValue)=>({
...currentValue,
[name]: checkedItems
}));
});
return [
checkedValues,
onCheckedValueChange
];
};
const useMenuOpenState = (state)=>{
'use no memo';
const { targetDocument } = useFluent();
const parentSetOpen = useMenuContext_unstable((context)=>context.setOpen);
const onOpenChange = useEventCallback((e, data)=>{
var _state_onOpenChange;
return (_state_onOpenChange = state.onOpenChange) === null || _state_onOpenChange === void 0 ? void 0 : _state_onOpenChange.call(state, e, data);
});
const enteringTriggerRef = React.useRef(false);
const [open, setOpenState] = useControllableState({
state: state.open,
defaultState: state.defaultOpen,
initialState: false
});
const trySetOpen = useEventCallback((e, data)=>{
const event = e instanceof CustomEvent && e.type === MENU_ENTER_EVENT ? e.detail.nativeEvent : e;
onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(event, {
...data
});
if (data.open && e.type === 'contextmenu') {
state.setContextTarget(e);
}
if (!data.open) {
state.setContextTarget(undefined);
}
if (data.bubble) {
parentSetOpen(e, {
...data
});
}
setOpenState(data.open);
});
const [setOpenTimeout, clearOpenTimeout] = useTimeout();
const setOpen = useEventCallback((e, data)=>{
clearOpenTimeout();
if (!(e instanceof Event) && e.persist) {
// < React 17 still uses pooled synthetic events
e.persist();
}
if (e.type === 'mouseleave' || e.type === 'mouseenter' || e.type === 'mousemove' || e.type === MENU_ENTER_EVENT) {
var _state_triggerRef_current;
if ((_state_triggerRef_current = state.triggerRef.current) === null || _state_triggerRef_current === void 0 ? void 0 : _state_triggerRef_current.contains(e.target)) {
enteringTriggerRef.current = e.type === 'mouseenter' || e.type === 'mousemove';
}
setOpenTimeout(()=>trySetOpen(e, data), state.hoverDelay);
} else {
trySetOpen(e, data);
}
});
useOnClickOutside({
contains: elementContains,
disabled: !open,
element: targetDocument,
refs: [
state.menuPopoverRef,
!state.openOnContext && state.triggerRef
].filter(Boolean),
callback: (event)=>setOpen(event, {
open: false,
type: 'clickOutside',
event
})
});
// only close on scroll for context, or when closeOnScroll is specified
const closeOnScroll = state.openOnContext || state.closeOnScroll;
useOnScrollOutside({
contains: elementContains,
element: targetDocument,
callback: (event)=>setOpen(event, {
open: false,
type: 'scrollOutside',
event
}),
refs: [
state.menuPopoverRef,
!state.openOnContext && state.triggerRef
].filter(Boolean),
disabled: !open || !closeOnScroll
});
useOnMenuMouseEnter({
element: targetDocument,
callback: (event)=>{
// When moving from a menu directly back to its trigger, this handler can close the menu
// Explicitly check a flag to see if this situation happens
if (!enteringTriggerRef.current) {
setOpen(event, {
open: false,
type: 'menuMouseEnter',
event
});
}
},
disabled: !open,
refs: [
state.menuPopoverRef
]
});
// Manage focus for open state
const { findFirstFocusable } = useFocusFinders();
const focusFirst = React.useCallback(()=>{
const firstFocusable = findFirstFocusable(state.menuPopoverRef.current);
firstFocusable === null || firstFocusable === void 0 ? void 0 : firstFocusable.focus();
}, [
findFirstFocusable,
state.menuPopoverRef
]);
const firstMount = useFirstMount();
React.useEffect(()=>{
if (open) {
focusFirst();
} else {
if (!firstMount) {
if ((targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.activeElement) === (targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body)) {
var // We know that React effects are sync so we focus the trigger here
// after any event handler (event handlers will update state and re-render).
// Since the browser only performs the default behaviour for the Tab key once
// keyboard events have fully bubbled up the window, the browser will move
// focus to the next tabbable element before/after the trigger if needed.
// If the Tab key was not pressed, focus will remain on the trigger as expected.
_state_triggerRef_current;
(_state_triggerRef_current = state.triggerRef.current) === null || _state_triggerRef_current === void 0 ? void 0 : _state_triggerRef_current.focus();
}
}
}
// firstMount change should not re-run this effect
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
state.triggerRef,
state.isSubmenu,
open,
focusFirst,
targetDocument,
state.menuPopoverRef
]);
return [
open,
setOpen
];
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,24 @@
export function useMenuContextValues_unstable(state) {
const { checkedValues, hasCheckmarks, hasIcons, inline, isSubmenu, menuPopoverRef, mountNode, onCheckedValueChange, open, openOnContext, openOnHover, persistOnItemClick, setOpen, triggerId, triggerRef } = state;
// This context is created with "@fluentui/react-context-selector", these is no sense to memoize it
const menu = {
checkedValues,
hasCheckmarks,
hasIcons,
inline,
isSubmenu,
menuPopoverRef,
mountNode,
onCheckedValueChange,
open,
openOnContext,
openOnHover,
persistOnItemClick,
setOpen,
triggerId,
triggerRef
};
return {
menu
};
}
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Menu/useMenuContextValues.ts"],"sourcesContent":["import type { MenuContextValues, MenuState } from './Menu.types';\n\nexport function useMenuContextValues_unstable(state: MenuState): MenuContextValues {\n const {\n checkedValues,\n hasCheckmarks,\n hasIcons,\n inline,\n isSubmenu,\n menuPopoverRef,\n mountNode,\n onCheckedValueChange,\n open,\n openOnContext,\n openOnHover,\n persistOnItemClick,\n setOpen,\n triggerId,\n triggerRef,\n } = state;\n\n // This context is created with \"@fluentui/react-context-selector\", these is no sense to memoize it\n const menu = {\n checkedValues,\n hasCheckmarks,\n hasIcons,\n inline,\n isSubmenu,\n menuPopoverRef,\n mountNode,\n onCheckedValueChange,\n open,\n openOnContext,\n openOnHover,\n persistOnItemClick,\n setOpen,\n triggerId,\n triggerRef,\n };\n\n return { menu };\n}\n"],"names":["useMenuContextValues_unstable","state","checkedValues","hasCheckmarks","hasIcons","inline","isSubmenu","menuPopoverRef","mountNode","onCheckedValueChange","open","openOnContext","openOnHover","persistOnItemClick","setOpen","triggerId","triggerRef","menu"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAEA,OAAO,SAASA,8BAA8BC,KAAgB;IAC5D,MAAM,EACJC,aAAa,EACbC,aAAa,EACbC,QAAQ,EACRC,MAAM,EACNC,SAAS,EACTC,cAAc,EACdC,SAAS,EACTC,oBAAoB,EACpBC,IAAI,EACJC,aAAa,EACbC,WAAW,EACXC,kBAAkB,EAClBC,OAAO,EACPC,SAAS,EACTC,UAAU,EACX,GAAGf;IAEJ,mGAAmG;IACnG,MAAMgB,OAAO;QACXf;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;IACF;IAEA,OAAO;QAAEC;IAAK;AAChB"}