fix
This commit is contained in:
@@ -6,6 +6,13 @@ import {
|
||||
|
||||
import { Icon } from "../Icon/Icon";
|
||||
import { SpinnerSvg } from "../../assets/svg/SpinnerSvg";
|
||||
import {
|
||||
brandRecipe,
|
||||
cpm,
|
||||
inlineSizeRecipe,
|
||||
itemSizeRecipe,
|
||||
itemVariantRecipe,
|
||||
} from "@dg/css";
|
||||
|
||||
export type ButtonSize = "xs" | "sm" | "md" | "lg";
|
||||
|
||||
@@ -46,7 +53,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
|
||||
const mergedClass = cpm(
|
||||
itemSizeRecipe({ size, shape, iconOnly, longPadding: false }),
|
||||
variantRecipe({ variant, disabled }),
|
||||
itemVariantRecipe({ variant, disabled }),
|
||||
brandRecipe({ brand }),
|
||||
className,
|
||||
);
|
||||
|
||||
@@ -1,50 +1,149 @@
|
||||
import {
|
||||
autoUpdate,
|
||||
flip,
|
||||
FloatingNode,
|
||||
offset,
|
||||
safePolygon,
|
||||
shift,
|
||||
useClick,
|
||||
useDismiss,
|
||||
useFloating,
|
||||
useFloatingNodeId,
|
||||
useFloatingParentNodeId,
|
||||
useFloatingTree,
|
||||
useHover,
|
||||
useInteractions,
|
||||
useListItem,
|
||||
useListNavigation,
|
||||
useMergeRefs,
|
||||
useRole,
|
||||
useTypeahead,
|
||||
type Placement,
|
||||
} from "@floating-ui/react";
|
||||
import { useRef, useState } from "react";
|
||||
import {
|
||||
forwardRef,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentPropsWithoutRef,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import type { CommonProps } from "../../utils/CommonProps";
|
||||
import { MenuContext } from "./MenuContext";
|
||||
import { Button } from "../Button/Button";
|
||||
import { MenuItem } from "./MenuItem";
|
||||
|
||||
export type MenuProps = CommonProps;
|
||||
export type MenuProps = ComponentPropsWithoutRef<"button"> & {
|
||||
size?: "sm" | "md";
|
||||
iconSvg?: ReactNode;
|
||||
};
|
||||
|
||||
export const Menu = (props: MenuProps) => {
|
||||
const { children } = props;
|
||||
export const Menu = forwardRef<HTMLElement, MenuProps>(
|
||||
(props: MenuProps, ref) => {
|
||||
const { children, size = "sm", iconSvg, ...rest } = props;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
||||
const [hasFocusInside, setHasFocusInside] = useState(false);
|
||||
|
||||
const elementsRef = useRef([]);
|
||||
const parent = useContext(MenuContext);
|
||||
const tree = useFloatingTree();
|
||||
const nodeId = useFloatingNodeId();
|
||||
const parentId = useFloatingParentNodeId();
|
||||
const item = useListItem();
|
||||
|
||||
const isNested = parentId != null;
|
||||
|
||||
const { refs, floatingStyles, context } = useFloating({
|
||||
placement: "bottom-start",
|
||||
placement: isNested ? "right-start" : "bottom-start",
|
||||
open: isOpen,
|
||||
onOpenChange: setIsOpen,
|
||||
whileElementsMounted: autoUpdate,
|
||||
middleware: [flip()],
|
||||
middleware: [
|
||||
offset({
|
||||
mainAxis: isNested ? 0 : 4,
|
||||
alignmentAxis: isNested ? -4 : 0,
|
||||
}),
|
||||
flip(),
|
||||
shift(),
|
||||
],
|
||||
});
|
||||
|
||||
const elementsRef = useRef([]);
|
||||
const hover = useHover(context, {
|
||||
enabled: isNested,
|
||||
delay: { open: 75 },
|
||||
handleClose: safePolygon({ blockPointerEvents: true }),
|
||||
});
|
||||
|
||||
const click = useClick(context, {
|
||||
event: "mousedown",
|
||||
toggle: !isNested,
|
||||
ignoreMouse: isNested,
|
||||
});
|
||||
|
||||
const role = useRole(context, { role: "menu" });
|
||||
|
||||
const dismiss = useDismiss(context, { bubbles: true });
|
||||
|
||||
const listNav = useListNavigation(context, {
|
||||
listRef: elementsRef,
|
||||
activeIndex,
|
||||
nested: isNested,
|
||||
onNavigate: setActiveIndex,
|
||||
});
|
||||
|
||||
const click = useClick(context);
|
||||
const dismiss = useDismiss(context);
|
||||
const role = useRole(context, { role: "listbox" });
|
||||
const { getReferenceProps, getFloatingProps, getItemProps } =
|
||||
useInteractions([hover, click, dismiss, role, listNav]);
|
||||
|
||||
const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions(
|
||||
[listNav, click, dismiss, role],
|
||||
// Event emitter allows you to communicate across tree components.
|
||||
// This effect closes all menus when an item gets clicked anywhere
|
||||
// in the tree.
|
||||
useEffect(() => {
|
||||
if (!tree) return;
|
||||
|
||||
function handleTreeClick() {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
function onSubMenuOpen(event: { nodeId: string; parentId: string }) {
|
||||
if (event.nodeId !== nodeId && event.parentId === parentId) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
tree.events.on("click", handleTreeClick);
|
||||
tree.events.on("menuopen", onSubMenuOpen);
|
||||
|
||||
return () => {
|
||||
tree.events.off("click", handleTreeClick);
|
||||
tree.events.off("menuopen", onSubMenuOpen);
|
||||
};
|
||||
}, [tree, nodeId, parentId]);
|
||||
|
||||
const Trigger = isNested ? MenuItem : Button;
|
||||
|
||||
return (
|
||||
<FloatingNode id={nodeId}>
|
||||
<Trigger
|
||||
ref={useMergeRefs([refs.setReference, item.ref, ref])}
|
||||
tabIndex={
|
||||
!isNested ? undefined : parent.activeIndex === item.index ? 0 : -1
|
||||
}
|
||||
role={isNested ? "menuitem" : undefined}
|
||||
iconSvg={iconSvg}
|
||||
size={size}
|
||||
{...getReferenceProps(
|
||||
parent.getItemProps({
|
||||
...rest,
|
||||
onFocus(event: React.FocusEvent<HTMLButtonElement>) {
|
||||
rest.onFocus?.(event);
|
||||
setHasFocusInside(false);
|
||||
parent.setHasFocusInside(true);
|
||||
},
|
||||
}),
|
||||
)}
|
||||
></Trigger>
|
||||
</FloatingNode>
|
||||
);
|
||||
|
||||
return;
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { useContext } from "react";
|
||||
import { MenuContext } from "./MenuContext";
|
||||
|
||||
export const MenuItem = () => {
|
||||
const menuContext = useContext(MenuContext);
|
||||
const isRoot = menuContext ?? false;
|
||||
return <div></div>;
|
||||
};
|
||||
Reference in New Issue
Block a user