Labeled Group Node

A group node with an optional label.

UI Component: labeled-group-node

index.tsx
import React, { type ReactNode, type ComponentProps } from "react";
import { Panel, type NodeProps, type PanelPosition } from "@xyflow/react";
 
import { BaseNode } from "@/registry/components/base-node";
import { cn } from "@/lib/utils";
 
/* GROUP NODE Label ------------------------------------------------------- */
 
export type GroupNodeLabelProps = ComponentProps<"div">;
 
export function GroupNodeLabel({
  children,
  className,
  ...props
}: GroupNodeLabelProps) {
  return (
    <div className="h-full w-full" {...props}>
      <div
        className={cn(
          "text-card-foreground bg-secondary w-fit p-2 text-xs",
          className,
        )}
      >
        {children}
      </div>
    </div>
  );
}
 
export type GroupNodeProps = Partial<NodeProps> & {
  label?: ReactNode;
  position?: PanelPosition;
};
 
/* GROUP NODE -------------------------------------------------------------- */
 
export function GroupNode({ label, position, ...props }: GroupNodeProps) {
  const getLabelClassName = (position?: PanelPosition) => {
    switch (position) {
      case "top-left":
        return "rounded-br-sm";
      case "top-center":
        return "rounded-b-sm";
      case "top-right":
        return "rounded-bl-sm";
      case "bottom-left":
        return "rounded-tr-sm";
      case "bottom-right":
        return "rounded-tl-sm";
      case "bottom-center":
        return "rounded-t-sm";
      default:
        return "rounded-br-sm";
    }
  };
 
  return (
    <BaseNode
      className="h-full overflow-hidden rounded-sm bg-white bg-opacity-50"
      {...props}
    >
      <Panel className="m-0 p-0" position={position}>
        {label && (
          <GroupNodeLabel className={getLabelClassName(position)}>
            {label}
          </GroupNodeLabel>
        )}
      </Panel>
    </BaseNode>
  );
}
component-example.tsx
import { memo } from "react";
 
import { GroupNode } from "@/registry/components/labeled-group-node";
 
const LabeledGroupNodeDemo = memo(() => <GroupNode label="Label" />);
 
LabeledGroupNodeDemo.displayName = "LabeledGroupNodeDemo";
 
export default LabeledGroupNodeDemo;