T05 buttons and actions: groups, toggles, segmented, confirm, copy, share, bars, sheet, quick actions
This commit is contained in:
29 files changed
+2659
-151
No files matched your search
@@ -0,0 +1,162 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user