import * as React from "react";
import { css, cx } from "@emotion/css";
import Downshift, { ControllerStateAndHelpers } from "downshift";
import { Dropdownable } from "../../dropdownable";
import { border, buttonReset, display } from "../../shared/styles/styleUtils";
import PopoverBox from "../../popover/components/PopoverBox";
import PopoverListItem from "../../popover/components/PopoverListItem";
import { DropdownSectionProps } from "./DropdownSection";
import { DropdownMenuItemProps } from "./DropdownMenuItem";
import { Direction } from "../../dropdownable/components/Dropdownable";
import {
spaceM,
themeBgPrimary
} from "../../design-tokens/build/js/designTokens";
const stickyFooter = css`
background-color: ${themeBgPrimary};
position: sticky;
bottom: 0;
left: 0;
right: 0;
padding: 2px 0;
width: 100%;
&::before {
content: " ";
top: -17px;
width: inherit;
height: ${spaceM};
position: absolute;
background: linear-gradient(transparent, ${themeBgPrimary});
}
`;
const SectionWrapper = ({ children, footer = false, sectionIndex }) => {
return (
{children}
);
};
export interface DropdownMenuProps {
/**
* Groups of dropdown menu items
*/
children:
| React.ReactElement
| Array>;
/**
* Whether the dropdown starts open
*/
initialIsOpen?: boolean;
/**
* Maximum height the menu can grow to. Can accept any number value and it will add `px` or any valid maxHeight value including vh, ems, or rems.
*/
menuMaxHeight?: React.CSSProperties["maxHeight"];
/**
* Maximum width the menu can grow to. Can accept any number value and it will add `px` or any valid maxWidth value including vw, ems, or rems.
*/
menuMaxWidth?: React.CSSProperties["maxWidth"];
/**
* Callback for when a menu item is clicked
*/
onSelect?: (
selectedItem: string,
stateAndHelpers?: ControllerStateAndHelpers<
React.ReactElement
>
) => void;
/**
* Which DOM node the dropdown menu will attach to
*/
overlayRoot?: HTMLElement;
/**
* The preferred direction to open the menu in relation to the trigger
*/
preferredDirections?: Direction[];
/**
* The node that opens the menu when clicked
*/
trigger: React.ReactNode;
/**
* Whether the dropdown node should be portalled to document.body, or open in it's parent DOM node
*/
disablePortal?: boolean;
/**
* Allows custom styling
*/
className?: string;
/**
* Human-readable selector used for writing tests
*/
"data-cy"?: string;
}
const defaultItemToString = (
item: React.ReactElement | any
) => (item ? item.props.value : "");
const DropdownMenu = (props: DropdownMenuProps) => {
const {
children,
disablePortal,
initialIsOpen,
menuMaxHeight,
menuMaxWidth,
onSelect,
overlayRoot,
preferredDirections = [
Direction.BottomRight,
Direction.BottomLeft,
Direction.BottomCenter,
Direction.TopRight,
Direction.TopCenter,
Direction.TopLeft
],
trigger,
"data-cy": dataCy = "dropdownmenu"
} = props;
const handleSelection = (
selectedItem: React.ReactElement | null,
stateAndHelpers?: ControllerStateAndHelpers<
React.ReactElement
>
) => {
if (onSelect) {
onSelect(defaultItemToString(selectedItem), stateAndHelpers);
}
};
const getDropdownContents = (highlightedIndex, getItemProps) =>
(
React.Children.toArray(children) as Array<
React.ReactElement
>
).reduce<{
sections: React.ReactNode[];
menuItemIndex: number;
}>(
(acc, item, sectionIndex) => {
const { sections = [] } = acc;
const { children, footer } = item.props;
const menuItems = React.Children.toArray(
children
) as React.ReactElement[];
const childrenWithKeys = menuItems.map((child, i) => {
acc.menuItemIndex++;
return (
{child}
);
});
return {
sections: [
...sections,
{childrenWithKeys}
],
menuItemIndex: acc.menuItemIndex
};
},
{ sections: [], menuItemIndex: -1 }
);
return (
{({
getToggleButtonProps,
getItemProps,
getMenuProps,
highlightedIndex,
isOpen
}) => (