move gpui-components to ui crate
This commit is contained in:
77
crates/ui/src/sidebar/footer.rs
Normal file
77
crates/ui/src/sidebar/footer.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use gpui::{
|
||||
prelude::FluentBuilder as _, Div, ElementId, InteractiveElement, IntoElement, ParentElement,
|
||||
RenderOnce, SharedString, Styled,
|
||||
};
|
||||
|
||||
use crate::{h_flex, popup_menu::PopupMenuExt, theme::ActiveTheme as _, Collapsible, Selectable};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct SidebarFooter {
|
||||
id: ElementId,
|
||||
base: Div,
|
||||
selected: bool,
|
||||
is_collapsed: bool,
|
||||
}
|
||||
|
||||
impl SidebarFooter {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id: SharedString::from("sidebar-footer").into(),
|
||||
base: h_flex().gap_2().w_full(),
|
||||
selected: false,
|
||||
is_collapsed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Selectable for SidebarFooter {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
|
||||
fn element_id(&self) -> &gpui::ElementId {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
impl Collapsible for SidebarFooter {
|
||||
fn is_collapsed(&self) -> bool {
|
||||
self.is_collapsed
|
||||
}
|
||||
|
||||
fn collapsed(mut self, collapsed: bool) -> Self {
|
||||
self.is_collapsed = collapsed;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl ParentElement for SidebarFooter {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
|
||||
self.base.extend(elements);
|
||||
}
|
||||
}
|
||||
impl Styled for SidebarFooter {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
impl PopupMenuExt for SidebarFooter {}
|
||||
impl RenderOnce for SidebarFooter {
|
||||
fn render(self, cx: &mut gpui::WindowContext) -> impl gpui::IntoElement {
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.gap_2()
|
||||
.p_2()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.cursor_pointer()
|
||||
.rounded_md()
|
||||
.hover(|this| {
|
||||
this.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.when(self.selected, |this| {
|
||||
this.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.child(self.base)
|
||||
}
|
||||
}
|
||||
71
crates/ui/src/sidebar/group.rs
Normal file
71
crates/ui/src/sidebar/group.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use crate::{theme::ActiveTheme, v_flex, Collapsible};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, Div, IntoElement, ParentElement, RenderOnce, SharedString,
|
||||
Styled as _, WindowContext,
|
||||
};
|
||||
|
||||
/// A sidebar group
|
||||
#[derive(IntoElement)]
|
||||
pub struct SidebarGroup<E: Collapsible + IntoElement + 'static> {
|
||||
base: Div,
|
||||
label: SharedString,
|
||||
is_collapsed: bool,
|
||||
children: Vec<E>,
|
||||
}
|
||||
|
||||
impl<E: Collapsible + IntoElement> SidebarGroup<E> {
|
||||
pub fn new(label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: div().gap_2().flex_col(),
|
||||
label: label.into(),
|
||||
is_collapsed: false,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn child(mut self, child: E) -> Self {
|
||||
self.children.push(child);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn children(mut self, children: impl IntoIterator<Item = E>) -> Self {
|
||||
self.children.extend(children);
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<E: Collapsible + IntoElement> Collapsible for SidebarGroup<E> {
|
||||
fn is_collapsed(&self) -> bool {
|
||||
self.is_collapsed
|
||||
}
|
||||
|
||||
fn collapsed(mut self, collapsed: bool) -> Self {
|
||||
self.is_collapsed = collapsed;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<E: Collapsible + IntoElement> RenderOnce for SidebarGroup<E> {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
v_flex()
|
||||
.relative()
|
||||
.p_2()
|
||||
.when(!self.is_collapsed, |this| {
|
||||
this.child(
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.px_2()
|
||||
.rounded_md()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().sidebar_foreground.opacity(0.7))
|
||||
.h_8()
|
||||
.child(self.label),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
self.base.children(
|
||||
self.children
|
||||
.into_iter()
|
||||
.map(|child| child.collapsed(self.is_collapsed)),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
77
crates/ui/src/sidebar/header.rs
Normal file
77
crates/ui/src/sidebar/header.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use gpui::{
|
||||
prelude::FluentBuilder as _, Div, ElementId, InteractiveElement, IntoElement, ParentElement,
|
||||
RenderOnce, SharedString, Styled,
|
||||
};
|
||||
|
||||
use crate::{h_flex, popup_menu::PopupMenuExt, theme::ActiveTheme as _, Collapsible, Selectable};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct SidebarHeader {
|
||||
id: ElementId,
|
||||
base: Div,
|
||||
selected: bool,
|
||||
is_collapsed: bool,
|
||||
}
|
||||
|
||||
impl SidebarHeader {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id: SharedString::from("sidebar-header").into(),
|
||||
base: h_flex().gap_2().w_full(),
|
||||
selected: false,
|
||||
is_collapsed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Selectable for SidebarHeader {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
|
||||
fn element_id(&self) -> &gpui::ElementId {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
impl Collapsible for SidebarHeader {
|
||||
fn is_collapsed(&self) -> bool {
|
||||
self.is_collapsed
|
||||
}
|
||||
|
||||
fn collapsed(mut self, collapsed: bool) -> Self {
|
||||
self.is_collapsed = collapsed;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl ParentElement for SidebarHeader {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
|
||||
self.base.extend(elements);
|
||||
}
|
||||
}
|
||||
impl Styled for SidebarHeader {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
impl PopupMenuExt for SidebarHeader {}
|
||||
impl RenderOnce for SidebarHeader {
|
||||
fn render(self, cx: &mut gpui::WindowContext) -> impl gpui::IntoElement {
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.gap_2()
|
||||
.p_2()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.cursor_pointer()
|
||||
.rounded_md()
|
||||
.hover(|this| {
|
||||
this.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.when(self.selected, |this| {
|
||||
this.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.child(self.base)
|
||||
}
|
||||
}
|
||||
237
crates/ui/src/sidebar/menu.rs
Normal file
237
crates/ui/src/sidebar/menu.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
use crate::{h_flex, theme::ActiveTheme as _, v_flex, Collapsible, Icon, IconName, StyledExt};
|
||||
use gpui::{
|
||||
div, percentage, prelude::FluentBuilder as _, ClickEvent, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement as _, Styled as _,
|
||||
WindowContext,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct SidebarMenu {
|
||||
is_collapsed: bool,
|
||||
items: Vec<SidebarMenuItem>,
|
||||
}
|
||||
|
||||
impl SidebarMenu {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
items: Vec::new(),
|
||||
is_collapsed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn menu(
|
||||
mut self,
|
||||
label: impl Into<SharedString>,
|
||||
icon: Option<Icon>,
|
||||
active: bool,
|
||||
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.items.push(SidebarMenuItem::Item {
|
||||
icon,
|
||||
label: label.into(),
|
||||
handler: Rc::new(handler),
|
||||
active,
|
||||
is_collapsed: self.is_collapsed,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn submenu(
|
||||
mut self,
|
||||
label: impl Into<SharedString>,
|
||||
icon: Option<Icon>,
|
||||
open: bool,
|
||||
items: impl FnOnce(SidebarMenu) -> Self,
|
||||
handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
let menu = SidebarMenu::new();
|
||||
let menu = items(menu);
|
||||
self.items.push(SidebarMenuItem::Submenu {
|
||||
icon,
|
||||
label: label.into(),
|
||||
items: menu.items,
|
||||
is_open: open,
|
||||
is_collapsed: self.is_collapsed,
|
||||
handler: Rc::new(handler),
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Collapsible for SidebarMenu {
|
||||
fn is_collapsed(&self) -> bool {
|
||||
self.is_collapsed
|
||||
}
|
||||
|
||||
fn collapsed(mut self, collapsed: bool) -> Self {
|
||||
self.is_collapsed = collapsed;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl RenderOnce for SidebarMenu {
|
||||
fn render(self, _: &mut WindowContext) -> impl IntoElement {
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.children(self.items.into_iter().map(|mut item| {
|
||||
match &mut item {
|
||||
SidebarMenuItem::Item { is_collapsed, .. } => *is_collapsed = self.is_collapsed,
|
||||
SidebarMenuItem::Submenu { is_collapsed, .. } => {
|
||||
*is_collapsed = self.is_collapsed
|
||||
}
|
||||
}
|
||||
item
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// A sidebar menu item
|
||||
#[derive(IntoElement)]
|
||||
enum SidebarMenuItem {
|
||||
Item {
|
||||
icon: Option<Icon>,
|
||||
label: SharedString,
|
||||
handler: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
|
||||
active: bool,
|
||||
is_collapsed: bool,
|
||||
},
|
||||
Submenu {
|
||||
icon: Option<Icon>,
|
||||
label: SharedString,
|
||||
handler: Rc<dyn Fn(&ClickEvent, &mut WindowContext)>,
|
||||
items: Vec<SidebarMenuItem>,
|
||||
is_open: bool,
|
||||
is_collapsed: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl SidebarMenuItem {
|
||||
fn is_submenu(&self) -> bool {
|
||||
matches!(self, SidebarMenuItem::Submenu { .. })
|
||||
}
|
||||
|
||||
fn icon(&self) -> Option<Icon> {
|
||||
match self {
|
||||
SidebarMenuItem::Item { icon, .. } => icon.clone(),
|
||||
SidebarMenuItem::Submenu { icon, .. } => icon.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn label(&self) -> SharedString {
|
||||
match self {
|
||||
SidebarMenuItem::Item { label, .. } => label.clone(),
|
||||
SidebarMenuItem::Submenu { label, .. } => label.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
match self {
|
||||
SidebarMenuItem::Item { active, .. } => *active,
|
||||
SidebarMenuItem::Submenu { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_open(&self) -> bool {
|
||||
match self {
|
||||
SidebarMenuItem::Item { .. } => false,
|
||||
SidebarMenuItem::Submenu { is_open, items, .. } => {
|
||||
*is_open || items.iter().any(|item| item.is_active())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_collapsed(&self) -> bool {
|
||||
match self {
|
||||
SidebarMenuItem::Item { is_collapsed, .. } => *is_collapsed,
|
||||
SidebarMenuItem::Submenu { is_collapsed, .. } => *is_collapsed,
|
||||
}
|
||||
}
|
||||
|
||||
fn render_menu_item(
|
||||
&self,
|
||||
is_submenu: bool,
|
||||
is_active: bool,
|
||||
is_open: bool,
|
||||
cx: &WindowContext,
|
||||
) -> impl IntoElement {
|
||||
let handler = match &self {
|
||||
SidebarMenuItem::Item { handler, .. } => Some(handler.clone()),
|
||||
SidebarMenuItem::Submenu { handler, .. } => Some(handler.clone()),
|
||||
};
|
||||
let is_collapsed = self.is_collapsed();
|
||||
|
||||
h_flex()
|
||||
.id("sidebar-menu-item")
|
||||
.overflow_hidden()
|
||||
.flex_shrink_0()
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.rounded_md()
|
||||
.text_sm()
|
||||
.cursor_pointer()
|
||||
.hover(|this| {
|
||||
this.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.when(is_active, |this| {
|
||||
this.font_medium()
|
||||
.bg(cx.theme().sidebar_accent)
|
||||
.text_color(cx.theme().sidebar_accent_foreground)
|
||||
})
|
||||
.when_some(self.icon(), |this, icon| this.child(icon.size_4()))
|
||||
.when(is_collapsed, |this| {
|
||||
this.justify_center().size_7().mx_auto()
|
||||
})
|
||||
.when(!is_collapsed, |this| {
|
||||
this.h_7()
|
||||
.child(div().flex_1().child(self.label()))
|
||||
.when(is_submenu, |this| {
|
||||
this.child(
|
||||
Icon::new(IconName::ChevronRight)
|
||||
.size_4()
|
||||
.when(is_open, |this| this.rotate(percentage(90. / 360.))),
|
||||
)
|
||||
})
|
||||
})
|
||||
.when_some(handler, |this, handler| {
|
||||
this.on_click(move |ev, cx| handler(ev, cx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for SidebarMenuItem {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let is_submenu = self.is_submenu();
|
||||
let is_active = self.is_active();
|
||||
let is_open = self.is_open();
|
||||
|
||||
div()
|
||||
.w_full()
|
||||
.child(self.render_menu_item(is_submenu, is_active, is_open, cx))
|
||||
.when(is_open, |this| {
|
||||
this.map(|this| match self {
|
||||
SidebarMenuItem::Submenu {
|
||||
items,
|
||||
is_collapsed,
|
||||
..
|
||||
} => {
|
||||
if is_collapsed {
|
||||
this
|
||||
} else {
|
||||
this.child(
|
||||
v_flex()
|
||||
.border_l_1()
|
||||
.border_color(cx.theme().sidebar_border)
|
||||
.gap_1()
|
||||
.mx_3p5()
|
||||
.px_2p5()
|
||||
.py_0p5()
|
||||
.children(items),
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => this,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
212
crates/ui/src/sidebar/mod.rs
Normal file
212
crates/ui/src/sidebar/mod.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use crate::{
|
||||
button::{Button, ButtonVariants},
|
||||
h_flex,
|
||||
scroll::ScrollbarAxis,
|
||||
theme::ActiveTheme,
|
||||
v_flex, Collapsible, Icon, IconName, Side, Sizable, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, AnyElement, ClickEvent, Entity, EntityId,
|
||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, RenderOnce, Styled, View,
|
||||
WindowContext,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
mod footer;
|
||||
mod group;
|
||||
mod header;
|
||||
mod menu;
|
||||
pub use footer::*;
|
||||
pub use group::*;
|
||||
pub use header::*;
|
||||
pub use menu::*;
|
||||
|
||||
const DEFAULT_WIDTH: Pixels = px(255.);
|
||||
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>,
|
||||
/// footer view
|
||||
footer: Option<AnyElement>,
|
||||
/// The side of the sidebar
|
||||
side: Side,
|
||||
collapsible: bool,
|
||||
width: Pixels,
|
||||
is_collapsed: bool,
|
||||
}
|
||||
|
||||
impl<E: Collapsible + IntoElement> Sidebar<E> {
|
||||
fn new(view_id: EntityId, side: Side) -> Self {
|
||||
Self {
|
||||
view_id,
|
||||
content: vec![],
|
||||
header: None,
|
||||
footer: None,
|
||||
side,
|
||||
collapsible: true,
|
||||
width: DEFAULT_WIDTH,
|
||||
is_collapsed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left<V: Render + 'static>(view: &View<V>) -> Self {
|
||||
Self::new(view.entity_id(), Side::Left)
|
||||
}
|
||||
|
||||
pub fn right<V: Render + 'static>(view: &View<V>) -> Self {
|
||||
Self::new(view.entity_id(), Side::Right)
|
||||
}
|
||||
|
||||
/// Set the width of the sidebar
|
||||
pub fn width(mut self, width: Pixels) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the sidebar to be collapsible, default is true
|
||||
pub fn collapsible(mut self, collapsible: bool) -> Self {
|
||||
self.collapsible = collapsible;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the sidebar to be collapsed
|
||||
pub fn collapsed(mut self, collapsed: bool) -> Self {
|
||||
self.is_collapsed = collapsed;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the header of the sidebar.
|
||||
pub fn header(mut self, header: impl IntoElement) -> Self {
|
||||
self.header = Some(header.into_any_element());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the footer of the sidebar.
|
||||
pub fn footer(mut self, footer: impl IntoElement) -> Self {
|
||||
self.footer = Some(footer.into_any_element());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a child element to the sidebar, the child must implement `Collapsible`
|
||||
pub fn child(mut self, child: E) -> Self {
|
||||
self.content.push(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add multiple children to the sidebar, the children must implement `Collapsible`
|
||||
pub fn children(mut self, children: impl IntoIterator<Item = E>) -> Self {
|
||||
self.content.extend(children);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sidebar collapse button with Icon.
|
||||
#[derive(IntoElement)]
|
||||
pub struct SidebarToggleButton {
|
||||
btn: Button,
|
||||
is_collapsed: bool,
|
||||
side: Side,
|
||||
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext)>>,
|
||||
}
|
||||
|
||||
impl SidebarToggleButton {
|
||||
fn new(side: Side) -> Self {
|
||||
Self {
|
||||
btn: Button::new("sidebar-collapse").ghost().small(),
|
||||
is_collapsed: false,
|
||||
side,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left() -> Self {
|
||||
Self::new(Side::Left)
|
||||
}
|
||||
|
||||
pub fn right() -> Self {
|
||||
Self::new(Side::Right)
|
||||
}
|
||||
|
||||
pub fn collapsed(mut self, is_collapsed: bool) -> Self {
|
||||
self.is_collapsed = is_collapsed;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(
|
||||
mut self,
|
||||
on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.on_click = Some(Rc::new(on_click));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for SidebarToggleButton {
|
||||
fn render(self, _: &mut WindowContext) -> impl IntoElement {
|
||||
let is_collapsed = self.is_collapsed;
|
||||
let on_click = self.on_click.clone();
|
||||
|
||||
let icon = if is_collapsed {
|
||||
if self.side.is_left() {
|
||||
IconName::PanelLeftOpen
|
||||
} else {
|
||||
IconName::PanelRightOpen
|
||||
}
|
||||
} else {
|
||||
if self.side.is_left() {
|
||||
IconName::PanelLeftClose
|
||||
} else {
|
||||
IconName::PanelRightClose
|
||||
}
|
||||
};
|
||||
|
||||
self.btn
|
||||
.when_some(on_click, |this, on_click| {
|
||||
this.on_click(move |ev, cx| {
|
||||
on_click(ev, cx);
|
||||
})
|
||||
})
|
||||
.icon(Icon::new(icon).size_4())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Collapsible + IntoElement> RenderOnce for Sidebar<E> {
|
||||
fn render(mut self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
let is_collaped = self.is_collapsed;
|
||||
v_flex()
|
||||
.id("sidebar")
|
||||
.w(self.width)
|
||||
.when(self.is_collapsed, |this| this.w(COLLAPSED_WIDTH))
|
||||
.flex_shrink_0()
|
||||
.h_full()
|
||||
.overflow_hidden()
|
||||
.relative()
|
||||
.bg(cx.theme().sidebar)
|
||||
.text_color(cx.theme().sidebar_foreground)
|
||||
.border_color(cx.theme().sidebar_border)
|
||||
.map(|this| match self.side {
|
||||
Side::Left => this.border_r_1(),
|
||||
Side::Right => this.text_2xl(),
|
||||
})
|
||||
.when_some(self.header.take(), |this, header| {
|
||||
this.child(h_flex().id("header").p_2().gap_2().child(header))
|
||||
})
|
||||
.child(
|
||||
v_flex().id("content").flex_1().min_h_0().child(
|
||||
div()
|
||||
.children(self.content.into_iter().map(|c| c.collapsed(is_collaped)))
|
||||
.gap_2()
|
||||
.scrollable(self.view_id, ScrollbarAxis::Vertical),
|
||||
),
|
||||
)
|
||||
.when_some(self.footer.take(), |this, footer| {
|
||||
this.child(h_flex().id("footer").gap_2().p_2().child(footer))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user