use crate::{prelude::*, ListItemVariant}; use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader}; pub enum ContextMenuItem { Header(SharedString), Entry(Label), Separator, } impl ContextMenuItem { fn to_list_item(self) -> ListItem { match self { ContextMenuItem::Header(label) => ListSubHeader::new(label).into(), ContextMenuItem::Entry(label) => { ListEntry::new(label).variant(ListItemVariant::Inset).into() } ContextMenuItem::Separator => ListSeparator::new().into(), } } pub fn header(label: impl Into) -> Self { Self::Header(label.into()) } pub fn separator() -> Self { Self::Separator } pub fn entry(label: Label) -> Self { Self::Entry(label) } } #[derive(Element)] pub struct ContextMenu { items: Vec>, } impl ContextMenu { pub fn new(items: impl IntoIterator>) -> Self { Self { items: items.into_iter().collect(), } } fn render(&mut self, _view: &mut S, cx: &mut ViewContext) -> impl Element { let color = ThemeColor::new(cx); v_stack() .flex() .bg(color.elevated_surface) .border() .border_color(color.border) .child( List::new( self.items .drain(..) .map(ContextMenuItem::to_list_item) .collect(), ) .toggle(ToggleState::Toggled), ) } } #[cfg(feature = "stories")] pub use stories::*; #[cfg(feature = "stories")] mod stories { use std::marker::PhantomData; use crate::story::Story; use super::*; #[derive(Element)] pub struct ContextMenuStory { state_type: PhantomData, } impl ContextMenuStory { pub fn new() -> Self { Self { state_type: PhantomData, } } fn render( &mut self, _view: &mut S, cx: &mut ViewContext, ) -> impl Element { Story::container(cx) .child(Story::title_for::<_, ContextMenu>(cx)) .child(Story::label(cx, "Default")) .child(ContextMenu::new([ ContextMenuItem::header("Section header"), ContextMenuItem::Separator, ContextMenuItem::entry(Label::new("Some entry")), ])) } } }