Initial commit
This commit is contained in:
869
node_modules/@radix-ui/react-menu/dist/index.mjs
generated
vendored
Normal file
869
node_modules/@radix-ui/react-menu/dist/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,869 @@
|
||||
"use client";
|
||||
|
||||
// packages/react/menu/src/menu.tsx
|
||||
import * as React from "react";
|
||||
import { composeEventHandlers } from "@radix-ui/primitive";
|
||||
import { createCollection } from "@radix-ui/react-collection";
|
||||
import { useComposedRefs, composeRefs } from "@radix-ui/react-compose-refs";
|
||||
import { createContextScope } from "@radix-ui/react-context";
|
||||
import { useDirection } from "@radix-ui/react-direction";
|
||||
import { DismissableLayer } from "@radix-ui/react-dismissable-layer";
|
||||
import { useFocusGuards } from "@radix-ui/react-focus-guards";
|
||||
import { FocusScope } from "@radix-ui/react-focus-scope";
|
||||
import { useId } from "@radix-ui/react-id";
|
||||
import * as PopperPrimitive from "@radix-ui/react-popper";
|
||||
import { createPopperScope } from "@radix-ui/react-popper";
|
||||
import { Portal as PortalPrimitive } from "@radix-ui/react-portal";
|
||||
import { Presence } from "@radix-ui/react-presence";
|
||||
import { Primitive, dispatchDiscreteCustomEvent } from "@radix-ui/react-primitive";
|
||||
import * as RovingFocusGroup from "@radix-ui/react-roving-focus";
|
||||
import { createRovingFocusGroupScope } from "@radix-ui/react-roving-focus";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
|
||||
import { hideOthers } from "aria-hidden";
|
||||
import { RemoveScroll } from "react-remove-scroll";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
var SELECTION_KEYS = ["Enter", " "];
|
||||
var FIRST_KEYS = ["ArrowDown", "PageUp", "Home"];
|
||||
var LAST_KEYS = ["ArrowUp", "PageDown", "End"];
|
||||
var FIRST_LAST_KEYS = [...FIRST_KEYS, ...LAST_KEYS];
|
||||
var SUB_OPEN_KEYS = {
|
||||
ltr: [...SELECTION_KEYS, "ArrowRight"],
|
||||
rtl: [...SELECTION_KEYS, "ArrowLeft"]
|
||||
};
|
||||
var SUB_CLOSE_KEYS = {
|
||||
ltr: ["ArrowLeft"],
|
||||
rtl: ["ArrowRight"]
|
||||
};
|
||||
var MENU_NAME = "Menu";
|
||||
var [Collection, useCollection, createCollectionScope] = createCollection(MENU_NAME);
|
||||
var [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, [
|
||||
createCollectionScope,
|
||||
createPopperScope,
|
||||
createRovingFocusGroupScope
|
||||
]);
|
||||
var usePopperScope = createPopperScope();
|
||||
var useRovingFocusGroupScope = createRovingFocusGroupScope();
|
||||
var [MenuProvider, useMenuContext] = createMenuContext(MENU_NAME);
|
||||
var [MenuRootProvider, useMenuRootContext] = createMenuContext(MENU_NAME);
|
||||
var Menu = (props) => {
|
||||
const { __scopeMenu, open = false, children, dir, onOpenChange, modal = true } = props;
|
||||
const popperScope = usePopperScope(__scopeMenu);
|
||||
const [content, setContent] = React.useState(null);
|
||||
const isUsingKeyboardRef = React.useRef(false);
|
||||
const handleOpenChange = useCallbackRef(onOpenChange);
|
||||
const direction = useDirection(dir);
|
||||
React.useEffect(() => {
|
||||
const handleKeyDown = () => {
|
||||
isUsingKeyboardRef.current = true;
|
||||
document.addEventListener("pointerdown", handlePointer, { capture: true, once: true });
|
||||
document.addEventListener("pointermove", handlePointer, { capture: true, once: true });
|
||||
};
|
||||
const handlePointer = () => isUsingKeyboardRef.current = false;
|
||||
document.addEventListener("keydown", handleKeyDown, { capture: true });
|
||||
return () => {
|
||||
document.removeEventListener("keydown", handleKeyDown, { capture: true });
|
||||
document.removeEventListener("pointerdown", handlePointer, { capture: true });
|
||||
document.removeEventListener("pointermove", handlePointer, { capture: true });
|
||||
};
|
||||
}, []);
|
||||
return /* @__PURE__ */ jsx(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ jsx(
|
||||
MenuProvider,
|
||||
{
|
||||
scope: __scopeMenu,
|
||||
open,
|
||||
onOpenChange: handleOpenChange,
|
||||
content,
|
||||
onContentChange: setContent,
|
||||
children: /* @__PURE__ */ jsx(
|
||||
MenuRootProvider,
|
||||
{
|
||||
scope: __scopeMenu,
|
||||
onClose: React.useCallback(() => handleOpenChange(false), [handleOpenChange]),
|
||||
isUsingKeyboardRef,
|
||||
dir: direction,
|
||||
modal,
|
||||
children
|
||||
}
|
||||
)
|
||||
}
|
||||
) });
|
||||
};
|
||||
Menu.displayName = MENU_NAME;
|
||||
var ANCHOR_NAME = "MenuAnchor";
|
||||
var MenuAnchor = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, ...anchorProps } = props;
|
||||
const popperScope = usePopperScope(__scopeMenu);
|
||||
return /* @__PURE__ */ jsx(PopperPrimitive.Anchor, { ...popperScope, ...anchorProps, ref: forwardedRef });
|
||||
}
|
||||
);
|
||||
MenuAnchor.displayName = ANCHOR_NAME;
|
||||
var PORTAL_NAME = "MenuPortal";
|
||||
var [PortalProvider, usePortalContext] = createMenuContext(PORTAL_NAME, {
|
||||
forceMount: void 0
|
||||
});
|
||||
var MenuPortal = (props) => {
|
||||
const { __scopeMenu, forceMount, children, container } = props;
|
||||
const context = useMenuContext(PORTAL_NAME, __scopeMenu);
|
||||
return /* @__PURE__ */ jsx(PortalProvider, { scope: __scopeMenu, forceMount, children: /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(PortalPrimitive, { asChild: true, container, children }) }) });
|
||||
};
|
||||
MenuPortal.displayName = PORTAL_NAME;
|
||||
var CONTENT_NAME = "MenuContent";
|
||||
var [MenuContentProvider, useMenuContentContext] = createMenuContext(CONTENT_NAME);
|
||||
var MenuContent = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const portalContext = usePortalContext(CONTENT_NAME, props.__scopeMenu);
|
||||
const { forceMount = portalContext.forceMount, ...contentProps } = props;
|
||||
const context = useMenuContext(CONTENT_NAME, props.__scopeMenu);
|
||||
const rootContext = useMenuRootContext(CONTENT_NAME, props.__scopeMenu);
|
||||
return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeMenu, children: /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeMenu, children: rootContext.modal ? /* @__PURE__ */ jsx(MenuRootContentModal, { ...contentProps, ref: forwardedRef }) : /* @__PURE__ */ jsx(MenuRootContentNonModal, { ...contentProps, ref: forwardedRef }) }) }) });
|
||||
}
|
||||
);
|
||||
var MenuRootContentModal = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const context = useMenuContext(CONTENT_NAME, props.__scopeMenu);
|
||||
const ref = React.useRef(null);
|
||||
const composedRefs = useComposedRefs(forwardedRef, ref);
|
||||
React.useEffect(() => {
|
||||
const content = ref.current;
|
||||
if (content) return hideOthers(content);
|
||||
}, []);
|
||||
return /* @__PURE__ */ jsx(
|
||||
MenuContentImpl,
|
||||
{
|
||||
...props,
|
||||
ref: composedRefs,
|
||||
trapFocus: context.open,
|
||||
disableOutsidePointerEvents: context.open,
|
||||
disableOutsideScroll: true,
|
||||
onFocusOutside: composeEventHandlers(
|
||||
props.onFocusOutside,
|
||||
(event) => event.preventDefault(),
|
||||
{ checkForDefaultPrevented: false }
|
||||
),
|
||||
onDismiss: () => context.onOpenChange(false)
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
var MenuRootContentNonModal = React.forwardRef((props, forwardedRef) => {
|
||||
const context = useMenuContext(CONTENT_NAME, props.__scopeMenu);
|
||||
return /* @__PURE__ */ jsx(
|
||||
MenuContentImpl,
|
||||
{
|
||||
...props,
|
||||
ref: forwardedRef,
|
||||
trapFocus: false,
|
||||
disableOutsidePointerEvents: false,
|
||||
disableOutsideScroll: false,
|
||||
onDismiss: () => context.onOpenChange(false)
|
||||
}
|
||||
);
|
||||
});
|
||||
var MenuContentImpl = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const {
|
||||
__scopeMenu,
|
||||
loop = false,
|
||||
trapFocus,
|
||||
onOpenAutoFocus,
|
||||
onCloseAutoFocus,
|
||||
disableOutsidePointerEvents,
|
||||
onEntryFocus,
|
||||
onEscapeKeyDown,
|
||||
onPointerDownOutside,
|
||||
onFocusOutside,
|
||||
onInteractOutside,
|
||||
onDismiss,
|
||||
disableOutsideScroll,
|
||||
...contentProps
|
||||
} = props;
|
||||
const context = useMenuContext(CONTENT_NAME, __scopeMenu);
|
||||
const rootContext = useMenuRootContext(CONTENT_NAME, __scopeMenu);
|
||||
const popperScope = usePopperScope(__scopeMenu);
|
||||
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeMenu);
|
||||
const getItems = useCollection(__scopeMenu);
|
||||
const [currentItemId, setCurrentItemId] = React.useState(null);
|
||||
const contentRef = React.useRef(null);
|
||||
const composedRefs = useComposedRefs(forwardedRef, contentRef, context.onContentChange);
|
||||
const timerRef = React.useRef(0);
|
||||
const searchRef = React.useRef("");
|
||||
const pointerGraceTimerRef = React.useRef(0);
|
||||
const pointerGraceIntentRef = React.useRef(null);
|
||||
const pointerDirRef = React.useRef("right");
|
||||
const lastPointerXRef = React.useRef(0);
|
||||
const ScrollLockWrapper = disableOutsideScroll ? RemoveScroll : React.Fragment;
|
||||
const scrollLockWrapperProps = disableOutsideScroll ? { as: Slot, allowPinchZoom: true } : void 0;
|
||||
const handleTypeaheadSearch = (key) => {
|
||||
const search = searchRef.current + key;
|
||||
const items = getItems().filter((item) => !item.disabled);
|
||||
const currentItem = document.activeElement;
|
||||
const currentMatch = items.find((item) => item.ref.current === currentItem)?.textValue;
|
||||
const values = items.map((item) => item.textValue);
|
||||
const nextMatch = getNextMatch(values, search, currentMatch);
|
||||
const newItem = items.find((item) => item.textValue === nextMatch)?.ref.current;
|
||||
(function updateSearch(value) {
|
||||
searchRef.current = value;
|
||||
window.clearTimeout(timerRef.current);
|
||||
if (value !== "") timerRef.current = window.setTimeout(() => updateSearch(""), 1e3);
|
||||
})(search);
|
||||
if (newItem) {
|
||||
setTimeout(() => newItem.focus());
|
||||
}
|
||||
};
|
||||
React.useEffect(() => {
|
||||
return () => window.clearTimeout(timerRef.current);
|
||||
}, []);
|
||||
useFocusGuards();
|
||||
const isPointerMovingToSubmenu = React.useCallback((event) => {
|
||||
const isMovingTowards = pointerDirRef.current === pointerGraceIntentRef.current?.side;
|
||||
return isMovingTowards && isPointerInGraceArea(event, pointerGraceIntentRef.current?.area);
|
||||
}, []);
|
||||
return /* @__PURE__ */ jsx(
|
||||
MenuContentProvider,
|
||||
{
|
||||
scope: __scopeMenu,
|
||||
searchRef,
|
||||
onItemEnter: React.useCallback(
|
||||
(event) => {
|
||||
if (isPointerMovingToSubmenu(event)) event.preventDefault();
|
||||
},
|
||||
[isPointerMovingToSubmenu]
|
||||
),
|
||||
onItemLeave: React.useCallback(
|
||||
(event) => {
|
||||
if (isPointerMovingToSubmenu(event)) return;
|
||||
contentRef.current?.focus();
|
||||
setCurrentItemId(null);
|
||||
},
|
||||
[isPointerMovingToSubmenu]
|
||||
),
|
||||
onTriggerLeave: React.useCallback(
|
||||
(event) => {
|
||||
if (isPointerMovingToSubmenu(event)) event.preventDefault();
|
||||
},
|
||||
[isPointerMovingToSubmenu]
|
||||
),
|
||||
pointerGraceTimerRef,
|
||||
onPointerGraceIntentChange: React.useCallback((intent) => {
|
||||
pointerGraceIntentRef.current = intent;
|
||||
}, []),
|
||||
children: /* @__PURE__ */ jsx(ScrollLockWrapper, { ...scrollLockWrapperProps, children: /* @__PURE__ */ jsx(
|
||||
FocusScope,
|
||||
{
|
||||
asChild: true,
|
||||
trapped: trapFocus,
|
||||
onMountAutoFocus: composeEventHandlers(onOpenAutoFocus, (event) => {
|
||||
event.preventDefault();
|
||||
contentRef.current?.focus({ preventScroll: true });
|
||||
}),
|
||||
onUnmountAutoFocus: onCloseAutoFocus,
|
||||
children: /* @__PURE__ */ jsx(
|
||||
DismissableLayer,
|
||||
{
|
||||
asChild: true,
|
||||
disableOutsidePointerEvents,
|
||||
onEscapeKeyDown,
|
||||
onPointerDownOutside,
|
||||
onFocusOutside,
|
||||
onInteractOutside,
|
||||
onDismiss,
|
||||
children: /* @__PURE__ */ jsx(
|
||||
RovingFocusGroup.Root,
|
||||
{
|
||||
asChild: true,
|
||||
...rovingFocusGroupScope,
|
||||
dir: rootContext.dir,
|
||||
orientation: "vertical",
|
||||
loop,
|
||||
currentTabStopId: currentItemId,
|
||||
onCurrentTabStopIdChange: setCurrentItemId,
|
||||
onEntryFocus: composeEventHandlers(onEntryFocus, (event) => {
|
||||
if (!rootContext.isUsingKeyboardRef.current) event.preventDefault();
|
||||
}),
|
||||
preventScrollOnEntryFocus: true,
|
||||
children: /* @__PURE__ */ jsx(
|
||||
PopperPrimitive.Content,
|
||||
{
|
||||
role: "menu",
|
||||
"aria-orientation": "vertical",
|
||||
"data-state": getOpenState(context.open),
|
||||
"data-radix-menu-content": "",
|
||||
dir: rootContext.dir,
|
||||
...popperScope,
|
||||
...contentProps,
|
||||
ref: composedRefs,
|
||||
style: { outline: "none", ...contentProps.style },
|
||||
onKeyDown: composeEventHandlers(contentProps.onKeyDown, (event) => {
|
||||
const target = event.target;
|
||||
const isKeyDownInside = target.closest("[data-radix-menu-content]") === event.currentTarget;
|
||||
const isModifierKey = event.ctrlKey || event.altKey || event.metaKey;
|
||||
const isCharacterKey = event.key.length === 1;
|
||||
if (isKeyDownInside) {
|
||||
if (event.key === "Tab") event.preventDefault();
|
||||
if (!isModifierKey && isCharacterKey) handleTypeaheadSearch(event.key);
|
||||
}
|
||||
const content = contentRef.current;
|
||||
if (event.target !== content) return;
|
||||
if (!FIRST_LAST_KEYS.includes(event.key)) return;
|
||||
event.preventDefault();
|
||||
const items = getItems().filter((item) => !item.disabled);
|
||||
const candidateNodes = items.map((item) => item.ref.current);
|
||||
if (LAST_KEYS.includes(event.key)) candidateNodes.reverse();
|
||||
focusFirst(candidateNodes);
|
||||
}),
|
||||
onBlur: composeEventHandlers(props.onBlur, (event) => {
|
||||
if (!event.currentTarget.contains(event.target)) {
|
||||
window.clearTimeout(timerRef.current);
|
||||
searchRef.current = "";
|
||||
}
|
||||
}),
|
||||
onPointerMove: composeEventHandlers(
|
||||
props.onPointerMove,
|
||||
whenMouse((event) => {
|
||||
const target = event.target;
|
||||
const pointerXHasChanged = lastPointerXRef.current !== event.clientX;
|
||||
if (event.currentTarget.contains(target) && pointerXHasChanged) {
|
||||
const newDir = event.clientX > lastPointerXRef.current ? "right" : "left";
|
||||
pointerDirRef.current = newDir;
|
||||
lastPointerXRef.current = event.clientX;
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
) })
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
MenuContent.displayName = CONTENT_NAME;
|
||||
var GROUP_NAME = "MenuGroup";
|
||||
var MenuGroup = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, ...groupProps } = props;
|
||||
return /* @__PURE__ */ jsx(Primitive.div, { role: "group", ...groupProps, ref: forwardedRef });
|
||||
}
|
||||
);
|
||||
MenuGroup.displayName = GROUP_NAME;
|
||||
var LABEL_NAME = "MenuLabel";
|
||||
var MenuLabel = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, ...labelProps } = props;
|
||||
return /* @__PURE__ */ jsx(Primitive.div, { ...labelProps, ref: forwardedRef });
|
||||
}
|
||||
);
|
||||
MenuLabel.displayName = LABEL_NAME;
|
||||
var ITEM_NAME = "MenuItem";
|
||||
var ITEM_SELECT = "menu.itemSelect";
|
||||
var MenuItem = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { disabled = false, onSelect, ...itemProps } = props;
|
||||
const ref = React.useRef(null);
|
||||
const rootContext = useMenuRootContext(ITEM_NAME, props.__scopeMenu);
|
||||
const contentContext = useMenuContentContext(ITEM_NAME, props.__scopeMenu);
|
||||
const composedRefs = useComposedRefs(forwardedRef, ref);
|
||||
const isPointerDownRef = React.useRef(false);
|
||||
const handleSelect = () => {
|
||||
const menuItem = ref.current;
|
||||
if (!disabled && menuItem) {
|
||||
const itemSelectEvent = new CustomEvent(ITEM_SELECT, { bubbles: true, cancelable: true });
|
||||
menuItem.addEventListener(ITEM_SELECT, (event) => onSelect?.(event), { once: true });
|
||||
dispatchDiscreteCustomEvent(menuItem, itemSelectEvent);
|
||||
if (itemSelectEvent.defaultPrevented) {
|
||||
isPointerDownRef.current = false;
|
||||
} else {
|
||||
rootContext.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
return /* @__PURE__ */ jsx(
|
||||
MenuItemImpl,
|
||||
{
|
||||
...itemProps,
|
||||
ref: composedRefs,
|
||||
disabled,
|
||||
onClick: composeEventHandlers(props.onClick, handleSelect),
|
||||
onPointerDown: (event) => {
|
||||
props.onPointerDown?.(event);
|
||||
isPointerDownRef.current = true;
|
||||
},
|
||||
onPointerUp: composeEventHandlers(props.onPointerUp, (event) => {
|
||||
if (!isPointerDownRef.current) event.currentTarget?.click();
|
||||
}),
|
||||
onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
|
||||
const isTypingAhead = contentContext.searchRef.current !== "";
|
||||
if (disabled || isTypingAhead && event.key === " ") return;
|
||||
if (SELECTION_KEYS.includes(event.key)) {
|
||||
event.currentTarget.click();
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
MenuItem.displayName = ITEM_NAME;
|
||||
var MenuItemImpl = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, disabled = false, textValue, ...itemProps } = props;
|
||||
const contentContext = useMenuContentContext(ITEM_NAME, __scopeMenu);
|
||||
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeMenu);
|
||||
const ref = React.useRef(null);
|
||||
const composedRefs = useComposedRefs(forwardedRef, ref);
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
const [textContent, setTextContent] = React.useState("");
|
||||
React.useEffect(() => {
|
||||
const menuItem = ref.current;
|
||||
if (menuItem) {
|
||||
setTextContent((menuItem.textContent ?? "").trim());
|
||||
}
|
||||
}, [itemProps.children]);
|
||||
return /* @__PURE__ */ jsx(
|
||||
Collection.ItemSlot,
|
||||
{
|
||||
scope: __scopeMenu,
|
||||
disabled,
|
||||
textValue: textValue ?? textContent,
|
||||
children: /* @__PURE__ */ jsx(RovingFocusGroup.Item, { asChild: true, ...rovingFocusGroupScope, focusable: !disabled, children: /* @__PURE__ */ jsx(
|
||||
Primitive.div,
|
||||
{
|
||||
role: "menuitem",
|
||||
"data-highlighted": isFocused ? "" : void 0,
|
||||
"aria-disabled": disabled || void 0,
|
||||
"data-disabled": disabled ? "" : void 0,
|
||||
...itemProps,
|
||||
ref: composedRefs,
|
||||
onPointerMove: composeEventHandlers(
|
||||
props.onPointerMove,
|
||||
whenMouse((event) => {
|
||||
if (disabled) {
|
||||
contentContext.onItemLeave(event);
|
||||
} else {
|
||||
contentContext.onItemEnter(event);
|
||||
if (!event.defaultPrevented) {
|
||||
const item = event.currentTarget;
|
||||
item.focus({ preventScroll: true });
|
||||
}
|
||||
}
|
||||
})
|
||||
),
|
||||
onPointerLeave: composeEventHandlers(
|
||||
props.onPointerLeave,
|
||||
whenMouse((event) => contentContext.onItemLeave(event))
|
||||
),
|
||||
onFocus: composeEventHandlers(props.onFocus, () => setIsFocused(true)),
|
||||
onBlur: composeEventHandlers(props.onBlur, () => setIsFocused(false))
|
||||
}
|
||||
) })
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
var CHECKBOX_ITEM_NAME = "MenuCheckboxItem";
|
||||
var MenuCheckboxItem = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { checked = false, onCheckedChange, ...checkboxItemProps } = props;
|
||||
return /* @__PURE__ */ jsx(ItemIndicatorProvider, { scope: props.__scopeMenu, checked, children: /* @__PURE__ */ jsx(
|
||||
MenuItem,
|
||||
{
|
||||
role: "menuitemcheckbox",
|
||||
"aria-checked": isIndeterminate(checked) ? "mixed" : checked,
|
||||
...checkboxItemProps,
|
||||
ref: forwardedRef,
|
||||
"data-state": getCheckedState(checked),
|
||||
onSelect: composeEventHandlers(
|
||||
checkboxItemProps.onSelect,
|
||||
() => onCheckedChange?.(isIndeterminate(checked) ? true : !checked),
|
||||
{ checkForDefaultPrevented: false }
|
||||
)
|
||||
}
|
||||
) });
|
||||
}
|
||||
);
|
||||
MenuCheckboxItem.displayName = CHECKBOX_ITEM_NAME;
|
||||
var RADIO_GROUP_NAME = "MenuRadioGroup";
|
||||
var [RadioGroupProvider, useRadioGroupContext] = createMenuContext(
|
||||
RADIO_GROUP_NAME,
|
||||
{ value: void 0, onValueChange: () => {
|
||||
} }
|
||||
);
|
||||
var MenuRadioGroup = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { value, onValueChange, ...groupProps } = props;
|
||||
const handleValueChange = useCallbackRef(onValueChange);
|
||||
return /* @__PURE__ */ jsx(RadioGroupProvider, { scope: props.__scopeMenu, value, onValueChange: handleValueChange, children: /* @__PURE__ */ jsx(MenuGroup, { ...groupProps, ref: forwardedRef }) });
|
||||
}
|
||||
);
|
||||
MenuRadioGroup.displayName = RADIO_GROUP_NAME;
|
||||
var RADIO_ITEM_NAME = "MenuRadioItem";
|
||||
var MenuRadioItem = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { value, ...radioItemProps } = props;
|
||||
const context = useRadioGroupContext(RADIO_ITEM_NAME, props.__scopeMenu);
|
||||
const checked = value === context.value;
|
||||
return /* @__PURE__ */ jsx(ItemIndicatorProvider, { scope: props.__scopeMenu, checked, children: /* @__PURE__ */ jsx(
|
||||
MenuItem,
|
||||
{
|
||||
role: "menuitemradio",
|
||||
"aria-checked": checked,
|
||||
...radioItemProps,
|
||||
ref: forwardedRef,
|
||||
"data-state": getCheckedState(checked),
|
||||
onSelect: composeEventHandlers(
|
||||
radioItemProps.onSelect,
|
||||
() => context.onValueChange?.(value),
|
||||
{ checkForDefaultPrevented: false }
|
||||
)
|
||||
}
|
||||
) });
|
||||
}
|
||||
);
|
||||
MenuRadioItem.displayName = RADIO_ITEM_NAME;
|
||||
var ITEM_INDICATOR_NAME = "MenuItemIndicator";
|
||||
var [ItemIndicatorProvider, useItemIndicatorContext] = createMenuContext(
|
||||
ITEM_INDICATOR_NAME,
|
||||
{ checked: false }
|
||||
);
|
||||
var MenuItemIndicator = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, forceMount, ...itemIndicatorProps } = props;
|
||||
const indicatorContext = useItemIndicatorContext(ITEM_INDICATOR_NAME, __scopeMenu);
|
||||
return /* @__PURE__ */ jsx(
|
||||
Presence,
|
||||
{
|
||||
present: forceMount || isIndeterminate(indicatorContext.checked) || indicatorContext.checked === true,
|
||||
children: /* @__PURE__ */ jsx(
|
||||
Primitive.span,
|
||||
{
|
||||
...itemIndicatorProps,
|
||||
ref: forwardedRef,
|
||||
"data-state": getCheckedState(indicatorContext.checked)
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
MenuItemIndicator.displayName = ITEM_INDICATOR_NAME;
|
||||
var SEPARATOR_NAME = "MenuSeparator";
|
||||
var MenuSeparator = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, ...separatorProps } = props;
|
||||
return /* @__PURE__ */ jsx(
|
||||
Primitive.div,
|
||||
{
|
||||
role: "separator",
|
||||
"aria-orientation": "horizontal",
|
||||
...separatorProps,
|
||||
ref: forwardedRef
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
MenuSeparator.displayName = SEPARATOR_NAME;
|
||||
var ARROW_NAME = "MenuArrow";
|
||||
var MenuArrow = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const { __scopeMenu, ...arrowProps } = props;
|
||||
const popperScope = usePopperScope(__scopeMenu);
|
||||
return /* @__PURE__ */ jsx(PopperPrimitive.Arrow, { ...popperScope, ...arrowProps, ref: forwardedRef });
|
||||
}
|
||||
);
|
||||
MenuArrow.displayName = ARROW_NAME;
|
||||
var SUB_NAME = "MenuSub";
|
||||
var [MenuSubProvider, useMenuSubContext] = createMenuContext(SUB_NAME);
|
||||
var MenuSub = (props) => {
|
||||
const { __scopeMenu, children, open = false, onOpenChange } = props;
|
||||
const parentMenuContext = useMenuContext(SUB_NAME, __scopeMenu);
|
||||
const popperScope = usePopperScope(__scopeMenu);
|
||||
const [trigger, setTrigger] = React.useState(null);
|
||||
const [content, setContent] = React.useState(null);
|
||||
const handleOpenChange = useCallbackRef(onOpenChange);
|
||||
React.useEffect(() => {
|
||||
if (parentMenuContext.open === false) handleOpenChange(false);
|
||||
return () => handleOpenChange(false);
|
||||
}, [parentMenuContext.open, handleOpenChange]);
|
||||
return /* @__PURE__ */ jsx(PopperPrimitive.Root, { ...popperScope, children: /* @__PURE__ */ jsx(
|
||||
MenuProvider,
|
||||
{
|
||||
scope: __scopeMenu,
|
||||
open,
|
||||
onOpenChange: handleOpenChange,
|
||||
content,
|
||||
onContentChange: setContent,
|
||||
children: /* @__PURE__ */ jsx(
|
||||
MenuSubProvider,
|
||||
{
|
||||
scope: __scopeMenu,
|
||||
contentId: useId(),
|
||||
triggerId: useId(),
|
||||
trigger,
|
||||
onTriggerChange: setTrigger,
|
||||
children
|
||||
}
|
||||
)
|
||||
}
|
||||
) });
|
||||
};
|
||||
MenuSub.displayName = SUB_NAME;
|
||||
var SUB_TRIGGER_NAME = "MenuSubTrigger";
|
||||
var MenuSubTrigger = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const context = useMenuContext(SUB_TRIGGER_NAME, props.__scopeMenu);
|
||||
const rootContext = useMenuRootContext(SUB_TRIGGER_NAME, props.__scopeMenu);
|
||||
const subContext = useMenuSubContext(SUB_TRIGGER_NAME, props.__scopeMenu);
|
||||
const contentContext = useMenuContentContext(SUB_TRIGGER_NAME, props.__scopeMenu);
|
||||
const openTimerRef = React.useRef(null);
|
||||
const { pointerGraceTimerRef, onPointerGraceIntentChange } = contentContext;
|
||||
const scope = { __scopeMenu: props.__scopeMenu };
|
||||
const clearOpenTimer = React.useCallback(() => {
|
||||
if (openTimerRef.current) window.clearTimeout(openTimerRef.current);
|
||||
openTimerRef.current = null;
|
||||
}, []);
|
||||
React.useEffect(() => clearOpenTimer, [clearOpenTimer]);
|
||||
React.useEffect(() => {
|
||||
const pointerGraceTimer = pointerGraceTimerRef.current;
|
||||
return () => {
|
||||
window.clearTimeout(pointerGraceTimer);
|
||||
onPointerGraceIntentChange(null);
|
||||
};
|
||||
}, [pointerGraceTimerRef, onPointerGraceIntentChange]);
|
||||
return /* @__PURE__ */ jsx(MenuAnchor, { asChild: true, ...scope, children: /* @__PURE__ */ jsx(
|
||||
MenuItemImpl,
|
||||
{
|
||||
id: subContext.triggerId,
|
||||
"aria-haspopup": "menu",
|
||||
"aria-expanded": context.open,
|
||||
"aria-controls": subContext.contentId,
|
||||
"data-state": getOpenState(context.open),
|
||||
...props,
|
||||
ref: composeRefs(forwardedRef, subContext.onTriggerChange),
|
||||
onClick: (event) => {
|
||||
props.onClick?.(event);
|
||||
if (props.disabled || event.defaultPrevented) return;
|
||||
event.currentTarget.focus();
|
||||
if (!context.open) context.onOpenChange(true);
|
||||
},
|
||||
onPointerMove: composeEventHandlers(
|
||||
props.onPointerMove,
|
||||
whenMouse((event) => {
|
||||
contentContext.onItemEnter(event);
|
||||
if (event.defaultPrevented) return;
|
||||
if (!props.disabled && !context.open && !openTimerRef.current) {
|
||||
contentContext.onPointerGraceIntentChange(null);
|
||||
openTimerRef.current = window.setTimeout(() => {
|
||||
context.onOpenChange(true);
|
||||
clearOpenTimer();
|
||||
}, 100);
|
||||
}
|
||||
})
|
||||
),
|
||||
onPointerLeave: composeEventHandlers(
|
||||
props.onPointerLeave,
|
||||
whenMouse((event) => {
|
||||
clearOpenTimer();
|
||||
const contentRect = context.content?.getBoundingClientRect();
|
||||
if (contentRect) {
|
||||
const side = context.content?.dataset.side;
|
||||
const rightSide = side === "right";
|
||||
const bleed = rightSide ? -5 : 5;
|
||||
const contentNearEdge = contentRect[rightSide ? "left" : "right"];
|
||||
const contentFarEdge = contentRect[rightSide ? "right" : "left"];
|
||||
contentContext.onPointerGraceIntentChange({
|
||||
area: [
|
||||
// Apply a bleed on clientX to ensure that our exit point is
|
||||
// consistently within polygon bounds
|
||||
{ x: event.clientX + bleed, y: event.clientY },
|
||||
{ x: contentNearEdge, y: contentRect.top },
|
||||
{ x: contentFarEdge, y: contentRect.top },
|
||||
{ x: contentFarEdge, y: contentRect.bottom },
|
||||
{ x: contentNearEdge, y: contentRect.bottom }
|
||||
],
|
||||
side
|
||||
});
|
||||
window.clearTimeout(pointerGraceTimerRef.current);
|
||||
pointerGraceTimerRef.current = window.setTimeout(
|
||||
() => contentContext.onPointerGraceIntentChange(null),
|
||||
300
|
||||
);
|
||||
} else {
|
||||
contentContext.onTriggerLeave(event);
|
||||
if (event.defaultPrevented) return;
|
||||
contentContext.onPointerGraceIntentChange(null);
|
||||
}
|
||||
})
|
||||
),
|
||||
onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
|
||||
const isTypingAhead = contentContext.searchRef.current !== "";
|
||||
if (props.disabled || isTypingAhead && event.key === " ") return;
|
||||
if (SUB_OPEN_KEYS[rootContext.dir].includes(event.key)) {
|
||||
context.onOpenChange(true);
|
||||
context.content?.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
}
|
||||
) });
|
||||
}
|
||||
);
|
||||
MenuSubTrigger.displayName = SUB_TRIGGER_NAME;
|
||||
var SUB_CONTENT_NAME = "MenuSubContent";
|
||||
var MenuSubContent = React.forwardRef(
|
||||
(props, forwardedRef) => {
|
||||
const portalContext = usePortalContext(CONTENT_NAME, props.__scopeMenu);
|
||||
const { forceMount = portalContext.forceMount, ...subContentProps } = props;
|
||||
const context = useMenuContext(CONTENT_NAME, props.__scopeMenu);
|
||||
const rootContext = useMenuRootContext(CONTENT_NAME, props.__scopeMenu);
|
||||
const subContext = useMenuSubContext(SUB_CONTENT_NAME, props.__scopeMenu);
|
||||
const ref = React.useRef(null);
|
||||
const composedRefs = useComposedRefs(forwardedRef, ref);
|
||||
return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeMenu, children: /* @__PURE__ */ jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeMenu, children: /* @__PURE__ */ jsx(
|
||||
MenuContentImpl,
|
||||
{
|
||||
id: subContext.contentId,
|
||||
"aria-labelledby": subContext.triggerId,
|
||||
...subContentProps,
|
||||
ref: composedRefs,
|
||||
align: "start",
|
||||
side: rootContext.dir === "rtl" ? "left" : "right",
|
||||
disableOutsidePointerEvents: false,
|
||||
disableOutsideScroll: false,
|
||||
trapFocus: false,
|
||||
onOpenAutoFocus: (event) => {
|
||||
if (rootContext.isUsingKeyboardRef.current) ref.current?.focus();
|
||||
event.preventDefault();
|
||||
},
|
||||
onCloseAutoFocus: (event) => event.preventDefault(),
|
||||
onFocusOutside: composeEventHandlers(props.onFocusOutside, (event) => {
|
||||
if (event.target !== subContext.trigger) context.onOpenChange(false);
|
||||
}),
|
||||
onEscapeKeyDown: composeEventHandlers(props.onEscapeKeyDown, (event) => {
|
||||
rootContext.onClose();
|
||||
event.preventDefault();
|
||||
}),
|
||||
onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
|
||||
const isKeyDownInside = event.currentTarget.contains(event.target);
|
||||
const isCloseKey = SUB_CLOSE_KEYS[rootContext.dir].includes(event.key);
|
||||
if (isKeyDownInside && isCloseKey) {
|
||||
context.onOpenChange(false);
|
||||
subContext.trigger?.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
}
|
||||
) }) }) });
|
||||
}
|
||||
);
|
||||
MenuSubContent.displayName = SUB_CONTENT_NAME;
|
||||
function getOpenState(open) {
|
||||
return open ? "open" : "closed";
|
||||
}
|
||||
function isIndeterminate(checked) {
|
||||
return checked === "indeterminate";
|
||||
}
|
||||
function getCheckedState(checked) {
|
||||
return isIndeterminate(checked) ? "indeterminate" : checked ? "checked" : "unchecked";
|
||||
}
|
||||
function focusFirst(candidates) {
|
||||
const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
|
||||
for (const candidate of candidates) {
|
||||
if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
|
||||
candidate.focus();
|
||||
if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
|
||||
}
|
||||
}
|
||||
function wrapArray(array, startIndex) {
|
||||
return array.map((_, index) => array[(startIndex + index) % array.length]);
|
||||
}
|
||||
function getNextMatch(values, search, currentMatch) {
|
||||
const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
|
||||
const normalizedSearch = isRepeated ? search[0] : search;
|
||||
const currentMatchIndex = currentMatch ? values.indexOf(currentMatch) : -1;
|
||||
let wrappedValues = wrapArray(values, Math.max(currentMatchIndex, 0));
|
||||
const excludeCurrentMatch = normalizedSearch.length === 1;
|
||||
if (excludeCurrentMatch) wrappedValues = wrappedValues.filter((v) => v !== currentMatch);
|
||||
const nextMatch = wrappedValues.find(
|
||||
(value) => value.toLowerCase().startsWith(normalizedSearch.toLowerCase())
|
||||
);
|
||||
return nextMatch !== currentMatch ? nextMatch : void 0;
|
||||
}
|
||||
function isPointInPolygon(point, polygon) {
|
||||
const { x, y } = point;
|
||||
let inside = false;
|
||||
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
const xi = polygon[i].x;
|
||||
const yi = polygon[i].y;
|
||||
const xj = polygon[j].x;
|
||||
const yj = polygon[j].y;
|
||||
const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
|
||||
if (intersect) inside = !inside;
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
function isPointerInGraceArea(event, area) {
|
||||
if (!area) return false;
|
||||
const cursorPos = { x: event.clientX, y: event.clientY };
|
||||
return isPointInPolygon(cursorPos, area);
|
||||
}
|
||||
function whenMouse(handler) {
|
||||
return (event) => event.pointerType === "mouse" ? handler(event) : void 0;
|
||||
}
|
||||
var Root3 = Menu;
|
||||
var Anchor2 = MenuAnchor;
|
||||
var Portal = MenuPortal;
|
||||
var Content2 = MenuContent;
|
||||
var Group = MenuGroup;
|
||||
var Label = MenuLabel;
|
||||
var Item2 = MenuItem;
|
||||
var CheckboxItem = MenuCheckboxItem;
|
||||
var RadioGroup = MenuRadioGroup;
|
||||
var RadioItem = MenuRadioItem;
|
||||
var ItemIndicator = MenuItemIndicator;
|
||||
var Separator = MenuSeparator;
|
||||
var Arrow2 = MenuArrow;
|
||||
var Sub = MenuSub;
|
||||
var SubTrigger = MenuSubTrigger;
|
||||
var SubContent = MenuSubContent;
|
||||
export {
|
||||
Anchor2 as Anchor,
|
||||
Arrow2 as Arrow,
|
||||
CheckboxItem,
|
||||
Content2 as Content,
|
||||
Group,
|
||||
Item2 as Item,
|
||||
ItemIndicator,
|
||||
Label,
|
||||
Menu,
|
||||
MenuAnchor,
|
||||
MenuArrow,
|
||||
MenuCheckboxItem,
|
||||
MenuContent,
|
||||
MenuGroup,
|
||||
MenuItem,
|
||||
MenuItemIndicator,
|
||||
MenuLabel,
|
||||
MenuPortal,
|
||||
MenuRadioGroup,
|
||||
MenuRadioItem,
|
||||
MenuSeparator,
|
||||
MenuSub,
|
||||
MenuSubContent,
|
||||
MenuSubTrigger,
|
||||
Portal,
|
||||
RadioGroup,
|
||||
RadioItem,
|
||||
Root3 as Root,
|
||||
Separator,
|
||||
Sub,
|
||||
SubContent,
|
||||
SubTrigger,
|
||||
createMenuScope
|
||||
};
|
||||
//# sourceMappingURL=index.mjs.map
|
||||
Reference in New Issue
Block a user