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
ElementOptions is a compound component for building inline configuration popovers attached to elements. It provides form controls (Select, Toggle, Slider, ColorPicker, Input) and helper hooks for updating element properties.
Features
Compound components — Root, Trigger, Content, Group, Label, Separator
Form controls — Select, Toggle, Slider, ColorPicker, Input
Helper hooks — useElementOptions, useUpdateElementProps
Radix UI — Built on Radix Popover for accessibility and positioning
Installation
npm install @yoopta/ui
# or
yarn add @yoopta/ui
Basic Usage
import { ElementOptions , useElementOptions , useUpdateElementProps } from '@yoopta/ui/element-options' ;
// In your element render component
function MyElement ({ attributes , children , element , blockId }) {
return (
< div { ... attributes } className = "group relative" >
< ElementOptions.Root blockId = { blockId } element = { element } >
< ElementOptions.Trigger className = "absolute right-0 top-0 opacity-0 group-hover:opacity-100" />
< MyElementOptions />
</ ElementOptions.Root >
{ children }
</ div >
);
}
// Options content component
function MyElementOptions () {
const { element } = useElementOptions ();
const updateProps = useUpdateElementProps <{ theme : string }>();
return (
< ElementOptions.Content side = "bottom" align = "end" >
< ElementOptions.Group >
< ElementOptions.Label > Theme </ ElementOptions.Label >
< ElementOptions.Select
value = { element . props ?. theme ?? 'default' }
options = { [
{ value: 'default' , label: 'Default' },
{ value: 'info' , label: 'Info' },
] }
onValueChange = { ( value ) => updateProps ({ theme: value }) }
/>
</ ElementOptions.Group >
</ ElementOptions.Content >
);
}
API Reference
ElementOptions.Root
Root component that provides context and manages popover state.
< ElementOptions.Root blockId = { blockId } element = { element } >
{ children }
</ ElementOptions.Root >
Props:
Prop Type Description blockIdstringBlock ID containing element elementSlateElementThe element being configured childrenReactNodeTrigger and Content classNamestringCustom CSS classes styleCSSPropertiesCustom inline styles
ElementOptions.Trigger
Button that opens the options popover.
< ElementOptions.Trigger className = "opacity-0 group-hover:opacity-100" >
< SettingsIcon />
</ ElementOptions.Trigger >
Props:
Prop Type Description childrenReactNodeCustom icon (defaults to MoreHorizontal) classNamestringCustom CSS classes styleCSSPropertiesCustom inline styles
ElementOptions.Content
Floating content panel containing form controls.
< ElementOptions.Content side = "bottom" align = "end" sideOffset = { 8 } >
{ /* Groups and controls here */ }
</ ElementOptions.Content >
Props:
Prop Type Default Description side'top' | 'right' | 'bottom' | 'left''bottom'Placement side align'start' | 'center' | 'end''end'Alignment sideOffsetnumber4Offset from trigger alignOffsetnumber0Alignment offset classNamestringCustom CSS classes
ElementOptions.Group
Groups related controls together.
< ElementOptions.Group >
< ElementOptions.Label > Settings </ ElementOptions.Label >
{ /* Controls here */ }
</ ElementOptions.Group >
ElementOptions.Label
Label for a group of controls.
< ElementOptions.Label > Theme </ ElementOptions.Label >
ElementOptions.Separator
Visual separator between groups.
< ElementOptions.Separator />
ElementOptions.Select
Dropdown select for choosing from predefined options.
< ElementOptions.Select
value = { currentValue }
options = { [
{ value: 'solid' , label: 'Solid' },
{ value: 'dashed' , label: 'Dashed' , icon: < DashedIcon /> },
] }
onValueChange = { ( value ) => updateProps ({ style: value }) }
renderOption = { ( option ) => < span > { option . icon } { option . label } </ span > }
renderValue = { ( option ) => < span > { option ?. label } </ span > }
/>
Props:
Prop Type Description valueTCurrent selected value optionsSelectOption<T>[]Available options onValueChange(value: T) => voidCalled when selection changes placeholderstringPlaceholder text renderOption(option: SelectOption<T>) => ReactNodeCustom option renderer renderValue(option?: SelectOption<T>) => ReactNodeCustom value renderer
SelectOption type:
type SelectOption < T > = {
value : T ;
label : string ;
icon ?: ReactNode ;
color ?: string ;
};
ElementOptions.Toggle
Boolean toggle switch.
< ElementOptions.Toggle
checked = { isEnabled }
onCheckedChange = { ( checked ) => updateProps ({ enabled: checked }) }
label = "Enable feature"
/>
Props:
Prop Type Description checkedbooleanCurrent state onCheckedChange(checked: boolean) => voidCalled when toggled labelstringOptional label text
ElementOptions.Slider
Numeric slider for range values.
< ElementOptions.Slider
value = { 50 }
onValueChange = { ( value ) => updateProps ({ opacity: value }) }
min = { 0 }
max = { 100 }
step = { 1 }
/>
Props:
Prop Type Default Description valuenumberCurrent value onValueChange(value: number) => voidCalled on change minnumber0Minimum value maxnumber100Maximum value stepnumber1Step increment
ElementOptions.ColorPicker
Color picker with presets and hex input.
< ElementOptions.ColorPicker
value = "#3B82F6"
onChange = { ( color ) => updateProps ({ color }) }
presetColors = { [ '#EF4444' , '#22C55E' , '#3B82F6' , '#8B5CF6' ] }
/>
Props:
Prop Type Description valuestringCurrent color (hex) onChange(color: string) => voidCalled on change presetColorsstring[]Preset color swatches
Text input for string values.
< ElementOptions.Input
value = { title }
onChange = { ( value ) => updateProps ({ title: value }) }
placeholder = "Enter title..."
type = "text"
/>
Props:
Prop Type Default Description valuestringCurrent value onChange(value: string) => voidCalled on change placeholderstringPlaceholder text type'text' | 'number' | 'url''text'Input type
Hooks
useElementOptions()
Access element context within Content.
const { blockId , element , editor , isOpen , setIsOpen } = useElementOptions ();
Returns:
Property Type Description blockIdstringBlock ID elementSlateElementCurrent element editorYooEditorEditor instance isOpenbooleanPopover open state setIsOpen(open: boolean) => voidToggle popover
useUpdateElementProps<T>()
Helper hook for updating element properties.
const updateProps = useUpdateElementProps <{ theme : string ; color : string }>();
// Usage
updateProps ({ theme: 'info' });
updateProps ({ color: '#3B82F6' });
Examples
Callout Theme Selector
const CALLOUT_THEMES = [
{ value: 'default' , label: 'Default' , icon: < MessageSquare className = "h-4 w-4" /> },
{ value: 'info' , label: 'Info' , icon: < Info className = "h-4 w-4 text-blue-500" /> },
{ value: 'success' , label: 'Success' , icon: < CheckCircle className = "h-4 w-4 text-green-500" /> },
{ value: 'warning' , label: 'Warning' , icon: < AlertCircle className = "h-4 w-4 text-yellow-500" /> },
{ value: 'error' , label: 'Error' , icon: < XCircle className = "h-4 w-4 text-red-500" /> },
];
function CalloutElementOptions () {
const { element } = useElementOptions ();
const updateProps = useUpdateElementProps <{ theme : string }>();
return (
< ElementOptions.Content side = "bottom" align = "end" sideOffset = { 8 } >
< ElementOptions.Group >
< ElementOptions.Label > Theme </ ElementOptions.Label >
< ElementOptions.Select
value = { element . props ?. theme ?? 'default' }
options = { CALLOUT_THEMES }
onValueChange = { ( value ) => updateProps ({ theme: value }) }
renderOption = { ( option ) => (
< span className = "flex items-center gap-2" >
{ option . icon }
{ option . label }
</ span >
) }
/>
</ ElementOptions.Group >
</ ElementOptions.Content >
);
}
Divider with Style and Color
const DIVIDER_THEMES = [
{ value: 'solid' , label: 'Solid' },
{ value: 'dashed' , label: 'Dashed' },
{ value: 'dotted' , label: 'Dotted' },
{ value: 'gradient' , label: 'Gradient' },
];
const DIVIDER_COLORS = [ '#E5E7EB' , '#6B7280' , '#EF4444' , '#22C55E' , '#3B82F6' ];
function DividerElementOptions () {
const { element } = useElementOptions ();
const updateProps = useUpdateElementProps <{ theme : string ; color : string }>();
return (
< ElementOptions.Content side = "bottom" align = "end" sideOffset = { 8 } >
< ElementOptions.Group >
< ElementOptions.Label > Style </ ElementOptions.Label >
< ElementOptions.Select
value = { element . props ?. theme ?? 'solid' }
options = { DIVIDER_THEMES }
onValueChange = { ( value ) => updateProps ({ theme: value }) }
/>
</ ElementOptions.Group >
< ElementOptions.Separator />
< ElementOptions.Group >
< ElementOptions.Label > Color </ ElementOptions.Label >
< ElementOptions.ColorPicker
value = { element . props ?. color ?? '#E5E7EB' }
onChange = { ( color ) => updateProps ({ color }) }
presetColors = { DIVIDER_COLORS }
/>
</ ElementOptions.Group >
</ ElementOptions.Content >
);
}
Table Appearance Toggles
function TableElementOptions () {
const { element } = useElementOptions ();
const updateProps = useUpdateElementProps <{
bordered : boolean ;
compact : boolean ;
scrollable : boolean ;
}>();
const props = element . props ?? {};
return (
< ElementOptions.Content side = "bottom" align = "end" sideOffset = { 8 } >
< ElementOptions.Group >
< ElementOptions.Label > Appearance </ ElementOptions.Label >
< div className = "flex items-center justify-between px-2" >
< span > Bordered </ span >
< ElementOptions.Toggle
checked = { props . bordered ?? true }
onCheckedChange = { ( checked ) => updateProps ({ bordered: checked }) }
/>
</ div >
< div className = "flex items-center justify-between px-2" >
< span > Compact </ span >
< ElementOptions.Toggle
checked = { props . compact ?? false }
onCheckedChange = { ( checked ) => updateProps ({ compact: checked }) }
/>
</ div >
< div className = "flex items-center justify-between px-2" >
< span > Scrollable </ span >
< ElementOptions.Toggle
checked = { props . scrollable ?? true }
onCheckedChange = { ( checked ) => updateProps ({ scrollable: checked }) }
/>
</ div >
</ ElementOptions.Group >
</ ElementOptions.Content >
);
}
Table of Contents with Input and Toggles
const DEPTH_OPTIONS = [
{ value: '1' , label: 'H1 only' },
{ value: '2' , label: 'H1 – H2' },
{ value: '3' , label: 'H1 – H3' },
];
function TocElementOptions () {
const { element } = useElementOptions ();
const updateProps = useUpdateElementProps <{
title : string ;
depth : number ;
showNumbers : boolean ;
collapsible : boolean ;
}>();
const props = element . props ?? {};
return (
< ElementOptions.Content side = "bottom" align = "end" sideOffset = { 8 } >
< ElementOptions.Group >
< ElementOptions.Label > Title </ ElementOptions.Label >
< ElementOptions.Input
value = { props . title ?? 'Table of Contents' }
onChange = { ( value ) => updateProps ({ title: value }) }
placeholder = "Title..."
/>
</ ElementOptions.Group >
< ElementOptions.Separator />
< ElementOptions.Group >
< ElementOptions.Label > Depth </ ElementOptions.Label >
< ElementOptions.Select
value = { String ( props . depth ?? 3 ) }
options = { DEPTH_OPTIONS }
onValueChange = { ( value ) => updateProps ({ depth: Number ( value ) }) }
/>
</ ElementOptions.Group >
< ElementOptions.Separator />
< ElementOptions.Group >
< div className = "flex items-center justify-between px-2" >
< span > Numbered </ span >
< ElementOptions.Toggle
checked = { props . showNumbers ?? false }
onCheckedChange = { ( checked ) => updateProps ({ showNumbers: checked }) }
/>
</ div >
< div className = "flex items-center justify-between px-2" >
< span > Collapsible </ span >
< ElementOptions.Toggle
checked = { props . collapsible ?? false }
onCheckedChange = { ( checked ) => updateProps ({ collapsible: checked }) }
/>
</ div >
</ ElementOptions.Group >
</ ElementOptions.Content >
);
}
Styling
CSS Variables
:root {
--yoopta-ui-element-options-shadow : 0 4 px 6 px -1 px rgb ( 0 0 0 / 0.1 );
--yoopta-ui-element-options-content-min-width : 180 px ;
--yoopta-ui-element-options-content-padding : 8 px ;
--yoopta-ui-element-options-content-radius : 0.5 rem ;
--yoopta-ui-element-options-control-padding-y : 6 px ;
--yoopta-ui-element-options-control-padding-x : 10 px ;
--yoopta-ui-element-options-control-radius : 6 px ;
--yoopta-ui-element-options-toggle-width : 36 px ;
--yoopta-ui-element-options-toggle-height : 20 px ;
}
Custom Styling
Apply Tailwind or custom classes to components:
< ElementOptions.Content className = "min-w-[200px] rounded-lg border bg-popover p-2 shadow-md" >
< ElementOptions.Group className = "flex flex-col gap-1" >
< ElementOptions.Label className = "px-2 text-xs font-medium text-muted-foreground" >
Theme
</ ElementOptions.Label >
< ElementOptions.Select
className = "flex h-8 w-full items-center justify-between rounded-md border px-3 text-sm"
{ ... props }
/>
</ ElementOptions.Group >
</ ElementOptions.Content >
Best Practices
Separate options into their own component
// Good: Separate component for options
function MyElementOptions () {
const { element } = useElementOptions ();
const updateProps = useUpdateElementProps ();
// ...
}
// In render:
< ElementOptions.Root blockId = { blockId } element = { element } >
< ElementOptions.Trigger />
< MyElementOptions />
</ ElementOptions.Root >
Use group-hover for trigger visibility
< div className = "group relative" >
< ElementOptions.Root blockId = { blockId } element = { element } >
< ElementOptions.Trigger
className = "absolute right-0 top-0 opacity-0 group-hover:opacity-100 transition-opacity"
/>
< MyElementOptions />
</ ElementOptions.Root >
{ children }
</ div >
// Always provide defaults when reading props
const theme = element . props ?. theme ?? 'default' ;
const enabled = element . props ?. enabled ?? false ;
BlockOptions Context menu for block-level actions
FloatingBlockActions Floating buttons for block operations