diff --git a/assets/icons/at-sign.svg b/assets/icons/at-sign.svg
new file mode 100644
index 0000000000..5adac38f62
--- /dev/null
+++ b/assets/icons/at-sign.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/bell-off.svg b/assets/icons/bell-off.svg
new file mode 100644
index 0000000000..db1021f2d3
--- /dev/null
+++ b/assets/icons/bell-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/bell-ring.svg b/assets/icons/bell-ring.svg
new file mode 100644
index 0000000000..da51fdc5be
--- /dev/null
+++ b/assets/icons/bell-ring.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/bell.svg b/assets/icons/bell.svg
index ea1c6dd42e..4c7d5472db 100644
--- a/assets/icons/bell.svg
+++ b/assets/icons/bell.svg
@@ -1,8 +1 @@
-
+
\ No newline at end of file
diff --git a/assets/icons/mail-open.svg b/assets/icons/mail-open.svg
new file mode 100644
index 0000000000..b63915bd73
--- /dev/null
+++ b/assets/icons/mail-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs
index ebc442afdc..ad7ec2214f 100644
--- a/crates/ui2/src/components/list.rs
+++ b/crates/ui2/src/components/list.rs
@@ -15,12 +15,20 @@ pub enum ListItemVariant {
Inset,
}
+pub enum ListHeaderMeta {
+ // TODO: These should be IconButtons
+ Tools(Vec),
+ // TODO: This should be a button
+ Button(Label),
+ Text(Label),
+}
+
#[derive(Component)]
pub struct ListHeader {
label: SharedString,
left_icon: Option,
+ meta: Option,
variant: ListItemVariant,
- state: InteractionState,
toggleable: Toggleable,
}
@@ -29,8 +37,8 @@ impl ListHeader {
Self {
label: label.into(),
left_icon: None,
+ meta: None,
variant: ListItemVariant::default(),
- state: InteractionState::default(),
toggleable: Toggleable::Toggleable(ToggleState::Toggled),
}
}
@@ -50,8 +58,8 @@ impl ListHeader {
self
}
- pub fn state(mut self, state: InteractionState) -> Self {
- self.state = state;
+ pub fn meta(mut self, meta: Option) -> Self {
+ self.meta = meta;
self
}
@@ -74,34 +82,37 @@ impl ListHeader {
}
}
- fn label_color(&self) -> LabelColor {
- match self.state {
- InteractionState::Disabled => LabelColor::Disabled,
- _ => Default::default(),
- }
- }
-
- fn icon_color(&self) -> IconColor {
- match self.state {
- InteractionState::Disabled => IconColor::Disabled,
- _ => Default::default(),
- }
- }
-
fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component {
let is_toggleable = self.toggleable != Toggleable::NotToggleable;
let is_toggled = self.toggleable.is_toggled();
let disclosure_control = self.disclosure_control();
+ let meta = match self.meta {
+ Some(ListHeaderMeta::Tools(icons)) => div().child(
+ h_stack()
+ .gap_2()
+ .items_center()
+ .children(icons.into_iter().map(|i| {
+ IconElement::new(i)
+ .color(IconColor::Muted)
+ .size(IconSize::Small)
+ })),
+ ),
+ Some(ListHeaderMeta::Button(label)) => div().child(label),
+ Some(ListHeaderMeta::Text(label)) => div().child(label),
+ None => div(),
+ };
+
h_stack()
.flex_1()
.w_full()
.bg(cx.theme().colors().surface)
- .when(self.state == InteractionState::Focused, |this| {
- this.border()
- .border_color(cx.theme().colors().border_focused)
- })
+ // TODO: Add focus state
+ // .when(self.state == InteractionState::Focused, |this| {
+ // this.border()
+ // .border_color(cx.theme().colors().border_focused)
+ // })
.relative()
.child(
div()
@@ -109,22 +120,28 @@ impl ListHeader {
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
.flex()
.flex_1()
+ .items_center()
+ .justify_between()
.w_full()
.gap_1()
- .items_center()
.child(
- div()
- .flex()
+ h_stack()
.gap_1()
- .items_center()
- .children(self.left_icon.map(|i| {
- IconElement::new(i)
- .color(IconColor::Muted)
- .size(IconSize::Small)
- }))
- .child(Label::new(self.label.clone()).color(LabelColor::Muted)),
+ .child(
+ div()
+ .flex()
+ .gap_1()
+ .items_center()
+ .children(self.left_icon.map(|i| {
+ IconElement::new(i)
+ .color(IconColor::Muted)
+ .size(IconSize::Small)
+ }))
+ .child(Label::new(self.label.clone()).color(LabelColor::Muted)),
+ )
+ .child(disclosure_control),
)
- .child(disclosure_control),
+ .child(meta),
)
}
}
@@ -593,6 +610,7 @@ impl List {
};
v_stack()
+ .w_full()
.py_1()
.children(self.header.map(|header| header.toggleable(self.toggleable)))
.child(list_content)
diff --git a/crates/ui2/src/components/notifications_panel.rs b/crates/ui2/src/components/notifications_panel.rs
index 10b0e07af6..c102a2cf57 100644
--- a/crates/ui2/src/components/notifications_panel.rs
+++ b/crates/ui2/src/components/notifications_panel.rs
@@ -1,4 +1,4 @@
-use crate::{prelude::*, static_new_notification_items, static_read_notification_items};
+use crate::{prelude::*, static_new_notification_items, Icon, ListHeaderMeta};
use crate::{List, ListHeader};
#[derive(Component)]
@@ -28,14 +28,16 @@ impl NotificationsPanel {
.overflow_y_scroll()
.child(
List::new(static_new_notification_items())
- .header(ListHeader::new("NEW").toggle(ToggleState::Toggled))
- .toggle(ToggleState::Toggled),
- )
- .child(
- List::new(static_read_notification_items())
- .header(ListHeader::new("EARLIER").toggle(ToggleState::Toggled))
- .empty_message("No new notifications")
- .toggle(ToggleState::Toggled),
+ .toggle(ToggleState::Toggled)
+ .header(
+ ListHeader::new("Notifications")
+ .toggle(ToggleState::Toggled)
+ .meta(Some(ListHeaderMeta::Tools(vec![
+ Icon::AtSign,
+ Icon::BellOff,
+ Icon::MailOpen,
+ ]))),
+ ),
),
)
}
diff --git a/crates/ui2/src/elements/icon.rs b/crates/ui2/src/elements/icon.rs
index f3d612562f..eef80cb5ad 100644
--- a/crates/ui2/src/elements/icon.rs
+++ b/crates/ui2/src/elements/icon.rs
@@ -40,7 +40,7 @@ impl IconColor {
}
}
-#[derive(Debug, Default, PartialEq, Copy, Clone, EnumIter)]
+#[derive(Debug, PartialEq, Copy, Clone, EnumIter)]
pub enum Icon {
Ai,
ArrowLeft,
@@ -67,7 +67,6 @@ pub enum Icon {
Folder,
FolderOpen,
FolderX,
- #[default]
Hash,
InlayHint,
MagicWand,
@@ -89,6 +88,11 @@ pub enum Icon {
XCircle,
Copilot,
Envelope,
+ Bell,
+ BellOff,
+ BellRing,
+ MailOpen,
+ AtSign,
}
impl Icon {
@@ -140,6 +144,11 @@ impl Icon {
Icon::XCircle => "icons/error.svg",
Icon::Copilot => "icons/copilot.svg",
Icon::Envelope => "icons/feedback.svg",
+ Icon::Bell => "icons/bell.svg",
+ Icon::BellOff => "icons/bell-off.svg",
+ Icon::BellRing => "icons/bell-ring.svg",
+ Icon::MailOpen => "icons/mail-open.svg",
+ Icon::AtSign => "icons/at-sign.svg",
}
}
}
diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs
index 7062c81954..ebbc89832c 100644
--- a/crates/ui2/src/static_data.rs
+++ b/crates/ui2/src/static_data.rs
@@ -7,9 +7,10 @@ use theme2::ActiveTheme;
use crate::{
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
- HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListItem,
- Livestream, MicStatus, ModifierKeys, PaletteItem, Player, PlayerCallStatus,
- PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState, VideoStatus,
+ HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, ListHeaderMeta,
+ ListItem, ListSubHeader, Livestream, MicStatus, ModifierKeys, PaletteItem, Player,
+ PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, ToggleState,
+ VideoStatus,
};
use crate::{HighlightedText, ListDetailsEntry};
@@ -327,25 +328,29 @@ pub fn static_players_with_call_status() -> Vec {
pub fn static_new_notification_items() -> Vec> {
vec![
- ListDetailsEntry::new("maxdeviant invited you to join a stream in #design.")
- .meta("4 people in stream."),
- ListDetailsEntry::new("nathansobo accepted your contact request."),
- ]
- .into_iter()
- .map(From::from)
- .collect()
-}
-
-pub fn static_read_notification_items() -> Vec> {
- vec![
- ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![
- Button::new("Decline"),
- Button::new("Accept").variant(crate::ButtonVariant::Filled),
- ]),
- ListDetailsEntry::new("maxdeviant invited you to a stream in #design.")
- .seen(true)
- .meta("This stream has ended."),
- ListDetailsEntry::new("as-cii accepted your contact request."),
+ ListItem::Header(ListSubHeader::new("New")),
+ ListItem::Details(
+ ListDetailsEntry::new("maxdeviant invited you to join a stream in #design.")
+ .meta("4 people in stream."),
+ ),
+ ListItem::Details(ListDetailsEntry::new(
+ "nathansobo accepted your contact request.",
+ )),
+ ListItem::Header(ListSubHeader::new("Earlier")),
+ ListItem::Details(
+ ListDetailsEntry::new("mikaylamaki added you as a contact.").actions(vec![
+ Button::new("Decline"),
+ Button::new("Accept").variant(crate::ButtonVariant::Filled),
+ ]),
+ ),
+ ListItem::Details(
+ ListDetailsEntry::new("maxdeviant invited you to a stream in #design.")
+ .seen(true)
+ .meta("This stream has ended."),
+ ),
+ ListItem::Details(ListDetailsEntry::new(
+ "as-cii accepted your contact request.",
+ )),
]
.into_iter()
.map(From::from)