gallery: Add to support search stories. (#802)
<img width="1196" alt="image" src="https://github.com/user-attachments/assets/daa3f47a-0c4d-404c-a673-8e18880e8f6c" />
This commit is contained in:
parent
be63d9701c
commit
16eb0f304b
2 changed files with 114 additions and 51 deletions
|
|
@ -1,6 +1,7 @@
|
|||
use gpui::{prelude::*, *};
|
||||
use gpui_component::{
|
||||
h_flex,
|
||||
input::TextInput,
|
||||
sidebar::{Sidebar, SidebarGroup, SidebarHeader, SidebarMenu, SidebarMenuItem},
|
||||
v_flex, ActiveTheme as _, Icon, IconName,
|
||||
};
|
||||
|
|
@ -8,13 +9,26 @@ use story::*;
|
|||
|
||||
pub struct Gallery {
|
||||
stories: Vec<(&'static str, Vec<Entity<StoryContainer>>)>,
|
||||
active_group_index: usize,
|
||||
active_index: usize,
|
||||
active_group_index: Option<usize>,
|
||||
active_index: Option<usize>,
|
||||
collapsed: bool,
|
||||
search_input: Entity<TextInput>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
impl Gallery {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let search_input = cx.new(|cx| {
|
||||
TextInput::new(window, cx)
|
||||
.appearance(false)
|
||||
.cleanable()
|
||||
.placeholder("Search...")
|
||||
});
|
||||
let _subscriptions = vec![cx.subscribe(&search_input, |this, _, _, cx| {
|
||||
this.active_group_index = None;
|
||||
this.active_index = None;
|
||||
cx.notify()
|
||||
})];
|
||||
let stories = vec![
|
||||
(
|
||||
"Getting Started",
|
||||
|
|
@ -61,10 +75,12 @@ impl Gallery {
|
|||
];
|
||||
|
||||
Self {
|
||||
search_input,
|
||||
stories,
|
||||
active_group_index: 0,
|
||||
active_index: 0,
|
||||
active_group_index: Some(0),
|
||||
active_index: Some(0),
|
||||
collapsed: false,
|
||||
_subscriptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,11 +91,37 @@ impl Gallery {
|
|||
|
||||
impl Render for Gallery {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let active_group = &self.stories[self.active_group_index];
|
||||
let stories = &active_group.1;
|
||||
let active_story = stories[self.active_index].clone();
|
||||
let story_name = active_story.read(cx).name.clone();
|
||||
let description = active_story.read(cx).description.clone();
|
||||
let query = self.search_input.read(cx).text().trim().to_lowercase();
|
||||
|
||||
let stories: Vec<_> = self
|
||||
.stories
|
||||
.iter()
|
||||
.filter_map(|(name, items)| {
|
||||
let filtered_items: Vec<_> = items
|
||||
.iter()
|
||||
.filter(|story| story.read(cx).name.to_lowercase().contains(&query))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if !filtered_items.is_empty() {
|
||||
Some((name, filtered_items))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let active_group = self.active_group_index.and_then(|index| stories.get(index));
|
||||
let active_story = self
|
||||
.active_index
|
||||
.and(active_group)
|
||||
.and_then(|group| group.1.get(self.active_index.unwrap()));
|
||||
let (story_name, description) =
|
||||
if let Some(story) = active_story.as_ref().map(|story| story.read(cx)) {
|
||||
(story.name.clone(), story.description.clone())
|
||||
} else {
|
||||
("".into(), "".into())
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.id("gallery-container")
|
||||
|
|
@ -88,56 +130,75 @@ impl Render for Gallery {
|
|||
Sidebar::left()
|
||||
.collapsed(self.collapsed)
|
||||
.header(
|
||||
SidebarHeader::new()
|
||||
v_flex()
|
||||
.w_full()
|
||||
.gap_4()
|
||||
.child(
|
||||
SidebarHeader::new()
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().primary)
|
||||
.text_color(cx.theme().primary_foreground)
|
||||
.size_8()
|
||||
.flex_shrink_0()
|
||||
.when(!self.collapsed, |this| {
|
||||
this.child(Icon::new(IconName::GalleryVerticalEnd))
|
||||
})
|
||||
.when(self.collapsed, |this| {
|
||||
this.size_4()
|
||||
.bg(cx.theme().transparent)
|
||||
.text_color(cx.theme().foreground)
|
||||
.child(Icon::new(IconName::GalleryVerticalEnd))
|
||||
})
|
||||
.rounded_lg(),
|
||||
)
|
||||
.when(!self.collapsed, |this| {
|
||||
this.child(
|
||||
v_flex()
|
||||
.gap_0()
|
||||
.text_sm()
|
||||
.flex_1()
|
||||
.line_height(relative(1.25))
|
||||
.overflow_hidden()
|
||||
.text_ellipsis()
|
||||
.child("GPUI Component")
|
||||
.child(
|
||||
div()
|
||||
.text_color(cx.theme().muted_foreground)
|
||||
.child("Gallery")
|
||||
.text_xs(),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().primary)
|
||||
.text_color(cx.theme().primary_foreground)
|
||||
.size_8()
|
||||
.flex_shrink_0()
|
||||
.when(!self.collapsed, |this| {
|
||||
this.child(Icon::new(IconName::GalleryVerticalEnd))
|
||||
})
|
||||
.when(self.collapsed, |this| {
|
||||
this.size_4()
|
||||
.bg(cx.theme().transparent)
|
||||
.text_color(cx.theme().foreground)
|
||||
.child(Icon::new(IconName::GalleryVerticalEnd))
|
||||
})
|
||||
.rounded_lg(),
|
||||
)
|
||||
.when(!self.collapsed, |this| {
|
||||
this.child(
|
||||
v_flex()
|
||||
.gap_0()
|
||||
.text_sm()
|
||||
.flex_1()
|
||||
.line_height(relative(1.25))
|
||||
.overflow_hidden()
|
||||
.text_ellipsis()
|
||||
.child("GPUI Component")
|
||||
.child(div().child("Gallery").text_xs()),
|
||||
)
|
||||
}),
|
||||
.bg(cx.theme().sidebar_border)
|
||||
.px_1()
|
||||
.rounded_full()
|
||||
.flex_1()
|
||||
.mx_1()
|
||||
.child(self.search_input.clone()),
|
||||
),
|
||||
)
|
||||
.children(self.stories.iter().enumerate().map(
|
||||
|(group_ix, (group_name, stories))| {
|
||||
.children(stories.clone().into_iter().enumerate().map(
|
||||
|(group_ix, (group_name, sub_stories))| {
|
||||
SidebarGroup::new(*group_name).child(SidebarMenu::new().children(
|
||||
stories.iter().enumerate().map(|(ix, story)| {
|
||||
sub_stories.iter().enumerate().map(|(ix, story)| {
|
||||
SidebarMenuItem::new(story.read(cx).name.clone())
|
||||
.active(
|
||||
self.active_group_index == group_ix
|
||||
&& self.active_index == ix,
|
||||
self.active_group_index == Some(group_ix)
|
||||
&& self.active_index == Some(ix),
|
||||
)
|
||||
.on_click(cx.listener(
|
||||
move |this, _: &ClickEvent, _, cx| {
|
||||
this.active_group_index = group_ix;
|
||||
this.active_index = ix;
|
||||
this.active_group_index = Some(group_ix);
|
||||
this.active_index = Some(ix);
|
||||
cx.notify();
|
||||
},
|
||||
))
|
||||
|
|
@ -175,7 +236,9 @@ impl Render for Gallery {
|
|||
.id("story")
|
||||
.flex_1()
|
||||
.overflow_y_scroll()
|
||||
.child(active_story),
|
||||
.when_some(active_story, |this, active_story| {
|
||||
this.child(active_story.clone())
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1859,7 +1859,7 @@ impl Render for TextInput {
|
|||
.id("suffix")
|
||||
.absolute()
|
||||
.gap(gap_x)
|
||||
.bg(bg)
|
||||
.when(self.appearance, |this| this.bg(bg))
|
||||
.items_center()
|
||||
.when(suffix.is_none(), |this| this.pr_1())
|
||||
.right_0()
|
||||
|
|
|
|||
Loading…
Reference in a new issue