collab_panel.rs

  1use crate::prelude::*;
  2use crate::{
  3    static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List,
  4    ListHeader, ToggleState,
  5};
  6use gpui2::{img, svg, SharedString};
  7use std::marker::PhantomData;
  8
  9#[derive(Element)]
 10pub struct CollabPanel<S: 'static + Send + Sync> {
 11    id: ElementId,
 12    state_type: PhantomData<S>,
 13}
 14
 15impl<S: 'static + Send + Sync> CollabPanel<S> {
 16    pub fn new(id: impl Into<ElementId>) -> Self {
 17        Self {
 18            id: id.into(),
 19            state_type: PhantomData,
 20        }
 21    }
 22
 23    fn render(&mut self, _view: &mut S, cx: &mut ViewContext<S>) -> impl Element<ViewState = S> {
 24        let color = ThemeColor::new(cx);
 25
 26        v_stack()
 27            .id(self.id.clone())
 28            .h_full()
 29            .bg(color.surface)
 30            .child(
 31                v_stack()
 32                    .id("crdb")
 33                    .w_full()
 34                    .overflow_y_scroll()
 35                    .child(
 36                        div().pb_1().border_color(color.border).border_b().child(
 37                            List::new(static_collab_panel_current_call())
 38                                .header(
 39                                    ListHeader::new("CRDB")
 40                                        .left_icon(Icon::Hash.into())
 41                                        .toggle(ToggleState::Toggled),
 42                                )
 43                                .toggle(ToggleState::Toggled),
 44                        ),
 45                    )
 46                    .child(
 47                        v_stack().id("channels").py_1().child(
 48                            List::new(static_collab_panel_channels())
 49                                .header(ListHeader::new("CHANNELS").toggle(ToggleState::Toggled))
 50                                .empty_message("No channels yet. Add a channel to get started.")
 51                                .toggle(ToggleState::Toggled),
 52                        ),
 53                    )
 54                    .child(
 55                        v_stack().id("contacts-online").py_1().child(
 56                            List::new(static_collab_panel_current_call())
 57                                .header(
 58                                    ListHeader::new("CONTACTS – ONLINE")
 59                                        .toggle(ToggleState::Toggled),
 60                                )
 61                                .toggle(ToggleState::Toggled),
 62                        ),
 63                    )
 64                    .child(
 65                        v_stack().id("contacts-offline").py_1().child(
 66                            List::new(static_collab_panel_current_call())
 67                                .header(
 68                                    ListHeader::new("CONTACTS – OFFLINE")
 69                                        .toggle(ToggleState::NotToggled),
 70                                )
 71                                .toggle(ToggleState::NotToggled),
 72                        ),
 73                    ),
 74            )
 75            .child(
 76                div()
 77                    .h_7()
 78                    .px_2()
 79                    .border_t()
 80                    .border_color(color.border)
 81                    .flex()
 82                    .items_center()
 83                    .child(
 84                        div()
 85                            .text_sm()
 86                            .text_color(color.text_placeholder)
 87                            .child("Find..."),
 88                    ),
 89            )
 90    }
 91
 92    fn list_section_header(
 93        &self,
 94        label: impl Into<SharedString>,
 95        expanded: bool,
 96        cx: &WindowContext,
 97    ) -> impl Element<ViewState = S> {
 98        let color = ThemeColor::new(cx);
 99        div()
100            .h_7()
101            .px_2()
102            .flex()
103            .justify_between()
104            .items_center()
105            .child(div().flex().gap_1().text_sm().child(label.into()))
106            .child(
107                div().flex().h_full().gap_1().items_center().child(
108                    svg()
109                        .path(if expanded {
110                            "icons/caret_down.svg"
111                        } else {
112                            "icons/caret_up.svg"
113                        })
114                        .w_3p5()
115                        .h_3p5()
116                        .text_color(color.icon_muted),
117                ),
118            )
119    }
120
121    fn list_item(
122        &self,
123        avatar_uri: impl Into<SharedString>,
124        label: impl Into<SharedString>,
125        cx: &WindowContext,
126    ) -> impl Element<ViewState = S> {
127        let color = ThemeColor::new(cx);
128
129        div()
130            .id("list_item")
131            .h_7()
132            .px_2()
133            .flex()
134            .items_center()
135            .hover(|style| style.bg(color.ghost_element_hover))
136            .active(|style| style.bg(color.ghost_element_active))
137            .child(
138                div()
139                    .flex()
140                    .items_center()
141                    .gap_1()
142                    .text_sm()
143                    .child(
144                        img()
145                            .uri(avatar_uri)
146                            .size_3p5()
147                            .rounded_full()
148                            .bg(color.image_fallback_background),
149                    )
150                    .child(label.into()),
151            )
152    }
153}
154
155#[cfg(feature = "stories")]
156pub use stories::*;
157
158#[cfg(feature = "stories")]
159mod stories {
160    use crate::Story;
161
162    use super::*;
163
164    #[derive(Element)]
165    pub struct CollabPanelStory<S: 'static + Send + Sync> {
166        state_type: PhantomData<S>,
167    }
168
169    impl<S: 'static + Send + Sync> CollabPanelStory<S> {
170        pub fn new() -> Self {
171            Self {
172                state_type: PhantomData,
173            }
174        }
175
176        fn render(
177            &mut self,
178            _view: &mut S,
179            cx: &mut ViewContext<S>,
180        ) -> impl Element<ViewState = S> {
181            Story::container(cx)
182                .child(Story::title_for::<_, CollabPanel<S>>(cx))
183                .child(Story::label(cx, "Default"))
184                .child(CollabPanel::new("collab-panel"))
185        }
186    }
187}