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::{prelude::*, *};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
h_flex,
|
h_flex,
|
||||||
|
input::TextInput,
|
||||||
sidebar::{Sidebar, SidebarGroup, SidebarHeader, SidebarMenu, SidebarMenuItem},
|
sidebar::{Sidebar, SidebarGroup, SidebarHeader, SidebarMenu, SidebarMenuItem},
|
||||||
v_flex, ActiveTheme as _, Icon, IconName,
|
v_flex, ActiveTheme as _, Icon, IconName,
|
||||||
};
|
};
|
||||||
|
|
@ -8,13 +9,26 @@ use story::*;
|
||||||
|
|
||||||
pub struct Gallery {
|
pub struct Gallery {
|
||||||
stories: Vec<(&'static str, Vec<Entity<StoryContainer>>)>,
|
stories: Vec<(&'static str, Vec<Entity<StoryContainer>>)>,
|
||||||
active_group_index: usize,
|
active_group_index: Option<usize>,
|
||||||
active_index: usize,
|
active_index: Option<usize>,
|
||||||
collapsed: bool,
|
collapsed: bool,
|
||||||
|
search_input: Entity<TextInput>,
|
||||||
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gallery {
|
impl Gallery {
|
||||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
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![
|
let stories = vec![
|
||||||
(
|
(
|
||||||
"Getting Started",
|
"Getting Started",
|
||||||
|
|
@ -61,10 +75,12 @@ impl Gallery {
|
||||||
];
|
];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
search_input,
|
||||||
stories,
|
stories,
|
||||||
active_group_index: 0,
|
active_group_index: Some(0),
|
||||||
active_index: 0,
|
active_index: Some(0),
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
_subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,11 +91,37 @@ impl Gallery {
|
||||||
|
|
||||||
impl Render for Gallery {
|
impl Render for Gallery {
|
||||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let active_group = &self.stories[self.active_group_index];
|
let query = self.search_input.read(cx).text().trim().to_lowercase();
|
||||||
let stories = &active_group.1;
|
|
||||||
let active_story = stories[self.active_index].clone();
|
let stories: Vec<_> = self
|
||||||
let story_name = active_story.read(cx).name.clone();
|
.stories
|
||||||
let description = active_story.read(cx).description.clone();
|
.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()
|
h_flex()
|
||||||
.id("gallery-container")
|
.id("gallery-container")
|
||||||
|
|
@ -88,56 +130,75 @@ impl Render for Gallery {
|
||||||
Sidebar::left()
|
Sidebar::left()
|
||||||
.collapsed(self.collapsed)
|
.collapsed(self.collapsed)
|
||||||
.header(
|
.header(
|
||||||
SidebarHeader::new()
|
v_flex()
|
||||||
.w_full()
|
.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(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.bg(cx.theme().sidebar_border)
|
||||||
.items_center()
|
.px_1()
|
||||||
.justify_center()
|
.rounded_full()
|
||||||
.rounded(cx.theme().radius)
|
.flex_1()
|
||||||
.bg(cx.theme().primary)
|
.mx_1()
|
||||||
.text_color(cx.theme().primary_foreground)
|
.child(self.search_input.clone()),
|
||||||
.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()),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.children(self.stories.iter().enumerate().map(
|
.children(stories.clone().into_iter().enumerate().map(
|
||||||
|(group_ix, (group_name, stories))| {
|
|(group_ix, (group_name, sub_stories))| {
|
||||||
SidebarGroup::new(*group_name).child(SidebarMenu::new().children(
|
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())
|
SidebarMenuItem::new(story.read(cx).name.clone())
|
||||||
.active(
|
.active(
|
||||||
self.active_group_index == group_ix
|
self.active_group_index == Some(group_ix)
|
||||||
&& self.active_index == ix,
|
&& self.active_index == Some(ix),
|
||||||
)
|
)
|
||||||
.on_click(cx.listener(
|
.on_click(cx.listener(
|
||||||
move |this, _: &ClickEvent, _, cx| {
|
move |this, _: &ClickEvent, _, cx| {
|
||||||
this.active_group_index = group_ix;
|
this.active_group_index = Some(group_ix);
|
||||||
this.active_index = ix;
|
this.active_index = Some(ix);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|
@ -175,7 +236,9 @@ impl Render for Gallery {
|
||||||
.id("story")
|
.id("story")
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.overflow_y_scroll()
|
.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")
|
.id("suffix")
|
||||||
.absolute()
|
.absolute()
|
||||||
.gap(gap_x)
|
.gap(gap_x)
|
||||||
.bg(bg)
|
.when(self.appearance, |this| this.bg(bg))
|
||||||
.items_center()
|
.items_center()
|
||||||
.when(suffix.is_none(), |this| this.pr_1())
|
.when(suffix.is_none(), |this| this.pr_1())
|
||||||
.right_0()
|
.right_0()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue