Style collab panel (#3638)

This PR styles the collab panel.

Release Notes:

- N/A

---------

Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Marshall Bowers <1486634+maxdeviant@users.noreply.github.com>
This commit is contained in:
Marshall Bowers 2023-12-13 18:20:04 -05:00 committed by GitHub
parent 1ad1cc1148
commit d59de96921
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 167 additions and 136 deletions

View file

@ -176,11 +176,11 @@ use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
use fuzzy::{match_strings, StringMatchCandidate}; use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{ use gpui::{
actions, canvas, div, img, impl_actions, overlay, point, prelude::*, px, rems, serde_json, actions, canvas, div, img, impl_actions, overlay, point, prelude::*, px, rems, serde_json,
size, Action, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, size, Action, AnyElement, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent,
EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement, Div, EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement,
Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render, IntoElement, Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad,
RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, View, Render, RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task,
ViewContext, VisualContext, WeakView, View, ViewContext, VisualContext, WeakView,
}; };
use project::{Fs, Project}; use project::{Fs, Project};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -402,7 +402,7 @@ impl CollabPanel {
let filter_editor = cx.build_view(|cx| { let filter_editor = cx.build_view(|cx| {
let mut editor = Editor::single_line(cx); let mut editor = Editor::single_line(cx);
editor.set_placeholder_text("Filter channels, contacts", cx); editor.set_placeholder_text("Filter...", cx);
editor editor
}); });
@ -1157,24 +1157,20 @@ impl CollabPanel {
ListItem::new(SharedString::from(user.github_login.clone())) ListItem::new(SharedString::from(user.github_login.clone()))
.start_slot(Avatar::new(user.avatar_uri.clone())) .start_slot(Avatar::new(user.avatar_uri.clone()))
.child( .child(Label::new(user.github_login.clone()))
h_stack() .end_slot(if is_pending {
.w_full() Label::new("Calling").color(Color::Muted).into_any_element()
.justify_between() } else if is_current_user {
.child(Label::new(user.github_login.clone())) IconButton::new("leave-call", Icon::Exit)
.child(if is_pending { .style(ButtonStyle::Subtle)
Label::new("Calling").color(Color::Muted).into_any_element() .on_click(cx.listener(move |this, _, cx| {
} else if is_current_user { Self::leave_call(cx);
IconButton::new("leave-call", Icon::ArrowRight) }))
.on_click(cx.listener(move |this, _, cx| { .tooltip(|cx| Tooltip::text("Leave Call", cx))
Self::leave_call(cx); .into_any_element()
})) } else {
.tooltip(|cx| Tooltip::text("Leave Call", cx)) div().into_any_element()
.into_any_element() })
} else {
div().into_any_element()
}),
)
.when_some(peer_id, |this, peer_id| { .when_some(peer_id, |this, peer_id| {
this.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx)) this.tooltip(move |cx| Tooltip::text(tooltip.clone(), cx))
.on_click(cx.listener(move |this, _, cx| { .on_click(cx.listener(move |this, _, cx| {
@ -1212,8 +1208,12 @@ impl CollabPanel {
.detach_and_log_err(cx); .detach_and_log_err(cx);
}); });
})) }))
.start_slot(render_tree_branch(is_last, cx)) .start_slot(
.child(IconButton::new(0, Icon::Folder)) h_stack()
.gap_1()
.child(render_tree_branch(is_last, cx))
.child(IconButton::new(0, Icon::Folder)),
)
.child(Label::new(project_name.clone())) .child(Label::new(project_name.clone()))
.tooltip(move |cx| Tooltip::text(format!("Open {}", project_name), cx)) .tooltip(move |cx| Tooltip::text(format!("Open {}", project_name), cx))
@ -1305,8 +1305,12 @@ impl CollabPanel {
let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize); let id = peer_id.map_or(usize::MAX, |id| id.as_u64() as usize);
ListItem::new(("screen", id)) ListItem::new(("screen", id))
.start_slot(render_tree_branch(is_last, cx)) .start_slot(
.child(IconButton::new(0, Icon::Screen)) h_stack()
.gap_1()
.child(render_tree_branch(is_last, cx))
.child(IconButton::new(0, Icon::Screen)),
)
.child(Label::new("Screen")) .child(Label::new("Screen"))
.when_some(peer_id, |this, _| { .when_some(peer_id, |this, _| {
this.on_click(cx.listener(move |this, _, cx| { this.on_click(cx.listener(move |this, _, cx| {
@ -1372,9 +1376,13 @@ impl CollabPanel {
.on_click(cx.listener(move |this, _, cx| { .on_click(cx.listener(move |this, _, cx| {
this.open_channel_notes(channel_id, cx); this.open_channel_notes(channel_id, cx);
})) }))
.start_slot(render_tree_branch(false, cx)) .start_slot(
.child(IconButton::new(0, Icon::File)) h_stack()
.child(Label::new("notes")) .gap_1()
.child(render_tree_branch(false, cx))
.child(IconButton::new(0, Icon::File)),
)
.child(div().h_7().w_full().child(Label::new("notes")))
.tooltip(move |cx| Tooltip::text("Open Channel Notes", cx)) .tooltip(move |cx| Tooltip::text("Open Channel Notes", cx))
} }
@ -1387,8 +1395,12 @@ impl CollabPanel {
.on_click(cx.listener(move |this, _, cx| { .on_click(cx.listener(move |this, _, cx| {
this.join_channel_chat(channel_id, cx); this.join_channel_chat(channel_id, cx);
})) }))
.start_slot(render_tree_branch(true, cx)) .start_slot(
.child(IconButton::new(0, Icon::MessageBubbles)) h_stack()
.gap_1()
.child(render_tree_branch(false, cx))
.child(IconButton::new(0, Icon::MessageBubbles)),
)
.child(Label::new("chat")) .child(Label::new("chat"))
.tooltip(move |cx| Tooltip::text("Open Chat", cx)) .tooltip(move |cx| Tooltip::text("Open Chat", cx))
} }
@ -2149,11 +2161,6 @@ impl CollabPanel {
fn render_signed_in(&mut self, cx: &mut ViewContext<Self>) -> Div { fn render_signed_in(&mut self, cx: &mut ViewContext<Self>) -> Div {
v_stack() v_stack()
.size_full() .size_full()
.child(
div()
.p_2()
.child(div().rounded(px(2.0)).child(self.filter_editor.clone())),
)
.child( .child(
v_stack() v_stack()
.size_full() .size_full()
@ -2223,6 +2230,14 @@ impl CollabPanel {
} }
})), })),
) )
.child(
div().p_2().child(
div()
.border_primary(cx)
.border_t()
.child(self.filter_editor.clone()),
),
)
} }
fn render_header( fn render_header(
@ -2274,22 +2289,32 @@ impl CollabPanel {
let button = match section { let button = match section {
Section::ActiveCall => channel_link.map(|channel_link| { Section::ActiveCall => channel_link.map(|channel_link| {
let channel_link_copy = channel_link.clone(); let channel_link_copy = channel_link.clone();
IconButton::new("channel-link", Icon::Copy) div()
.on_click(move |_, cx| { .invisible()
let item = ClipboardItem::new(channel_link_copy.clone()); .group_hover("section-header", |this| this.visible())
cx.write_to_clipboard(item) .child(
}) IconButton::new("channel-link", Icon::Copy)
.tooltip(|cx| Tooltip::text("Copy channel link", cx)) .icon_size(IconSize::Small)
.size(ButtonSize::None)
.on_click(move |_, cx| {
let item = ClipboardItem::new(channel_link_copy.clone());
cx.write_to_clipboard(item)
})
.tooltip(|cx| Tooltip::text("Copy channel link", cx)),
)
.into_any_element()
}), }),
Section::Contacts => Some( Section::Contacts => Some(
IconButton::new("add-contact", Icon::Plus) IconButton::new("add-contact", Icon::Plus)
.on_click(cx.listener(|this, _, cx| this.toggle_contact_finder(cx))) .on_click(cx.listener(|this, _, cx| this.toggle_contact_finder(cx)))
.tooltip(|cx| Tooltip::text("Search for new contact", cx)), .tooltip(|cx| Tooltip::text("Search for new contact", cx))
.into_any_element(),
), ),
Section::Channels => Some( Section::Channels => Some(
IconButton::new("add-channel", Icon::Plus) IconButton::new("add-channel", Icon::Plus)
.on_click(cx.listener(|this, _, cx| this.new_root_channel(cx))) .on_click(cx.listener(|this, _, cx| this.new_root_channel(cx)))
.tooltip(|cx| Tooltip::text("Create a channel", cx)), .tooltip(|cx| Tooltip::text("Create a channel", cx))
.into_any_element(),
), ),
_ => None, _ => None,
}; };
@ -2304,25 +2329,18 @@ impl CollabPanel {
h_stack() h_stack()
.w_full() .w_full()
.map(|el| { .group("section-header")
if can_collapse { .child(
el.child( ListHeader::new(text)
ListItem::new(text.clone()) .toggle(if can_collapse {
.child(div().w_full().child(Label::new(text))) Some(!is_collapsed)
.selected(is_selected) } else {
.toggle(Some(!is_collapsed)) None
.on_click(cx.listener(move |this, _, cx| { })
this.toggle_section_expanded(section, cx) .inset(true)
})), .end_slot::<AnyElement>(button)
) .selected(is_selected),
} else { )
el.child(
ListHeader::new(text)
.when_some(button, |el, button| el.end_slot(button))
.selected(is_selected),
)
}
})
.when(section == Section::Channels, |el| { .when(section == Section::Channels, |el| {
el.drag_over::<DraggedChannelView>(|style| { el.drag_over::<DraggedChannelView>(|style| {
style.bg(cx.theme().colors().ghost_element_hover) style.bg(cx.theme().colors().ghost_element_hover)
@ -2460,7 +2478,7 @@ impl CollabPanel {
.child(Label::new(github_login.clone())) .child(Label::new(github_login.clone()))
.child(h_stack().children(controls)), .child(h_stack().children(controls)),
) )
.start_slot::<Avatar>(user.avatar_uri.clone().map(|avatar| Avatar::new(avatar))) .start_slot(Avatar::new(user.avatar_uri.clone()))
} }
fn render_contact_placeholder( fn render_contact_placeholder(
@ -2541,6 +2559,8 @@ impl CollabPanel {
div() div()
.id(channel_id as usize) .id(channel_id as usize)
.group("") .group("")
.flex()
.w_full()
.on_drag({ .on_drag({
let channel = channel.clone(); let channel = channel.clone();
move |cx| { move |cx| {
@ -2566,71 +2586,10 @@ impl CollabPanel {
) )
.child( .child(
ListItem::new(channel_id as usize) ListItem::new(channel_id as usize)
.indent_level(depth) // Offset the indent depth by one to give us room to show the disclosure.
.indent_level(depth + 1)
.indent_step_size(cx.rem_size() * 14.0 / 16.0) // @todo()! @nate this is to step over the disclosure toggle .indent_step_size(cx.rem_size() * 14.0 / 16.0) // @todo()! @nate this is to step over the disclosure toggle
.start_slot(
IconElement::new(if is_public { Icon::Public } else { Icon::Hash })
.size(IconSize::Small)
.color(Color::Muted),
)
.selected(is_selected || is_active) .selected(is_selected || is_active)
.child(
h_stack()
.w_full()
.justify_between()
.child(
h_stack()
.id(channel_id as usize)
.child(Label::new(channel.name.clone()))
.children(face_pile.map(|face_pile| face_pile.render(cx))),
)
.child(
h_stack()
.child(
div()
.id("channel_chat")
.when(!has_messages_notification, |el| el.invisible())
.group_hover("", |style| style.visible())
.child(
IconButton::new(
"channel_chat",
Icon::MessageBubbles,
)
.icon_color(if has_messages_notification {
Color::Default
} else {
Color::Muted
})
.on_click(cx.listener(move |this, _, cx| {
this.join_channel_chat(channel_id, cx)
}))
.tooltip(|cx| {
Tooltip::text("Open channel chat", cx)
}),
),
)
.child(
div()
.id("channel_notes")
.when(!has_notes_notification, |el| el.invisible())
.group_hover("", |style| style.visible())
.child(
IconButton::new("channel_notes", Icon::File)
.icon_color(if has_notes_notification {
Color::Default
} else {
Color::Muted
})
.on_click(cx.listener(move |this, _, cx| {
this.open_channel_notes(channel_id, cx)
}))
.tooltip(|cx| {
Tooltip::text("Open channel notes", cx)
}),
),
),
),
)
.toggle(disclosed) .toggle(disclosed)
.on_toggle( .on_toggle(
cx.listener(move |this, _, cx| { cx.listener(move |this, _, cx| {
@ -2650,7 +2609,57 @@ impl CollabPanel {
move |this, event: &MouseDownEvent, cx| { move |this, event: &MouseDownEvent, cx| {
this.deploy_channel_context_menu(event.position, channel_id, ix, cx) this.deploy_channel_context_menu(event.position, channel_id, ix, cx)
}, },
)), ))
.start_slot(
IconElement::new(if is_public { Icon::Public } else { Icon::Hash })
.size(IconSize::Small)
.color(Color::Muted),
)
.child(
h_stack()
.id(channel_id as usize)
.child(Label::new(channel.name.clone()))
.children(face_pile.map(|face_pile| face_pile.render(cx))),
)
.end_slot(
h_stack()
.child(
div()
.id("channel_chat")
.when(!has_messages_notification, |el| el.invisible())
.group_hover("", |style| style.visible())
.child(
IconButton::new("channel_chat", Icon::MessageBubbles)
.icon_color(if has_messages_notification {
Color::Default
} else {
Color::Muted
})
.on_click(cx.listener(move |this, _, cx| {
this.join_channel_chat(channel_id, cx)
}))
.tooltip(|cx| Tooltip::text("Open channel chat", cx)),
),
)
.child(
div()
.id("channel_notes")
.when(!has_notes_notification, |el| el.invisible())
.group_hover("", |style| style.visible())
.child(
IconButton::new("channel_notes", Icon::File)
.icon_color(if has_notes_notification {
Color::Default
} else {
Color::Muted
})
.on_click(cx.listener(move |this, _, cx| {
this.open_channel_notes(channel_id, cx)
}))
.tooltip(|cx| Tooltip::text("Open channel notes", cx)),
),
),
),
) )
.tooltip(|cx| Tooltip::text("Join channel", cx)) .tooltip(|cx| Tooltip::text("Join channel", cx))

View file

@ -1,6 +1,7 @@
use crate::{prelude::*, Color, Icon, IconButton, IconSize};
use gpui::ClickEvent; use gpui::ClickEvent;
use crate::{prelude::*, Color, Icon, IconButton, IconSize};
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct Disclosure { pub struct Disclosure {
is_open: bool, is_open: bool,

View file

@ -1,9 +1,10 @@
use crate::{prelude::*, Disclosure};
use gpui::{ use gpui::{
px, AnyElement, AnyView, ClickEvent, Div, MouseButton, MouseDownEvent, Pixels, Stateful, px, AnyElement, AnyView, ClickEvent, Div, MouseButton, MouseDownEvent, Pixels, Stateful,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{prelude::*, Disclosure};
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct ListItem { pub struct ListItem {
id: ElementId, id: ElementId,
@ -192,10 +193,15 @@ impl RenderOnce for ListItem {
this.ml(self.indent_level as f32 * self.indent_step_size) this.ml(self.indent_level as f32 * self.indent_step_size)
} }
}) })
.children( .children(self.toggle.map(|is_open| {
self.toggle div()
.map(|is_open| Disclosure::new(is_open).on_toggle(self.on_toggle)), .flex()
) .absolute()
.left(rems(-1.))
.invisible()
.group_hover("", |style| style.visible())
.child(Disclosure::new(is_open).on_toggle(self.on_toggle))
}))
.child( .child(
h_stack() h_stack()
.flex_1() .flex_1()

15
script/storybook Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
# This script takes a single text input and replaces 'list_item' with the input in a cargo run command
# Check if an argument is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <component_name>"
exit 1
fi
# Assign the argument to a variable
COMPONENT_NAME="$1"
# Run the cargo command with the provided component name
cargo run -p storybook2 -- components/"$COMPONENT_NAME"