daat-locus 0.4.0

A long-running local agent runtime with memory, workflows, apps, and sleep-time self-improvement.
import { useState, useCallback, type ReactNode } from "react";
import { ChevronDownIcon } from "lucide-react";
import { cn } from "@/lib/utils";

type CollapsibleContextValue = {
  open: boolean;
  toggle: () => void;
};

function useCollapsibleState(
  defaultOpen: boolean,
  controlledOpen?: boolean,
  onOpenChange?: (open: boolean) => void,
): CollapsibleContextValue {
  const [internalOpen, setInternalOpen] = useState(defaultOpen);
  const open = controlledOpen ?? internalOpen;

  const toggle = useCallback(() => {
    const next = !open;
    if (onOpenChange) {
      onOpenChange(next);
    } else {
      setInternalOpen(next);
    }
  }, [open, onOpenChange]);

  return { open, toggle };
}

export function CollapsibleTrigger({
  children,
  className,
  open,
  onToggle,
}: {
  children: ReactNode;
  className?: string;
  open: boolean;
  onToggle: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onToggle}
      className={cn(
        "flex w-full items-center gap-1.5 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors cursor-pointer select-none",
        className,
      )}
    >
      <ChevronDownIcon
        className={cn(
          "size-3.5 shrink-0 transition-transform duration-200",
          open && "rotate-180",
        )}
      />
      {children}
    </button>
  );
}

export function CollapsibleContent({
  children,
  open,
  className,
}: {
  children: ReactNode;
  open: boolean;
  className?: string;
}) {
  if (!open) return null;
  return (
    <div
      className={cn(
        "mt-2 overflow-hidden animate-in slide-in-from-top-1 fade-in-0 duration-200",
        className,
      )}
    >
      {children}
    </div>
  );
}

export { useCollapsibleState };