sidebar: Fix Sidebar details, something like icon size, remove view_id argument. (#774)
Also updated the Sidebar example to support expand multiple root items. ## Break Change - Sidebar create not need assign parent view_id now.
This commit is contained in:
parent
b016d9fff4
commit
d186bf7c9e
5 changed files with 81 additions and 89 deletions
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use gpui::{
|
||||
div, impl_internal_actions, prelude::FluentBuilder, relative, App, AppContext, ClickEvent,
|
||||
Context, Entity, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Window,
|
||||
|
|
@ -14,7 +16,7 @@ use gpui_component::{
|
|||
SidebarToggleButton,
|
||||
},
|
||||
switch::Switch,
|
||||
v_flex, white, ActiveTheme, Collapsible, Icon, IconName, Side,
|
||||
v_flex, white, ActiveTheme, Icon, IconName, Side,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
|
|
@ -24,7 +26,8 @@ pub struct SelectCompany(SharedString);
|
|||
impl_internal_actions!(sidebar_story, [SelectCompany]);
|
||||
|
||||
pub struct SidebarStory {
|
||||
active_item: Item,
|
||||
active_items: HashMap<Item, bool>,
|
||||
last_active_item: Item,
|
||||
active_subitem: Option<SubItem>,
|
||||
collapsed: bool,
|
||||
side: Side,
|
||||
|
|
@ -37,8 +40,12 @@ impl SidebarStory {
|
|||
}
|
||||
|
||||
fn new(_: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let mut active_items = HashMap::new();
|
||||
active_items.insert(Item::Playground, true);
|
||||
|
||||
Self {
|
||||
active_item: Item::Playground,
|
||||
active_items,
|
||||
last_active_item: Item::Playground,
|
||||
active_subitem: None,
|
||||
collapsed: false,
|
||||
side: Side::Left,
|
||||
|
|
@ -61,7 +68,7 @@ impl SidebarStory {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum Item {
|
||||
Playground,
|
||||
Models,
|
||||
|
|
@ -121,8 +128,13 @@ impl Item {
|
|||
{
|
||||
let item = *self;
|
||||
move |this, _, _, cx| {
|
||||
this.active_item = item;
|
||||
this.active_subitem = None;
|
||||
if this.active_items.contains_key(&item) {
|
||||
this.active_items.remove(&item);
|
||||
} else {
|
||||
this.active_items.insert(item, true);
|
||||
}
|
||||
|
||||
this.last_active_item = item;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +188,13 @@ impl SubItem {
|
|||
let item = *item;
|
||||
let subitem = *self;
|
||||
move |this, _, _, cx| {
|
||||
this.active_item = item;
|
||||
println!(
|
||||
"Clicked on item: {}, child: {}",
|
||||
item.label(),
|
||||
subitem.label()
|
||||
);
|
||||
this.active_items.insert(item, true);
|
||||
this.last_active_item = item;
|
||||
this.active_subitem = Some(subitem);
|
||||
cx.notify();
|
||||
}
|
||||
|
|
@ -219,12 +237,6 @@ impl Render for SidebarStory {
|
|||
],
|
||||
];
|
||||
|
||||
let sidebar = if self.side.is_left() {
|
||||
Sidebar::left(&cx.entity())
|
||||
} else {
|
||||
Sidebar::right(&cx.entity())
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.rounded(cx.theme().radius)
|
||||
.border_1()
|
||||
|
|
@ -232,11 +244,10 @@ impl Render for SidebarStory {
|
|||
.h_full()
|
||||
.when(self.side.is_right(), |this| this.flex_row_reverse())
|
||||
.child(
|
||||
sidebar
|
||||
Sidebar::new(self.side)
|
||||
.collapsed(self.collapsed)
|
||||
.header(
|
||||
SidebarHeader::new()
|
||||
.collapsed(self.collapsed)
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
|
|
@ -249,13 +260,13 @@ impl Render for SidebarStory {
|
|||
.size_8()
|
||||
.flex_shrink_0()
|
||||
.when(!self.collapsed, |this| {
|
||||
this.child(Icon::new(IconName::GalleryVerticalEnd).size_4())
|
||||
this.child(Icon::new(IconName::GalleryVerticalEnd))
|
||||
})
|
||||
.when(self.collapsed, |this| {
|
||||
this.size_4()
|
||||
.bg(cx.theme().transparent)
|
||||
.text_color(cx.theme().foreground)
|
||||
.child(Icon::new(IconName::GalleryVerticalEnd).size_5())
|
||||
.child(Icon::new(IconName::GalleryVerticalEnd))
|
||||
}),
|
||||
)
|
||||
.when(!self.collapsed, |this| {
|
||||
|
|
@ -291,9 +302,33 @@ impl Render for SidebarStory {
|
|||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
SidebarGroup::new("Platform").child(SidebarMenu::new().children(
|
||||
groups[0].iter().map(|item| {
|
||||
SidebarMenuItem::new(item.label())
|
||||
.icon(item.icon())
|
||||
.active(self.active_items.contains_key(item))
|
||||
.children(item.items().into_iter().map(|sub_item| {
|
||||
SidebarMenuItem::new(sub_item.label())
|
||||
.active(self.active_subitem == Some(sub_item))
|
||||
.on_click(cx.listener(sub_item.handler(&item)))
|
||||
}))
|
||||
.on_click(cx.listener(item.handler()))
|
||||
}),
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
SidebarGroup::new("Projects").child(SidebarMenu::new().children(
|
||||
groups[1].iter().map(|item| {
|
||||
SidebarMenuItem::new(item.label())
|
||||
.icon(item.icon())
|
||||
.active(self.last_active_item == *item)
|
||||
.on_click(cx.listener(item.handler()))
|
||||
}),
|
||||
)),
|
||||
)
|
||||
.footer(
|
||||
SidebarFooter::new()
|
||||
.collapsed(self.collapsed)
|
||||
.justify_between()
|
||||
.child(
|
||||
h_flex()
|
||||
|
|
@ -302,55 +337,8 @@ impl Render for SidebarStory {
|
|||
.when(!self.collapsed, |this| this.child("Jason Lee")),
|
||||
)
|
||||
.when(!self.collapsed, |this| {
|
||||
this.child(
|
||||
Icon::new(IconName::ChevronsUpDown).size_4().flex_shrink_0(),
|
||||
)
|
||||
this.child(Icon::new(IconName::ChevronsUpDown).size_4())
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
SidebarGroup::new("Platform").child(SidebarMenu::new().children({
|
||||
let mut items = Vec::with_capacity(groups[0].len());
|
||||
for item in groups[0].iter() {
|
||||
let item = *item;
|
||||
items.push(
|
||||
SidebarMenuItem::new(item.label())
|
||||
.icon(item.icon().into())
|
||||
.active(self.active_item == item)
|
||||
.children({
|
||||
let mut sub_items =
|
||||
Vec::with_capacity(item.items().len());
|
||||
for sub_item in item.items() {
|
||||
sub_items.push(
|
||||
SidebarMenuItem::new(sub_item.label())
|
||||
.active(
|
||||
self.active_subitem == Some(sub_item),
|
||||
)
|
||||
.on_click(
|
||||
cx.listener(sub_item.handler(&item)),
|
||||
),
|
||||
);
|
||||
}
|
||||
sub_items
|
||||
})
|
||||
.on_click(cx.listener(item.handler())),
|
||||
);
|
||||
}
|
||||
items
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
SidebarGroup::new("Projects").child(SidebarMenu::new().children({
|
||||
let mut items = Vec::with_capacity(groups[1].len());
|
||||
for item in groups[1].iter() {
|
||||
items.push(
|
||||
SidebarMenuItem::new(item.label())
|
||||
.icon(item.icon().into())
|
||||
.active(self.active_item == *item)
|
||||
.on_click(cx.listener(item.handler())),
|
||||
);
|
||||
}
|
||||
items
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
|
|
@ -379,12 +367,12 @@ impl Render for SidebarStory {
|
|||
Breadcrumb::new()
|
||||
.item(BreadcrumbItem::new("0", "Home").on_click(cx.listener(
|
||||
|this, _, _, cx| {
|
||||
this.active_item = Item::Playground;
|
||||
this.last_active_item = Item::Playground;
|
||||
cx.notify();
|
||||
},
|
||||
)))
|
||||
.item(
|
||||
BreadcrumbItem::new("1", self.active_item.label())
|
||||
BreadcrumbItem::new("1", self.last_active_item.label())
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.active_subitem = None;
|
||||
cx.notify();
|
||||
|
|
|
|||
|
|
@ -290,7 +290,8 @@ impl RenderOnce for Icon {
|
|||
let mut base = self.base;
|
||||
*base.style() = self.style;
|
||||
|
||||
base.text_color(text_color)
|
||||
base.flex_shrink_0()
|
||||
.text_color(text_color)
|
||||
.when(!has_base_size, |this| this.size(text_size))
|
||||
.when_some(self.size, |this, size| match size {
|
||||
Size::Size(px) => this.size(px),
|
||||
|
|
@ -318,7 +319,8 @@ impl Render for Icon {
|
|||
let mut base = svg().flex_none();
|
||||
*base.style() = self.style.clone();
|
||||
|
||||
base.text_color(text_color)
|
||||
base.flex_shrink_0()
|
||||
.text_color(text_color)
|
||||
.when(!has_base_size, |this| this.size(text_size))
|
||||
.when_some(self.size, |this, size| match size {
|
||||
Size::Size(px) => this.size(px),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ impl Selectable for SidebarHeader {
|
|||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Collapsible for SidebarHeader {
|
||||
fn is_collapsed(&self) -> bool {
|
||||
self.collapsed
|
||||
|
|
|
|||
|
|
@ -81,8 +81,8 @@ impl SidebarMenuItem {
|
|||
}
|
||||
|
||||
/// Set the icon for the menu item
|
||||
pub fn icon(mut self, icon: Icon) -> Self {
|
||||
self.icon = Some(icon);
|
||||
pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
|
||||
self.icon = Some(icon.into());
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +138,7 @@ impl SidebarMenuItem {
|
|||
let is_submenu = self.is_submenu();
|
||||
|
||||
h_flex()
|
||||
.id(self.id.clone())
|
||||
.id("item")
|
||||
.overflow_hidden()
|
||||
.flex_shrink_0()
|
||||
.p_2()
|
||||
|
|
@ -155,9 +155,12 @@ impl SidebarMenuItem {
|
|||
.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.when_some(self.icon.clone(), |this, icon| this.child(icon.size_4()))
|
||||
.when_some(self.icon.clone(), |this, icon| this.child(icon))
|
||||
.when(is_collapsed, |this| {
|
||||
this.justify_center().size_7().mx_auto()
|
||||
this.justify_center().when(is_active, |this| {
|
||||
this.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
})
|
||||
.when(!is_collapsed, |this| {
|
||||
this.h_7()
|
||||
|
|
@ -181,6 +184,7 @@ impl RenderOnce for SidebarMenuItem {
|
|||
let is_collapsed = self.collapsed;
|
||||
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.w_full()
|
||||
.child(self.render_menu_item(window, cx))
|
||||
.when(is_submenu && is_open && !is_collapsed, |this| {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ use crate::{
|
|||
v_flex, ActiveTheme, Collapsible, Icon, IconName, Side, Sizable, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, AnyElement, App, ClickEvent, Entity, EntityId,
|
||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, RenderOnce, Styled,
|
||||
Window,
|
||||
div, prelude::FluentBuilder, px, AnyElement, App, ClickEvent, InteractiveElement as _,
|
||||
IntoElement, ParentElement, Pixels, RenderOnce, Styled, Window,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
|
|
@ -26,8 +25,6 @@ const COLLAPSED_WIDTH: Pixels = px(48.);
|
|||
/// A sidebar
|
||||
#[derive(IntoElement)]
|
||||
pub struct Sidebar<E: Collapsible + IntoElement + 'static> {
|
||||
/// The parent view id
|
||||
view_id: EntityId,
|
||||
content: Vec<E>,
|
||||
/// header view
|
||||
header: Option<AnyElement>,
|
||||
|
|
@ -41,9 +38,8 @@ pub struct Sidebar<E: Collapsible + IntoElement + 'static> {
|
|||
}
|
||||
|
||||
impl<E: Collapsible + IntoElement> Sidebar<E> {
|
||||
fn new(view_id: EntityId, side: Side) -> Self {
|
||||
pub fn new(side: Side) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
content: vec![],
|
||||
header: None,
|
||||
footer: None,
|
||||
|
|
@ -54,12 +50,12 @@ impl<E: Collapsible + IntoElement> Sidebar<E> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn left<V: Render + 'static>(view: &Entity<V>) -> Self {
|
||||
Self::new(view.entity_id(), Side::Left)
|
||||
pub fn left() -> Self {
|
||||
Self::new(Side::Left)
|
||||
}
|
||||
|
||||
pub fn right<V: Render + 'static>(view: &Entity<V>) -> Self {
|
||||
Self::new(view.entity_id(), Side::Right)
|
||||
pub fn right() -> Self {
|
||||
Self::new(Side::Right)
|
||||
}
|
||||
|
||||
/// Set the width of the sidebar
|
||||
|
|
@ -117,7 +113,7 @@ pub struct SidebarToggleButton {
|
|||
impl SidebarToggleButton {
|
||||
fn new(side: Side) -> Self {
|
||||
Self {
|
||||
btn: Button::new("sidebar-collapse").ghost().small(),
|
||||
btn: Button::new("collapse").ghost().small(),
|
||||
collapsed: false,
|
||||
side,
|
||||
on_click: None,
|
||||
|
|
@ -181,7 +177,8 @@ impl RenderOnce for SidebarToggleButton {
|
|||
}
|
||||
|
||||
impl<E: Collapsible + IntoElement> RenderOnce for Sidebar<E> {
|
||||
fn render(mut self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render(mut self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let view_id = window.current_view();
|
||||
v_flex()
|
||||
.id("sidebar")
|
||||
.w(self.width)
|
||||
|
|
@ -210,7 +207,7 @@ impl<E: Collapsible + IntoElement> RenderOnce for Sidebar<E> {
|
|||
.map(|(ix, c)| div().id(ix).child(c.collapsed(self.collapsed))),
|
||||
)
|
||||
.gap_2()
|
||||
.scrollable(self.view_id, ScrollbarAxis::Vertical),
|
||||
.scrollable(view_id, ScrollbarAxis::Vertical),
|
||||
),
|
||||
)
|
||||
.when_some(self.footer.take(), |this, footer| {
|
||||
|
|
|
|||
Loading…
Reference in a new issue