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