This PR renames the `rounded_md` style method to `rounded_sm`. Follow up to https://github.com/zed-industries/zed/pull/26221, which freed up the `rounded_sm` name. Release Notes: - N/A
233 lines
7.4 KiB
Rust
233 lines
7.4 KiB
Rust
//! # Component Preview
|
|
//!
|
|
//! A view for exploring Zed components.
|
|
|
|
use component::{components, ComponentMetadata};
|
|
use gpui::{list, prelude::*, uniform_list, App, EventEmitter, FocusHandle, Focusable, Window};
|
|
use gpui::{ListState, ScrollHandle, UniformListScrollHandle};
|
|
use ui::{prelude::*, ListItem};
|
|
|
|
use workspace::{item::ItemEvent, Item, Workspace, WorkspaceId};
|
|
|
|
pub fn init(cx: &mut App) {
|
|
cx.observe_new(|workspace: &mut Workspace, _, _cx| {
|
|
workspace.register_action(
|
|
|workspace, _: &workspace::OpenComponentPreview, window, cx| {
|
|
let component_preview = cx.new(|cx| ComponentPreview::new(window, cx));
|
|
workspace.add_item_to_active_pane(
|
|
Box::new(component_preview),
|
|
None,
|
|
true,
|
|
window,
|
|
cx,
|
|
)
|
|
},
|
|
);
|
|
})
|
|
.detach();
|
|
}
|
|
|
|
struct ComponentPreview {
|
|
focus_handle: FocusHandle,
|
|
_view_scroll_handle: ScrollHandle,
|
|
nav_scroll_handle: UniformListScrollHandle,
|
|
components: Vec<ComponentMetadata>,
|
|
component_list: ListState,
|
|
selected_index: usize,
|
|
}
|
|
|
|
impl ComponentPreview {
|
|
pub fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
|
let components = components().all_sorted();
|
|
let initial_length = components.len();
|
|
|
|
let component_list = ListState::new(initial_length, gpui::ListAlignment::Top, px(500.0), {
|
|
let this = cx.entity().downgrade();
|
|
move |ix, window: &mut Window, cx: &mut App| {
|
|
this.update(cx, |this, cx| {
|
|
this.render_preview(ix, window, cx).into_any_element()
|
|
})
|
|
.unwrap()
|
|
}
|
|
});
|
|
|
|
Self {
|
|
focus_handle: cx.focus_handle(),
|
|
_view_scroll_handle: ScrollHandle::new(),
|
|
nav_scroll_handle: UniformListScrollHandle::new(),
|
|
components,
|
|
component_list,
|
|
selected_index: 0,
|
|
}
|
|
}
|
|
|
|
fn scroll_to_preview(&mut self, ix: usize, cx: &mut Context<Self>) {
|
|
self.component_list.scroll_to_reveal_item(ix);
|
|
self.selected_index = ix;
|
|
cx.notify();
|
|
}
|
|
|
|
fn get_component(&self, ix: usize) -> ComponentMetadata {
|
|
self.components[ix].clone()
|
|
}
|
|
|
|
fn render_sidebar_entry(
|
|
&self,
|
|
ix: usize,
|
|
selected: bool,
|
|
cx: &Context<Self>,
|
|
) -> impl IntoElement {
|
|
let component = self.get_component(ix);
|
|
|
|
ListItem::new(ix)
|
|
.child(Label::new(component.name().clone()).color(Color::Default))
|
|
.selectable(true)
|
|
.toggle_state(selected)
|
|
.inset(true)
|
|
.on_click(cx.listener(move |this, _, _, cx| {
|
|
this.scroll_to_preview(ix, cx);
|
|
}))
|
|
}
|
|
|
|
fn render_preview(
|
|
&self,
|
|
ix: usize,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) -> impl IntoElement {
|
|
let component = self.get_component(ix);
|
|
|
|
let name = component.name();
|
|
let scope = component.scope();
|
|
|
|
let description = component.description();
|
|
|
|
v_flex()
|
|
.py_2()
|
|
.child(
|
|
v_flex()
|
|
.border_1()
|
|
.border_color(cx.theme().colors().border)
|
|
.rounded_sm()
|
|
.w_full()
|
|
.gap_4()
|
|
.py_4()
|
|
.px_6()
|
|
.flex_none()
|
|
.child(
|
|
v_flex()
|
|
.gap_1()
|
|
.child(
|
|
h_flex()
|
|
.gap_1()
|
|
.text_xl()
|
|
.child(div().child(name))
|
|
.when_some(scope, |this, scope| {
|
|
this.child(div().opacity(0.5).child(format!("({})", scope)))
|
|
}),
|
|
)
|
|
.when_some(description, |this, description| {
|
|
this.child(
|
|
div()
|
|
.text_ui_sm(cx)
|
|
.text_color(cx.theme().colors().text_muted)
|
|
.max_w(px(600.0))
|
|
.child(description),
|
|
)
|
|
}),
|
|
)
|
|
.when_some(component.preview(), |this, preview| {
|
|
this.child(preview(window, cx))
|
|
}),
|
|
)
|
|
.into_any_element()
|
|
}
|
|
}
|
|
|
|
impl Render for ComponentPreview {
|
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
|
h_flex()
|
|
.id("component-preview")
|
|
.key_context("ComponentPreview")
|
|
.items_start()
|
|
.overflow_hidden()
|
|
.size_full()
|
|
.track_focus(&self.focus_handle)
|
|
.px_2()
|
|
.bg(cx.theme().colors().editor_background)
|
|
.child(
|
|
uniform_list(
|
|
cx.entity().clone(),
|
|
"component-nav",
|
|
self.components.len(),
|
|
move |this, range, _window, cx| {
|
|
range
|
|
.map(|ix| this.render_sidebar_entry(ix, ix == this.selected_index, cx))
|
|
.collect()
|
|
},
|
|
)
|
|
.track_scroll(self.nav_scroll_handle.clone())
|
|
.pt_4()
|
|
.w(px(240.))
|
|
.h_full()
|
|
.flex_grow(),
|
|
)
|
|
.child(
|
|
v_flex()
|
|
.id("component-list")
|
|
.px_8()
|
|
.pt_4()
|
|
.size_full()
|
|
.child(
|
|
list(self.component_list.clone())
|
|
.flex_grow()
|
|
.with_sizing_behavior(gpui::ListSizingBehavior::Auto),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl EventEmitter<ItemEvent> for ComponentPreview {}
|
|
|
|
impl Focusable for ComponentPreview {
|
|
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
|
self.focus_handle.clone()
|
|
}
|
|
}
|
|
|
|
impl Item for ComponentPreview {
|
|
type Event = ItemEvent;
|
|
|
|
fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option<SharedString> {
|
|
Some("Component Preview".into())
|
|
}
|
|
|
|
fn telemetry_event_text(&self) -> Option<&'static str> {
|
|
None
|
|
}
|
|
|
|
fn show_toolbar(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn clone_on_split(
|
|
&self,
|
|
_workspace_id: Option<WorkspaceId>,
|
|
window: &mut Window,
|
|
cx: &mut Context<Self>,
|
|
) -> Option<gpui::Entity<Self>>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
Some(cx.new(|cx| Self::new(window, cx)))
|
|
}
|
|
|
|
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
|
|
f(*event)
|
|
}
|
|
}
|
|
|
|
// TODO: impl serializable item for component preview so it will restore with the workspace
|
|
// ref: https://github.com/zed-industries/zed/blob/32201ac70a501e63dfa2ade9c00f85aea2d4dd94/crates/image_viewer/src/image_viewer.rs#L199
|
|
// Use `ImageViewer` as a model for how to do it, except it'll be even simpler
|