Bring UI crate up to date

This commit is contained in:
Nate Butler
2023-09-21 23:46:06 -04:00
parent 66358f2900
commit f54634aeb2
24 changed files with 1090 additions and 167 deletions
-65
View File
@@ -1,65 +0,0 @@
use std::marker::PhantomData;
use gpui2::elements::div;
use gpui2::elements::div::ScrollState;
use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::{icon_button, theme};
#[derive(Element)]
pub struct ChatPanel<V: 'static> {
view_type: PhantomData<V>,
scroll_state: ScrollState,
}
pub fn chat_panel<V: 'static>(scroll_state: ScrollState) -> ChatPanel<V> {
ChatPanel {
view_type: PhantomData,
scroll_state,
}
}
impl<V: 'static> ChatPanel<V> {
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
div()
.h_full()
.flex()
// Header
.child(
div()
.px_2()
.flex()
.gap_2()
// Nav Buttons
.child("#gpui2"),
)
// Chat Body
.child(
div()
.w_full()
.flex()
.flex_col()
.overflow_y_scroll(self.scroll_state.clone())
.child("body"),
)
// Composer
.child(
div()
.px_2()
.flex()
.gap_2()
// Nav Buttons
.child(
div()
.flex()
.items_center()
.gap_px()
.child(icon_button("icons/plus.svg"))
.child(icon_button("icons/split.svg")),
),
)
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::theme::theme;
use crate::tokens::token;
use crate::{icon, label, prelude::*, IconAsset, LabelColor, ListItem, ListSectionHeader};
use gpui2::style::StyleHelpers;
use gpui2::{elements::div, IntoElement};
use gpui2::{Element, ParentElement, ViewContext};
#[derive(Element)]
pub struct List {
header: Option<ListSectionHeader>,
items: Vec<ListItem>,
empty_message: &'static str,
toggle: Option<ToggleState>,
// footer: Option<ListSectionFooter>,
}
pub fn list(items: Vec<ListItem>) -> List {
List {
header: None,
items,
empty_message: "No items",
toggle: None,
}
}
impl List {
pub fn header(mut self, header: ListSectionHeader) -> Self {
self.header = Some(header);
self
}
pub fn empty_message(mut self, empty_message: &'static str) -> Self {
self.empty_message = empty_message;
self
}
pub fn set_toggle(mut self, toggle: ToggleState) -> Self {
self.toggle = Some(toggle);
self
}
fn render<V: 'static>(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
let token = token();
let disclosure_control = match self.toggle {
Some(ToggleState::NotToggled) => Some(icon(IconAsset::ChevronRight)),
Some(ToggleState::Toggled) => Some(icon(IconAsset::ChevronDown)),
None => None,
};
div()
.py_1()
.flex()
.flex_col()
.children(self.header.map(|h| h))
.children(
self.items
.is_empty()
.then(|| label(self.empty_message).color(LabelColor::Muted)),
)
.children(self.items.iter().cloned())
}
}
+124
View File
@@ -0,0 +1,124 @@
use std::marker::PhantomData;
use crate::prelude::OrderMethod;
use crate::theme::theme;
use crate::{label, palette_item, LabelColor, PaletteItem};
use gpui2::elements::div::ScrollState;
use gpui2::style::{StyleHelpers, Styleable};
use gpui2::{elements::div, IntoElement};
use gpui2::{Element, ParentElement, ViewContext};
#[derive(Element)]
pub struct Palette<V: 'static> {
view_type: PhantomData<V>,
scroll_state: ScrollState,
input_placeholder: &'static str,
empty_string: &'static str,
items: Vec<PaletteItem>,
default_order: OrderMethod,
}
pub fn palette<V: 'static>(scroll_state: ScrollState) -> Palette<V> {
Palette {
view_type: PhantomData,
scroll_state,
input_placeholder: "Find something...",
empty_string: "No items found.",
items: vec![],
default_order: OrderMethod::default(),
}
}
impl<V: 'static> Palette<V> {
pub fn items(mut self, mut items: Vec<PaletteItem>) -> Self {
items.sort_by_key(|item| item.label);
self.items = items;
self
}
pub fn placeholder(mut self, input_placeholder: &'static str) -> Self {
self.input_placeholder = input_placeholder;
self
}
pub fn empty_string(mut self, empty_string: &'static str) -> Self {
self.empty_string = empty_string;
self
}
// TODO: Hook up sort order
pub fn default_order(mut self, default_order: OrderMethod) -> Self {
self.default_order = default_order;
self
}
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
div()
.w_96()
.rounded_lg()
.fill(theme.lowest.base.default.background)
.border()
.border_color(theme.lowest.base.default.border)
.flex()
.flex_col()
.child(
div()
.flex()
.flex_col()
.gap_px()
.child(
div().py_0p5().px_1().flex().flex_col().child(
div().px_2().py_0p5().child(
label(self.input_placeholder).color(LabelColor::Placeholder),
),
),
)
.child(div().h_px().w_full().fill(theme.lowest.base.default.border))
.child(
div()
.py_0p5()
.px_1()
.flex()
.flex_col()
.grow()
.max_h_96()
.overflow_y_scroll(self.scroll_state.clone())
.children(
vec![if self.items.is_empty() {
Some(
div()
.flex()
.flex_row()
.justify_between()
.px_2()
.py_1()
.child(
label(self.empty_string).color(LabelColor::Muted),
),
)
} else {
None
}]
.into_iter()
.flatten(),
)
.children(self.items.iter().map(|item| {
div()
.flex()
.flex_row()
.justify_between()
.px_2()
.py_0p5()
.rounded_lg()
.hover()
.fill(theme.lowest.base.hovered.background)
.active()
.fill(theme.lowest.base.pressed.background)
.child(palette_item(item.label, item.keybinding))
})),
),
)
}
}
-94
View File
@@ -1,94 +0,0 @@
use std::marker::PhantomData;
use gpui2::elements::div;
use gpui2::elements::div::ScrollState;
use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::prelude::*;
use crate::{details, input, label, list_item, theme, IconAsset, LabelColor};
#[derive(Element)]
pub struct ProjectPanel<V: 'static> {
view_type: PhantomData<V>,
scroll_state: ScrollState,
}
pub fn project_panel<V: 'static>(scroll_state: ScrollState) -> ProjectPanel<V> {
ProjectPanel {
view_type: PhantomData,
scroll_state,
}
}
impl<V: 'static> ProjectPanel<V> {
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
div()
.w_56()
.h_full()
.flex()
.flex_col()
.fill(theme.middle.base.default.background)
.child(
div()
.w_56()
.flex()
.flex_col()
.overflow_y_scroll(self.scroll_state.clone())
.child(details("This is a long string that should wrap when it keeps going for a long time.").meta_text("6 h ago)"))
.child(
div().flex().flex_col().children(
std::iter::repeat_with(|| {
vec![
list_item(label("sqlez").color(LabelColor::Modified))
.left_icon(IconAsset::FolderOpen.into())
.indent_level(0)
.set_toggle(ToggleState::NotToggled),
list_item(label("storybook").color(LabelColor::Modified))
.left_icon(IconAsset::FolderOpen.into())
.indent_level(0)
.set_toggle(ToggleState::Toggled),
list_item(label("docs").color(LabelColor::Default))
.left_icon(IconAsset::Folder.into())
.indent_level(1)
.set_toggle(ToggleState::Toggled),
list_item(label("src").color(LabelColor::Modified))
.left_icon(IconAsset::FolderOpen.into())
.indent_level(2)
.set_toggle(ToggleState::Toggled),
list_item(label("ui").color(LabelColor::Modified))
.left_icon(IconAsset::FolderOpen.into())
.indent_level(3)
.set_toggle(ToggleState::Toggled),
list_item(label("component").color(LabelColor::Created))
.left_icon(IconAsset::FolderOpen.into())
.indent_level(4)
.set_toggle(ToggleState::Toggled),
list_item(label("facepile.rs").color(LabelColor::Default))
.left_icon(IconAsset::File.into())
.indent_level(5),
list_item(label("follow_group.rs").color(LabelColor::Default))
.left_icon(IconAsset::File.into())
.indent_level(5),
list_item(label("list_item.rs").color(LabelColor::Created))
.left_icon(IconAsset::File.into())
.indent_level(5),
list_item(label("tab.rs").color(LabelColor::Default))
.left_icon(IconAsset::File.into())
.indent_level(5),
]
})
.take(10)
.flatten(),
),
),
)
.child(
input("Find something...")
.value("buffe".to_string())
.state(InteractionState::Focused),
)
}
}
-147
View File
@@ -1,147 +0,0 @@
use std::marker::PhantomData;
use gpui2::elements::div;
use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::theme::{theme, Theme};
use crate::{icon_button, text_button, tool_divider};
#[derive(Default, PartialEq)]
pub enum Tool {
#[default]
ProjectPanel,
CollaborationPanel,
Terminal,
Assistant,
Feedback,
Diagnostics,
}
struct ToolGroup {
active_index: Option<usize>,
tools: Vec<Tool>,
}
impl Default for ToolGroup {
fn default() -> Self {
ToolGroup {
active_index: None,
tools: vec![],
}
}
}
#[derive(Element)]
pub struct StatusBar<V: 'static> {
view_type: PhantomData<V>,
left_tools: Option<ToolGroup>,
right_tools: Option<ToolGroup>,
bottom_tools: Option<ToolGroup>,
}
pub fn status_bar<V: 'static>() -> StatusBar<V> {
StatusBar {
view_type: PhantomData,
left_tools: None,
right_tools: None,
bottom_tools: None,
}
}
impl<V: 'static> StatusBar<V> {
pub fn left_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
self.left_tools = {
let mut tools = vec![tool];
tools.extend(self.left_tools.take().unwrap_or_default().tools);
Some(ToolGroup {
active_index,
tools,
})
};
self
}
pub fn right_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
self.right_tools = {
let mut tools = vec![tool];
tools.extend(self.left_tools.take().unwrap_or_default().tools);
Some(ToolGroup {
active_index,
tools,
})
};
self
}
pub fn bottom_tool(mut self, tool: Tool, active_index: Option<usize>) -> Self {
self.bottom_tools = {
let mut tools = vec![tool];
tools.extend(self.left_tools.take().unwrap_or_default().tools);
Some(ToolGroup {
active_index,
tools,
})
};
self
}
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
div()
.py_0p5()
.px_1()
.flex()
.items_center()
.justify_between()
.w_full()
.fill(theme.lowest.base.default.background)
.child(self.left_tools(theme))
.child(self.right_tools(theme))
}
fn left_tools(&self, theme: &Theme) -> impl Element<V> {
div()
.flex()
.items_center()
.gap_1()
.child(icon_button("icons/project.svg"))
.child(icon_button("icons/hash.svg"))
.child(tool_divider())
.child(icon_button("icons/error.svg"))
}
fn right_tools(&self, theme: &Theme) -> impl Element<V> {
div()
.flex()
.items_center()
.gap_2()
.child(
div()
.flex()
.items_center()
.gap_1()
.child(text_button("116:25"))
.child(text_button("Rust")),
)
.child(tool_divider())
.child(
div()
.flex()
.items_center()
.gap_1()
.child(icon_button("icons/copilot.svg"))
.child(icon_button("icons/feedback.svg")),
)
.child(tool_divider())
.child(
div()
.flex()
.items_center()
.gap_1()
.child(icon_button("icons/terminal.svg"))
.child(icon_button("icons/conversations.svg"))
.child(icon_button("icons/ai.svg")),
)
}
}
-91
View File
@@ -1,91 +0,0 @@
use std::marker::PhantomData;
use gpui2::elements::div;
use gpui2::elements::div::ScrollState;
use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::prelude::InteractionState;
use crate::{icon_button, tab, theme};
#[derive(Element)]
pub struct TabBar<V: 'static> {
view_type: PhantomData<V>,
scroll_state: ScrollState,
}
pub fn tab_bar<V: 'static>(scroll_state: ScrollState) -> TabBar<V> {
TabBar {
view_type: PhantomData,
scroll_state,
}
}
impl<V: 'static> TabBar<V> {
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
let can_navigate_back = true;
let can_navigate_forward = false;
div()
.w_full()
.flex()
// Left Side
.child(
div()
.px_1()
.flex()
.flex_none()
.gap_2()
// Nav Buttons
.child(
div()
.flex()
.items_center()
.gap_px()
.child(
icon_button("icons/arrow_left.svg")
.state(InteractionState::Enabled.if_enabled(can_navigate_back)),
)
.child(
icon_button("icons/arrow_right.svg").state(
InteractionState::Enabled.if_enabled(can_navigate_forward),
),
),
),
)
.child(
div().w_0().flex_1().h_full().child(
div()
.flex()
.gap_1()
.overflow_x_scroll(self.scroll_state.clone())
.child(tab("Cargo.toml", false))
.child(tab("Channels Panel", true))
.child(tab("channels_panel.rs", false))
.child(tab("workspace.rs", false))
.child(tab("icon_button.rs", false))
.child(tab("storybook.rs", false))
.child(tab("theme.rs", false))
.child(tab("theme_registry.rs", false))
.child(tab("styleable_helpers.rs", false)),
),
)
// Right Side
.child(
div()
.px_1()
.flex()
.flex_none()
.gap_2()
// Nav Buttons
.child(
div()
.flex()
.items_center()
.gap_px()
.child(icon_button("icons/plus.svg"))
.child(icon_button("icons/split.svg")),
),
)
}
}
-117
View File
@@ -1,117 +0,0 @@
use std::marker::PhantomData;
use gpui2::elements::div;
use gpui2::style::StyleHelpers;
use gpui2::{Element, IntoElement, ParentElement, ViewContext};
use crate::prelude::Shape;
use crate::{avatar, follow_group, icon_button, text_button, theme, tool_divider};
#[derive(Element)]
pub struct TitleBar<V: 'static> {
view_type: PhantomData<V>,
}
pub fn title_bar<V: 'static>() -> TitleBar<V> {
TitleBar {
view_type: PhantomData,
}
}
impl<V: 'static> TitleBar<V> {
fn render(&mut self, _: &mut V, cx: &mut ViewContext<V>) -> impl IntoElement<V> {
let theme = theme(cx);
let player_list = vec![
avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
avatar("https://avatars.githubusercontent.com/u/1714999?v=4"),
];
div()
.flex()
.items_center()
.justify_between()
.w_full()
.h_8()
.fill(theme.lowest.base.default.background)
.child(
div()
.flex()
.items_center()
.h_full()
.gap_4()
.px_2()
// === Traffic Lights === //
.child(
div()
.flex()
.items_center()
.gap_2()
.child(
div()
.w_3()
.h_3()
.rounded_full()
.fill(theme.lowest.positive.default.foreground),
)
.child(
div()
.w_3()
.h_3()
.rounded_full()
.fill(theme.lowest.warning.default.foreground),
)
.child(
div()
.w_3()
.h_3()
.rounded_full()
.fill(theme.lowest.negative.default.foreground),
),
)
// === Project Info === //
.child(
div()
.flex()
.items_center()
.gap_1()
.child(text_button("maxbrunsfeld"))
.child(text_button("zed"))
.child(text_button("nate/gpui2-ui-components")),
)
.child(follow_group(player_list.clone()).player(0))
.child(follow_group(player_list.clone()).player(1))
.child(follow_group(player_list.clone()).player(2)),
)
.child(
div()
.flex()
.items_center()
.child(
div()
.px_2()
.flex()
.items_center()
.gap_1()
.child(icon_button("icons/stop_sharing.svg"))
.child(icon_button("icons/exit.svg")),
)
.child(tool_divider())
.child(
div()
.px_2()
.flex()
.items_center()
.gap_1()
.child(icon_button("icons/mic.svg"))
.child(icon_button("icons/speaker-loud.svg"))
.child(icon_button("icons/desktop.svg")),
)
.child(
div().px_2().flex().items_center().child(
avatar("https://avatars.githubusercontent.com/u/1714999?v=4")
.shape(Shape::RoundedRectangle),
),
),
)
}
}