/* Copyright 2026 Marimo. All rights reserved. */ import { CommandList } from "cmdk"; import { useAtomValue } from "jotai"; /* Copyright 2026 Marimo. All rights reserved. */ import React, { Fragment, type PropsWithChildren, useImperativeHandle, useMemo, useRef, useState, } from "react"; import useEvent from "react-use-event-hook"; import { getCellForDomProps } from "@/components/data-table/cell-utils"; import { renderMinimalShortcut, renderShortcut, } from "@/components/shortcuts/renderShortcut"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandSeparator, } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Tooltip, TooltipContent, TooltipRoot, TooltipTrigger, } from "@/components/ui/tooltip"; import { useRestoreFocus } from "@/components/ui/use-restore-focus"; import { cellFocusDetailsAtom } from "@/core/cells/focus"; import type { CellId } from "@/core/cells/ids"; import { cn } from "@/utils/cn"; import { type CellActionButtonProps, useCellActionButtons, } from "../actions/useCellActionButton"; import { raf2 } from "../navigation/focus-utils"; interface Props extends CellActionButtonProps { children: React.ReactNode; showTooltip?: boolean; } export interface CellActionsDropdownHandle { toggle: () => void; } const CellActionsDropdownInternal = ( { children, showTooltip = true, ...props }: Props, ref: React.Ref, ) => { const [open, setOpen] = useState(false); const closePopover = () => setOpen(false); const buttonRef = useRef(null); const actions = useCellActionButtons({ cell: props, closePopover }); const visibleActions = actions .map((group) => group.filter((action) => !action.redundant)) .filter((group) => group.length > 0); // store the last focused element so we can restore it when the popover closes const restoreFocus = useRestoreFocus(); const handleScrollIntoView = useEvent(() => { raf2(() => { if (buttonRef.current) { buttonRef.current.scrollIntoView({ behavior: "auto", block: "nearest", }); } }); }); const handleOpenChange: typeof setOpen = useEvent((nextOpenOrCallback) => { // Scroll the button into view when opening the popover // This can be out of view if the popover and a cell's code is // hidden at the same time. setOpen((prevOpen) => { const nextOpen = typeof nextOpenOrCallback === "function" ? nextOpenOrCallback(prevOpen) : nextOpenOrCallback; if (nextOpen) { handleScrollIntoView(); } return nextOpen; }); }); useImperativeHandle(ref, () => ({ toggle: () => handleOpenChange((prev) => !prev), })); const content = ( No results {visibleActions.map((group, i) => ( {group.map((action) => { let body = (
{action.icon && (
{action.icon}
)}
{action.label}
{action.hotkey && renderMinimalShortcut(action.hotkey)} {action.rightElement}
); if (action.tooltip) { body = ( {body} ); } return ( { if (action.disableClick || action.disabled) { return; } action.handle(); setOpen(false); }} variant={action.variant} > {body} ); })}
{i < visibleActions.length - 1 && }
))}
); if (!showTooltip) { return ( {children} {content} ); } const tooltipContent = ( {renderShortcut("cell.cellActions")} ); return ( {!open && tooltipContent} {/* This creates a warning in React due to nested