/* Copyright 2026 Marimo. All rights reserved. */
import {
ChevronRightIcon,
MoreVerticalIcon,
RefreshCwIcon,
} from "lucide-react";
import React, { useCallback, useState } from "react";
import { Button } from "@/components/ui/button";
import { Tooltip } from "@/components/ui/tooltip";
import { cn } from "@/utils/cn";
/**
* Animated chevron for tree expand/collapse.
* Rotates 90° when `isExpanded` is true.
*/
export const TreeChevron: React.FC<{
isExpanded: boolean;
className?: string;
}> = ({ isExpanded, className }) => (
);
/**
* Refresh button that briefly spins its icon when clicked.
* Keeps spinning for at least 500ms, or until the onClick Promise resolves.
*/
export const RefreshIconButton: React.FC<{
onClick: (e: React.MouseEvent) => void | Promise;
tooltip?: string;
className?: string;
iconClassName?: string;
}> = ({ onClick, tooltip = "Refresh", className, iconClassName }) => {
const [isSpinning, setIsSpinning] = useState(false);
const handleClick = useCallback(
async (e: React.MouseEvent) => {
setIsSpinning(true);
// Artificially spin for 500ms to show the user that the button is working.
const minDelay = new Promise((r) => setTimeout(r, 500));
try {
await Promise.all([onClick(e), minDelay]);
} finally {
setIsSpinning(false);
}
},
[onClick],
);
const button = (
);
return {button};
};
/**
* Three-dot menu trigger that fades in on row hover.
* Must be inside a `group` container.
* Forwards ref so it works with Radix `asChild`.
*/
export const MoreActionsButton = React.forwardRef<
HTMLButtonElement,
{
onClick?: (e: React.MouseEvent) => void;
className?: string;
iconClassName?: string;
} & Omit, "variant" | "size">
>(({ className, iconClassName, ...props }, ref) => (
));
MoreActionsButton.displayName = "MoreActionsButton";
/** Standard class string for icons inside dropdown menu items. */
export const MENU_ITEM_ICON_CLASS = "h-3.5 w-3.5 mr-2";