Skip to content

Commit

Permalink
Merge pull request #144 from abusix/pla-1318-hailstorm-popover-menu-a…
Browse files Browse the repository at this point in the history
…dd-option-for-auto-closing-panel-on

Add closeOnClick to Popover Menus
  • Loading branch information
Coderwelsch authored Aug 6, 2024
2 parents 875f4de + 0a02b6a commit 5913d2a
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 37 deletions.
49 changes: 49 additions & 0 deletions src/components/popover-menu/popover-menu-panel-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { PopoverButton as HeadlessUiPopoverButton } from "@headlessui/react";
import { classNames } from "../../util/class-names";

const itemIntents = {
neutral: "text-neutral-700 fill-neutral-700 hover:bg-neutral-100",
danger: "text-danger-500 fill-danger-500 hover:bg-danger-100",
};

const activeItemIntents = {
neutral: "bg-primary-100 fill-primary-400 text-primary-400 before:bg-primary-400",
danger: "bg-danger-100 fill-danger-400 text-danger-500 before:bg-danger-400",
};

export interface PopoverMenuPanelButtonProps {
children: React.ReactNode;
onClick?: () => void;
Icon?: React.ComponentType<{ className: string }>;
variant?: keyof typeof itemIntents;
selected?: boolean;
disabled?: boolean;
}

export const PopoverMenuPanelButton = ({
children,
onClick,
Icon,
variant = "neutral",
selected,
disabled,
}: PopoverMenuPanelButtonProps) => {
return (
<HeadlessUiPopoverButton
className={classNames(
"relative flex w-full cursor-pointer flex-row items-center gap-3 overflow-hidden px-4 py-2 text-sm font-normal focus:ring-2 focus:ring-primary-200",
itemIntents[variant],
selected && activeItemIntents[variant],
selected &&
"before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:rounded-r-md",
disabled &&
"cursor-not-allowed bg-neutral-100 fill-neutral-400 text-neutral-500 hover:bg-neutral-100 hover:fill-neutral-400 hover:text-neutral-500 focus:ring-0"
)}
onClick={disabled ? undefined : onClick}
>
{Icon && <Icon className={classNames("h-3.5 w-3.5")} />}
{children}
</HeadlessUiPopoverButton>
);
};
33 changes: 18 additions & 15 deletions src/components/popover-menu/popover-menu-panel-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,48 @@ import React from "react";
import { classNames } from "../../util/class-names";

const itemIntents = {
neutral: "text-neutral-700 fill-neutral-700 hover:bg-primary-100",
danger: "text-danger-500 fill-danger-500 hover:bg-danger-100",
neutral: "text-neutral-700 fill-neutral-700 hover:bg-neutral-100",
danger: "text-danger-500 fill-danger-500 hover:bg-danger-50",
};

const activeItemIntents = {
neutral: "bg-primary-100 fill-primary-400 text-primary-400 before:bg-primary-400",
danger: "bg-danger-100 fill-danger-400 text-danger-500 before:bg-danger-400",
const selectedItemIntents = {
neutral:
"bg-primary-100 fill-primary-400 text-primary-400 before:bg-primary-400 hover:text-primary-400 hover:fill-primary-400 hover:bg-primary-100",
danger: "bg-danger-100 fill-danger-700 text-danger-500 before:bg-danger-400 hover:text-danger-500 hover:fill-danger-400 hover:bg-danger-100",
};

export interface PopoverMenuPanelItemProps {
children: React.ReactNode;
onClick?: () => void;
Icon?: React.ComponentType<{ className: string }>;
variant?: keyof typeof itemIntents;
active?: boolean;
selected?: boolean;
disabled?: boolean;
}

export const PopoverMenuPanelItem = ({
children,
onClick,
Icon,
variant = "neutral",
active,
selected,
disabled,
}: PopoverMenuPanelItemProps) => {
const intentStyles = itemIntents[variant];

return (
<div
className={classNames(
"relative flex w-full cursor-pointer flex-row items-center gap-3 overflow-hidden px-4 py-2 text-sm font-normal focus:ring-2 focus:ring-primary-200",
intentStyles,
active && activeItemIntents[variant],
active &&
"before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:rounded-r-md"
itemIntents[variant],
selected && selectedItemIntents[variant],
selected &&
"before:absolute before:left-0 before:top-0 before:h-full before:w-0.5 before:rounded-r-md",
disabled &&
"cursor-not-allowed bg-neutral-100 fill-neutral-400 text-neutral-500 hover:bg-neutral-100 hover:fill-neutral-400 hover:text-neutral-500 focus:ring-0"
)}
onClick={disabled ? undefined : onClick}
onKeyDown={disabled ? undefined : onClick}
role="menuitem"
tabIndex={0}
onClick={onClick}
onKeyDown={onClick}
>
{Icon && <Icon className={classNames("h-3.5 w-3.5")} />}
{children}
Expand Down
2 changes: 2 additions & 0 deletions src/components/popover-menu/popover-menu-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PopoverMenuPanelGroup } from "./popover-menu-panel-group";
import { PopoverMenuPanelItem } from "./popover-menu-panel-item";
import { PopoverMenuPanelDivider } from "./popover-menu-panel-divider";
import { PopoverMenuPanelTitle } from "./popover-menu-panel-title";
import { PopoverMenuPanelButton } from "./popover-menu-panel-button";

export interface PopoverMenuPanelProps {
children: React.ReactNode;
Expand All @@ -28,6 +29,7 @@ const PopoverMenuPanel = ({ children }: PopoverMenuPanelProps) => {
};

PopoverMenuPanel.Item = PopoverMenuPanelItem;
PopoverMenuPanel.Button = PopoverMenuPanelButton;
PopoverMenuPanel.Group = PopoverMenuPanelGroup;
PopoverMenuPanel.Divider = PopoverMenuPanelDivider;
PopoverMenuPanel.Title = PopoverMenuPanelTitle;
Expand Down
56 changes: 34 additions & 22 deletions src/components/popover-menu/popover-menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";
import { PopoverMenu } from "./popover-menu";
import { AddIcon, ChatIcon, DeleteIcon, EditIcon } from "../../icons";
import { ChatIcon, DeleteIcon, EditIcon } from "../../icons";

const meta: Meta<typeof PopoverMenu> = {
title: "Popover Menu",
Expand All @@ -18,31 +18,43 @@ export default meta;
type Story = StoryObj<typeof PopoverMenu>;

export const Default: Story = {
render: () => (
<div className="relative flex min-h-screen min-w-736 flex-col">
<PopoverMenu>
<PopoverMenu.Button variant="secondary">Open Popover Menu</PopoverMenu.Button>
render: () => {
const [isActive, setIsActive] = React.useState(false);

<PopoverMenu.Overlay />
return (
<div className="relative flex min-h-screen min-w-736 flex-col">
<PopoverMenu>
<PopoverMenu.Button variant="secondary">Open Popover Menu</PopoverMenu.Button>

<PopoverMenu.Panel>
<PopoverMenu.Panel.Title>You</PopoverMenu.Panel.Title>
<PopoverMenu.Overlay />

<PopoverMenu.Panel.Item Icon={EditIcon}>Edit profile</PopoverMenu.Panel.Item>
<PopoverMenu.Panel.Item Icon={ChatIcon}>Support</PopoverMenu.Panel.Item>
<PopoverMenu.Panel.Item Icon={AddIcon}>Invite member</PopoverMenu.Panel.Item>
<PopoverMenu.Panel>
<PopoverMenu.Panel.Title>You</PopoverMenu.Panel.Title>

<PopoverMenu.Panel.Divider />

<PopoverMenu.Panel.Group>
<PopoverMenu.Panel.Title>Danger Zone</PopoverMenu.Panel.Title>
<PopoverMenu.Panel.Item
Icon={EditIcon}
selected={isActive}
onClick={() => setIsActive(!isActive)}
>
Activate Mfa
</PopoverMenu.Panel.Item>

<PopoverMenu.Panel.Item Icon={DeleteIcon} variant="danger">
Item 1
<PopoverMenu.Panel.Item Icon={ChatIcon} disabled>
Support
</PopoverMenu.Panel.Item>
</PopoverMenu.Panel.Group>
</PopoverMenu.Panel>
</PopoverMenu>
</div>
),

<PopoverMenu.Panel.Divider />

<PopoverMenu.Panel.Group>
<PopoverMenu.Panel.Title>Danger Zone</PopoverMenu.Panel.Title>

<PopoverMenu.Panel.Button Icon={DeleteIcon} variant="danger">
Close this Dialog
</PopoverMenu.Panel.Button>
</PopoverMenu.Panel.Group>
</PopoverMenu.Panel>
</PopoverMenu>
</div>
);
},
};

0 comments on commit 5913d2a

Please sign in to comment.