mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-01 08:59:46 +02:00
Initial Commit 🚀 🚀
This commit is contained in:
commit
4f2a629340
444 changed files with 76863 additions and 0 deletions
193
ui/src/components/flow/nodes/NodeHeader.tsx
Normal file
193
ui/src/components/flow/nodes/NodeHeader.tsx
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { useNodeId, useReactFlow } from "@xyflow/react";
|
||||
import { EllipsisVertical, Trash } from "lucide-react";
|
||||
import { forwardRef, HTMLAttributes, ReactNode,useCallback } from "react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
/* NODE HEADER -------------------------------------------------------------- */
|
||||
|
||||
export type NodeHeaderProps = HTMLAttributes<HTMLElement>;
|
||||
|
||||
/**
|
||||
* A container for a consistent header layout intended to be used inside the
|
||||
* `<BaseNode />` component.
|
||||
*/
|
||||
export const NodeHeader = forwardRef<HTMLElement, NodeHeaderProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<header
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
"flex items-center justify-between gap-2 px-3 py-2",
|
||||
// Remove or modify these classes if you modify the padding in the
|
||||
// `<BaseNode />` component.
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
NodeHeader.displayName = "NodeHeader";
|
||||
|
||||
/* NODE HEADER TITLE -------------------------------------------------------- */
|
||||
|
||||
export type NodeHeaderTitleProps = HTMLAttributes<HTMLHeadingElement> & {
|
||||
asChild?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The title text for the node. To maintain a native application feel, the title
|
||||
* text is not selectable.
|
||||
*/
|
||||
export const NodeHeaderTitle = forwardRef<
|
||||
HTMLHeadingElement,
|
||||
NodeHeaderTitleProps
|
||||
>(({ className, asChild, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "h3";
|
||||
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(className, "user-select-none flex-1 font-semibold")}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
NodeHeaderTitle.displayName = "NodeHeaderTitle";
|
||||
|
||||
/* NODE HEADER ICON --------------------------------------------------------- */
|
||||
|
||||
export type NodeHeaderIconProps = HTMLAttributes<HTMLSpanElement>;
|
||||
|
||||
export const NodeHeaderIcon = forwardRef<HTMLSpanElement, NodeHeaderIconProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<span ref={ref} {...props} className={cn(className, "[&>*]:size-5")} />
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
NodeHeaderIcon.displayName = "NodeHeaderIcon";
|
||||
|
||||
/* NODE HEADER ACTIONS ------------------------------------------------------ */
|
||||
|
||||
export type NodeHeaderActionsProps = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
/**
|
||||
* A container for right-aligned action buttons in the node header.
|
||||
*/
|
||||
export const NodeHeaderActions = forwardRef<
|
||||
HTMLDivElement,
|
||||
NodeHeaderActionsProps
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
"ml-auto flex items-center gap-1 justify-self-end",
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
NodeHeaderActions.displayName = "NodeHeaderActions";
|
||||
|
||||
/* NODE HEADER ACTION ------------------------------------------------------- */
|
||||
|
||||
export type NodeHeaderActionProps = React.ComponentProps<"button"> & {
|
||||
label: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A thin wrapper around the `<Button />` component with a fixed sized suitable
|
||||
* for icons.
|
||||
*
|
||||
* Because the `<NodeHeaderAction />` component is intended to render icons, it's
|
||||
* important to provide a meaningful and accessible `label` prop that describes
|
||||
* the action.
|
||||
*/
|
||||
export const NodeHeaderAction = forwardRef<
|
||||
HTMLButtonElement,
|
||||
NodeHeaderActionProps
|
||||
>(({ className, label, title, ...props }, ref) => {
|
||||
return (
|
||||
<Button
|
||||
ref={ref}
|
||||
variant="ghost"
|
||||
aria-label={label}
|
||||
title={title ?? label}
|
||||
className={cn(className, "nodrag size-6 p-1")}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
NodeHeaderAction.displayName = "NodeHeaderAction";
|
||||
|
||||
//
|
||||
|
||||
export type NodeHeaderMenuActionProps = Omit<
|
||||
NodeHeaderActionProps,
|
||||
"onClick"
|
||||
> & {
|
||||
trigger?: ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a header action that opens a dropdown menu when clicked. The dropdown
|
||||
* trigger is a button with an ellipsis icon. The trigger's content can be changed
|
||||
* by using the `trigger` prop.
|
||||
*
|
||||
* Any children passed to the `<NodeHeaderMenuAction />` component will be rendered
|
||||
* inside the dropdown menu. You can read the docs for the shadcn dropdown menu
|
||||
* here: https://ui.shadcn.com/docs/components/dropdown-menu
|
||||
*
|
||||
*/
|
||||
export const NodeHeaderMenuAction = forwardRef<
|
||||
HTMLButtonElement,
|
||||
NodeHeaderMenuActionProps
|
||||
>(({ trigger, children, ...props }, ref) => {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<NodeHeaderAction ref={ref} {...props}>
|
||||
{trigger ?? <EllipsisVertical />}
|
||||
</NodeHeaderAction>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>{children}</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
});
|
||||
|
||||
NodeHeaderMenuAction.displayName = "NodeHeaderMenuAction";
|
||||
|
||||
/* NODE HEADER DELETE ACTION --------------------------------------- */
|
||||
|
||||
export const NodeHeaderDeleteAction = () => {
|
||||
const id = useNodeId();
|
||||
const { setNodes } = useReactFlow();
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
setNodes((prevNodes) => prevNodes.filter((node) => node.id !== id));
|
||||
}, [id, setNodes]);
|
||||
|
||||
return (
|
||||
<NodeHeaderAction onClick={handleClick} label="Delete node">
|
||||
<Trash />
|
||||
</NodeHeaderAction>
|
||||
);
|
||||
};
|
||||
|
||||
NodeHeaderDeleteAction.displayName = "NodeHeaderDeleteAction";
|
||||
Loading…
Add table
Add a link
Reference in a new issue