component: Component crate cleanup (#29967)
This PR further organizes and documents the component crate. It: - Simplifies the component registry - Gives access to `ComponentMetadata` sooner - Enables lookup by id in preview extension implementations (`ComponentId` -> `ComponentMetadata`) - Should slightly improve the performance of ComponentPreview It also brings component statuses to the Component trait:    Release Notes: - N/A
This commit is contained in:
@@ -5,12 +5,13 @@
|
||||
mod persistence;
|
||||
mod preview_support;
|
||||
|
||||
use std::iter::Iterator;
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::iter::Iterator;
|
||||
|
||||
use agent::{ActiveThread, TextThreadStore, ThreadStore};
|
||||
use client::UserStore;
|
||||
use component::{ComponentId, ComponentMetadata, components};
|
||||
use component::{ComponentId, ComponentMetadata, ComponentStatus, components};
|
||||
use gpui::{
|
||||
App, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, Window, list, prelude::*,
|
||||
};
|
||||
@@ -25,7 +26,7 @@ use preview_support::active_thread::{
|
||||
load_preview_text_thread_store, load_preview_thread_store, static_active_thread,
|
||||
};
|
||||
use project::Project;
|
||||
use ui::{Divider, HighlightedLabel, ListItem, ListSubHeader, prelude::*};
|
||||
use ui::{ButtonLike, Divider, HighlightedLabel, ListItem, ListSubHeader, Tooltip, prelude::*};
|
||||
use ui_input::SingleLineInput;
|
||||
use util::ResultExt as _;
|
||||
use workspace::{AppState, ItemId, SerializableItem, delete_unloaded_items};
|
||||
@@ -104,26 +105,24 @@ enum PreviewPage {
|
||||
}
|
||||
|
||||
struct ComponentPreview {
|
||||
workspace_id: Option<WorkspaceId>,
|
||||
focus_handle: FocusHandle,
|
||||
_view_scroll_handle: ScrollHandle,
|
||||
nav_scroll_handle: UniformListScrollHandle,
|
||||
component_map: HashMap<ComponentId, ComponentMetadata>,
|
||||
active_page: PreviewPage,
|
||||
components: Vec<ComponentMetadata>,
|
||||
active_thread: Option<Entity<ActiveThread>>,
|
||||
component_list: ListState,
|
||||
component_map: HashMap<ComponentId, ComponentMetadata>,
|
||||
components: Vec<ComponentMetadata>,
|
||||
cursor_index: usize,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
project: Entity<Project>,
|
||||
user_store: Entity<UserStore>,
|
||||
filter_editor: Entity<SingleLineInput>,
|
||||
filter_text: String,
|
||||
|
||||
// preview support
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
focus_handle: FocusHandle,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
nav_scroll_handle: UniformListScrollHandle,
|
||||
project: Entity<Project>,
|
||||
text_thread_store: Option<Entity<TextThreadStore>>,
|
||||
active_thread: Option<Entity<ActiveThread>>,
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
user_store: Entity<UserStore>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
workspace_id: Option<WorkspaceId>,
|
||||
_view_scroll_handle: ScrollHandle,
|
||||
}
|
||||
|
||||
impl ComponentPreview {
|
||||
@@ -164,7 +163,8 @@ impl ComponentPreview {
|
||||
})
|
||||
.detach();
|
||||
|
||||
let sorted_components = components().all_sorted();
|
||||
let component_registry = Arc::new(components());
|
||||
let sorted_components = component_registry.sorted_components();
|
||||
let selected_index = selected_index.into().unwrap_or(0);
|
||||
let active_page = active_page.unwrap_or(PreviewPage::AllComponents);
|
||||
let filter_editor =
|
||||
@@ -188,24 +188,24 @@ impl ComponentPreview {
|
||||
);
|
||||
|
||||
let mut component_preview = Self {
|
||||
workspace_id: None,
|
||||
focus_handle: cx.focus_handle(),
|
||||
_view_scroll_handle: ScrollHandle::new(),
|
||||
nav_scroll_handle: UniformListScrollHandle::new(),
|
||||
language_registry,
|
||||
user_store,
|
||||
workspace,
|
||||
project,
|
||||
active_page,
|
||||
component_map: components().0,
|
||||
components: sorted_components,
|
||||
active_thread: None,
|
||||
component_list,
|
||||
component_map: component_registry.component_map(),
|
||||
components: sorted_components,
|
||||
cursor_index: selected_index,
|
||||
filter_editor,
|
||||
filter_text: String::new(),
|
||||
thread_store: None,
|
||||
focus_handle: cx.focus_handle(),
|
||||
language_registry,
|
||||
nav_scroll_handle: UniformListScrollHandle::new(),
|
||||
project,
|
||||
text_thread_store: None,
|
||||
active_thread: None,
|
||||
thread_store: None,
|
||||
user_store,
|
||||
workspace,
|
||||
workspace_id: None,
|
||||
_view_scroll_handle: ScrollHandle::new(),
|
||||
};
|
||||
|
||||
if component_preview.cursor_index > 0 {
|
||||
@@ -412,6 +412,88 @@ impl ComponentPreview {
|
||||
entries
|
||||
}
|
||||
|
||||
fn update_component_list(&mut self, cx: &mut Context<Self>) {
|
||||
let entries = self.scope_ordered_entries();
|
||||
let new_len = entries.len();
|
||||
let weak_entity = cx.entity().downgrade();
|
||||
|
||||
if new_len > 0 {
|
||||
self.nav_scroll_handle
|
||||
.scroll_to_item(0, ScrollStrategy::Top);
|
||||
}
|
||||
|
||||
let filtered_components = self.filtered_components();
|
||||
|
||||
if !self.filter_text.is_empty() && !matches!(self.active_page, PreviewPage::AllComponents) {
|
||||
if let PreviewPage::Component(ref component_id) = self.active_page {
|
||||
let component_still_visible = filtered_components
|
||||
.iter()
|
||||
.any(|component| component.id() == *component_id);
|
||||
|
||||
if !component_still_visible {
|
||||
if !filtered_components.is_empty() {
|
||||
let first_component = &filtered_components[0];
|
||||
self.set_active_page(PreviewPage::Component(first_component.id()), cx);
|
||||
} else {
|
||||
self.set_active_page(PreviewPage::AllComponents, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.component_list = ListState::new(
|
||||
filtered_components.len(),
|
||||
gpui::ListAlignment::Top,
|
||||
px(1500.0),
|
||||
{
|
||||
let components = filtered_components.clone();
|
||||
let this = cx.entity().downgrade();
|
||||
move |ix, window: &mut Window, cx: &mut App| {
|
||||
if ix >= components.len() {
|
||||
return div().w_full().h_0().into_any_element();
|
||||
}
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
let component = &components[ix];
|
||||
this.render_preview(component, window, cx)
|
||||
.into_any_element()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let new_list = ListState::new(
|
||||
new_len,
|
||||
gpui::ListAlignment::Top,
|
||||
px(1500.0),
|
||||
move |ix, window, cx| {
|
||||
if ix >= entries.len() {
|
||||
return div().w_full().h_0().into_any_element();
|
||||
}
|
||||
|
||||
let entry = &entries[ix];
|
||||
|
||||
weak_entity
|
||||
.update(cx, |this, cx| match entry {
|
||||
PreviewEntry::Component(component, _) => this
|
||||
.render_preview(component, window, cx)
|
||||
.into_any_element(),
|
||||
PreviewEntry::SectionHeader(shared_string) => this
|
||||
.render_scope_header(ix, shared_string.clone(), window, cx)
|
||||
.into_any_element(),
|
||||
PreviewEntry::AllComponents => div().w_full().h_0().into_any_element(),
|
||||
PreviewEntry::ActiveThread => div().w_full().h_0().into_any_element(),
|
||||
PreviewEntry::Separator => div().w_full().h_0().into_any_element(),
|
||||
})
|
||||
.unwrap()
|
||||
},
|
||||
);
|
||||
|
||||
self.component_list = new_list;
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
}
|
||||
|
||||
fn render_sidebar_entry(
|
||||
&self,
|
||||
ix: usize,
|
||||
@@ -495,88 +577,6 @@ impl ComponentPreview {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_component_list(&mut self, cx: &mut Context<Self>) {
|
||||
let entries = self.scope_ordered_entries();
|
||||
let new_len = entries.len();
|
||||
let weak_entity = cx.entity().downgrade();
|
||||
|
||||
if new_len > 0 {
|
||||
self.nav_scroll_handle
|
||||
.scroll_to_item(0, ScrollStrategy::Top);
|
||||
}
|
||||
|
||||
let filtered_components = self.filtered_components();
|
||||
|
||||
if !self.filter_text.is_empty() && !matches!(self.active_page, PreviewPage::AllComponents) {
|
||||
if let PreviewPage::Component(ref component_id) = self.active_page {
|
||||
let component_still_visible = filtered_components
|
||||
.iter()
|
||||
.any(|component| component.id() == *component_id);
|
||||
|
||||
if !component_still_visible {
|
||||
if !filtered_components.is_empty() {
|
||||
let first_component = &filtered_components[0];
|
||||
self.set_active_page(PreviewPage::Component(first_component.id()), cx);
|
||||
} else {
|
||||
self.set_active_page(PreviewPage::AllComponents, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.component_list = ListState::new(
|
||||
filtered_components.len(),
|
||||
gpui::ListAlignment::Top,
|
||||
px(1500.0),
|
||||
{
|
||||
let components = filtered_components.clone();
|
||||
let this = cx.entity().downgrade();
|
||||
move |ix, window: &mut Window, cx: &mut App| {
|
||||
if ix >= components.len() {
|
||||
return div().w_full().h_0().into_any_element();
|
||||
}
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
let component = &components[ix];
|
||||
this.render_preview(component, window, cx)
|
||||
.into_any_element()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let new_list = ListState::new(
|
||||
new_len,
|
||||
gpui::ListAlignment::Top,
|
||||
px(1500.0),
|
||||
move |ix, window, cx| {
|
||||
if ix >= entries.len() {
|
||||
return div().w_full().h_0().into_any_element();
|
||||
}
|
||||
|
||||
let entry = &entries[ix];
|
||||
|
||||
weak_entity
|
||||
.update(cx, |this, cx| match entry {
|
||||
PreviewEntry::Component(component, _) => this
|
||||
.render_preview(component, window, cx)
|
||||
.into_any_element(),
|
||||
PreviewEntry::SectionHeader(shared_string) => this
|
||||
.render_scope_header(ix, shared_string.clone(), window, cx)
|
||||
.into_any_element(),
|
||||
PreviewEntry::AllComponents => div().w_full().h_0().into_any_element(),
|
||||
PreviewEntry::ActiveThread => div().w_full().h_0().into_any_element(),
|
||||
PreviewEntry::Separator => div().w_full().h_0().into_any_element(),
|
||||
})
|
||||
.unwrap()
|
||||
},
|
||||
);
|
||||
|
||||
self.component_list = new_list;
|
||||
cx.emit(ItemEvent::UpdateTab);
|
||||
}
|
||||
|
||||
fn render_scope_header(
|
||||
&self,
|
||||
_ix: usize,
|
||||
@@ -695,7 +695,7 @@ impl ComponentPreview {
|
||||
if let Some(component) = component {
|
||||
v_flex()
|
||||
.id("render-component-page")
|
||||
.size_full()
|
||||
.flex_1()
|
||||
.child(ComponentPreviewPage::new(
|
||||
component.clone(),
|
||||
self.workspace.clone(),
|
||||
@@ -971,7 +971,7 @@ impl SerializableItem for ComponentPreview {
|
||||
} else {
|
||||
let component_str = deserialized_active_page.0;
|
||||
let component_registry = components();
|
||||
let all_components = component_registry.all();
|
||||
let all_components = component_registry.components();
|
||||
let found_component = all_components.iter().find(|c| c.id().0 == component_str);
|
||||
|
||||
if let Some(component) = found_component {
|
||||
@@ -1065,6 +1065,43 @@ impl ComponentPreviewPage {
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the component status when it would be useful
|
||||
///
|
||||
/// Doesn't render if the component is `ComponentStatus::Live`
|
||||
/// as that is the default state
|
||||
fn render_component_status(&self, cx: &App) -> Option<impl IntoElement> {
|
||||
let status = self.component.status();
|
||||
let status_description = status.description().to_string();
|
||||
|
||||
let color = match status {
|
||||
ComponentStatus::Deprecated => Color::Error,
|
||||
ComponentStatus::EngineeringReady => Color::Info,
|
||||
ComponentStatus::Live => Color::Success,
|
||||
ComponentStatus::WorkInProgress => Color::Warning,
|
||||
};
|
||||
|
||||
if status != ComponentStatus::Live {
|
||||
Some(
|
||||
ButtonLike::new("component_status")
|
||||
.child(
|
||||
div()
|
||||
.px_1p5()
|
||||
.rounded_sm()
|
||||
.bg(color.color(cx).alpha(0.12))
|
||||
.child(
|
||||
Label::new(status.clone().to_string())
|
||||
.size(LabelSize::Small)
|
||||
.color(color),
|
||||
),
|
||||
)
|
||||
.tooltip(Tooltip::text(status_description))
|
||||
.disabled(true),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn render_header(&self, _: &Window, cx: &App) -> impl IntoElement {
|
||||
v_flex()
|
||||
.px_12()
|
||||
@@ -1083,7 +1120,14 @@ impl ComponentPreviewPage {
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(
|
||||
Headline::new(self.component.scopeless_name()).size(HeadlineSize::XLarge),
|
||||
h_flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
Headline::new(self.component.scopeless_name())
|
||||
.size(HeadlineSize::XLarge),
|
||||
)
|
||||
.children(self.render_component_status(cx)),
|
||||
),
|
||||
)
|
||||
.when_some(self.component.description(), |this, description| {
|
||||
|
||||
Reference in New Issue
Block a user