mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-14 13:52:11 +00:00
Move popover
This commit is contained in:
1 parent
1708c6094f
commit
7577bd977a
5 files changed
+2
-217
No files matched your search
@@ -1,213 +0,0 @@
|
||||
import { ReactNode } from "react";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Button } from "@storyteller/ui-button"
|
||||
|
||||
export interface PopoverItem {
|
||||
label: string;
|
||||
selected: boolean;
|
||||
icon?: ReactNode;
|
||||
action?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface PopoverMenuProps {
|
||||
items?: PopoverItem[];
|
||||
onSelect?: (item: PopoverItem) => void;
|
||||
onAdd?: () => void;
|
||||
triggerIcon?: ReactNode;
|
||||
showAddButton?: boolean;
|
||||
disableAddButton?: boolean;
|
||||
showIconsInList?: boolean;
|
||||
mode?: "default" | "toggle" | "button";
|
||||
triggerLabel?: string;
|
||||
children?: ReactNode;
|
||||
buttonClassName?: string;
|
||||
panelClassName?: string;
|
||||
onPanelAction?: (action: string) => void;
|
||||
panelTitle?: string;
|
||||
position?: "top" | "bottom";
|
||||
align?: "start" | "center" | "end";
|
||||
panelActionLabel?: string;
|
||||
}
|
||||
|
||||
export const PopoverMenu = ({
|
||||
items = [],
|
||||
onSelect,
|
||||
onAdd,
|
||||
triggerIcon,
|
||||
showAddButton = false,
|
||||
disableAddButton = false,
|
||||
showIconsInList = false,
|
||||
mode = "default",
|
||||
triggerLabel,
|
||||
children,
|
||||
buttonClassName,
|
||||
panelClassName,
|
||||
onPanelAction,
|
||||
panelTitle,
|
||||
position = "top",
|
||||
align = "start",
|
||||
panelActionLabel,
|
||||
}: PopoverMenuProps) => {
|
||||
const selectedItem = items.find((item) => item.selected);
|
||||
|
||||
const handleItemClick = (item: PopoverItem, close: () => void) => {
|
||||
if (mode === "button" && item.action && onPanelAction) {
|
||||
onPanelAction(item.action);
|
||||
close();
|
||||
} else {
|
||||
onSelect?.(item);
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
const className = twMerge(
|
||||
"text-sm font-medium rounded-lg px-3 py-2 shadow-sm",
|
||||
"flex gap-2 items-center justify-center outline-none",
|
||||
"transition-all duration-150",
|
||||
"bg-[#5F5F68]/60 px-3 text-white hover:bg-[#5F5F68]/90",
|
||||
buttonClassName,
|
||||
);
|
||||
|
||||
const positionClasses = {
|
||||
top: "bottom-full",
|
||||
bottom: "top-full",
|
||||
};
|
||||
|
||||
const alignClasses = {
|
||||
start: "left-0",
|
||||
center: "left-1/2 -translate-x-1/2",
|
||||
end: "right-0",
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
{({ close }) => (
|
||||
<>
|
||||
<Popover.Button className={className}>
|
||||
<>
|
||||
{triggerIcon}
|
||||
{mode === "toggle" && selectedItem ? (
|
||||
<span className="truncate">{selectedItem.label}</span>
|
||||
) : null}
|
||||
{mode === "default" && triggerLabel ? (
|
||||
<span className="truncate">{triggerLabel}</span>
|
||||
) : null}
|
||||
</>
|
||||
</Popover.Button>
|
||||
|
||||
<div
|
||||
className={twMerge(
|
||||
"absolute transform-gpu",
|
||||
positionClasses[position],
|
||||
alignClasses[align],
|
||||
)}
|
||||
>
|
||||
<Transition
|
||||
enter="transition duration-100 ease-out"
|
||||
enterFrom={
|
||||
position === "bottom"
|
||||
? "translate-y-2 opacity-0"
|
||||
: "-translate-y-2 opacity-0"
|
||||
}
|
||||
enterTo="translate-y-0 opacity-100"
|
||||
leave="transition duration-100 ease-in"
|
||||
leaveFrom="translate-y-0 opacity-100"
|
||||
leaveTo={
|
||||
position === "bottom"
|
||||
? "translate-y-2 opacity-0"
|
||||
: "-translate-y-2 opacity-0"
|
||||
}
|
||||
>
|
||||
<Popover.Panel
|
||||
className={twMerge(
|
||||
position === "bottom" ? "origin-top" : "origin-bottom",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={twMerge(
|
||||
"z-10 min-w-48 rounded-lg bg-[#46464B] p-1.5 shadow-lg",
|
||||
position === "top" && "mb-2",
|
||||
position === "bottom" && "mt-2",
|
||||
panelClassName,
|
||||
)}
|
||||
>
|
||||
{panelTitle && (
|
||||
<div className="mb-2 mt-0.5 flex justify-between px-1.5 text-sm font-normal text-white opacity-70">
|
||||
{panelTitle}
|
||||
{panelActionLabel && (
|
||||
<button
|
||||
onClick={() => {
|
||||
onPanelAction?.(panelActionLabel);
|
||||
close();
|
||||
}}
|
||||
className="text-end text-sm text-white/85 hover:underline"
|
||||
>
|
||||
{panelActionLabel}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{mode === "default" && children ? (
|
||||
<div className="text-sm text-white">{children}</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-0 text-sm text-white">
|
||||
{items.map((item, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
className={twMerge(
|
||||
"flex items-center justify-between border-transparent bg-transparent px-1.5",
|
||||
"hover:bg-[#63636B]/60",
|
||||
mode === "toggle" && item.selected
|
||||
? "hover:bg-[#63636B]"
|
||||
: "bg-transparent",
|
||||
item.disabled
|
||||
? "!cursor-not-allowed opacity-50"
|
||||
: "",
|
||||
)}
|
||||
onClick={() =>
|
||||
!item.disabled && handleItemClick(item, close)
|
||||
}
|
||||
variant="secondary"
|
||||
disabled={item.disabled}
|
||||
>
|
||||
<div className="flex items-center gap-2 truncate">
|
||||
{showIconsInList && item.icon}
|
||||
<span className="truncate">{item.label}</span>
|
||||
</div>
|
||||
{mode === "toggle" && (
|
||||
<input
|
||||
type="radio"
|
||||
checked={item.selected}
|
||||
readOnly
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
))}
|
||||
{showAddButton && onAdd && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className={twMerge(
|
||||
"mb-0.5 mt-2 border-none py-1",
|
||||
disableAddButton
|
||||
? "cursor-not-allowed bg-[#7B7B84]/50 opacity-50"
|
||||
: "bg-[#7B7B84] hover:bg-[#8c8c96]",
|
||||
)}
|
||||
onClick={onAdd}
|
||||
disabled={disableAddButton}
|
||||
>
|
||||
+ Add
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./Popover";
|
||||
@@ -2,7 +2,6 @@ export * from "./FilterButtons";
|
||||
export * from "./InputVector";
|
||||
export * from "./NumberInput";
|
||||
export * from "./Pagination";
|
||||
export * from "./Popover";
|
||||
export * from "./SearchFilter";
|
||||
export * from "./UploadModal";
|
||||
export * from "./UploadModal3D";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { faBell, faSpinnerThird } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { PopoverMenu } from "~/components/reusable/Popover";
|
||||
import { PopoverMenu } from "@storyteller/ui-popover";
|
||||
import { CompletedCard } from "./CompletedCard";
|
||||
import { InProgressCard } from "./InProgressCard";
|
||||
import { useJobContext } from "~/components/JobContext";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import { faBell, faSpinnerThird } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { PopoverMenu } from "~/components/reusable/Popover/Popover";
|
||||
import { PopoverMenu } from "@storyteller/ui-popover";
|
||||
import { CompletedCard } from "./CompletedCard";
|
||||
import { InProgressCard } from "./InProgressCard";
|
||||
import { Tooltip } from "@storyteller/ui-tooltip";
|
||||
|
||||
Reference in new issue
Block a user