menubar.rs
1 use dioxus::html::GlobalAttributesExtension; 2 use dioxus::prelude::*; 3 use dioxus_primitives::menubar::{ 4 Menubar as PrimitiveMenubar, MenubarContent as PrimitiveMenubarContent, 5 MenubarItem as PrimitiveMenubarItem, MenubarMenu as PrimitiveMenubarMenu, 6 MenubarTrigger as PrimitiveMenubarTrigger, 7 }; 8 9 /// Menubar main container, styled with Tailwind 10 #[derive(Props, Clone, PartialEq)] 11 pub struct MenubarProps { 12 #[props(default)] 13 pub class: Option<String>, 14 pub children: Element, 15 } 16 17 #[component] 18 pub fn Menubar(props: MenubarProps) -> Element { 19 let default_classes = 20 "flex items-center gap-1 px-1 py-1 border border-border bg-background rounded-md shadow-sm"; 21 let class = if let Some(extra) = &props.class { 22 format!("{} {}", extra, default_classes) 23 } else { 24 default_classes.to_string() 25 }; 26 27 rsx! { 28 PrimitiveMenubar { 29 class: class, 30 {props.children} 31 } 32 } 33 } 34 35 /// MenubarMenu: A single menu in the menubar, styled with Tailwind 36 #[derive(Props, Clone, PartialEq)] 37 pub struct MenubarMenuProps { 38 #[props(default)] 39 pub class: Option<String>, 40 pub index: ReadSignal<usize>, 41 pub children: Element, 42 } 43 44 #[component] 45 pub fn MenubarMenu(props: MenubarMenuProps) -> Element { 46 let default_classes = "relative group flex flex-col items-stretch"; 47 let class = if let Some(extra) = &props.class { 48 format!("{} {}", extra, default_classes) 49 } else { 50 default_classes.to_string() 51 }; 52 53 rsx! { 54 PrimitiveMenubarMenu { 55 class: class, 56 index: props.index, 57 {props.children} 58 } 59 } 60 } 61 62 /// MenubarTrigger: The button that opens a menu, styled with Tailwind 63 #[derive(Props, Clone, PartialEq)] 64 pub struct MenubarTriggerProps { 65 #[props(default)] 66 pub class: Option<String>, 67 pub children: Element, 68 } 69 70 #[component] 71 pub fn MenubarTrigger(props: MenubarTriggerProps) -> Element { 72 let default_classes = "px-3 py-1.5 rounded-sm text-sm font-medium text-foreground hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring transition-colors"; 73 let class = if let Some(extra) = &props.class { 74 format!("{} {}", extra, default_classes) 75 } else { 76 default_classes.to_string() 77 }; 78 79 rsx! { 80 PrimitiveMenubarTrigger { 81 class: class, 82 {props.children} 83 } 84 } 85 } 86 87 /// MenubarContent: The dropdown content for a menu, styled with Tailwind 88 #[derive(Props, Clone, PartialEq)] 89 pub struct MenubarContentProps { 90 #[props(default)] 91 pub class: Option<String>, 92 pub children: Element, 93 } 94 95 #[component] 96 pub fn MenubarContent(props: MenubarContentProps) -> Element { 97 let default_classes = "hidden group-data-[state=open]:block absolute left-0 top-full mt-2 min-w-[10rem] bg-popover border border-border rounded-md shadow-lg z-50 p-1"; 98 let class = if let Some(extra) = &props.class { 99 format!("{} {}", extra, default_classes) 100 } else { 101 default_classes.to_string() 102 }; 103 104 rsx! { 105 PrimitiveMenubarContent { 106 class: class, 107 {props.children} 108 } 109 } 110 } 111 112 /// MenubarItem: An item in a menu, styled with Tailwind 113 #[derive(Props, Clone, PartialEq)] 114 pub struct MenubarItemProps { 115 /// The index of this item within the [`MenubarContent`]. This is used to define the focus order for keyboard navigation. 116 pub index: ReadSignal<usize>, 117 #[props(default)] 118 pub class: Option<String>, 119 pub value: String, 120 #[props(default)] 121 pub on_select: Callback<String>, 122 pub children: Element, 123 } 124 125 #[component] 126 pub fn MenubarItem(props: MenubarItemProps) -> Element { 127 let default_classes = "menubar-item cursor-pointer select-none px-3 py-1.5 rounded-sm text-sm text-foreground hover:bg-accent focus:bg-accent focus:outline-none transition-colors"; 128 let class = if let Some(extra) = &props.class { 129 format!("{} {}", extra, default_classes) 130 } else { 131 default_classes.to_string() 132 }; 133 134 rsx! { 135 PrimitiveMenubarItem { 136 index: props.index, 137 class: class, 138 value: props.value.clone(), 139 on_select: props.on_select, 140 {props.children} 141 } 142 } 143 }