Add dialogs

This commit is contained in:
momo5502
2025-04-30 08:06:28 +02:00
parent 3531d1d5ea
commit af5c2a9d5b
4 changed files with 269 additions and 8 deletions

View File

@@ -32,10 +32,12 @@ export interface FolderElement {
}
type ClickHandler = (element: FolderElement) => void;
type CreateFolderHandler = () => void;
export interface FolderProps {
elements: FolderElement[];
clickHandler: ClickHandler;
createFolderHandler: CreateFolderHandler;
//deleteHandler: (element: FolderElement) => void;
//renameHandler: (element: FolderElement, name: string) => void;
}
@@ -70,19 +72,18 @@ function getIcon(element: FolderElement, className: string = "") {
}
function renderIcon(element: FolderElement) {
let className = "w-10 h-10";
let className = "w-6 h-6 flex-1";
return getIcon(element, className);
}
function renderElement(element: FolderElement, clickHandler: ClickHandler) {
return (
<div
key={`folder-element-${element.name}`}
onClick={() => clickHandler(element)}
className="folder-element select-none flex flex-col gap-4 items-center text-center p-4 m-4 w-30 h-25 rounded-lg border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
className="folder-element select-none flex flex-col gap-2 items-center text-center text-xs p-2 m-2 w-25 h-18 rounded-lg border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
>
{renderIcon(element)}
<span className="whitespace-nowrap text-ellipsis overflow-hidden w-24">
<span className="whitespace-nowrap text-ellipsis overflow-hidden w-20">
{element.name}
</span>
</div>
@@ -119,6 +120,21 @@ function renderElementWithContext(
);
}
function renderElementWrapper( element: FolderElement,
clickHandler: ClickHandler,) {
return <div key={`folder-element-${element.name}`}>
{renderElementWithContext(element, clickHandler)}
</div>
}
function renderFolderCreator(createFolderHandler: CreateFolderHandler) {
return (
<ContextMenuItem onClick={createFolderHandler}>
Create new Folder
</ContextMenuItem>
);
}
export function Folder(props: FolderProps) {
return (
<ScrollArea className="h-[50dvh]">
@@ -128,11 +144,11 @@ export function Folder(props: FolderProps) {
<div className="folder flex flex-wrap">
{props.elements
.sort(elementComparator)
.map((e) => renderElementWithContext(e, props.clickHandler))}
.map((e) => renderElementWrapper(e, props.clickHandler))}
</div>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>Create new folder</ContextMenuItem>
{renderFolderCreator(props.createFolderHandler)}
</ContextMenuContent>
</ContextMenu>
</TooltipProvider>

View File

@@ -0,0 +1,133 @@
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { cn } from "@/lib/utils";
function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
function DialogOverlay({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className,
)}
{...props}
/>
);
}
function DialogContent({
className,
children,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
{...props}
/>
);
}
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className,
)}
{...props}
/>
);
}
function DialogTitle({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("text-lg leading-none font-semibold", className)}
{...props}
/>
);
}
function DialogDescription({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
};