collab_panel.rs

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