nightshade_api/web/
toolbar.rs1use leptos::prelude::*;
4
5#[component]
7pub fn Toolbar(#[prop(into, optional)] class: String, children: Children) -> impl IntoView {
8 view! { <div class=format!("nightshade-toolbar {class}") role="toolbar">{children()}</div> }
9}
10
11#[component]
13pub fn ToolbarGroup(children: Children) -> impl IntoView {
14 view! { <div class="nightshade-toolbar-group">{children()}</div> }
15}
16
17#[component]
19pub fn ToolbarSpacer() -> impl IntoView {
20 view! { <div class="nightshade-toolbar-spacer"></div> }
21}
22
23#[component]
25pub fn ToolButton(
26 #[prop(into, optional)] class: String,
27 #[prop(into, optional)] active: Signal<bool>,
28 #[prop(into, optional)] disabled: Signal<bool>,
29 #[prop(into, optional)] title: String,
30 #[prop(optional)] on_click: Option<Callback<web_sys::MouseEvent>>,
31 children: Children,
32) -> impl IntoView {
33 let handle = move |event: web_sys::MouseEvent| {
34 if !disabled.get_untracked()
35 && let Some(callback) = on_click
36 {
37 callback.run(event);
38 }
39 };
40 view! {
41 <button
42 class=format!("nightshade-tool-button {class}")
43 class:active=move || active.get()
44 title=title
45 aria-pressed=move || active.get().to_string()
46 disabled=move || disabled.get()
47 on:click=handle
48 >
49 {children()}
50 </button>
51 }
52}
53
54#[derive(Clone)]
56pub struct ActivityItem {
57 pub id: String,
59 pub icon: String,
61 pub label: String,
63}
64
65impl ActivityItem {
66 pub fn new(id: impl Into<String>, icon: impl Into<String>, label: impl Into<String>) -> Self {
68 Self {
69 id: id.into(),
70 icon: icon.into(),
71 label: label.into(),
72 }
73 }
74}
75
76#[component]
78pub fn ActivityBar(
79 items: Vec<ActivityItem>,
80 active: RwSignal<String>,
81 #[prop(optional)] on_select: Option<Callback<String>>,
82) -> impl IntoView {
83 view! {
84 <div class="nightshade-activity-bar" role="tablist">
85 {items
86 .into_iter()
87 .map(|item| {
88 let id_click = item.id.clone();
89 let id_active = StoredValue::new(item.id.clone());
90 let is_active = move || active.get() == id_active.get_value();
91 view! {
92 <button
93 class="nightshade-activity-item"
94 class:active=is_active
95 role="tab"
96 title=item.label
97 aria-selected=move || is_active().to_string()
98 on:click=move |_| {
99 active.set(id_click.clone());
100 if let Some(callback) = on_select {
101 callback.run(id_click.clone());
102 }
103 }
104 >
105 {item.icon}
106 </button>
107 }
108 })
109 .collect_view()}
110 </div>
111 }
112}
113
114#[derive(Clone, Copy)]
115struct MenuBarContext(RwSignal<Option<String>>);
116
117#[component]
119pub fn MenuBar(children: Children) -> impl IntoView {
120 let active = RwSignal::new(None::<String>);
121 provide_context(MenuBarContext(active));
122 view! {
123 <div
124 class="nightshade-menu-bar"
125 role="menubar"
126 on:pointerleave=move |_| active.set(None)
127 >
128 {children()}
129 </div>
130 }
131}
132
133#[component]
135pub fn MenuBarMenu(
136 #[prop(into)] id: String,
137 #[prop(into)] label: String,
138 children: ChildrenFn,
139) -> impl IntoView {
140 let active = use_context::<MenuBarContext>()
141 .map(|context| context.0)
142 .unwrap_or_else(|| RwSignal::new(None));
143 let id = StoredValue::new(id);
144 let children = StoredValue::new(children);
145 let is_open = move || active.get().as_deref() == Some(id.get_value().as_str());
146 let toggle = move |_: web_sys::MouseEvent| {
147 let key = id.get_value();
148 active.update(|current| {
149 *current = if current.as_deref() == Some(key.as_str()) {
150 None
151 } else {
152 Some(key)
153 };
154 });
155 };
156 let hover = move |_: web_sys::PointerEvent| {
157 if active.get_untracked().is_some() {
158 active.set(Some(id.get_value()));
159 }
160 };
161 view! {
162 <div class="nightshade-menu-bar-item">
163 <button
164 class="nightshade-menu-bar-trigger"
165 class:active=is_open
166 aria-haspopup="menu"
167 aria-expanded=move || is_open().to_string()
168 on:click=toggle
169 on:pointerenter=hover
170 >
171 {label}
172 </button>
173 <Show when=is_open fallback=|| ()>
174 <div class="nightshade-menu-list" role="menu" on:click=move |_| active.set(None)>
175 {children.with_value(|render| render())}
176 </div>
177 </Show>
178 </div>
179 }
180}