Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2ddb676fe | ||
|
|
fe99421ec9 | ||
|
|
0aa760a786 | ||
|
|
b5e0848436 | ||
|
|
0386449ec1 |
No files matched your search
@@ -2,6 +2,7 @@ import { Box, Card } from "@mantine/core";
|
||||
|
||||
import type { DynamicSection } from "~/app/[locale]/boards/_types";
|
||||
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { DynamicSectionProvider } from "./dynamic/dynamic-context";
|
||||
import { BoardDynamicSectionMenu } from "./dynamic/dynamic-menu";
|
||||
import { GridStack } from "./gridstack/gridstack";
|
||||
import classes from "./item.module.css";
|
||||
@@ -12,6 +13,7 @@ interface Props {
|
||||
|
||||
export const BoardDynamicSection = ({ section }: Props) => {
|
||||
const board = useRequiredBoard();
|
||||
|
||||
return (
|
||||
<Box className="grid-stack-item-content">
|
||||
<Card
|
||||
@@ -27,7 +29,9 @@ export const BoardDynamicSection = ({ section }: Props) => {
|
||||
}}
|
||||
p={0}
|
||||
>
|
||||
<GridStack section={section} className="min-row" />
|
||||
<DynamicSectionProvider section={section}>
|
||||
<GridStack section={section} className="min-row" />
|
||||
</DynamicSectionProvider>
|
||||
</Card>
|
||||
<BoardDynamicSectionMenu section={section} />
|
||||
</Box>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
"use client";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
import type { DynamicSection } from "~/app/[locale]/boards/_types";
|
||||
import { useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
|
||||
interface DynamicSectionContextProps {
|
||||
sectionIds: string[];
|
||||
}
|
||||
|
||||
const DynamicSectionContext = createContext<DynamicSectionContextProps>({
|
||||
sectionIds: [],
|
||||
});
|
||||
|
||||
interface DynamicSectionProviderProps {
|
||||
section: DynamicSection;
|
||||
}
|
||||
|
||||
export const DynamicSectionProvider = ({ section, children }: PropsWithChildren<DynamicSectionProviderProps>) => {
|
||||
const aboveIds = useAboveDynamicSectionIds();
|
||||
const resetPrevious = usePlacedTopRightAndInDynamicSection({ sectionOrItem: section, previousId: aboveIds.at(-1) });
|
||||
|
||||
return (
|
||||
<DynamicSectionContext.Provider value={{ sectionIds: resetPrevious ? [section.id] : [...aboveIds, section.id] }}>
|
||||
{children}
|
||||
</DynamicSectionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAboveDynamicSectionIds = () => {
|
||||
return useContext(DynamicSectionContext).sectionIds;
|
||||
};
|
||||
|
||||
interface UsePlacedTopRightAndInDynamicSectionProps {
|
||||
sectionOrItem: { width: number; xOffset: number; yOffset: number };
|
||||
previousId: string | undefined;
|
||||
}
|
||||
|
||||
export const usePlacedTopRightAndInDynamicSection = ({
|
||||
sectionOrItem,
|
||||
previousId,
|
||||
}: UsePlacedTopRightAndInDynamicSectionProps) => {
|
||||
const board = useRequiredBoard();
|
||||
const aboveSection = board.sections.find(
|
||||
(section): section is DynamicSection => section.id === previousId && section.kind === "dynamic",
|
||||
);
|
||||
|
||||
// When not on top, no above section or not reaching the end of the above section, we want to reset the previous sections
|
||||
return (
|
||||
sectionOrItem.yOffset >= 1 || !aboveSection || sectionOrItem.xOffset + sectionOrItem.width !== aboveSection.width
|
||||
);
|
||||
};
|
||||
@@ -1,39 +1,144 @@
|
||||
import { ActionIcon, Menu } from "@mantine/core";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { Fragment, useEffect, useRef } from "react";
|
||||
import { ActionIcon, Group, Indicator, Menu, Popover, Stack, UnstyledButton } from "@mantine/core";
|
||||
import { useHover } from "@mantine/hooks";
|
||||
import { IconDotsVertical, IconTrash } from "@tabler/icons-react";
|
||||
|
||||
import { useConfirmModal } from "@homarr/modals";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { DynamicSection } from "~/app/[locale]/boards/_types";
|
||||
import { useEditMode } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { useEditMode, useRequiredBoard } from "~/app/[locale]/boards/(content)/_context";
|
||||
import { useDynamicSectionActions } from "./dynamic-actions";
|
||||
import { useAboveDynamicSectionIds } from "./dynamic-context";
|
||||
|
||||
export const BoardDynamicSectionMenu = ({ section }: { section: DynamicSection }) => {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const [isEditMode] = useEditMode();
|
||||
const aboveIds = useAboveDynamicSectionIds();
|
||||
const board = useRequiredBoard();
|
||||
const hasItemInTopRightCorner =
|
||||
section.items.some((item) => item.xOffset + item.width === section.width && item.yOffset === 0) ||
|
||||
board.sections.some(
|
||||
(innerSection) =>
|
||||
innerSection.kind === "dynamic" &&
|
||||
innerSection.parentSectionId === section.id &&
|
||||
innerSection.xOffset + innerSection.width == section.width &&
|
||||
innerSection.yOffset == 0,
|
||||
);
|
||||
|
||||
if (!isEditMode) return null;
|
||||
|
||||
if (aboveIds.length >= 1 && !hasItemInTopRightCorner) {
|
||||
return (
|
||||
<Popover width="auto" position="right" withArrow shadow="md" returnFocus>
|
||||
<Popover.Target popupType="menu">
|
||||
<UnstyledButton
|
||||
pos="absolute"
|
||||
top={4}
|
||||
right={4}
|
||||
style={{ zIndex: 10 }}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
document.querySelectorAll<HTMLButtonElement>("[data-section-item]")[0]?.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Indicator label={aboveIds.length + 1} styles={{ indicator: { height: "1rem" } }} offset={4}>
|
||||
<ActionIcon component="div" variant="default" radius={"xl"}>
|
||||
<IconDotsVertical size={"1rem"} />
|
||||
</ActionIcon>
|
||||
</Indicator>
|
||||
</UnstyledButton>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown
|
||||
ref={dropdownRef}
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
tabIndex={-1}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
document.querySelectorAll<HTMLButtonElement>("[data-section-item]")[0]?.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div tabIndex={-1} data-autofocus data-mantine-stop-propagation style={{ outline: 0 }} />
|
||||
<Stack>
|
||||
{[...aboveIds, section.id].map((id, index) => (
|
||||
<Fragment key={id}>
|
||||
<Group hiddenFrom="md"></Group>
|
||||
<DesktopButton id={id} index={index} />
|
||||
</Fragment>
|
||||
))}
|
||||
</Stack>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
if (aboveIds.length >= 1 && !hasItemInTopRightCorner) {
|
||||
return (
|
||||
<InnerMenu id={section.id}>
|
||||
<ActionIcon variant="default" radius={"xl"} pos="absolute" top={4} right={4} style={{ zIndex: 10 }}>
|
||||
<IconDotsVertical size={"1rem"} />
|
||||
</ActionIcon>
|
||||
</InnerMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
interface DesktopButtonProps {
|
||||
index: number;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const DesktopButton = ({ index, id }: DesktopButtonProps) => {
|
||||
const { hovered, ref } = useHover<HTMLButtonElement>();
|
||||
useEffect(() => {
|
||||
const sectionRef = document.querySelector<HTMLDivElement>(`[data-section-id="${id}"]`);
|
||||
if (!sectionRef) return;
|
||||
|
||||
if (hovered) {
|
||||
const previousBackground = sectionRef.style.backgroundColor;
|
||||
sectionRef.style.backgroundColor = "rgba(255, 0, 0, 0.1)";
|
||||
|
||||
return () => {
|
||||
sectionRef.style.backgroundColor = previousBackground;
|
||||
};
|
||||
}
|
||||
}, [hovered]);
|
||||
|
||||
return (
|
||||
<InnerMenu id={id} trigger="hover">
|
||||
<UnstyledButton data-section-item visibleFrom="md" ref={ref}>
|
||||
Section {index + 1}
|
||||
</UnstyledButton>
|
||||
</InnerMenu>
|
||||
);
|
||||
};
|
||||
|
||||
const InnerMenu = ({ children, id, trigger }: PropsWithChildren<{ id: string; trigger?: "hover" }>) => {
|
||||
const t = useI18n();
|
||||
const tDynamic = useScopedI18n("section.dynamic");
|
||||
const { removeDynamicSection } = useDynamicSectionActions();
|
||||
const { openConfirmModal } = useConfirmModal();
|
||||
const [isEditMode] = useEditMode();
|
||||
|
||||
if (!isEditMode) return null;
|
||||
|
||||
const openRemoveModal = () => {
|
||||
openConfirmModal({
|
||||
title: tDynamic("remove.title"),
|
||||
children: tDynamic("remove.message"),
|
||||
onConfirm: () => {
|
||||
removeDynamicSection({ id: section.id });
|
||||
removeDynamicSection({ id });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu withinPortal withArrow position="right-start" arrowPosition="center">
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="default" radius={"xl"} pos="absolute" top={4} right={4} style={{ zIndex: 10 }}>
|
||||
<IconDotsVertical size={"1rem"} />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu withinPortal withArrow position="right-start" arrowPosition="center" trigger={trigger}>
|
||||
<Menu.Target>{children}</Menu.Target>
|
||||
<Menu.Dropdown miw={128}>
|
||||
<Menu.Label c="red.6">{t("common.dangerZone")}</Menu.Label>
|
||||
<Menu.Item c="red.6" leftSection={<IconTrash size={16} />} onClick={openRemoveModal}>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { GetStylesApi } from "@mantine/core";
|
||||
import { createSafeContext } from "@mantine/core";
|
||||
|
||||
import type { SectionMenuFactory } from "./section-menu";
|
||||
|
||||
interface SectionMenuContext {
|
||||
toggleDropdown: () => void;
|
||||
closeDropdownImmediately: () => void;
|
||||
closeDropdown: () => void;
|
||||
openDropdown: () => void;
|
||||
getItemIndex: (node: HTMLButtonElement) => number | null;
|
||||
setHovered: (index: number | null) => void;
|
||||
hovered: number | null;
|
||||
closeOnItemClick: boolean | undefined;
|
||||
loop: boolean | undefined;
|
||||
trigger: "click" | "hover" | "click-hover" | undefined;
|
||||
opened: boolean;
|
||||
unstyled: boolean | undefined;
|
||||
getStyles: GetStylesApi<SectionMenuFactory>;
|
||||
menuItemTabIndex: -1 | 0 | undefined;
|
||||
openedViaClick: boolean;
|
||||
setOpenedViaClick: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const [SectionMenuContextProvider, useSectionMenuContext] = createSafeContext<SectionMenuContext>(
|
||||
"SectionMenu component was not found in the tree",
|
||||
);
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useRef } from "react";
|
||||
import type { Factory } from "@mantine/core";
|
||||
import { factory, Popover } from "@mantine/core";
|
||||
import { useMergedRef } from "@mantine/hooks";
|
||||
|
||||
import { useSectionMenuContext } from "./section-menu-context";
|
||||
|
||||
export type SectionMenuDropdownStylesNames = "dropdown";
|
||||
|
||||
export interface SectionMenuDropdownProps {
|
||||
childOpened: boolean;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export type SectionMenuDropdownFactory = Factory<{
|
||||
props: SectionMenuDropdownProps;
|
||||
ref: HTMLDivElement;
|
||||
stylesNames: SectionMenuDropdownStylesNames;
|
||||
compound: true;
|
||||
}>;
|
||||
|
||||
export const SectionMenuDropdown = factory<SectionMenuDropdownFactory>(({ childOpened, children }, ref) => {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const ctx = useSectionMenuContext();
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (childOpened) return;
|
||||
if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
event.preventDefault();
|
||||
wrapperRef.current?.querySelectorAll<HTMLButtonElement>("[data-section-menu-item]:not(:disabled)")[0]?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover.Dropdown
|
||||
role="menu"
|
||||
aria-orientation="vertical"
|
||||
ref={useMergedRef(ref, wrapperRef)}
|
||||
{...ctx.getStyles("dropdown")}
|
||||
tabIndex={-1}
|
||||
data-section-menu-dropdown
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
<div tabIndex={-1} data-autofocus data-mantine-stop-propagation style={{ outline: 0 }} />
|
||||
{children}
|
||||
</Popover.Dropdown>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
import { KeyboardEvent, useRef } from 'react';
|
||||
import {
|
||||
BoxProps,
|
||||
CompoundStylesApiProps,
|
||||
createEventHandler,
|
||||
createScopedKeydownHandler,
|
||||
polymorphicFactory,
|
||||
PolymorphicFactory,
|
||||
UnstyledButton,
|
||||
useDirection,
|
||||
useProps,
|
||||
} from '@mantine/core';
|
||||
import { useMergedRef } from '@mantine/hooks';
|
||||
import { useSectionMenuContext } from './section-menu-context';
|
||||
|
||||
export type SectionMenuItemStylesNames = 'item' | 'itemLabel' | 'itemSection';
|
||||
|
||||
export interface SectionMenuItemProps
|
||||
extends BoxProps,
|
||||
CompoundStylesApiProps<SectionMenuItemFactory> {
|
||||
/** Item label */
|
||||
children?: React.ReactNode;
|
||||
childMenu: {
|
||||
opened: boolean;
|
||||
close: () => void;
|
||||
open: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
export type SectionMenuItemFactory = PolymorphicFactory<{
|
||||
props: SectionMenuItemProps;
|
||||
defaultRef: HTMLButtonElement;
|
||||
defaultComponent: 'button';
|
||||
stylesNames: SectionMenuItemStylesNames;
|
||||
compound: true;
|
||||
}>;
|
||||
|
||||
const defaultProps: Partial<SectionMenuItemProps> = {};
|
||||
|
||||
export const SectionMenuItem = polymorphicFactory<SectionMenuItemFactory>((props, ref) => {
|
||||
const { childMenu, classNames, className, style, styles, vars, children, ...others } = useProps(
|
||||
'SectionMenuItem',
|
||||
defaultProps,
|
||||
props
|
||||
);
|
||||
|
||||
const ctx = useSectionMenuContext();
|
||||
const { dir } = useDirection();
|
||||
const itemRef = useRef<HTMLButtonElement>();
|
||||
const itemIndex = ctx.getItemIndex(itemRef.current!);
|
||||
const _others: any = others;
|
||||
|
||||
const handleMouseLeave = createEventHandler(_others.onMouseLeave, () => ctx.setHovered(-1));
|
||||
const handleMouseEnter = createEventHandler(_others.onMouseEnter, () =>
|
||||
ctx.setHovered(ctx.getItemIndex(itemRef.current!))
|
||||
);
|
||||
|
||||
const handleFocus = createEventHandler(_others.onFocus, () =>
|
||||
ctx.setHovered(ctx.getItemIndex(itemRef.current!))
|
||||
);
|
||||
|
||||
const scopedKeydownHandler = createScopedKeydownHandler({
|
||||
siblingSelector: '[data-section-menu-item]',
|
||||
parentSelector: '[data-section-menu-dropdown]',
|
||||
activateOnFocus: false,
|
||||
loop: ctx.loop,
|
||||
dir,
|
||||
orientation: 'vertical',
|
||||
onKeyDown: _others.onKeyDown,
|
||||
});
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
|
||||
console.log('hi');
|
||||
if (
|
||||
(event.key === 'ArrowRight' && dir === 'ltr') ||
|
||||
(event.key === 'ArrowLeft' && dir === 'rtl')
|
||||
) {
|
||||
childMenu.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't want to handle keydown event if child menu is opened
|
||||
if (childMenu.opened) {
|
||||
console.log('hey');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('hallo');
|
||||
scopedKeydownHandler(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<UnstyledButton
|
||||
{...others}
|
||||
unstyled={ctx.unstyled}
|
||||
tabIndex={ctx.menuItemTabIndex}
|
||||
onFocus={handleFocus}
|
||||
{...ctx.getStyles('item', { className, style, styles, classNames })}
|
||||
ref={useMergedRef(itemRef, ref)}
|
||||
role="menuitem"
|
||||
data-section-menu-item
|
||||
data-hovered={ctx.hovered === itemIndex ? true : undefined}
|
||||
data-mantine-stop-propagation
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
{children}
|
||||
</UnstyledButton>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import { cloneElement, forwardRef } from "react";
|
||||
import { createEventHandler, isElement, Popover, useProps } from "@mantine/core";
|
||||
|
||||
import { useSectionMenuContext } from "./section-menu-context";
|
||||
|
||||
export interface SectionMenuTargetProps {
|
||||
/** Target element */
|
||||
children: React.ReactNode;
|
||||
|
||||
/** Key of the prop that should be used to get element ref */
|
||||
refProp?: string;
|
||||
}
|
||||
|
||||
const defaultProps: Partial<SectionMenuTargetProps> = {
|
||||
refProp: "ref",
|
||||
};
|
||||
|
||||
export const SectionMenuTarget = forwardRef<HTMLElement, SectionMenuTargetProps>((props, ref) => {
|
||||
const { children, refProp, ...others } = useProps("SectionMenuTarget", defaultProps, props);
|
||||
|
||||
if (!isElement(children)) {
|
||||
throw new Error(
|
||||
"SectionMenu.Target component children should be an element or a component that accepts ref. Fragments, strings, numbers and other primitive values are not supported",
|
||||
);
|
||||
}
|
||||
|
||||
const ctx = useSectionMenuContext();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
||||
const onClick = createEventHandler(children.props.onClick, () => {
|
||||
ctx.toggleDropdown();
|
||||
});
|
||||
|
||||
return (
|
||||
<Popover.Target refProp={refProp} popupType="menu" ref={ref} {...others}>
|
||||
{cloneElement(children, {
|
||||
onClick,
|
||||
"data-expanded": ctx.opened ? true : undefined,
|
||||
})}
|
||||
</Popover.Target>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useState } from "react";
|
||||
import type { Factory, PopoverStylesNames } from "@mantine/core";
|
||||
import { getContextItemIndex, Popover, useHovered, useStyles } from "@mantine/core";
|
||||
import { useDidUpdate, useUncontrolled } from "@mantine/hooks";
|
||||
|
||||
import { SectionMenuContextProvider } from "./section-menu-context";
|
||||
import classes from "./SectionMenu.module.css";
|
||||
|
||||
export type SectionMenuStylesNames = "item" | "itemLabel" | "itemSection" | "label" | "divider" | PopoverStylesNames;
|
||||
|
||||
export type SectionMenuFactory = Factory<{
|
||||
props: SectionMenuProps;
|
||||
ref: HTMLDivElement;
|
||||
stylesNames: SectionMenuStylesNames;
|
||||
}>;
|
||||
|
||||
export interface SectionMenuProps {
|
||||
/** Menu content */
|
||||
children?: React.ReactNode;
|
||||
|
||||
/** Controlled menu opened state */
|
||||
opened?: boolean;
|
||||
|
||||
/** Called when Menu is opened */
|
||||
onOpen?: () => void;
|
||||
|
||||
/** Called when Menu is closed */
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export function SectionMenu(props: SectionMenuProps) {
|
||||
const { children, onOpen, onClose, opened, ...others } = props;
|
||||
|
||||
const getStyles = useStyles<SectionMenuFactory>({
|
||||
name: "SectionMenu",
|
||||
classes,
|
||||
props,
|
||||
classNames: [],
|
||||
styles: {},
|
||||
unstyled: false,
|
||||
});
|
||||
|
||||
const [hovered, { setHovered, resetHovered }] = useHovered();
|
||||
const [_opened, setOpened] = useUncontrolled({
|
||||
value: opened,
|
||||
defaultValue: false,
|
||||
finalValue: false,
|
||||
onChange: () => undefined,
|
||||
});
|
||||
const [openedViaClick, setOpenedViaClick] = useState(false);
|
||||
|
||||
const close = () => {
|
||||
setOpened(false);
|
||||
setOpenedViaClick(false);
|
||||
if (_opened) {
|
||||
onClose?.();
|
||||
}
|
||||
};
|
||||
|
||||
const open = () => {
|
||||
setOpened(true);
|
||||
if (!_opened) {
|
||||
onOpen?.();
|
||||
}
|
||||
};
|
||||
|
||||
const toggleDropdown = () => {
|
||||
if (_opened) {
|
||||
close();
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
};
|
||||
|
||||
const getItemIndex = (node: HTMLButtonElement) =>
|
||||
getContextItemIndex("[data-section-menu-item]", "[data-section-menu-dropdown]", node);
|
||||
|
||||
useDidUpdate(() => {
|
||||
resetHovered();
|
||||
}, [_opened]);
|
||||
|
||||
return (
|
||||
<SectionMenuContextProvider
|
||||
value={{
|
||||
getStyles,
|
||||
opened: _opened,
|
||||
toggleDropdown,
|
||||
getItemIndex,
|
||||
hovered,
|
||||
setHovered,
|
||||
openedViaClick,
|
||||
setOpenedViaClick,
|
||||
closeOnItemClick: false,
|
||||
closeDropdown: close,
|
||||
openDropdown: open,
|
||||
closeDropdownImmediately: close,
|
||||
loop: true,
|
||||
trigger: "click",
|
||||
unstyled: false,
|
||||
menuItemTabIndex: -1,
|
||||
}}
|
||||
>
|
||||
<Popover
|
||||
{...others}
|
||||
opened={_opened}
|
||||
onChange={toggleDropdown}
|
||||
__staticSelector="SectionMenu"
|
||||
clickOutsideEvents={["mousedown", "touchstart", "keydown"]}
|
||||
withArrow
|
||||
withinPortal={false}
|
||||
returnFocus
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
</SectionMenuContextProvider>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user