Documentation Index Fetch the complete documentation index at: https://docs.yoopta.dev/llms.txt
Use this file to discover all available pages before exploring further.
Overview
BlockOptions is compound component for block context menus. It supports controlled/uncontrolled modes, Floating UI positioning, and integrates seamlessly with other UI components.
Features
API — controlled/uncontrolled with open/onOpenChange
Compound components — Root, Content, Trigger, Item, Group, Separator
Floating positioning — automatic positioning via Floating UI
Helper hook — useBlockActions for common operations
Installation
npm install @yoopta/ui
# or
yarn add @yoopta/ui
Basic Usage
import { BlockOptions , useBlockActions } from '@yoopta/ui/block-options' ;
function MyBlockOptions ({ open , onOpenChange , blockId , anchor }) {
const { duplicateBlock , copyBlockLink , deleteBlock } = useBlockActions ();
return (
< BlockOptions open = { open } onOpenChange = { onOpenChange } anchor = { anchor } >
< BlockOptions.Content side = "right" align = "start" >
< BlockOptions.Group >
< BlockOptions.Item onSelect = { () => duplicateBlock ( blockId ) } > Duplicate </ BlockOptions.Item >
< BlockOptions.Item onSelect = { () => copyBlockLink ( blockId ) } > Copy link </ BlockOptions.Item >
</ BlockOptions.Group >
< BlockOptions.Separator />
< BlockOptions.Group >
< BlockOptions.Item variant = "destructive" onSelect = { () => deleteBlock ( blockId ) } >
Delete
</ BlockOptions.Item >
</ BlockOptions.Group >
</ BlockOptions.Content >
</ BlockOptions >
);
}
API Reference
BlockOptions (Root)
Root component that manages open state and context.
< BlockOptions open = { isOpen } onOpenChange = { setIsOpen } anchor = { anchorElement } defaultOpen = { false } >
{ children }
</ BlockOptions >
Props:
Prop Type Description openbooleanControlled open state onOpenChange(open: boolean) => voidCalled when open state changes defaultOpenbooleanDefault open state (uncontrolled) anchorHTMLElement | nullAnchor element for positioning childrenReactNodeContent and trigger components
BlockOptions.Trigger
Optional trigger button (for uncontrolled usage).
< BlockOptions >
< BlockOptions.Trigger asChild >
< button > Open Menu </ button >
</ BlockOptions.Trigger >
< BlockOptions.Content > ... </ BlockOptions.Content >
</ BlockOptions >
Props:
Prop Type Description asChildbooleanMerge props onto child element classNamestringCustom CSS classes
BlockOptions.Content
Floating content panel.
< BlockOptions.Content side = "right" align = "start" sideOffset = { 5 } >
{ /* Items here */ }
</ BlockOptions.Content >
Props:
Prop Type Description side'top' | 'right' | 'bottom' | 'left'Placement side align'start' | 'center' | 'end'Alignment sideOffsetnumberOffset from anchor (default: 5) classNamestringCustom CSS classes
BlockOptions.Item
Menu item button.
< BlockOptions.Item onSelect = { handleAction } variant = "default" icon = { < CopyIcon /> } keepOpen = { false } >
Duplicate
</ BlockOptions.Item >
Props:
Prop Type Description onSelect(event) => voidCalled when item is selected variant'default' | 'destructive'Visual variant iconReactNodeIcon to display keepOpenbooleanKeep menu open after selection disabledbooleanDisable the item
BlockOptions.Group
Groups items together.
< BlockOptions.Group >
< BlockOptions.Item > Item 1 </ BlockOptions.Item >
< BlockOptions.Item > Item 2 </ BlockOptions.Item >
</ BlockOptions.Group >
BlockOptions.Separator
Visual separator between groups.
< BlockOptions.Separator />
useBlockActions()
Helper hook with common block operations.
const { duplicateBlock , copyBlockLink , deleteBlock } = useBlockActions ();
// Usage
duplicateBlock ( blockId );
copyBlockLink ( blockId );
deleteBlock ( blockId );
Examples
function MyBlockOptions ({ open , onOpenChange , blockId , anchor }) {
const { duplicateBlock , deleteBlock } = useBlockActions ();
const turnIntoRef = useRef < HTMLButtonElement >( null );
const [ actionMenuOpen , setActionMenuOpen ] = useState ( false );
const onActionMenuClose = ( menuOpen : boolean ) => {
setActionMenuOpen ( menuOpen );
if ( ! menuOpen ) onOpenChange ?.( false ); // Close both
};
return (
<>
< BlockOptions open = { open } onOpenChange = { onOpenChange } anchor = { anchor } >
< BlockOptions.Content side = "right" >
< BlockOptions.Group >
< BlockOptions.Item ref = { turnIntoRef } onSelect = { () => setActionMenuOpen ( true ) } keepOpen >
Turn into
</ BlockOptions.Item >
</ BlockOptions.Group >
< BlockOptions.Separator />
< BlockOptions.Group >
< BlockOptions.Item onSelect = { () => duplicateBlock ( blockId ) } >
Duplicate
</ BlockOptions.Item >
< BlockOptions.Item variant = "destructive" onSelect = { () => deleteBlock ( blockId ) } >
Delete
</ BlockOptions.Item >
</ BlockOptions.Group >
</ BlockOptions.Content >
</ BlockOptions >
< ActionMenuList
open = { actionMenuOpen }
onOpenChange = { onActionMenuClose }
anchor = { turnIntoRef . current }
blockId = { blockId }
view = "small"
placement = "right-start" >
< ActionMenuList.Content />
</ ActionMenuList >
</>
);
}
Uncontrolled with Trigger
< BlockOptions >
< BlockOptions.Trigger >
< button > Open Options </ button >
</ BlockOptions.Trigger >
< BlockOptions.Content >
< BlockOptions.Group >
< BlockOptions.Item onSelect = { handleDuplicate } > Duplicate </ BlockOptions.Item >
< BlockOptions.Item variant = "destructive" onSelect = { handleDelete } >
Delete
</ BlockOptions.Item >
</ BlockOptions.Group >
</ BlockOptions.Content >
</ BlockOptions >
Styling
CSS Variables
:root {
--yoopta-ui-block-options-bg : var ( --yoopta-ui-background );
--yoopta-ui-block-options-border : var ( --yoopta-ui-border );
--yoopta-ui-block-options-shadow : 0 10 px 15 px -3 px rgb ( 0 0 0 / 0.1 );
--yoopta-ui-block-options-radius : 0.5 rem ;
--yoopta-ui-block-options-button-hover : var ( --yoopta-ui-accent );
--yoopta-ui-block-options-button-destructive-color : hsl ( var ( --yoopta-ui-destructive ));
}
Best Practices
Use keepOpen for submenu triggers
Close both menus after action
FloatingBlockActions Trigger BlockOptions from drag handle
ActionMenuList Open from BlockOptions for “Turn into”