Initial attempt @ submenu implementation.
This commit is contained in:
parent
9c3ff73a73
commit
14ad745d00
@ -24,10 +24,11 @@ const DropdownMenu = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const MenuTriggerComp = getMenuTriggerComponent(children);
|
const MenuTriggerComp = getMenuTriggerComponent(children);
|
||||||
const MenuContentComp = getMenuContentComponent(children);
|
const MenuContentComp = getMenuContentComponent(children);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.Root open={open}>
|
<DropdownMenuPrimitive.Root open={open}>
|
||||||
{MenuTriggerComp}
|
{MenuTriggerComp}
|
||||||
{open && MenuContentComp}
|
{MenuContentComp}
|
||||||
</DropdownMenuPrimitive.Root>
|
</DropdownMenuPrimitive.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
26
src/components/dropdownMenu/DropdownMenuSub.tsx
Normal file
26
src/components/dropdownMenu/DropdownMenuSub.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
|
import {
|
||||||
|
getSubMenuContentComponent,
|
||||||
|
getSubMenuTriggerComponent,
|
||||||
|
} from "./dropdownMenuUtils";
|
||||||
|
import DropdownMenuSubTrigger from "./DropdownMenuSubTrigger";
|
||||||
|
import DropdownMenuSubContent from "./DropdownMenuSubContent";
|
||||||
|
import DropdownMenuSubItem from "./DropdownMenuSubItem";
|
||||||
|
|
||||||
|
const DropdownMenuSub = ({ children }: { children?: React.ReactNode }) => {
|
||||||
|
const MenuTriggerComp = getSubMenuTriggerComponent(children);
|
||||||
|
const MenuContentComp = getSubMenuContentComponent(children);
|
||||||
|
return (
|
||||||
|
<DropdownMenuPrimitive.Sub>
|
||||||
|
{MenuTriggerComp}
|
||||||
|
{MenuContentComp}
|
||||||
|
</DropdownMenuPrimitive.Sub>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DropdownMenuSub.Trigger = DropdownMenuSubTrigger;
|
||||||
|
DropdownMenuSub.Content = DropdownMenuSubContent;
|
||||||
|
DropdownMenuSub.Item = DropdownMenuSubItem;
|
||||||
|
|
||||||
|
export default DropdownMenuSub;
|
||||||
|
DropdownMenuSub.displayName = "DropdownMenuSub";
|
38
src/components/dropdownMenu/DropdownMenuSubContent.tsx
Normal file
38
src/components/dropdownMenu/DropdownMenuSubContent.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
|
import { useDevice } from "../App";
|
||||||
|
import Stack from "../Stack";
|
||||||
|
import { Island } from "../Island";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
const DropdownMenuSubContent = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const device = useDevice();
|
||||||
|
|
||||||
|
const classNames = clsx(`dropdown-menu ${className}`, {
|
||||||
|
"dropdown-menu--mobile": device.isMobile,
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenuPrimitive.SubContent className={classNames}>
|
||||||
|
{device.isMobile ? (
|
||||||
|
<Stack.Col className="dropdown-menu-container">{children}</Stack.Col>
|
||||||
|
) : (
|
||||||
|
<Island
|
||||||
|
className="dropdown-menu-container"
|
||||||
|
padding={2}
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Island>
|
||||||
|
)}
|
||||||
|
</DropdownMenuPrimitive.SubContent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DropdownMenuSubContent;
|
||||||
|
DropdownMenuSubContent.displayName = "DropdownMenuSubContent";
|
39
src/components/dropdownMenu/DropdownMenuSubItem.tsx
Normal file
39
src/components/dropdownMenu/DropdownMenuSubItem.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import MenuItemContent from "./DropdownMenuItemContent";
|
||||||
|
import {
|
||||||
|
getDropdownMenuItemClassName,
|
||||||
|
useHandleDropdownMenuItemClick,
|
||||||
|
} from "./common";
|
||||||
|
|
||||||
|
const DropdownMenuSubItem = ({
|
||||||
|
icon,
|
||||||
|
onSelect,
|
||||||
|
children,
|
||||||
|
shortcut,
|
||||||
|
className,
|
||||||
|
...rest
|
||||||
|
}: {
|
||||||
|
icon?: JSX.Element;
|
||||||
|
onSelect: (event: Event) => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
shortcut?: string;
|
||||||
|
className?: string;
|
||||||
|
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
|
||||||
|
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...rest}
|
||||||
|
onClick={handleClick}
|
||||||
|
type="button"
|
||||||
|
className={getDropdownMenuItemClassName(className)}
|
||||||
|
title={rest.title ?? rest["aria-label"]}
|
||||||
|
>
|
||||||
|
<MenuItemContent icon={icon} shortcut={shortcut}>
|
||||||
|
{children}
|
||||||
|
</MenuItemContent>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DropdownMenuSubItem;
|
||||||
|
DropdownMenuSubItem.displayName = "DropdownMenuSubItem";
|
29
src/components/dropdownMenu/DropdownMenuSubTrigger.tsx
Normal file
29
src/components/dropdownMenu/DropdownMenuSubTrigger.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
|
import React from "react";
|
||||||
|
import MenuItemContent from "./DropdownMenuItemContent";
|
||||||
|
import { getDropdownMenuItemClassName } from "./common";
|
||||||
|
|
||||||
|
const DropdownMenuSubTrigger = ({
|
||||||
|
children,
|
||||||
|
icon,
|
||||||
|
shortcut,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
shortcut?: string;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
|
className={getDropdownMenuItemClassName(className)}
|
||||||
|
>
|
||||||
|
<MenuItemContent icon={icon} shortcut={shortcut}>
|
||||||
|
{children}
|
||||||
|
</MenuItemContent>
|
||||||
|
</DropdownMenuPrimitive.SubTrigger>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DropdownMenuSubTrigger;
|
||||||
|
DropdownMenuSubTrigger.displayName = "DropdownMenuSubTrigger";
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
export const getMenuTriggerComponent = (children: React.ReactNode) => {
|
const getMenuComponent = (component: string) => (children: React.ReactNode) => {
|
||||||
const comp = React.Children.toArray(children).find(
|
const comp = React.Children.toArray(children).find(
|
||||||
(child) =>
|
(child) =>
|
||||||
React.isValidElement(child) &&
|
React.isValidElement(child) &&
|
||||||
@ -8,7 +8,7 @@ export const getMenuTriggerComponent = (children: React.ReactNode) => {
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
child?.type.displayName &&
|
child?.type.displayName &&
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
child.type.displayName === "DropdownMenuTrigger",
|
child.type.displayName === component,
|
||||||
);
|
);
|
||||||
if (!comp) {
|
if (!comp) {
|
||||||
return null;
|
return null;
|
||||||
@ -17,19 +17,11 @@ export const getMenuTriggerComponent = (children: React.ReactNode) => {
|
|||||||
return comp;
|
return comp;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMenuContentComponent = (children: React.ReactNode) => {
|
export const getMenuTriggerComponent = getMenuComponent("DropdownMenuTrigger");
|
||||||
const comp = React.Children.toArray(children).find(
|
export const getMenuContentComponent = getMenuComponent("DropdownMenuContent");
|
||||||
(child) =>
|
export const getSubMenuTriggerComponent = getMenuComponent(
|
||||||
React.isValidElement(child) &&
|
"DropdownMenuSubTrigger",
|
||||||
typeof child.type !== "string" &&
|
);
|
||||||
//@ts-ignore
|
export const getSubMenuContentComponent = getMenuComponent(
|
||||||
child?.type.displayName &&
|
"DropdownMenuSubContent",
|
||||||
//@ts-ignore
|
);
|
||||||
child.type.displayName === "DropdownMenuContent",
|
|
||||||
);
|
|
||||||
if (!comp) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
//@ts-ignore
|
|
||||||
return comp;
|
|
||||||
};
|
|
||||||
|
@ -14,6 +14,7 @@ import { HamburgerMenuIcon } from "../icons";
|
|||||||
import { withInternalFallback } from "../hoc/withInternalFallback";
|
import { withInternalFallback } from "../hoc/withInternalFallback";
|
||||||
import { composeEventHandlers } from "../../utils";
|
import { composeEventHandlers } from "../../utils";
|
||||||
import { useTunnels } from "../context/tunnels";
|
import { useTunnels } from "../context/tunnels";
|
||||||
|
import DropdownMenuSub from "../dropdownMenu/DropdownMenuSub";
|
||||||
|
|
||||||
const MainMenu = Object.assign(
|
const MainMenu = Object.assign(
|
||||||
withInternalFallback(
|
withInternalFallback(
|
||||||
@ -77,6 +78,7 @@ const MainMenu = Object.assign(
|
|||||||
ItemCustom: DropdownMenu.ItemCustom,
|
ItemCustom: DropdownMenu.ItemCustom,
|
||||||
Group: DropdownMenu.Group,
|
Group: DropdownMenu.Group,
|
||||||
Separator: DropdownMenu.Separator,
|
Separator: DropdownMenu.Separator,
|
||||||
|
Sub: DropdownMenuSub,
|
||||||
DefaultItems,
|
DefaultItems,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -506,6 +506,16 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
|
|||||||
const renderMenu = () => {
|
const renderMenu = () => {
|
||||||
return (
|
return (
|
||||||
<MainMenu>
|
<MainMenu>
|
||||||
|
<MainMenu.Sub>
|
||||||
|
<MainMenu.Sub.Trigger>Trigger</MainMenu.Sub.Trigger>
|
||||||
|
<MainMenu.Sub.Content>
|
||||||
|
<MainMenu.Sub.Item
|
||||||
|
onSelect={() => window.alert("You clicked on sub item")}
|
||||||
|
>
|
||||||
|
Sub item
|
||||||
|
</MainMenu.Sub.Item>
|
||||||
|
</MainMenu.Sub.Content>
|
||||||
|
</MainMenu.Sub>
|
||||||
<MainMenu.DefaultItems.SaveAsImage />
|
<MainMenu.DefaultItems.SaveAsImage />
|
||||||
<MainMenu.DefaultItems.Export />
|
<MainMenu.DefaultItems.Export />
|
||||||
<MainMenu.Separator />
|
<MainMenu.Separator />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user