Add ProjectPanel component

This commit is contained in:
Marshall Bowers 2023-10-06 17:58:23 -04:00
parent 208d5df106
commit 56c2ac048d
10 changed files with 379 additions and 1 deletions

View file

@ -1,3 +1,4 @@
pub mod assistant_panel;
pub mod buffer;
pub mod panel;
pub mod project_panel;

View file

@ -0,0 +1,30 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::{Panel, ProjectPanel};
use crate::story::Story;
#[derive(Element)]
pub struct ProjectPanelStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> ProjectPanelStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, ProjectPanel<S>>(cx))
.child(Story::label(cx, "Default"))
.child(Panel::new(
ScrollState::default(),
|_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()],
Box::new(()),
))
}
}

View file

@ -1,3 +1,4 @@
pub mod avatar;
pub mod icon;
pub mod input;
pub mod label;

View file

@ -0,0 +1,26 @@
use std::marker::PhantomData;
use ui::prelude::*;
use ui::Input;
use crate::story::Story;
#[derive(Element)]
pub struct InputStory<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
}
impl<S: 'static + Send + Sync + Clone> InputStory<S> {
pub fn new() -> Self {
Self {
state_type: PhantomData,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
Story::container(cx)
.child(Story::title_for::<_, Input<S>>(cx))
.child(Story::label(cx, "Default"))
.child(div().flex().child(Input::new("Search")))
}
}

View file

@ -14,6 +14,7 @@ use ui::prelude::*;
pub enum ElementStory {
Avatar,
Icon,
Input,
Label,
}
@ -24,6 +25,7 @@ impl ElementStory {
match self {
Self::Avatar => elements::avatar::AvatarStory::new().into_any(),
Self::Icon => elements::icon::IconStory::new().into_any(),
Self::Input => elements::input::InputStory::new().into_any(),
Self::Label => elements::label::LabelStory::new().into_any(),
}
}
@ -35,6 +37,7 @@ pub enum ComponentStory {
AssistantPanel,
Buffer,
Panel,
ProjectPanel,
}
impl ComponentStory {
@ -47,6 +50,7 @@ impl ComponentStory {
}
Self::Buffer => components::buffer::BufferStory::new().into_any(),
Self::Panel => components::panel::PanelStory::new().into_any(),
Self::ProjectPanel => components::project_panel::ProjectPanelStory::new().into_any(),
}
}
}

View file

@ -3,9 +3,11 @@ mod buffer;
mod icon_button;
mod list;
mod panel;
mod project_panel;
pub use assistant_panel::*;
pub use buffer::*;
pub use icon_button::*;
pub use list::*;
pub use panel::*;
pub use project_panel::*;

View file

@ -0,0 +1,58 @@
use std::marker::PhantomData;
use crate::prelude::*;
use crate::{
static_project_panel_project_items, static_project_panel_single_items, theme, Input, List,
ListHeader,
};
#[derive(Element)]
pub struct ProjectPanel<S: 'static + Send + Sync + Clone> {
state_type: PhantomData<S>,
scroll_state: ScrollState,
}
impl<S: 'static + Send + Sync + Clone> ProjectPanel<S> {
pub fn new(scroll_state: ScrollState) -> Self {
Self {
state_type: PhantomData,
scroll_state,
}
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
div()
.flex()
.flex_col()
.w_full()
.h_full()
.px_2()
.fill(theme.middle.base.default.background)
.child(
div()
.w_56()
.flex()
.flex_col()
.overflow_y_scroll(ScrollState::default())
.child(
List::new(static_project_panel_single_items())
.header(ListHeader::new("FILES").set_toggle(ToggleState::Toggled))
.empty_message("No files in directory")
.set_toggle(ToggleState::Toggled),
)
.child(
List::new(static_project_panel_project_items())
.header(ListHeader::new("PROJECT").set_toggle(ToggleState::Toggled))
.empty_message("No folders in directory")
.set_toggle(ToggleState::Toggled),
),
)
.child(
Input::new("Find something...")
.value("buffe".to_string())
.state(InteractionState::Focused),
)
}
}

View file

@ -1,11 +1,13 @@
mod avatar;
mod button;
mod icon;
mod input;
mod label;
mod stack;
pub use avatar::*;
pub use button::*;
pub use icon::*;
pub use input::*;
pub use label::*;
pub use stack::*;

View file

@ -0,0 +1,110 @@
use std::marker::PhantomData;
use crate::prelude::*;
use crate::theme;
#[derive(Default, PartialEq)]
pub enum InputVariant {
#[default]
Ghost,
Filled,
}
#[derive(Element)]
pub struct Input<S: 'static + Send + Sync> {
state_type: PhantomData<S>,
placeholder: &'static str,
value: String,
state: InteractionState,
variant: InputVariant,
}
impl<S: 'static + Send + Sync> Input<S> {
pub fn new(placeholder: &'static str) -> Self {
Self {
state_type: PhantomData,
placeholder,
value: "".to_string(),
state: InteractionState::default(),
variant: InputVariant::default(),
}
}
pub fn value(mut self, value: String) -> Self {
self.value = value;
self
}
pub fn state(mut self, state: InteractionState) -> Self {
self.state = state;
self
}
pub fn variant(mut self, variant: InputVariant) -> Self {
self.variant = variant;
self
}
fn render(&mut self, cx: &mut ViewContext<S>) -> impl Element<State = S> {
let theme = theme(cx);
let text_el;
let text_color;
let background_color_default;
let background_color_active;
let mut border_color_default = theme.middle.base.default.border;
let mut border_color_hover = theme.middle.base.hovered.border;
let mut border_color_active = theme.middle.base.pressed.border;
let border_color_focus = theme.middle.base.pressed.background;
match self.variant {
InputVariant::Ghost => {
background_color_default = theme.middle.base.default.background;
background_color_active = theme.middle.base.active.background;
}
InputVariant::Filled => {
background_color_default = theme.middle.on.default.background;
background_color_active = theme.middle.on.active.background;
}
};
if self.state == InteractionState::Focused {
border_color_default = theme.players[0].cursor;
border_color_hover = theme.players[0].cursor;
border_color_active = theme.players[0].cursor;
}
if self.state == InteractionState::Focused || self.state == InteractionState::Active {
text_el = self.value.clone();
text_color = theme.lowest.base.default.foreground;
} else {
text_el = self.placeholder.to_string().clone();
text_color = theme.lowest.base.disabled.foreground;
}
div()
.h_7()
.w_full()
.px_2()
.border()
.border_color(border_color_default)
.fill(background_color_default)
// .hover()
// .border_color(border_color_hover)
// .active()
// .border_color(border_color_active)
.fill(background_color_active)
.flex()
.items_center()
.child(
div()
.flex()
.items_center()
.text_sm()
.text_color(text_color)
.child(text_el)
.child(div().text_color(theme.players[0].cursor).child("|")),
)
}
}

View file

@ -1,8 +1,152 @@
use crate::{
Buffer, BufferRow, BufferRows, GitStatus, HighlightColor, HighlightedLine, HighlightedText,
Theme,
Icon, Label, LabelColor, ListEntry, ListItem, Theme, ToggleState,
};
pub fn static_project_panel_project_items<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
vec![
ListEntry::new(Label::new("zed"))
.left_icon(Icon::FolderOpen.into())
.indent_level(0)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new(".cargo"))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".config"))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".git").color(LabelColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".cargo"))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".idea").color(LabelColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new("assets"))
.left_icon(Icon::Folder.into())
.indent_level(1)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new("crates"))
.left_icon(Icon::FolderOpen.into())
.indent_level(1)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("activity_indicator"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("ai"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("audio"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("auto_update"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("breadcrumbs"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("call"))
.left_icon(Icon::Folder.into())
.indent_level(2),
ListEntry::new(Label::new("sqlez").color(LabelColor::Modified))
.left_icon(Icon::Folder.into())
.indent_level(2)
.set_toggle(ToggleState::NotToggled),
ListEntry::new(Label::new("gpui2"))
.left_icon(Icon::FolderOpen.into())
.indent_level(2)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("src"))
.left_icon(Icon::FolderOpen.into())
.indent_level(3)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("derive_element.rs"))
.left_icon(Icon::FileRust.into())
.indent_level(4),
ListEntry::new(Label::new("storybook").color(LabelColor::Modified))
.left_icon(Icon::FolderOpen.into())
.indent_level(1)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("docs").color(LabelColor::Default))
.left_icon(Icon::Folder.into())
.indent_level(2)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("src").color(LabelColor::Modified))
.left_icon(Icon::FolderOpen.into())
.indent_level(3)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("ui").color(LabelColor::Modified))
.left_icon(Icon::FolderOpen.into())
.indent_level(4)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("component").color(LabelColor::Created))
.left_icon(Icon::FolderOpen.into())
.indent_level(5)
.set_toggle(ToggleState::Toggled),
ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("follow_group.rs").color(LabelColor::Default))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("list_item.rs").color(LabelColor::Created))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("tab.rs").color(LabelColor::Default))
.left_icon(Icon::FileRust.into())
.indent_level(6),
ListEntry::new(Label::new("target").color(LabelColor::Hidden))
.left_icon(Icon::Folder.into())
.indent_level(1),
ListEntry::new(Label::new(".dockerignore"))
.left_icon(Icon::FileGeneric.into())
.indent_level(1),
ListEntry::new(Label::new(".DS_Store").color(LabelColor::Hidden))
.left_icon(Icon::FileGeneric.into())
.indent_level(1),
ListEntry::new(Label::new("Cargo.lock"))
.left_icon(Icon::FileLock.into())
.indent_level(1),
ListEntry::new(Label::new("Cargo.toml"))
.left_icon(Icon::FileToml.into())
.indent_level(1),
ListEntry::new(Label::new("Dockerfile"))
.left_icon(Icon::FileGeneric.into())
.indent_level(1),
ListEntry::new(Label::new("Procfile"))
.left_icon(Icon::FileGeneric.into())
.indent_level(1),
ListEntry::new(Label::new("README.md"))
.left_icon(Icon::FileDoc.into())
.indent_level(1),
]
.into_iter()
.map(From::from)
.collect()
}
pub fn static_project_panel_single_items<S: 'static + Send + Sync + Clone>() -> Vec<ListItem<S>> {
vec![
ListEntry::new(Label::new("todo.md"))
.left_icon(Icon::FileDoc.into())
.indent_level(0),
ListEntry::new(Label::new("README.md"))
.left_icon(Icon::FileDoc.into())
.indent_level(0),
ListEntry::new(Label::new("config.json"))
.left_icon(Icon::FileGeneric.into())
.indent_level(0),
]
.into_iter()
.map(From::from)
.collect()
}
pub fn empty_buffer_example<S: 'static + Send + Sync + Clone>() -> Buffer<S> {
Buffer::new().set_rows(Some(BufferRows::default()))
}