Files
Ely-GPUI-Components/src/buttons/bars.rs
T

163 lines
4.5 KiB
Rust

use std::rc::Rc;
use gpui::{
Animation, AnimationExt, AnyElement, App, Div, ElementId, IntoElement, ParentElement,
RenderOnce, StyleRefinement, Styled, Window, div, prelude::*,
};
use smallvec::SmallVec;
use super::IconButton;
use crate::{
motion,
primitives::IconName,
theme::{ActiveTheme, ControlSize, Elevation, TextSize},
typography::AnimatedNumber,
};
/// Actions in a row at a region's foot: docked, or floating as a pill.
#[derive(IntoElement)]
pub struct ActionBar {
base: Div,
floating: bool,
children: SmallVec<[AnyElement; 4]>,
}
impl ActionBar {
pub fn new() -> Self {
Self {
base: div(),
floating: false,
children: SmallVec::new(),
}
}
/// A raised pill instead of a full-width strip.
pub fn floating(mut self) -> Self {
self.floating = true;
self
}
}
impl Default for ActionBar {
fn default() -> Self {
Self::new()
}
}
impl Styled for ActionBar {
fn style(&mut self) -> &mut StyleRefinement {
self.base.style()
}
}
impl ParentElement for ActionBar {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements);
}
}
impl RenderOnce for ActionBar {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let theme = cx.theme();
let colors = &theme.colors;
let bar = self.base.flex().items_center().gap_2();
let bar = if self.floating {
bar.p_1p5()
.rounded_full()
.bg(colors.overlay)
.border_1()
.border_color(colors.border)
.shadow(theme.elevation(Elevation::Floating))
} else {
bar.w_full()
.justify_end()
.px_4()
.py_3()
.bg(colors.surface)
.border_t_1()
.border_color(colors.border)
};
bar.children(self.children).with_animation(
"action-bar-in",
Animation::new(motion::duration(motion::BASE, cx)).with_easing(motion::ease_out_cubic),
|bar, t| bar.opacity(t).mt(motion::NUDGE * 2.0 * (1.0 - t)),
)
}
}
type OnClear = Rc<dyn Fn(&mut Window, &mut App)>;
/// Rises when items are selected: the count, bulk actions, and a clear button.
#[derive(IntoElement)]
pub struct BulkActionBar {
id: ElementId,
count: usize,
actions: SmallVec<[AnyElement; 4]>,
on_clear: Option<OnClear>,
}
impl BulkActionBar {
pub fn new(id: impl Into<ElementId>, count: usize) -> Self {
Self {
id: id.into(),
count,
actions: SmallVec::new(),
on_clear: None,
}
}
pub fn action(mut self, action: impl IntoElement) -> Self {
self.actions.push(action.into_any_element());
self
}
pub fn on_clear(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
self.on_clear = Some(Rc::new(handler));
self
}
}
impl RenderOnce for BulkActionBar {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
if self.count == 0 {
return div().into_any_element();
}
let theme = cx.theme();
let colors = &theme.colors;
ActionBar::new()
.floating()
.child(
div()
.flex()
.items_center()
.gap_1()
.pl_2()
.text_size(theme.text_size(TextSize::Sm))
.text_color(colors.fg)
.child(
AnimatedNumber::new(self.id.clone(), self.count as f64).size(TextSize::Sm),
)
.child("selected"),
)
.child(
div()
.h(theme.control_height(ControlSize::Sm))
.border_l_1()
.border_color(colors.border),
)
.children(self.actions)
.when_some(self.on_clear, |bar, clear| {
bar.child(
IconButton::new("bulk-clear", IconName::X)
.size(ControlSize::Sm)
.tooltip("Clear selection")
.on_click(move |_, window, cx| {
log::info!("bulk action bar: cleared");
clear(window, cx)
}),
)
})
.into_any_element()
}
}