Detailed changes
@@ -3329,6 +3329,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
+]
+
[[package]]
name = "convert_case"
version = "0.8.0"
@@ -4461,6 +4470,32 @@ dependencies = [
"workspace-hack",
]
+[[package]]
+name = "documented"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6db32f0995bc4553d2de888999075acd0dbeef75ba923503f6a724263dc6f3"
+dependencies = [
+ "documented-macros",
+ "phf",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "documented-macros"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a394bb35929b58f9a5fd418f7c6b17a4b616efcc1e53e6995ca123948f87e5fa"
+dependencies = [
+ "convert_case 0.6.0",
+ "itertools 0.13.0",
+ "optfield",
+ "proc-macro2",
+ "quote",
+ "strum",
+ "syn 2.0.100",
+]
+
[[package]]
name = "dotenvy"
version = "0.15.7"
@@ -7875,7 +7910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -9542,6 +9577,17 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "optfield"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa59f025cde9c698fcb4fcb3533db4621795374065bee908215263488f2d2a1d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
[[package]]
name = "option-ext"
version = "0.2.0"
@@ -15349,6 +15395,7 @@ version = "0.1.0"
dependencies = [
"chrono",
"component",
+ "documented",
"gpui",
"icons",
"itertools 0.14.0",
@@ -17614,6 +17661,7 @@ dependencies = [
"indexmap",
"inout",
"itertools 0.12.1",
+ "itertools 0.13.0",
"lazy_static",
"libc",
"libsqlite3-sys",
@@ -3,37 +3,62 @@ use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
use collections::HashMap;
-use gpui::{AnyElement, App, IntoElement, RenderOnce, SharedString, Window, div, prelude::*, px};
+use gpui::{
+ AnyElement, App, IntoElement, RenderOnce, SharedString, Window, div, pattern_slash, prelude::*,
+ px, rems,
+};
use linkme::distributed_slice;
use parking_lot::RwLock;
use theme::ActiveTheme;
pub trait Component {
- fn scope() -> Option<ComponentScope>;
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
fn name() -> &'static str {
std::any::type_name::<Self>()
}
+ /// Returns a name that the component should be sorted by.
+ ///
+ /// Implement this if the component should be sorted in an alternate order than its name.
+ ///
+ /// Example:
+ ///
+ /// For example, to group related components together when sorted:
+ ///
+ /// - Button -> ButtonA
+ /// - IconButton -> ButtonBIcon
+ /// - ToggleButton -> ButtonCToggle
+ ///
+ /// This naming scheme keeps these components together and allows them to /// be sorted in a logical order.
+ fn sort_name() -> &'static str {
+ Self::name()
+ }
fn description() -> Option<&'static str> {
None
}
-}
-
-pub trait ComponentPreview: Component {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement;
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ None
+ }
}
#[distributed_slice]
pub static __ALL_COMPONENTS: [fn()] = [..];
-#[distributed_slice]
-pub static __ALL_PREVIEWS: [fn()] = [..];
-
pub static COMPONENT_DATA: LazyLock<RwLock<ComponentRegistry>> =
LazyLock::new(|| RwLock::new(ComponentRegistry::new()));
pub struct ComponentRegistry {
- components: Vec<(Option<ComponentScope>, &'static str, Option<&'static str>)>,
- previews: HashMap<&'static str, fn(&mut Window, &mut App) -> AnyElement>,
+ components: Vec<(
+ ComponentScope,
+ // name
+ &'static str,
+ // sort name
+ &'static str,
+ // description
+ Option<&'static str>,
+ )>,
+ previews: HashMap<&'static str, fn(&mut Window, &mut App) -> Option<AnyElement>>,
}
impl ComponentRegistry {
@@ -47,30 +72,16 @@ impl ComponentRegistry {
pub fn init() {
let component_fns: Vec<_> = __ALL_COMPONENTS.iter().cloned().collect();
- let preview_fns: Vec<_> = __ALL_PREVIEWS.iter().cloned().collect();
-
for f in component_fns {
f();
}
- for f in preview_fns {
- f();
- }
}
pub fn register_component<T: Component>() {
- let component_data = (T::scope(), T::name(), T::description());
- COMPONENT_DATA.write().components.push(component_data);
-}
-
-pub fn register_preview<T: ComponentPreview>() {
- let preview_data = (
- T::name(),
- T::preview as fn(&mut Window, &mut App) -> AnyElement,
- );
- COMPONENT_DATA
- .write()
- .previews
- .insert(preview_data.0, preview_data.1);
+ let component_data = (T::scope(), T::name(), T::sort_name(), T::description());
+ let mut data = COMPONENT_DATA.write();
+ data.components.push(component_data);
+ data.previews.insert(T::name(), T::preview);
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -80,29 +91,41 @@ pub struct ComponentId(pub &'static str);
pub struct ComponentMetadata {
id: ComponentId,
name: SharedString,
- scope: Option<ComponentScope>,
+ sort_name: SharedString,
+ scope: ComponentScope,
description: Option<SharedString>,
- preview: Option<fn(&mut Window, &mut App) -> AnyElement>,
+ preview: Option<fn(&mut Window, &mut App) -> Option<AnyElement>>,
}
impl ComponentMetadata {
pub fn id(&self) -> ComponentId {
self.id.clone()
}
-
pub fn name(&self) -> SharedString {
self.name.clone()
}
- pub fn scope(&self) -> Option<ComponentScope> {
- self.scope.clone()
+ pub fn sort_name(&self) -> SharedString {
+ self.sort_name.clone()
+ }
+
+ pub fn scopeless_name(&self) -> SharedString {
+ self.name
+ .clone()
+ .split("::")
+ .last()
+ .unwrap_or(&self.name)
+ .to_string()
+ .into()
}
+ pub fn scope(&self) -> ComponentScope {
+ self.scope.clone()
+ }
pub fn description(&self) -> Option<SharedString> {
self.description.clone()
}
-
- pub fn preview(&self) -> Option<fn(&mut Window, &mut App) -> AnyElement> {
+ pub fn preview(&self) -> Option<fn(&mut Window, &mut App) -> Option<AnyElement>> {
self.preview
}
}
@@ -113,26 +136,18 @@ impl AllComponents {
pub fn new() -> Self {
AllComponents(HashMap::default())
}
-
- /// Returns all components with previews
pub fn all_previews(&self) -> Vec<&ComponentMetadata> {
self.0.values().filter(|c| c.preview.is_some()).collect()
}
-
- /// Returns all components with previews sorted by name
pub fn all_previews_sorted(&self) -> Vec<ComponentMetadata> {
let mut previews: Vec<ComponentMetadata> =
self.all_previews().into_iter().cloned().collect();
previews.sort_by_key(|a| a.name());
previews
}
-
- /// Returns all components
pub fn all(&self) -> Vec<&ComponentMetadata> {
self.0.values().collect()
}
-
- /// Returns all components sorted by name
pub fn all_sorted(&self) -> Vec<ComponentMetadata> {
let mut components: Vec<ComponentMetadata> = self.all().into_iter().cloned().collect();
components.sort_by_key(|a| a.name());
@@ -142,7 +157,6 @@ impl AllComponents {
impl Deref for AllComponents {
type Target = HashMap<ComponentId, ComponentMetadata>;
-
fn deref(&self) -> &Self::Target {
&self.0
}
@@ -157,139 +171,127 @@ impl DerefMut for AllComponents {
pub fn components() -> AllComponents {
let data = COMPONENT_DATA.read();
let mut all_components = AllComponents::new();
-
- for (scope, name, description) in &data.components {
+ for (scope, name, sort_name, description) in &data.components {
let preview = data.previews.get(name).cloned();
let component_name = SharedString::new_static(name);
+ let sort_name = SharedString::new_static(sort_name);
let id = ComponentId(name);
all_components.insert(
id.clone(),
ComponentMetadata {
id,
name: component_name,
+ sort_name,
scope: scope.clone(),
description: description.map(Into::into),
preview,
},
);
}
-
all_components
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ComponentScope {
- Layout,
+ Collaboration,
+ DataDisplay,
+ Editor,
+ Images,
Input,
+ Layout,
+ Loading,
+ Navigation,
+ None,
Notification,
- Editor,
- Collaboration,
+ Overlays,
+ Status,
+ Typography,
VersionControl,
- Unknown(SharedString),
}
impl Display for ComponentScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- ComponentScope::Layout => write!(f, "Layout"),
- ComponentScope::Input => write!(f, "Input"),
- ComponentScope::Notification => write!(f, "Notification"),
- ComponentScope::Editor => write!(f, "Editor"),
ComponentScope::Collaboration => write!(f, "Collaboration"),
+ ComponentScope::DataDisplay => write!(f, "Data Display"),
+ ComponentScope::Editor => write!(f, "Editor"),
+ ComponentScope::Images => write!(f, "Images & Icons"),
+ ComponentScope::Input => write!(f, "Forms & Input"),
+ ComponentScope::Layout => write!(f, "Layout & Structure"),
+ ComponentScope::Loading => write!(f, "Loading & Progress"),
+ ComponentScope::Navigation => write!(f, "Navigation"),
+ ComponentScope::None => write!(f, "Unsorted"),
+ ComponentScope::Notification => write!(f, "Notification"),
+ ComponentScope::Overlays => write!(f, "Overlays & Layering"),
+ ComponentScope::Status => write!(f, "Status"),
+ ComponentScope::Typography => write!(f, "Typography"),
ComponentScope::VersionControl => write!(f, "Version Control"),
- ComponentScope::Unknown(name) => write!(f, "Unknown: {}", name),
}
}
}
-impl From<&str> for ComponentScope {
- fn from(value: &str) -> Self {
- match value {
- "Layout" => ComponentScope::Layout,
- "Input" => ComponentScope::Input,
- "Notification" => ComponentScope::Notification,
- "Editor" => ComponentScope::Editor,
- "Collaboration" => ComponentScope::Collaboration,
- "Version Control" | "VersionControl" => ComponentScope::VersionControl,
- _ => ComponentScope::Unknown(SharedString::new(value)),
- }
- }
-}
-
-impl From<String> for ComponentScope {
- fn from(value: String) -> Self {
- match value.as_str() {
- "Layout" => ComponentScope::Layout,
- "Input" => ComponentScope::Input,
- "Notification" => ComponentScope::Notification,
- "Editor" => ComponentScope::Editor,
- "Collaboration" => ComponentScope::Collaboration,
- "Version Control" | "VersionControl" => ComponentScope::VersionControl,
- _ => ComponentScope::Unknown(SharedString::new(value)),
- }
- }
-}
-
-/// Which side of the preview to show labels on
-#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ExampleLabelSide {
- /// Left side
- Left,
- /// Right side
- Right,
- /// Top side
- #[default]
- Top,
- /// Bottom side
- Bottom,
-}
-
/// A single example of a component.
#[derive(IntoElement)]
pub struct ComponentExample {
- variant_name: SharedString,
- element: AnyElement,
- label_side: ExampleLabelSide,
- grow: bool,
+ pub variant_name: SharedString,
+ pub description: Option<SharedString>,
+ pub element: AnyElement,
}
impl RenderOnce for ComponentExample {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
- let base = div().flex();
-
- let base = match self.label_side {
- ExampleLabelSide::Right => base.flex_row(),
- ExampleLabelSide::Left => base.flex_row_reverse(),
- ExampleLabelSide::Bottom => base.flex_col(),
- ExampleLabelSide::Top => base.flex_col_reverse(),
- };
-
- base.gap_2()
- .p_2()
- .text_size(px(10.))
- .text_color(cx.theme().colors().text_muted)
- .when(self.grow, |this| this.flex_1())
- .when(!self.grow, |this| this.flex_none())
- .child(self.element)
- .child(self.variant_name)
+ div()
+ .w_full()
+ .flex()
+ .flex_col()
+ .gap_3()
+ .child(
+ div()
+ .child(self.variant_name.clone())
+ .text_size(rems(1.25))
+ .text_color(cx.theme().colors().text),
+ )
+ .when_some(self.description, |this, description| {
+ this.child(
+ div()
+ .text_size(rems(0.9375))
+ .text_color(cx.theme().colors().text_muted)
+ .child(description.clone()),
+ )
+ })
+ .child(
+ div()
+ .flex()
+ .w_full()
+ .rounded_xl()
+ .min_h(px(100.))
+ .justify_center()
+ .p_8()
+ .border_1()
+ .border_color(cx.theme().colors().border)
+ .bg(pattern_slash(
+ cx.theme().colors().surface_background.opacity(0.5),
+ 24.0,
+ 24.0,
+ ))
+ .shadow_sm()
+ .child(self.element),
+ )
.into_any_element()
}
}
impl ComponentExample {
- /// Create a new example with the given variant name and example value.
pub fn new(variant_name: impl Into<SharedString>, element: AnyElement) -> Self {
Self {
variant_name: variant_name.into(),
element,
- label_side: ExampleLabelSide::default(),
- grow: false,
+ description: None,
}
}
- /// Set the example to grow to fill the available horizontal space.
- pub fn grow(mut self) -> Self {
- self.grow = true;
+ pub fn description(mut self, description: impl Into<SharedString>) -> Self {
+ self.description = Some(description.into());
self
}
}
@@ -309,7 +311,7 @@ impl RenderOnce for ComponentExampleGroup {
.flex_col()
.text_sm()
.text_color(cx.theme().colors().text_muted)
- .when(self.grow, |this| this.w_full().flex_1())
+ .w_full()
.when_some(self.title, |this, title| {
this.gap_4().child(
div()
@@ -336,7 +338,7 @@ impl RenderOnce for ComponentExampleGroup {
.child(
div()
.flex()
- .when(self.vertical, |this| this.flex_col())
+ .flex_col()
.items_start()
.w_full()
.gap_6()
@@ -348,7 +350,6 @@ impl RenderOnce for ComponentExampleGroup {
}
impl ComponentExampleGroup {
- /// Create a new group of examples with the given title.
pub fn new(examples: Vec<ComponentExample>) -> Self {
Self {
title: None,
@@ -357,8 +358,6 @@ impl ComponentExampleGroup {
vertical: false,
}
}
-
- /// Create a new group of examples with the given title.
pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample>) -> Self {
Self {
title: Some(title.into()),
@@ -367,21 +366,16 @@ impl ComponentExampleGroup {
vertical: false,
}
}
-
- /// Set the group to grow to fill the available horizontal space.
pub fn grow(mut self) -> Self {
self.grow = true;
self
}
-
- /// Lay the group out vertically.
pub fn vertical(mut self) -> Self {
self.vertical = true;
self
}
}
-/// Create a single example
pub fn single_example(
variant_name: impl Into<SharedString>,
example: AnyElement,
@@ -389,12 +383,10 @@ pub fn single_example(
ComponentExample::new(variant_name, example)
}
-/// Create a group of examples without a title
pub fn example_group(examples: Vec<ComponentExample>) -> ComponentExampleGroup {
ComponentExampleGroup::new(examples)
}
-/// Create a group of examples with a title
pub fn example_group_with_title(
title: impl Into<SharedString>,
examples: Vec<ComponentExample>,
@@ -43,6 +43,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut App) {
language_registry,
user_store,
None,
+ None,
cx,
)
});
@@ -106,10 +107,12 @@ impl ComponentPreview {
language_registry: Arc<LanguageRegistry>,
user_store: Entity<UserStore>,
selected_index: impl Into<Option<usize>>,
+ active_page: Option<PreviewPage>,
cx: &mut Context<Self>,
) -> Self {
let sorted_components = components().all_sorted();
let selected_index = selected_index.into().unwrap_or(0);
+ let active_page = active_page.unwrap_or(PreviewPage::AllComponents);
let component_list = ListState::new(
sorted_components.len(),
@@ -135,7 +138,7 @@ impl ComponentPreview {
language_registry,
user_store,
workspace,
- active_page: PreviewPage::AllComponents,
+ active_page,
component_map: components().0,
components: sorted_components,
component_list,
@@ -169,8 +172,7 @@ impl ComponentPreview {
fn scope_ordered_entries(&self) -> Vec<PreviewEntry> {
use std::collections::HashMap;
- let mut scope_groups: HashMap<Option<ComponentScope>, Vec<ComponentMetadata>> =
- HashMap::default();
+ let mut scope_groups: HashMap<ComponentScope, Vec<ComponentMetadata>> = HashMap::default();
for component in &self.components {
scope_groups
@@ -192,6 +194,7 @@ impl ComponentPreview {
ComponentScope::Notification,
ComponentScope::Collaboration,
ComponentScope::VersionControl,
+ ComponentScope::None,
];
// Always show all components first
@@ -199,38 +202,27 @@ impl ComponentPreview {
entries.push(PreviewEntry::Separator);
for scope in known_scopes.iter() {
- let scope_key = Some(scope.clone());
- if let Some(components) = scope_groups.remove(&scope_key) {
+ if let Some(components) = scope_groups.remove(scope) {
if !components.is_empty() {
entries.push(PreviewEntry::SectionHeader(scope.to_string().into()));
+ let mut sorted_components = components;
+ sorted_components.sort_by_key(|component| component.sort_name());
- for component in components {
+ for component in sorted_components {
entries.push(PreviewEntry::Component(component));
}
}
}
}
- for (scope, components) in &scope_groups {
- if let Some(ComponentScope::Unknown(_)) = scope {
- if !components.is_empty() {
- if let Some(scope_value) = scope {
- entries.push(PreviewEntry::SectionHeader(scope_value.to_string().into()));
- }
-
- for component in components {
- entries.push(PreviewEntry::Component(component.clone()));
- }
- }
- }
- }
-
- if let Some(components) = scope_groups.get(&None) {
+ if let Some(components) = scope_groups.get(&ComponentScope::None) {
if !components.is_empty() {
entries.push(PreviewEntry::Separator);
entries.push(PreviewEntry::SectionHeader("Uncategorized".into()));
+ let mut sorted_components = components.clone();
+ sorted_components.sort_by_key(|c| c.sort_name());
- for component in components {
+ for component in sorted_components {
entries.push(PreviewEntry::Component(component.clone()));
}
}
@@ -250,7 +242,10 @@ impl ComponentPreview {
let id = component_metadata.id();
let selected = self.active_page == PreviewPage::Component(id.clone());
ListItem::new(ix)
- .child(Label::new(component_metadata.name().clone()).color(Color::Default))
+ .child(
+ Label::new(component_metadata.scopeless_name().clone())
+ .color(Color::Default),
+ )
.selectable(true)
.toggle_state(selected)
.inset(true)
@@ -333,7 +328,7 @@ impl ComponentPreview {
window: &mut Window,
cx: &mut App,
) -> impl IntoElement {
- let name = component.name();
+ let name = component.scopeless_name();
let scope = component.scope();
let description = component.description();
@@ -354,13 +349,12 @@ impl ComponentPreview {
v_flex()
.gap_1()
.child(
- h_flex()
- .gap_1()
- .text_xl()
- .child(div().child(name))
- .when_some(scope, |this, scope| {
+ h_flex().gap_1().text_xl().child(div().child(name)).when(
+ !matches!(scope, ComponentScope::None),
+ |this| {
this.child(div().opacity(0.5).child(format!("({})", scope)))
- }),
+ },
+ ),
)
.when_some(description, |this, description| {
this.child(
@@ -373,7 +367,7 @@ impl ComponentPreview {
}),
)
.when_some(component.preview(), |this, preview| {
- this.child(preview(window, cx))
+ this.children(preview(window, cx))
}),
)
.into_any_element()
@@ -395,17 +389,16 @@ impl ComponentPreview {
fn render_component_page(
&mut self,
component_id: &ComponentId,
- window: &mut Window,
- cx: &mut Context<Self>,
+ _window: &mut Window,
+ _cx: &mut Context<Self>,
) -> impl IntoElement {
let component = self.component_map.get(&component_id);
if let Some(component) = component {
v_flex()
- .w_full()
- .flex_initial()
- .min_h_full()
- .child(self.render_preview(component, window, cx))
+ .id("render-component-page")
+ .size_full()
+ .child(ComponentPreviewPage::new(component.clone()))
.into_any_element()
} else {
v_flex()
@@ -445,10 +438,11 @@ impl Render for ComponentPreview {
.overflow_hidden()
.size_full()
.track_focus(&self.focus_handle)
- .px_2()
.bg(cx.theme().colors().editor_background)
.child(
v_flex()
+ .border_r_1()
+ .border_color(cx.theme().colors().border)
.h_full()
.child(
uniform_list(
@@ -465,6 +459,7 @@ impl Render for ComponentPreview {
)
.track_scroll(self.nav_scroll_handle.clone())
.pt_4()
+ .px_4()
.w(px(240.))
.h_full()
.flex_1(),
@@ -527,6 +522,7 @@ impl Item for ComponentPreview {
let user_store = self.user_store.clone();
let weak_workspace = self.workspace.clone();
let selected_index = self.cursor_index;
+ let active_page = self.active_page.clone();
Some(cx.new(|cx| {
Self::new(
@@ -534,6 +530,7 @@ impl Item for ComponentPreview {
language_registry,
user_store,
selected_index,
+ Some(active_page),
cx,
)
}))
@@ -566,7 +563,14 @@ impl SerializableItem for ComponentPreview {
let weak_workspace = workspace.clone();
cx.update(|_, cx| {
Ok(cx.new(|cx| {
- ComponentPreview::new(weak_workspace, language_registry, user_store, None, cx)
+ ComponentPreview::new(
+ weak_workspace,
+ language_registry,
+ user_store,
+ None,
+ None,
+ cx,
+ )
}))
})?
})
@@ -600,3 +604,76 @@ impl SerializableItem for ComponentPreview {
false
}
}
+
+#[derive(IntoElement)]
+pub struct ComponentPreviewPage {
+ // languages: Arc<LanguageRegistry>,
+ component: ComponentMetadata,
+}
+
+impl ComponentPreviewPage {
+ pub fn new(
+ component: ComponentMetadata,
+ // languages: Arc<LanguageRegistry>
+ ) -> Self {
+ Self {
+ // languages,
+ component,
+ }
+ }
+
+ fn render_header(&self, _: &Window, cx: &App) -> impl IntoElement {
+ v_flex()
+ .px_12()
+ .pt_16()
+ .pb_12()
+ .gap_6()
+ .bg(cx.theme().colors().surface_background)
+ .border_b_1()
+ .border_color(cx.theme().colors().border)
+ .child(
+ v_flex()
+ .gap_0p5()
+ .child(
+ Label::new(self.component.scope().to_string())
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ )
+ .child(
+ Headline::new(self.component.scopeless_name()).size(HeadlineSize::XLarge),
+ ),
+ )
+ .when_some(self.component.description(), |this, description| {
+ this.child(div().text_sm().child(description))
+ })
+ }
+
+ fn render_preview(&self, window: &mut Window, cx: &mut App) -> impl IntoElement {
+ v_flex()
+ .flex_1()
+ .px_12()
+ .py_6()
+ .bg(cx.theme().colors().editor_background)
+ .child(if let Some(preview) = self.component.preview() {
+ preview(window, cx).unwrap_or_else(|| {
+ div()
+ .child("Failed to load preview. This path should be unreachable")
+ .into_any_element()
+ })
+ } else {
+ div().child("No preview available").into_any_element()
+ })
+ }
+}
+
+impl RenderOnce for ComponentPreviewPage {
+ fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
+ v_flex()
+ .id("component-preview-page")
+ .overflow_y_scroll()
+ .overflow_x_hidden()
+ .w_full()
+ .child(self.render_header(window, cx))
+ .child(self.render_preview(window, cx))
+ }
+}
@@ -3953,8 +3953,7 @@ impl Render for GitPanelMessageTooltip {
}
}
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Version Control")]
+#[derive(IntoElement, RegisterComponent)]
pub struct PanelRepoFooter {
active_repository: SharedString,
branch: Option<Branch>,
@@ -4134,8 +4133,12 @@ impl RenderOnce for PanelRepoFooter {
}
}
-impl ComponentPreview for PanelRepoFooter {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+impl Component for PanelRepoFooter {
+ fn scope() -> ComponentScope {
+ ComponentScope::VersionControl
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
let unknown_upstream = None;
let no_remote_upstream = Some(UpstreamTracking::Gone);
let ahead_of_upstream = Some(
@@ -4207,192 +4210,180 @@ impl ComponentPreview for PanelRepoFooter {
}
let example_width = px(340.);
-
- v_flex()
- .gap_6()
- .w_full()
- .flex_none()
- .children(vec![
- example_group_with_title(
- "Action Button States",
- vec![
- single_example(
- "No Branch",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(1).clone(),
- None,
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Remote status unknown",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(2).clone(),
- Some(branch(unknown_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "No Remote Upstream",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(3).clone(),
- Some(branch(no_remote_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Not Ahead or Behind",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(4).clone(),
- Some(branch(not_ahead_or_behind_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Behind remote",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(5).clone(),
- Some(branch(behind_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Ahead of remote",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(6).clone(),
- Some(branch(ahead_of_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Ahead and behind remote",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- active_repository(7).clone(),
- Some(branch(ahead_and_behind_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- ],
- )
- .grow()
- .vertical(),
- ])
- .children(vec![
- example_group_with_title(
- "Labels",
- vec![
- single_example(
- "Short Branch & Repo",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- SharedString::from("zed"),
- Some(custom("main", behind_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Long Branch",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- SharedString::from("zed"),
- Some(custom(
- "redesign-and-update-git-ui-list-entry-style",
- behind_upstream,
- )),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Long Repo",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- SharedString::from("zed-industries-community-examples"),
- Some(custom("gpui", ahead_of_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Long Repo & Branch",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- SharedString::from("zed-industries-community-examples"),
- Some(custom(
- "redesign-and-update-git-ui-list-entry-style",
- behind_upstream,
- )),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Uppercase Repo",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- SharedString::from("LICENSES"),
- Some(custom("main", ahead_of_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Uppercase Branch",
- div()
- .w(example_width)
- .overflow_hidden()
- .child(PanelRepoFooter::new_preview(
- SharedString::from("zed"),
- Some(custom("update-README", behind_upstream)),
- ))
- .into_any_element(),
- )
- .grow(),
- ],
- )
- .grow()
- .vertical(),
- ])
- .into_any_element()
+ Some(
+ v_flex()
+ .gap_6()
+ .w_full()
+ .flex_none()
+ .children(vec![
+ example_group_with_title(
+ "Action Button States",
+ vec![
+ single_example(
+ "No Branch",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(1).clone(),
+ None,
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Remote status unknown",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(2).clone(),
+ Some(branch(unknown_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "No Remote Upstream",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(3).clone(),
+ Some(branch(no_remote_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Not Ahead or Behind",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(4).clone(),
+ Some(branch(not_ahead_or_behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Behind remote",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(5).clone(),
+ Some(branch(behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Ahead of remote",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(6).clone(),
+ Some(branch(ahead_of_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Ahead and behind remote",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ active_repository(7).clone(),
+ Some(branch(ahead_and_behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ ],
+ )
+ .grow()
+ .vertical(),
+ ])
+ .children(vec![
+ example_group_with_title(
+ "Labels",
+ vec![
+ single_example(
+ "Short Branch & Repo",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ SharedString::from("zed"),
+ Some(custom("main", behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Long Branch",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ SharedString::from("zed"),
+ Some(custom(
+ "redesign-and-update-git-ui-list-entry-style",
+ behind_upstream,
+ )),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Long Repo",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ SharedString::from("zed-industries-community-examples"),
+ Some(custom("gpui", ahead_of_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Long Repo & Branch",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ SharedString::from("zed-industries-community-examples"),
+ Some(custom(
+ "redesign-and-update-git-ui-list-entry-style",
+ behind_upstream,
+ )),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Uppercase Repo",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ SharedString::from("LICENSES"),
+ Some(custom("main", ahead_of_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Uppercase Branch",
+ div()
+ .w(example_width)
+ .overflow_hidden()
+ .child(PanelRepoFooter::new_preview(
+ SharedString::from("zed"),
+ Some(custom("update-README", behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ ],
+ )
+ .grow()
+ .vertical(),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -441,8 +441,8 @@ mod remote_button {
}
}
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Version Control")]
+/// A visual representation of a file's Git status.
+#[derive(IntoElement, RegisterComponent)]
pub struct GitStatusIcon {
status: FileStatus,
}
@@ -484,8 +484,12 @@ impl RenderOnce for GitStatusIcon {
}
// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for GitStatusIcon {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+impl Component for GitStatusIcon {
+ fn scope() -> ComponentScope {
+ ComponentScope::VersionControl
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
fn tracked_file_status(code: StatusCode) -> FileStatus {
FileStatus::Tracked(git::status::TrackedStatus {
index_status: code,
@@ -502,17 +506,19 @@ impl ComponentPreview for GitStatusIcon {
}
.into();
- v_flex()
- .gap_6()
- .children(vec![example_group(vec![
- single_example("Modified", GitStatusIcon::new(modified).into_any_element()),
- single_example("Added", GitStatusIcon::new(added).into_any_element()),
- single_example("Deleted", GitStatusIcon::new(deleted).into_any_element()),
- single_example(
- "Conflicted",
- GitStatusIcon::new(conflict).into_any_element(),
- ),
- ])])
- .into_any_element()
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![example_group(vec![
+ single_example("Modified", GitStatusIcon::new(modified).into_any_element()),
+ single_example("Added", GitStatusIcon::new(added).into_any_element()),
+ single_example("Deleted", GitStatusIcon::new(deleted).into_any_element()),
+ single_example(
+ "Conflicted",
+ GitStatusIcon::new(conflict).into_any_element(),
+ ),
+ ])])
+ .into_any_element(),
+ )
}
}
@@ -1005,8 +1005,7 @@ impl Render for ProjectDiffToolbar {
}
}
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Version Control")]
+#[derive(IntoElement, RegisterComponent)]
pub struct ProjectDiffEmptyState {
pub no_repo: bool,
pub can_push_and_pull: bool,
@@ -1178,8 +1177,12 @@ mod preview {
use super::ProjectDiffEmptyState;
// View this component preview using `workspace: open component-preview`
- impl ComponentPreview for ProjectDiffEmptyState {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+ impl Component for ProjectDiffEmptyState {
+ fn scope() -> ComponentScope {
+ ComponentScope::VersionControl
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
let unknown_upstream: Option<UpstreamTracking> = None;
let ahead_of_upstream: Option<UpstreamTracking> = Some(
UpstreamTrackingStatus {
@@ -1244,46 +1247,48 @@ mod preview {
let (width, height) = (px(480.), px(320.));
- v_flex()
- .gap_6()
- .children(vec![
- example_group(vec![
- single_example(
- "No Repo",
- div()
- .w(width)
- .h(height)
- .child(no_repo_state)
- .into_any_element(),
- ),
- single_example(
- "No Changes",
- div()
- .w(width)
- .h(height)
- .child(no_changes_state)
- .into_any_element(),
- ),
- single_example(
- "Unknown Upstream",
- div()
- .w(width)
- .h(height)
- .child(unknown_upstream_state)
- .into_any_element(),
- ),
- single_example(
- "Ahead of Remote",
- div()
- .w(width)
- .h(height)
- .child(ahead_of_upstream_state)
- .into_any_element(),
- ),
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group(vec![
+ single_example(
+ "No Repo",
+ div()
+ .w(width)
+ .h(height)
+ .child(no_repo_state)
+ .into_any_element(),
+ ),
+ single_example(
+ "No Changes",
+ div()
+ .w(width)
+ .h(height)
+ .child(no_changes_state)
+ .into_any_element(),
+ ),
+ single_example(
+ "Unknown Upstream",
+ div()
+ .w(width)
+ .h(height)
+ .child(unknown_upstream_state)
+ .into_any_element(),
+ ),
+ single_example(
+ "Ahead of Remote",
+ div()
+ .w(width)
+ .h(height)
+ .child(ahead_of_upstream_state)
+ .into_any_element(),
+ ),
+ ])
+ .vertical(),
])
- .vertical(),
- ])
- .into_any_element()
+ .into_any_element(),
+ )
}
}
}
@@ -33,8 +33,7 @@ impl From<IconName> for ToastIcon {
}
}
-#[derive(IntoComponent)]
-#[component(scope = "Notification")]
+#[derive(RegisterComponent)]
pub struct StatusToast {
icon: Option<ToastIcon>,
text: SharedString,
@@ -135,8 +134,12 @@ impl Focusable for StatusToast {
impl EventEmitter<DismissEvent> for StatusToast {}
-impl ComponentPreview for StatusToast {
- fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
+impl Component for StatusToast {
+ fn scope() -> ComponentScope {
+ ComponentScope::Notification
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let text_example = StatusToast::new("Operation completed", cx, |this, _| this);
let action_example = StatusToast::new("Update ready to install", cx, |this, _cx| {
@@ -175,29 +178,40 @@ impl ComponentPreview for StatusToast {
})
});
- v_flex()
- .gap_6()
- .p_4()
- .children(vec![
- example_group_with_title(
- "Basic Toast",
- vec![
- single_example("Text", div().child(text_example).into_any_element()),
- single_example("Action", div().child(action_example).into_any_element()),
- single_example("Icon", div().child(icon_example).into_any_element()),
- ],
- ),
- example_group_with_title(
- "Examples",
- vec![
- single_example("Success", div().child(success_example).into_any_element()),
- single_example("Error", div().child(error_example).into_any_element()),
- single_example("Warning", div().child(warning_example).into_any_element()),
- single_example("Create PR", div().child(pr_example).into_any_element()),
- ],
- )
- .vertical(),
- ])
- .into_any_element()
+ Some(
+ v_flex()
+ .gap_6()
+ .p_4()
+ .children(vec![
+ example_group_with_title(
+ "Basic Toast",
+ vec![
+ single_example("Text", div().child(text_example).into_any_element()),
+ single_example(
+ "Action",
+ div().child(action_example).into_any_element(),
+ ),
+ single_example("Icon", div().child(icon_example).into_any_element()),
+ ],
+ ),
+ example_group_with_title(
+ "Examples",
+ vec![
+ single_example(
+ "Success",
+ div().child(success_example).into_any_element(),
+ ),
+ single_example("Error", div().child(error_example).into_any_element()),
+ single_example(
+ "Warning",
+ div().child(warning_example).into_any_element(),
+ ),
+ single_example("Create PR", div().child(pr_example).into_any_element()),
+ ],
+ )
+ .vertical(),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -18,9 +18,7 @@ pub enum ComponentStory {
ContextMenu,
Cursor,
DefaultColors,
- Disclosure,
Focus,
- Icon,
IconButton,
Keybinding,
List,
@@ -35,7 +33,6 @@ pub enum ComponentStory {
ToggleButton,
ViewportUnits,
WithRemSize,
- Vector,
}
impl ComponentStory {
@@ -51,9 +48,7 @@ impl ComponentStory {
Self::ContextMenu => cx.new(|_| ui::ContextMenuStory).into(),
Self::Cursor => cx.new(|_| crate::stories::CursorStory).into(),
Self::DefaultColors => DefaultColorsStory::model(cx).into(),
- Self::Disclosure => cx.new(|_| ui::DisclosureStory).into(),
Self::Focus => FocusStory::model(window, cx).into(),
- Self::Icon => cx.new(|_| ui::IconStory).into(),
Self::IconButton => cx.new(|_| ui::IconButtonStory).into(),
Self::Keybinding => cx.new(|_| ui::KeybindingStory).into(),
Self::List => cx.new(|_| ui::ListStory).into(),
@@ -68,7 +63,6 @@ impl ComponentStory {
Self::ToggleButton => cx.new(|_| ui::ToggleButtonStory).into(),
Self::ViewportUnits => cx.new(|_| crate::stories::ViewportUnitsStory).into(),
Self::WithRemSize => cx.new(|_| crate::stories::WithRemSizeStory).into(),
- Self::Vector => cx.new(|_| ui::VectorStory).into(),
}
}
}
@@ -28,6 +28,7 @@ strum.workspace = true
theme.workspace = true
ui_macros.workspace = true
util.workspace = true
+documented = "0.9.1"
workspace-hack.workspace = true
[target.'cfg(windows)'.dependencies]
@@ -0,0 +1,5 @@
+pub use component::{
+ Component, ComponentScope, example_group, example_group_with_title, single_example,
+};
+pub use documented::Documented;
+pub use ui_macros::RegisterComponent;
@@ -73,7 +73,5 @@ pub use table::*;
pub use toggle::*;
pub use tooltip::*;
-#[cfg(feature = "stories")]
-pub use image::story::*;
#[cfg(feature = "stories")]
pub use stories::*;
@@ -1,5 +1,6 @@
use crate::prelude::*;
+use documented::Documented;
use gpui::{AnyElement, Hsla, ImageSource, Img, IntoElement, Styled, img};
/// An element that renders a user avatar with customizable appearance options.
@@ -14,7 +15,7 @@ use gpui::{AnyElement, Hsla, ImageSource, Img, IntoElement, Styled, img};
/// .grayscale(true)
/// .border_color(gpui::red());
/// ```
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, Documented, RegisterComponent)]
pub struct Avatar {
image: Img,
size: Option<AbsoluteLength>,
@@ -219,84 +220,102 @@ impl RenderOnce for AvatarAvailabilityIndicator {
}
// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Avatar {
- fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
+impl Component for Avatar {
+ fn scope() -> ComponentScope {
+ ComponentScope::Collaboration
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(Avatar::DOCS)
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4";
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Sizes",
- vec![
- single_example("Default", Avatar::new(example_avatar).into_any_element()),
- single_example(
- "Small",
- Avatar::new(example_avatar).size(px(24.)).into_any_element(),
- ),
- single_example(
- "Large",
- Avatar::new(example_avatar).size(px(48.)).into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Styles",
- vec![
- single_example("Default", Avatar::new(example_avatar).into_any_element()),
- single_example(
- "Grayscale",
- Avatar::new(example_avatar)
- .grayscale(true)
- .into_any_element(),
- ),
- single_example(
- "With Border",
- Avatar::new(example_avatar)
- .border_color(cx.theme().colors().border)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Audio Status",
- vec![
- single_example(
- "Muted",
- Avatar::new(example_avatar)
- .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted))
- .into_any_element(),
- ),
- single_example(
- "Deafened",
- Avatar::new(example_avatar)
- .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened))
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Availability",
- vec![
- single_example(
- "Free",
- Avatar::new(example_avatar)
- .indicator(AvatarAvailabilityIndicator::new(
- CollaboratorAvailability::Free,
- ))
- .into_any_element(),
- ),
- single_example(
- "Busy",
- Avatar::new(example_avatar)
- .indicator(AvatarAvailabilityIndicator::new(
- CollaboratorAvailability::Busy,
- ))
- .into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Sizes",
+ vec![
+ single_example(
+ "Default",
+ Avatar::new(example_avatar).into_any_element(),
+ ),
+ single_example(
+ "Small",
+ Avatar::new(example_avatar).size(px(24.)).into_any_element(),
+ ),
+ single_example(
+ "Large",
+ Avatar::new(example_avatar).size(px(48.)).into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Styles",
+ vec![
+ single_example(
+ "Default",
+ Avatar::new(example_avatar).into_any_element(),
+ ),
+ single_example(
+ "Grayscale",
+ Avatar::new(example_avatar)
+ .grayscale(true)
+ .into_any_element(),
+ ),
+ single_example(
+ "With Border",
+ Avatar::new(example_avatar)
+ .border_color(cx.theme().colors().border)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Audio Status",
+ vec![
+ single_example(
+ "Muted",
+ Avatar::new(example_avatar)
+ .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted))
+ .into_any_element(),
+ ),
+ single_example(
+ "Deafened",
+ Avatar::new(example_avatar)
+ .indicator(AvatarAudioStatusIndicator::new(
+ AudioStatus::Deafened,
+ ))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Availability",
+ vec![
+ single_example(
+ "Free",
+ Avatar::new(example_avatar)
+ .indicator(AvatarAvailabilityIndicator::new(
+ CollaboratorAvailability::Free,
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Busy",
+ Avatar::new(example_avatar)
+ .indicator(AvatarAvailabilityIndicator::new(
+ CollaboratorAvailability::Busy,
+ ))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -28,8 +28,7 @@ pub enum Severity {
/// .icon_position(IconPosition::End),
/// )
/// ```
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Notification")]
+#[derive(IntoElement, RegisterComponent)]
pub struct Banner {
severity: Severity,
children: Option<AnyElement>,
@@ -137,8 +136,12 @@ impl RenderOnce for Banner {
}
}
-impl ComponentPreview for Banner {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+impl Component for Banner {
+ fn scope() -> ComponentScope {
+ ComponentScope::Notification
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
let severity_examples = vec![
single_example(
"Default",
@@ -185,8 +188,10 @@ impl ComponentPreview for Banner {
),
];
- example_group(severity_examples)
- .vertical()
- .into_any_element()
+ Some(
+ example_group(severity_examples)
+ .vertical()
+ .into_any_element(),
+ )
}
}
@@ -1,6 +1,6 @@
-use component::{ComponentPreview, example_group_with_title, single_example};
+use crate::component_prelude::*;
use gpui::{AnyElement, AnyView, DefiniteLength};
-use ui_macros::IntoComponent;
+use ui_macros::RegisterComponent;
use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label};
use crate::{
@@ -77,8 +77,7 @@ use super::button_icon::ButtonIcon;
/// });
/// ```
///
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Input")]
+#[derive(IntoElement, Documented, RegisterComponent)]
pub struct Button {
base: ButtonLike,
label: SharedString,
@@ -466,121 +465,135 @@ impl RenderOnce for Button {
}
// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Button {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Button Styles",
- vec![
- single_example(
- "Default",
- Button::new("default", "Default").into_any_element(),
- ),
- single_example(
- "Filled",
- Button::new("filled", "Filled")
- .style(ButtonStyle::Filled)
- .into_any_element(),
- ),
- single_example(
- "Subtle",
- Button::new("outline", "Subtle")
- .style(ButtonStyle::Subtle)
- .into_any_element(),
- ),
- single_example(
- "Tinted",
- Button::new("tinted_accent_style", "Accent")
- .style(ButtonStyle::Tinted(TintColor::Accent))
- .into_any_element(),
- ),
- single_example(
- "Transparent",
- Button::new("transparent", "Transparent")
- .style(ButtonStyle::Transparent)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Tint Styles",
- vec![
- single_example(
- "Accent",
- Button::new("tinted_accent", "Accent")
- .style(ButtonStyle::Tinted(TintColor::Accent))
- .into_any_element(),
- ),
- single_example(
- "Error",
- Button::new("tinted_negative", "Error")
- .style(ButtonStyle::Tinted(TintColor::Error))
- .into_any_element(),
- ),
- single_example(
- "Warning",
- Button::new("tinted_warning", "Warning")
- .style(ButtonStyle::Tinted(TintColor::Warning))
- .into_any_element(),
- ),
- single_example(
- "Success",
- Button::new("tinted_positive", "Success")
- .style(ButtonStyle::Tinted(TintColor::Success))
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Special States",
- vec![
- single_example(
- "Default",
- Button::new("default_state", "Default").into_any_element(),
- ),
- single_example(
- "Disabled",
- Button::new("disabled", "Disabled")
- .disabled(true)
- .into_any_element(),
- ),
- single_example(
- "Selected",
- Button::new("selected", "Selected")
- .toggle_state(true)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Buttons with Icons",
- vec![
- single_example(
- "Icon Start",
- Button::new("icon_start", "Icon Start")
- .icon(IconName::Check)
- .icon_position(IconPosition::Start)
- .into_any_element(),
- ),
- single_example(
- "Icon End",
- Button::new("icon_end", "Icon End")
- .icon(IconName::Check)
- .icon_position(IconPosition::End)
- .into_any_element(),
- ),
- single_example(
- "Icon Color",
- Button::new("icon_color", "Icon Color")
- .icon(IconName::Check)
- .icon_color(Color::Accent)
- .into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+impl Component for Button {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn sort_name() -> &'static str {
+ "ButtonA"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A button triggers an event or action.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Button Styles",
+ vec![
+ single_example(
+ "Default",
+ Button::new("default", "Default").into_any_element(),
+ ),
+ single_example(
+ "Filled",
+ Button::new("filled", "Filled")
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "Subtle",
+ Button::new("outline", "Subtle")
+ .style(ButtonStyle::Subtle)
+ .into_any_element(),
+ ),
+ single_example(
+ "Tinted",
+ Button::new("tinted_accent_style", "Accent")
+ .style(ButtonStyle::Tinted(TintColor::Accent))
+ .into_any_element(),
+ ),
+ single_example(
+ "Transparent",
+ Button::new("transparent", "Transparent")
+ .style(ButtonStyle::Transparent)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Tint Styles",
+ vec![
+ single_example(
+ "Accent",
+ Button::new("tinted_accent", "Accent")
+ .style(ButtonStyle::Tinted(TintColor::Accent))
+ .into_any_element(),
+ ),
+ single_example(
+ "Error",
+ Button::new("tinted_negative", "Error")
+ .style(ButtonStyle::Tinted(TintColor::Error))
+ .into_any_element(),
+ ),
+ single_example(
+ "Warning",
+ Button::new("tinted_warning", "Warning")
+ .style(ButtonStyle::Tinted(TintColor::Warning))
+ .into_any_element(),
+ ),
+ single_example(
+ "Success",
+ Button::new("tinted_positive", "Success")
+ .style(ButtonStyle::Tinted(TintColor::Success))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Special States",
+ vec![
+ single_example(
+ "Default",
+ Button::new("default_state", "Default").into_any_element(),
+ ),
+ single_example(
+ "Disabled",
+ Button::new("disabled", "Disabled")
+ .disabled(true)
+ .into_any_element(),
+ ),
+ single_example(
+ "Selected",
+ Button::new("selected", "Selected")
+ .toggle_state(true)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Buttons with Icons",
+ vec![
+ single_example(
+ "Icon Start",
+ Button::new("icon_start", "Icon Start")
+ .icon(IconName::Check)
+ .icon_position(IconPosition::Start)
+ .into_any_element(),
+ ),
+ single_example(
+ "Icon End",
+ Button::new("icon_end", "Icon End")
+ .icon(IconName::Check)
+ .icon_position(IconPosition::End)
+ .into_any_element(),
+ ),
+ single_example(
+ "Icon Color",
+ Button::new("icon_color", "Icon Color")
+ .icon(IconName::Check)
+ .icon_color(Color::Accent)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -5,7 +5,7 @@ use gpui::Hsla;
///
/// Can be used as either an icon alongside a label, like in [`Button`](crate::Button),
/// or as a standalone icon, like in [`IconButton`](crate::IconButton).
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub(super) struct ButtonIcon {
icon: IconName,
size: IconSize,
@@ -39,7 +39,6 @@ impl ButtonIcon {
if let Some(size) = size.into() {
self.size = size;
}
-
self
}
@@ -47,7 +46,6 @@ impl ButtonIcon {
if let Some(color) = color.into() {
self.color = color;
}
-
self
}
@@ -120,3 +118,82 @@ impl RenderOnce for ButtonIcon {
}
}
}
+
+impl Component for ButtonIcon {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn name() -> &'static str {
+ "ButtonIcon"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("An icon component specifically designed for use within buttons.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Default",
+ ButtonIcon::new(IconName::Star).into_any_element(),
+ ),
+ single_example(
+ "Custom Size",
+ ButtonIcon::new(IconName::Star)
+ .size(IconSize::Medium)
+ .into_any_element(),
+ ),
+ single_example(
+ "Custom Color",
+ ButtonIcon::new(IconName::Star)
+ .color(Color::Accent)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "States",
+ vec![
+ single_example(
+ "Selected",
+ ButtonIcon::new(IconName::Star)
+ .toggle_state(true)
+ .into_any_element(),
+ ),
+ single_example(
+ "Disabled",
+ ButtonIcon::new(IconName::Star)
+ .disabled(true)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "With Indicator",
+ vec![
+ single_example(
+ "Default Indicator",
+ ButtonIcon::new(IconName::Star)
+ .indicator(Indicator::dot())
+ .into_any_element(),
+ ),
+ single_example(
+ "Custom Indicator",
+ ButtonIcon::new(IconName::Star)
+ .indicator(Indicator::dot().color(Color::Error))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -1,5 +1,8 @@
-use gpui::{AnyElement, AnyView, ClickEvent, Hsla, Rems, transparent_black};
-use gpui::{CursorStyle, DefiniteLength, MouseButton, MouseDownEvent, MouseUpEvent, relative};
+use documented::Documented;
+use gpui::{
+ AnyElement, AnyView, ClickEvent, CursorStyle, DefiniteLength, Hsla, MouseButton,
+ MouseDownEvent, MouseUpEvent, Rems, relative, transparent_black,
+};
use smallvec::SmallVec;
use crate::{DynamicSpacing, ElevationIndex, prelude::*};
@@ -343,7 +346,7 @@ impl ButtonSize {
/// unconstrained and may make the UI feel less consistent.
///
/// This is also used to build the prebuilt buttons.
-#[derive(IntoElement)]
+#[derive(IntoElement, Documented, RegisterComponent)]
pub struct ButtonLike {
pub(super) base: Div,
id: ElementId,
@@ -585,3 +588,99 @@ impl RenderOnce for ButtonLike {
.children(self.children)
}
}
+
+impl Component for ButtonLike {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn sort_name() -> &'static str {
+ // ButtonLike should be at the bottom of the button list
+ "ButtonZ"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(ButtonLike::DOCS)
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group(vec![
+ single_example(
+ "Default",
+ ButtonLike::new("default")
+ .child(Label::new("Default"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Filled",
+ ButtonLike::new("filled")
+ .style(ButtonStyle::Filled)
+ .child(Label::new("Filled"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Subtle",
+ ButtonLike::new("outline")
+ .style(ButtonStyle::Subtle)
+ .child(Label::new("Subtle"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Tinted",
+ ButtonLike::new("tinted_accent_style")
+ .style(ButtonStyle::Tinted(TintColor::Accent))
+ .child(Label::new("Accent"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Transparent",
+ ButtonLike::new("transparent")
+ .style(ButtonStyle::Transparent)
+ .child(Label::new("Transparent"))
+ .into_any_element(),
+ ),
+ ]),
+ example_group_with_title(
+ "Button Group Constructors",
+ vec![
+ single_example(
+ "Left Rounded",
+ ButtonLike::new_rounded_left("left_rounded")
+ .child(Label::new("Left Rounded"))
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "Right Rounded",
+ ButtonLike::new_rounded_right("right_rounded")
+ .child(Label::new("Right Rounded"))
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "Button Group",
+ h_flex()
+ .gap_px()
+ .child(
+ ButtonLike::new_rounded_left("bg_left")
+ .child(Label::new("Left"))
+ .style(ButtonStyle::Filled),
+ )
+ .child(
+ ButtonLike::new_rounded_right("bg_right")
+ .child(Label::new("Right"))
+ .style(ButtonStyle::Filled),
+ )
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -13,8 +13,7 @@ pub enum IconButtonShape {
Wide,
}
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Input")]
+#[derive(IntoElement, RegisterComponent)]
pub struct IconButton {
base: ButtonLike,
shape: IconButtonShape,
@@ -210,159 +209,169 @@ impl RenderOnce for IconButton {
}
}
-impl ComponentPreview for IconButton {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Icon Button Styles",
- vec![
- single_example(
- "Default",
- IconButton::new("default", IconName::Check)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "Filled",
- IconButton::new("filled", IconName::Check)
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .into_any_element(),
- ),
- single_example(
- "Subtle",
- IconButton::new("subtle", IconName::Check)
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Subtle)
- .into_any_element(),
- ),
- single_example(
- "Tinted",
- IconButton::new("tinted", IconName::Check)
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Tinted(TintColor::Accent))
- .into_any_element(),
- ),
- single_example(
- "Transparent",
- IconButton::new("transparent", IconName::Check)
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Transparent)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Icon Button Shapes",
- vec![
- single_example(
- "Square",
- IconButton::new("square", IconName::Check)
- .shape(IconButtonShape::Square)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "Wide",
- IconButton::new("wide", IconName::Check)
- .shape(IconButtonShape::Wide)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Icon Button Sizes",
- vec![
- single_example(
- "Small",
- IconButton::new("small", IconName::Check)
- .icon_size(IconSize::XSmall)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "Small",
- IconButton::new("small", IconName::Check)
- .icon_size(IconSize::Small)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "Medium",
- IconButton::new("medium", IconName::Check)
- .icon_size(IconSize::Medium)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "XLarge",
- IconButton::new("xlarge", IconName::Check)
- .icon_size(IconSize::XLarge)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Special States",
- vec![
- single_example(
- "Disabled",
- IconButton::new("disabled", IconName::Check)
- .disabled(true)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "Selected",
- IconButton::new("selected", IconName::Check)
- .toggle_state(true)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "With Indicator",
- IconButton::new("indicator", IconName::Check)
- .indicator(Indicator::dot().color(Color::Success))
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Custom Colors",
- vec![
- single_example(
- "Custom Icon Color",
- IconButton::new("custom_color", IconName::Check)
- .icon_color(Color::Accent)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- single_example(
- "With Alpha",
- IconButton::new("alpha", IconName::Check)
- .alpha(0.5)
- .style(ButtonStyle::Filled)
- .layer(ElevationIndex::Background)
- .into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+impl Component for IconButton {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn sort_name() -> &'static str {
+ "ButtonB"
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Icon Button Styles",
+ vec![
+ single_example(
+ "Default",
+ IconButton::new("default", IconName::Check)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "Filled",
+ IconButton::new("filled", IconName::Check)
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "Subtle",
+ IconButton::new("subtle", IconName::Check)
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Subtle)
+ .into_any_element(),
+ ),
+ single_example(
+ "Tinted",
+ IconButton::new("tinted", IconName::Check)
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Tinted(TintColor::Accent))
+ .into_any_element(),
+ ),
+ single_example(
+ "Transparent",
+ IconButton::new("transparent", IconName::Check)
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Transparent)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Icon Button Shapes",
+ vec![
+ single_example(
+ "Square",
+ IconButton::new("square", IconName::Check)
+ .shape(IconButtonShape::Square)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "Wide",
+ IconButton::new("wide", IconName::Check)
+ .shape(IconButtonShape::Wide)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Icon Button Sizes",
+ vec![
+ single_example(
+ "XSmall",
+ IconButton::new("xsmall", IconName::Check)
+ .icon_size(IconSize::XSmall)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "Small",
+ IconButton::new("small", IconName::Check)
+ .icon_size(IconSize::Small)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "Medium",
+ IconButton::new("medium", IconName::Check)
+ .icon_size(IconSize::Medium)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "XLarge",
+ IconButton::new("xlarge", IconName::Check)
+ .icon_size(IconSize::XLarge)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Special States",
+ vec![
+ single_example(
+ "Disabled",
+ IconButton::new("disabled", IconName::Check)
+ .disabled(true)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "Selected",
+ IconButton::new("selected", IconName::Check)
+ .toggle_state(true)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "With Indicator",
+ IconButton::new("indicator", IconName::Check)
+ .indicator(Indicator::dot().color(Color::Success))
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Custom Colors",
+ vec![
+ single_example(
+ "Custom Icon Color",
+ IconButton::new("custom_color", IconName::Check)
+ .icon_color(Color::Accent)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ single_example(
+ "With Alpha",
+ IconButton::new("alpha", IconName::Check)
+ .alpha(0.5)
+ .style(ButtonStyle::Filled)
+ .layer(ElevationIndex::Background)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -15,8 +15,7 @@ pub enum ToggleButtonPosition {
Last,
}
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Input")]
+#[derive(IntoElement, RegisterComponent)]
pub struct ToggleButton {
base: ButtonLike,
position_in_group: Option<ToggleButtonPosition>,
@@ -155,129 +154,139 @@ impl RenderOnce for ToggleButton {
}
}
-impl ComponentPreview for ToggleButton {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Button Styles",
- vec![
- single_example(
- "Off",
- ToggleButton::new("off", "Off")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .into_any_element(),
- ),
- single_example(
- "On",
- ToggleButton::new("on", "On")
- .layer(ElevationIndex::Background)
- .toggle_state(true)
- .style(ButtonStyle::Filled)
- .into_any_element(),
- ),
- single_example(
- "Off – Disabled",
- ToggleButton::new("disabled_off", "Disabled Off")
- .layer(ElevationIndex::Background)
- .disabled(true)
- .style(ButtonStyle::Filled)
- .into_any_element(),
- ),
- single_example(
- "On – Disabled",
- ToggleButton::new("disabled_on", "Disabled On")
- .layer(ElevationIndex::Background)
- .disabled(true)
- .toggle_state(true)
- .style(ButtonStyle::Filled)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Button Group",
- vec![
- single_example(
- "Three Buttons",
- h_flex()
- .child(
- ToggleButton::new("three_btn_first", "First")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .first()
- .into_any_element(),
- )
- .child(
- ToggleButton::new("three_btn_middle", "Middle")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .middle()
- .toggle_state(true)
- .into_any_element(),
- )
- .child(
- ToggleButton::new("three_btn_last", "Last")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .last()
- .into_any_element(),
- )
- .into_any_element(),
- ),
- single_example(
- "Two Buttons",
- h_flex()
- .child(
- ToggleButton::new("two_btn_first", "First")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .first()
- .into_any_element(),
- )
- .child(
- ToggleButton::new("two_btn_last", "Last")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .last()
- .into_any_element(),
- )
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Alternate Sizes",
- vec![
- single_example(
- "None",
- ToggleButton::new("none", "None")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .size(ButtonSize::None)
- .into_any_element(),
- ),
- single_example(
- "Compact",
- ToggleButton::new("compact", "Compact")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .size(ButtonSize::Compact)
- .into_any_element(),
- ),
- single_example(
- "Large",
- ToggleButton::new("large", "Large")
- .layer(ElevationIndex::Background)
- .style(ButtonStyle::Filled)
- .size(ButtonSize::Large)
- .into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+impl Component for ToggleButton {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn sort_name() -> &'static str {
+ "ButtonC"
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Button Styles",
+ vec![
+ single_example(
+ "Off",
+ ToggleButton::new("off", "Off")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "On",
+ ToggleButton::new("on", "On")
+ .layer(ElevationIndex::Background)
+ .toggle_state(true)
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "Off – Disabled",
+ ToggleButton::new("disabled_off", "Disabled Off")
+ .layer(ElevationIndex::Background)
+ .disabled(true)
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ single_example(
+ "On – Disabled",
+ ToggleButton::new("disabled_on", "Disabled On")
+ .layer(ElevationIndex::Background)
+ .disabled(true)
+ .toggle_state(true)
+ .style(ButtonStyle::Filled)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Button Group",
+ vec![
+ single_example(
+ "Three Buttons",
+ h_flex()
+ .child(
+ ToggleButton::new("three_btn_first", "First")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .first()
+ .into_any_element(),
+ )
+ .child(
+ ToggleButton::new("three_btn_middle", "Middle")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .middle()
+ .toggle_state(true)
+ .into_any_element(),
+ )
+ .child(
+ ToggleButton::new("three_btn_last", "Last")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .last()
+ .into_any_element(),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "Two Buttons",
+ h_flex()
+ .child(
+ ToggleButton::new("two_btn_first", "First")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .first()
+ .into_any_element(),
+ )
+ .child(
+ ToggleButton::new("two_btn_last", "Last")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .last()
+ .into_any_element(),
+ )
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Alternate Sizes",
+ vec![
+ single_example(
+ "None",
+ ToggleButton::new("none", "None")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .size(ButtonSize::None)
+ .into_any_element(),
+ ),
+ single_example(
+ "Compact",
+ ToggleButton::new("compact", "Compact")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .size(ButtonSize::Compact)
+ .into_any_element(),
+ ),
+ single_example(
+ "Large",
+ ToggleButton::new("large", "Large")
+ .layer(ElevationIndex::Background)
+ .style(ButtonStyle::Filled)
+ .size(ButtonSize::Large)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -1,5 +1,5 @@
+use crate::component_prelude::*;
use crate::prelude::*;
-use component::{ComponentPreview, example_group, single_example};
use gpui::{AnyElement, IntoElement, ParentElement, StyleRefinement, Styled};
use smallvec::SmallVec;
@@ -23,8 +23,7 @@ pub fn h_container() -> ContentGroup {
}
/// A flexible container component that can hold other elements.
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Layout")]
+#[derive(IntoElement, Documented, RegisterComponent)]
pub struct ContentGroup {
base: Div,
border: bool,
@@ -83,51 +82,56 @@ impl RenderOnce for ContentGroup {
this.border_1().border_color(cx.theme().colors().border)
})
.rounded_sm()
- .p_2()
.children(self.children)
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for ContentGroup {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- example_group(vec![
- single_example(
- "Default",
- ContentGroup::new()
- .flex_1()
- .items_center()
- .justify_center()
- .h_48()
- .child(Label::new("Default ContentBox"))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Without Border",
- ContentGroup::new()
- .flex_1()
- .items_center()
- .justify_center()
- .h_48()
- .borderless()
- .child(Label::new("Borderless ContentBox"))
- .into_any_element(),
- )
- .grow(),
- single_example(
- "Without Fill",
- ContentGroup::new()
- .flex_1()
- .items_center()
- .justify_center()
- .h_48()
- .unfilled()
- .child(Label::new("Unfilled ContentBox"))
- .into_any_element(),
- )
- .grow(),
- ])
- .into_any_element()
+impl Component for ContentGroup {
+ fn scope() -> ComponentScope {
+ ComponentScope::Layout
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(ContentGroup::DOCS)
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ example_group(vec![
+ single_example(
+ "Default",
+ ContentGroup::new()
+ .flex_1()
+ .items_center()
+ .justify_center()
+ .h_48()
+ .child(Label::new("Default ContentGroup"))
+ .into_any_element(),
+ ).description("A contained style for laying out groups of content. Has a default background and border color."),
+ single_example(
+ "Without Border",
+ ContentGroup::new()
+ .flex_1()
+ .items_center()
+ .justify_center()
+ .h_48()
+ .borderless()
+ .child(Label::new("Borderless ContentGroup"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Without Fill",
+ ContentGroup::new()
+ .flex_1()
+ .items_center()
+ .justify_center()
+ .h_48()
+ .unfilled()
+ .child(Label::new("Unfilled ContentGroup"))
+ .into_any_element(),
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -4,7 +4,7 @@ use gpui::{ClickEvent, CursorStyle};
use crate::{Color, IconButton, IconButtonShape, IconName, IconSize, prelude::*};
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Disclosure {
id: ElementId,
is_open: bool,
@@ -84,3 +84,55 @@ impl RenderOnce for Disclosure {
})
}
}
+
+impl Component for Disclosure {
+ fn scope() -> ComponentScope {
+ ComponentScope::Navigation
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "An interactive element used to show or hide content, typically used in expandable sections or tree-like structures.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Disclosure States",
+ vec![
+ single_example(
+ "Closed",
+ Disclosure::new("closed", false).into_any_element(),
+ ),
+ single_example(
+ "Open",
+ Disclosure::new("open", true).into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Interactive Example",
+ vec![single_example(
+ "Toggleable",
+ v_flex()
+ .gap_2()
+ .child(
+ Disclosure::new("interactive", false)
+ // .on_toggle(Some(Arc::new(|_, _, cx| {
+ // cx.refresh();
+ // })))
+ .into_any_element(),
+ )
+ .child(Label::new("Click to toggle"))
+ .into_any_element(),
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -49,7 +49,7 @@ impl DividerColor {
}
}
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Divider {
style: DividerStyle,
direction: DividerDirection,
@@ -158,3 +158,90 @@ impl Divider {
)
}
}
+
+impl Component for Divider {
+ fn scope() -> ComponentScope {
+ ComponentScope::Layout
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "Visual separator used to create divisions between groups of content or sections in a layout.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Horizontal Dividers",
+ vec![
+ single_example("Default", Divider::horizontal().into_any_element()),
+ single_example(
+ "Border Color",
+ Divider::horizontal()
+ .color(DividerColor::Border)
+ .into_any_element(),
+ ),
+ single_example(
+ "Inset",
+ Divider::horizontal().inset().into_any_element(),
+ ),
+ single_example(
+ "Dashed",
+ Divider::horizontal_dashed().into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Vertical Dividers",
+ vec![
+ single_example(
+ "Default",
+ div().h_16().child(Divider::vertical()).into_any_element(),
+ ),
+ single_example(
+ "Border Color",
+ div()
+ .h_16()
+ .child(Divider::vertical().color(DividerColor::Border))
+ .into_any_element(),
+ ),
+ single_example(
+ "Inset",
+ div()
+ .h_16()
+ .child(Divider::vertical().inset())
+ .into_any_element(),
+ ),
+ single_example(
+ "Dashed",
+ div()
+ .h_16()
+ .child(Divider::vertical_dashed())
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Example Usage",
+ vec![single_example(
+ "Between Content",
+ v_flex()
+ .gap_4()
+ .px_4()
+ .child(Label::new("Section One"))
+ .child(Divider::horizontal())
+ .child(Label::new("Section Two"))
+ .child(Divider::horizontal_dashed())
+ .child(Label::new("Section Three"))
+ .into_any_element(),
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -7,7 +7,7 @@ enum LabelKind {
Element(AnyElement),
}
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct DropdownMenu {
id: ElementId,
label: LabelKind,
@@ -72,6 +72,69 @@ impl RenderOnce for DropdownMenu {
}
}
+impl Component for DropdownMenu {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn name() -> &'static str {
+ "DropdownMenu"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "A dropdown menu displays a list of actions or options. A dropdown menu is always activated by clicking a trigger (or via a keybinding).",
+ )
+ }
+
+ fn preview(window: &mut Window, cx: &mut App) -> Option<AnyElement> {
+ let menu = ContextMenu::build(window, cx, |this, _, _| {
+ this.entry("Option 1", None, |_, _| {})
+ .entry("Option 2", None, |_, _| {})
+ .entry("Option 3", None, |_, _| {})
+ .separator()
+ .entry("Option 4", None, |_, _| {})
+ });
+
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Default",
+ DropdownMenu::new("default", "Select an option", menu.clone())
+ .into_any_element(),
+ ),
+ single_example(
+ "Full Width",
+ DropdownMenu::new(
+ "full-width",
+ "Full Width Dropdown",
+ menu.clone(),
+ )
+ .full_width(true)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "States",
+ vec![single_example(
+ "Disabled",
+ DropdownMenu::new("disabled", "Disabled Dropdown", menu.clone())
+ .disabled(true)
+ .into_any_element(),
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
+
#[derive(IntoElement)]
struct DropdownMenuTrigger {
label: LabelKind,
@@ -1,13 +1,31 @@
-use crate::{Avatar, prelude::*};
+use crate::component_prelude::*;
+use crate::prelude::*;
use gpui::{AnyElement, StyleRefinement};
use smallvec::SmallVec;
-/// A facepile is a collection of faces stacked horizontally–
-/// always with the leftmost face on top and descending in z-index
+use super::Avatar;
+
+/// An element that displays a collection of (usually) faces stacked
+/// horizontally, with the left-most face on top, visually descending
+/// from left to right.
///
/// Facepiles are used to display a group of people or things,
/// such as a list of participants in a collaboration session.
-#[derive(IntoElement, IntoComponent)]
+///
+/// # Examples
+///
+/// ## Default
+///
+/// A default, horizontal facepile.
+///
+/// ```
+/// use ui::{Avatar, Facepile, EXAMPLE_FACES};
+///
+/// Facepile::new(
+/// EXAMPLE_FACES.iter().take(3).iter().map(|&url|
+/// Avatar::new(url).into_any_element()).collect())
+/// ```
+#[derive(IntoElement, Documented, RegisterComponent)]
pub struct Facepile {
base: Div,
faces: SmallVec<[AnyElement; 2]>,
@@ -60,27 +78,37 @@ impl RenderOnce for Facepile {
}
}
-impl ComponentPreview for Facepile {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- let faces: [&'static str; 6] = [
- "https://avatars.githubusercontent.com/u/326587?s=60&v=4",
- "https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
- "https://avatars.githubusercontent.com/u/1789?s=60&v=4",
- "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
- "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
- "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
- ];
+pub const EXAMPLE_FACES: [&'static str; 6] = [
+ "https://avatars.githubusercontent.com/u/326587?s=60&v=4",
+ "https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
+ "https://avatars.githubusercontent.com/u/1789?s=60&v=4",
+ "https://avatars.githubusercontent.com/u/67129314?s=60&v=4",
+ "https://avatars.githubusercontent.com/u/482957?s=60&v=4",
+ "https://avatars.githubusercontent.com/u/1714999?s=60&v=4",
+];
+
+impl Component for Facepile {
+ fn scope() -> ComponentScope {
+ ComponentScope::Collaboration
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "Displays a collection of avatars or initials in a compact format. Often used to represent active collaborators or a subset of contributors.",
+ )
+ }
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![example_group_with_title(
"Facepile Examples",
vec![
single_example(
"Default",
Facepile::new(
- faces
+ EXAMPLE_FACES
.iter()
.map(|&url| Avatar::new(url).into_any_element())
.collect(),
@@ -90,7 +118,7 @@ impl ComponentPreview for Facepile {
single_example(
"Custom Size",
Facepile::new(
- faces
+ EXAMPLE_FACES
.iter()
.map(|&url| Avatar::new(url).size(px(24.)).into_any_element())
.collect(),
@@ -98,19 +126,8 @@ impl ComponentPreview for Facepile {
.into_any_element(),
),
],
- ),
- example_group_with_title(
- "Special Cases",
- vec![
- single_example("Empty Facepile", Facepile::empty().into_any_element()),
- single_example(
- "Single Face",
- Facepile::new(vec![Avatar::new(faces[0]).into_any_element()].into())
- .into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+ )])
+ .into_any_element(),
+ )
}
}
@@ -136,7 +136,7 @@ impl IconSource {
}
}
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Icon {
source: IconSource,
color: Color,
@@ -265,43 +265,54 @@ impl RenderOnce for IconWithIndicator {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Icon {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Sizes",
- vec![
- single_example("Default", Icon::new(IconName::Star).into_any_element()),
- single_example(
- "Small",
- Icon::new(IconName::Star)
- .size(IconSize::Small)
- .into_any_element(),
- ),
- single_example(
- "Large",
- Icon::new(IconName::Star)
- .size(IconSize::XLarge)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Colors",
- vec![
- single_example("Default", Icon::new(IconName::Bell).into_any_element()),
- single_example(
- "Custom Color",
- Icon::new(IconName::Bell)
- .color(Color::Error)
- .into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+impl Component for Icon {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "A versatile icon component that supports SVG and image-based icons with customizable size, color, and transformations.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Sizes",
+ vec![
+ single_example("Default", Icon::new(IconName::Star).into_any_element()),
+ single_example(
+ "Small",
+ Icon::new(IconName::Star)
+ .size(IconSize::Small)
+ .into_any_element(),
+ ),
+ single_example(
+ "Large",
+ Icon::new(IconName::Star)
+ .size(IconSize::XLarge)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Colors",
+ vec![
+ single_example("Default", Icon::new(IconName::Bell).into_any_element()),
+ single_example(
+ "Custom Color",
+ Icon::new(IconName::Bell)
+ .color(Color::Error)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -2,7 +2,7 @@ use gpui::{AnyElement, IntoElement, Point};
use crate::{IconDecoration, IconDecorationKind, prelude::*};
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, RegisterComponent)]
pub struct DecoratedIcon {
icon: Icon,
decoration: Option<IconDecoration>,
@@ -24,9 +24,18 @@ impl RenderOnce for DecoratedIcon {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for DecoratedIcon {
- fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
+impl Component for DecoratedIcon {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "An icon with an optional decoration overlay (like an X, triangle, or dot) that can be positioned relative to the icon",
+ )
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let decoration_x = IconDecoration::new(
IconDecorationKind::X,
cx.theme().colors().surface_background,
@@ -60,32 +69,38 @@ impl ComponentPreview for DecoratedIcon {
y: px(-2.),
});
- v_flex()
- .gap_6()
- .children(vec![example_group_with_title(
- "Decorations",
- vec![
- single_example(
- "No Decoration",
- DecoratedIcon::new(Icon::new(IconName::FileDoc), None).into_any_element(),
- ),
- single_example(
- "X Decoration",
- DecoratedIcon::new(Icon::new(IconName::FileDoc), Some(decoration_x))
- .into_any_element(),
- ),
- single_example(
- "Triangle Decoration",
- DecoratedIcon::new(Icon::new(IconName::FileDoc), Some(decoration_triangle))
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![example_group_with_title(
+ "Decorations",
+ vec![
+ single_example(
+ "No Decoration",
+ DecoratedIcon::new(Icon::new(IconName::FileDoc), None)
+ .into_any_element(),
+ ),
+ single_example(
+ "X Decoration",
+ DecoratedIcon::new(Icon::new(IconName::FileDoc), Some(decoration_x))
+ .into_any_element(),
+ ),
+ single_example(
+ "Triangle Decoration",
+ DecoratedIcon::new(
+ Icon::new(IconName::FileDoc),
+ Some(decoration_triangle),
+ )
.into_any_element(),
- ),
- single_example(
- "Dot Decoration",
- DecoratedIcon::new(Icon::new(IconName::FileDoc), Some(decoration_dot))
- .into_any_element(),
- ),
- ],
- )])
- .into_any_element()
+ ),
+ single_example(
+ "Dot Decoration",
+ DecoratedIcon::new(Icon::new(IconName::FileDoc), Some(decoration_dot))
+ .into_any_element(),
+ ),
+ ],
+ )])
+ .into_any_element(),
+ )
}
}
@@ -4,6 +4,7 @@ use strum::{EnumIter, EnumString, IntoStaticStr};
use ui_macros::{DerivePathStr, path_str};
use crate::Color;
+use crate::prelude::*;
#[derive(
Debug,
@@ -30,7 +31,7 @@ pub enum VectorName {
/// A [`Vector`] is different from an [`crate::Icon`] in that it is intended
/// to be displayed at a specific size, or series of sizes, rather
/// than conforming to the standard size of an icon.
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Vector {
path: &'static str,
color: Color,
@@ -61,7 +62,6 @@ impl Vector {
/// Sets the vector size.
pub fn size(mut self, size: impl Into<Size<Rems>>) -> Self {
let size = size.into();
-
self.size = size;
self
}
@@ -83,24 +83,72 @@ impl RenderOnce for Vector {
}
}
-#[cfg(feature = "stories")]
-pub mod story {
- use gpui::Render;
- use story::{Story, StoryItem, StorySection};
- use strum::IntoEnumIterator;
-
- use crate::prelude::*;
+impl Component for Vector {
+ fn scope() -> ComponentScope {
+ ComponentScope::Images
+ }
- use super::{Vector, VectorName};
+ fn name() -> &'static str {
+ "Vector"
+ }
- pub struct VectorStory;
+ fn description() -> Option<&'static str> {
+ Some("A vector image component that can be displayed at specific sizes.")
+ }
- impl Render for VectorStory {
- fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
- Story::container().child(StorySection::new().children(VectorName::iter().map(
- |vector| StoryItem::new(format!("{:?}", vector), Vector::square(vector, rems(8.))),
- )))
- }
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Default",
+ Vector::square(VectorName::ZedLogo, rems(8.)).into_any_element(),
+ ),
+ single_example(
+ "Custom Size",
+ Vector::new(VectorName::ZedLogo, rems(12.), rems(6.))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Colored",
+ vec![
+ single_example(
+ "Accent Color",
+ Vector::square(VectorName::ZedLogo, rems(8.))
+ .color(Color::Accent)
+ .into_any_element(),
+ ),
+ single_example(
+ "Error Color",
+ Vector::square(VectorName::ZedLogo, rems(8.))
+ .color(Color::Error)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Different Vectors",
+ vec![
+ single_example(
+ "Zed Logo",
+ Vector::square(VectorName::ZedLogo, rems(8.)).into_any_element(),
+ ),
+ single_example(
+ "Zed X Copilot",
+ Vector::square(VectorName::ZedXCopilot, rems(8.))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -1,4 +1,5 @@
-use crate::{AnyIcon, prelude::*};
+use super::AnyIcon;
+use crate::prelude::*;
#[derive(Default)]
enum IndicatorKind {
@@ -8,7 +9,7 @@ enum IndicatorKind {
Icon(AnyIcon),
}
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Indicator {
kind: IndicatorKind,
border_color: Option<Color>,
@@ -82,3 +83,95 @@ impl RenderOnce for Indicator {
}
}
}
+
+impl Component for Indicator {
+ fn scope() -> ComponentScope {
+ ComponentScope::Status
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "Visual indicators used to represent status, notifications, or draw attention to specific elements.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Dot Indicators",
+ vec![
+ single_example("Default", Indicator::dot().into_any_element()),
+ single_example(
+ "Success",
+ Indicator::dot().color(Color::Success).into_any_element(),
+ ),
+ single_example(
+ "Warning",
+ Indicator::dot().color(Color::Warning).into_any_element(),
+ ),
+ single_example(
+ "Error",
+ Indicator::dot().color(Color::Error).into_any_element(),
+ ),
+ single_example(
+ "With Border",
+ Indicator::dot()
+ .color(Color::Accent)
+ .border_color(Color::Default)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Bar Indicators",
+ vec![
+ single_example("Default", Indicator::bar().into_any_element()),
+ single_example(
+ "Success",
+ Indicator::bar().color(Color::Success).into_any_element(),
+ ),
+ single_example(
+ "Warning",
+ Indicator::bar().color(Color::Warning).into_any_element(),
+ ),
+ single_example(
+ "Error",
+ Indicator::bar().color(Color::Error).into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Icon Indicators",
+ vec![
+ single_example(
+ "Default",
+ Indicator::icon(Icon::new(IconName::Circle)).into_any_element(),
+ ),
+ single_example(
+ "Success",
+ Indicator::icon(Icon::new(IconName::Check))
+ .color(Color::Success)
+ .into_any_element(),
+ ),
+ single_example(
+ "Warning",
+ Indicator::icon(Icon::new(IconName::Warning))
+ .color(Color::Warning)
+ .into_any_element(),
+ ),
+ single_example(
+ "Error",
+ Indicator::icon(Icon::new(IconName::X))
+ .color(Color::Error)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -6,7 +6,7 @@ use gpui::{
};
use itertools::Itertools;
-#[derive(Debug, IntoElement, Clone)]
+#[derive(Debug, IntoElement, Clone, RegisterComponent)]
pub struct KeyBinding {
/// A keybinding consists of a key and a set of modifier keys.
/// More then one keybinding produces a chord.
@@ -449,6 +449,93 @@ fn keystroke_text(keystroke: &Keystroke, platform_style: PlatformStyle, vim_mode
text
}
+impl Component for KeyBinding {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn name() -> &'static str {
+ "KeyBinding"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "A component that displays a key binding, supporting different platform styles and vim mode.",
+ )
+ }
+
+ fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Default",
+ KeyBinding::new(
+ gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "Mac Style",
+ KeyBinding::new(
+ gpui::KeyBinding::new("cmd-s", gpui::NoAction, None),
+ cx,
+ )
+ .platform_style(PlatformStyle::Mac)
+ .into_any_element(),
+ ),
+ single_example(
+ "Windows Style",
+ KeyBinding::new(
+ gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
+ cx,
+ )
+ .platform_style(PlatformStyle::Windows)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Vim Mode",
+ vec![single_example(
+ "Vim Mode Enabled",
+ KeyBinding::new(gpui::KeyBinding::new("dd", gpui::NoAction, None), cx)
+ .vim_mode(true)
+ .into_any_element(),
+ )],
+ ),
+ example_group_with_title(
+ "Complex Bindings",
+ vec![
+ single_example(
+ "Multiple Keys",
+ KeyBinding::new(
+ gpui::KeyBinding::new("ctrl-k ctrl-b", gpui::NoAction, None),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "With Shift",
+ KeyBinding::new(
+ gpui::KeyBinding::new("shift-cmd-p", gpui::NoAction, None),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -18,7 +18,7 @@ use theme::Appearance;
/// .prefix("Save:")
/// .size(Pixels::from(14.0));
/// ```
-#[derive(Debug, IntoElement, IntoComponent)]
+#[derive(Debug, IntoElement, RegisterComponent)]
pub struct KeybindingHint {
prefix: Option<SharedString>,
suffix: Option<SharedString>,
@@ -205,68 +205,81 @@ impl RenderOnce for KeybindingHint {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for KeybindingHint {
- fn preview(window: &mut Window, cx: &mut App) -> AnyElement {
+impl Component for KeybindingHint {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("Displays a keyboard shortcut hint with optional prefix and suffix text")
+ }
+
+ fn preview(window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let enter_fallback = gpui::KeyBinding::new("enter", menu::Confirm, None);
let enter = KeyBinding::for_action(&menu::Confirm, window, cx)
.unwrap_or(KeyBinding::new(enter_fallback, cx));
let bg_color = cx.theme().colors().surface_background;
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Basic",
- vec![
- single_example(
- "With Prefix",
- KeybindingHint::with_prefix("Go to Start:", enter.clone(), bg_color)
- .into_any_element(),
- ),
- single_example(
- "With Suffix",
- KeybindingHint::with_suffix(enter.clone(), "Go to End", bg_color)
- .into_any_element(),
- ),
- single_example(
- "With Prefix and Suffix",
- KeybindingHint::new(enter.clone(), bg_color)
- .prefix("Confirm:")
- .suffix("Execute selected action")
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Sizes",
- vec![
- single_example(
- "Small",
- KeybindingHint::new(enter.clone(), bg_color)
- .size(Pixels::from(12.0))
- .prefix("Small:")
- .into_any_element(),
- ),
- single_example(
- "Medium",
- KeybindingHint::new(enter.clone(), bg_color)
- .size(Pixels::from(16.0))
- .suffix("Medium")
- .into_any_element(),
- ),
- single_example(
- "Large",
- KeybindingHint::new(enter.clone(), bg_color)
- .size(Pixels::from(20.0))
- .prefix("Large:")
- .suffix("Size")
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic",
+ vec![
+ single_example(
+ "With Prefix",
+ KeybindingHint::with_prefix(
+ "Go to Start:",
+ enter.clone(),
+ bg_color,
+ )
.into_any_element(),
- ),
- ],
- ),
- ])
- .into_any_element()
+ ),
+ single_example(
+ "With Suffix",
+ KeybindingHint::with_suffix(enter.clone(), "Go to End", bg_color)
+ .into_any_element(),
+ ),
+ single_example(
+ "With Prefix and Suffix",
+ KeybindingHint::new(enter.clone(), bg_color)
+ .prefix("Confirm:")
+ .suffix("Execute selected action")
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Sizes",
+ vec![
+ single_example(
+ "Small",
+ KeybindingHint::new(enter.clone(), bg_color)
+ .size(Pixels::from(12.0))
+ .prefix("Small:")
+ .into_any_element(),
+ ),
+ single_example(
+ "Medium",
+ KeybindingHint::new(enter.clone(), bg_color)
+ .size(Pixels::from(16.0))
+ .suffix("Medium")
+ .into_any_element(),
+ ),
+ single_example(
+ "Large",
+ KeybindingHint::new(enter.clone(), bg_color)
+ .size(Pixels::from(20.0))
+ .prefix("Large:")
+ .suffix("Size")
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -4,7 +4,7 @@ use gpui::{FontWeight, HighlightStyle, StyledText};
use crate::{LabelCommon, LabelLike, LabelSize, LineHeightStyle, prelude::*};
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct HighlightedLabel {
base: LabelLike,
label: SharedString,
@@ -129,3 +129,99 @@ impl RenderOnce for HighlightedLabel {
.child(StyledText::new(self.label).with_default_highlights(&text_style, highlights))
}
}
+
+impl Component for HighlightedLabel {
+ fn scope() -> ComponentScope {
+ ComponentScope::Typography
+ }
+
+ fn name() -> &'static str {
+ "HighlightedLabel"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A label with highlighted characters based on specified indices.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Default",
+ HighlightedLabel::new("Highlighted Text", vec![0, 1, 2, 3]).into_any_element(),
+ ),
+ single_example(
+ "Custom Color",
+ HighlightedLabel::new("Colored Highlight", vec![0, 1, 7, 8, 9])
+ .color(Color::Accent)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Styles",
+ vec![
+ single_example(
+ "Bold",
+ HighlightedLabel::new("Bold Highlight", vec![0, 1, 2, 3])
+ .weight(FontWeight::BOLD)
+ .into_any_element(),
+ ),
+ single_example(
+ "Italic",
+ HighlightedLabel::new("Italic Highlight", vec![0, 1, 6, 7, 8])
+ .italic()
+ .into_any_element(),
+ ),
+ single_example(
+ "Underline",
+ HighlightedLabel::new("Underlined Highlight", vec![0, 1, 10, 11, 12])
+ .underline()
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Sizes",
+ vec![
+ single_example(
+ "Small",
+ HighlightedLabel::new("Small Highlight", vec![0, 1, 5, 6, 7])
+ .size(LabelSize::Small)
+ .into_any_element(),
+ ),
+ single_example(
+ "Large",
+ HighlightedLabel::new("Large Highlight", vec![0, 1, 5, 6, 7])
+ .size(LabelSize::Large)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Special Cases",
+ vec![
+ single_example(
+ "Single Line",
+ HighlightedLabel::new("Single Line Highlight\nWith Newline", vec![0, 1, 7, 8, 9])
+ .single_line()
+ .into_any_element(),
+ ),
+ single_example(
+ "Truncate",
+ HighlightedLabel::new("This is a very long text that should be truncated with highlights", vec![0, 1, 2, 3, 4, 5])
+ .truncate()
+ .into_any_element(),
+ ),
+ ],
+ ),
+ ])
+ .into_any_element()
+ )
+ }
+}
@@ -29,7 +29,7 @@ use gpui::StyleRefinement;
///
/// let my_label = Label::new("Deleted").strikethrough(true);
/// ```
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Label {
base: LabelLike,
label: SharedString,
@@ -58,9 +58,6 @@ impl Label {
}
}
-// nate: If we are going to do this, we might as well just
-// impl Styled for Label and not constrain styles
-
// Style methods.
impl Label {
fn style(&mut self) -> &mut StyleRefinement {
@@ -200,12 +197,17 @@ impl RenderOnce for Label {
}
}
-mod label_preview {
- use crate::prelude::*;
+impl Component for Label {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
- // View this component preview using `workspace: open component-preview`
- impl ComponentPreview for Label {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+ fn description() -> Option<&'static str> {
+ Some("A text label component that supports various styles, sizes, and formatting options.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
v_flex()
.gap_6()
.children(vec![
@@ -251,6 +253,6 @@ mod label_preview {
),
])
.into_any_element()
- }
+ )
}
}
@@ -232,3 +232,70 @@ impl RenderOnce for LabelLike {
.children(self.children)
}
}
+
+impl Component for LabelLike {
+ fn scope() -> ComponentScope {
+ ComponentScope::Typography
+ }
+
+ fn name() -> &'static str {
+ "LabelLike"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "A flexible, customizable label-like component that serves as a base for other label types.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Sizes",
+ vec![
+ single_example("Default", LabelLike::new().child("Default size").into_any_element()),
+ single_example("Large", LabelLike::new().size(LabelSize::Large).child("Large size").into_any_element()),
+ single_example("Small", LabelLike::new().size(LabelSize::Small).child("Small size").into_any_element()),
+ single_example("XSmall", LabelLike::new().size(LabelSize::XSmall).child("Extra small size").into_any_element()),
+ ],
+ ),
+ example_group_with_title(
+ "Styles",
+ vec![
+ single_example("Bold", LabelLike::new().weight(FontWeight::BOLD).child("Bold text").into_any_element()),
+ single_example("Italic", LabelLike::new().italic().child("Italic text").into_any_element()),
+ single_example("Underline", LabelLike::new().underline().child("Underlined text").into_any_element()),
+ single_example("Strikethrough", LabelLike::new().strikethrough().child("Strikethrough text").into_any_element()),
+ ],
+ ),
+ example_group_with_title(
+ "Colors",
+ vec![
+ single_example("Default", LabelLike::new().child("Default color").into_any_element()),
+ single_example("Accent", LabelLike::new().color(Color::Accent).child("Accent color").into_any_element()),
+ single_example("Error", LabelLike::new().color(Color::Error).child("Error color").into_any_element()),
+ single_example("Alpha", LabelLike::new().alpha(0.5).child("50% opacity").into_any_element()),
+ ],
+ ),
+ example_group_with_title(
+ "Line Height",
+ vec![
+ single_example("Default", LabelLike::new().child("Default line height\nMulti-line text").into_any_element()),
+ single_example("UI Label", LabelLike::new().line_height_style(LineHeightStyle::UiLabel).child("UI label line height\nMulti-line text").into_any_element()),
+ ],
+ ),
+ example_group_with_title(
+ "Special Cases",
+ vec![
+ single_example("Single Line", LabelLike::new().single_line().child("This is a very long text that should be displayed in a single line").into_any_element()),
+ single_example("Truncate", LabelLike::new().truncate().child("This is a very long text that should be truncated with an ellipsis").into_any_element()),
+ ],
+ ),
+ ])
+ .into_any_element()
+ )
+ }
+}
@@ -2,8 +2,7 @@ use crate::prelude::*;
use gpui::IntoElement;
use smallvec::{SmallVec, smallvec};
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Notification")]
+#[derive(IntoElement, RegisterComponent)]
pub struct AlertModal {
id: ElementId,
children: SmallVec<[AnyElement; 2]>,
@@ -77,23 +76,33 @@ impl ParentElement for AlertModal {
}
}
-impl ComponentPreview for AlertModal {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .p_4()
- .children(vec![example_group(
- vec![
- single_example(
- "Basic Alert",
- AlertModal::new("simple-modal", "Do you want to leave the current call?")
- .child("The current window will be closed, and connections to any shared projects will be terminated."
- )
- .primary_action("Leave Call")
- .into_any_element(),
- )
- ],
- )])
- .into_any_element()
+impl Component for AlertModal {
+ fn scope() -> ComponentScope {
+ ComponentScope::Notification
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A modal dialog that presents an alert message with primary and dismiss actions.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .p_4()
+ .children(vec![example_group(
+ vec![
+ single_example(
+ "Basic Alert",
+ AlertModal::new("simple-modal", "Do you want to leave the current call?")
+ .child("The current window will be closed, and connections to any shared projects will be terminated."
+ )
+ .primary_action("Leave Call")
+ .into_any_element(),
+ )
+ ],
+ )])
+ .into_any_element()
+ )
}
}
@@ -3,7 +3,9 @@ use smallvec::SmallVec;
use crate::prelude::*;
-#[derive(IntoElement)]
+use super::Checkbox;
+
+#[derive(IntoElement, RegisterComponent)]
pub struct SettingsContainer {
children: SmallVec<[AnyElement; 2]>,
}
@@ -33,3 +35,55 @@ impl RenderOnce for SettingsContainer {
v_flex().px_2().gap_1().children(self.children)
}
}
+
+impl Component for SettingsContainer {
+ fn scope() -> ComponentScope {
+ ComponentScope::Layout
+ }
+
+ fn name() -> &'static str {
+ "SettingsContainer"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A container for organizing and displaying settings in a structured manner.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Empty Container",
+ SettingsContainer::new().into_any_element(),
+ ),
+ single_example(
+ "With Content",
+ SettingsContainer::new()
+ .child(Label::new("Setting 1"))
+ .child(Label::new("Setting 2"))
+ .child(Label::new("Setting 3"))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "With Different Elements",
+ vec![single_example(
+ "Mixed Content",
+ SettingsContainer::new()
+ .child(Label::new("Text Setting"))
+ .child(Checkbox::new("checkbox", ToggleState::Unselected))
+ .child(Button::new("button", "Click me"))
+ .into_any_element(),
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -3,8 +3,10 @@ use smallvec::SmallVec;
use crate::{ListHeader, prelude::*};
+use super::Checkbox;
+
/// A group of settings.
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct SettingsGroup {
header: SharedString,
children: SmallVec<[AnyElement; 2]>,
@@ -34,3 +36,75 @@ impl RenderOnce for SettingsGroup {
.children(self.children)
}
}
+
+impl Component for SettingsGroup {
+ fn scope() -> ComponentScope {
+ ComponentScope::Layout
+ }
+
+ fn name() -> &'static str {
+ "SettingsGroup"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A group of settings with a header, used to organize related settings.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Empty Group",
+ SettingsGroup::new("General Settings").into_any_element(),
+ ),
+ single_example(
+ "With Children",
+ SettingsGroup::new("Appearance")
+ .child(
+ Checkbox::new("dark_mode", ToggleState::Unselected)
+ .label("Dark Mode"),
+ )
+ .child(
+ Checkbox::new("high_contrast", ToggleState::Unselected)
+ .label("High Contrast"),
+ )
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Multiple Groups",
+ vec![single_example(
+ "Two Groups",
+ v_flex()
+ .gap_4()
+ .child(
+ SettingsGroup::new("General").child(
+ Checkbox::new("auto_update", ToggleState::Selected)
+ .label("Auto Update"),
+ ),
+ )
+ .child(
+ SettingsGroup::new("Editor")
+ .child(
+ Checkbox::new("line_numbers", ToggleState::Selected)
+ .label("Show Line Numbers"),
+ )
+ .child(
+ Checkbox::new("word_wrap", ToggleState::Unselected)
+ .label("Word Wrap"),
+ ),
+ )
+ .into_any_element(),
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -1,6 +1,4 @@
mod context_menu;
-mod disclosure;
-mod icon;
mod icon_button;
mod keybinding;
mod list;
@@ -11,8 +9,6 @@ mod tab_bar;
mod toggle_button;
pub use context_menu::*;
-pub use disclosure::*;
-pub use icon::*;
pub use icon_button::*;
pub use keybinding::*;
pub use list::*;
@@ -1,20 +0,0 @@
-use gpui::Render;
-use story::Story;
-use strum::IntoEnumIterator;
-
-use crate::prelude::*;
-use crate::{Icon, IconName};
-
-pub struct IconStory;
-
-impl Render for IconStory {
- fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
- let icons = IconName::iter();
-
- Story::container()
- .child(Story::title_for::<Icon>())
- .child(Story::label("DecoratedIcon"))
- .child(Story::label("All Icons"))
- .child(div().flex().gap_3().children(icons.map(Icon::new)))
- }
-}
@@ -26,7 +26,7 @@ pub enum TabCloseSide {
End,
}
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Tab {
div: Stateful<Div>,
selected: bool,
@@ -171,48 +171,59 @@ impl RenderOnce for Tab {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Tab {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![example_group_with_title(
- "Variations",
- vec![
- single_example(
- "Default",
- Tab::new("default").child("Default Tab").into_any_element(),
- ),
- single_example(
- "Selected",
- Tab::new("selected")
- .toggle_state(true)
- .child("Selected Tab")
- .into_any_element(),
- ),
- single_example(
- "First",
- Tab::new("first")
- .position(TabPosition::First)
- .child("First Tab")
- .into_any_element(),
- ),
- single_example(
- "Middle",
- Tab::new("middle")
- .position(TabPosition::Middle(Ordering::Equal))
- .child("Middle Tab")
- .into_any_element(),
- ),
- single_example(
- "Last",
- Tab::new("last")
- .position(TabPosition::Last)
- .child("Last Tab")
- .into_any_element(),
- ),
- ],
- )])
- .into_any_element()
+impl Component for Tab {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "A tab component that can be used in a tabbed interface, supporting different positions and states.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![example_group_with_title(
+ "Variations",
+ vec![
+ single_example(
+ "Default",
+ Tab::new("default").child("Default Tab").into_any_element(),
+ ),
+ single_example(
+ "Selected",
+ Tab::new("selected")
+ .toggle_state(true)
+ .child("Selected Tab")
+ .into_any_element(),
+ ),
+ single_example(
+ "First",
+ Tab::new("first")
+ .position(TabPosition::First)
+ .child("First Tab")
+ .into_any_element(),
+ ),
+ single_example(
+ "Middle",
+ Tab::new("middle")
+ .position(TabPosition::Middle(Ordering::Equal))
+ .child("Middle Tab")
+ .into_any_element(),
+ ),
+ single_example(
+ "Last",
+ Tab::new("last")
+ .position(TabPosition::Last)
+ .child("Last Tab")
+ .into_any_element(),
+ ),
+ ],
+ )])
+ .into_any_element(),
+ )
}
}
@@ -4,7 +4,7 @@ use smallvec::SmallVec;
use crate::Tab;
use crate::prelude::*;
-#[derive(IntoElement)]
+#[derive(IntoElement, RegisterComponent)]
pub struct TabBar {
id: ElementId,
start_children: SmallVec<[AnyElement; 2]>,
@@ -151,3 +151,57 @@ impl RenderOnce for TabBar {
})
}
}
+
+impl Component for TabBar {
+ fn scope() -> ComponentScope {
+ ComponentScope::Navigation
+ }
+
+ fn name() -> &'static str {
+ "TabBar"
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A horizontal bar containing tabs for navigation between different views or sections.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Usage",
+ vec![
+ single_example(
+ "Empty TabBar",
+ TabBar::new("empty_tab_bar").into_any_element(),
+ ),
+ single_example(
+ "With Tabs",
+ TabBar::new("tab_bar_with_tabs")
+ .child(Tab::new("tab1"))
+ .child(Tab::new("tab2"))
+ .child(Tab::new("tab3"))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "With Start and End Children",
+ vec![single_example(
+ "Full TabBar",
+ TabBar::new("full_tab_bar")
+ .start_child(Button::new("start_button", "Start"))
+ .child(Tab::new("tab1"))
+ .child(Tab::new("tab2"))
+ .child(Tab::new("tab3"))
+ .end_child(Button::new("end_button", "End"))
+ .into_any_element(),
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -2,7 +2,7 @@ use crate::{Indicator, prelude::*};
use gpui::{AnyElement, FontWeight, IntoElement, Length, div};
/// A table component
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Table {
column_headers: Vec<SharedString>,
rows: Vec<Vec<TableCell>>,
@@ -151,112 +151,121 @@ where
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Table {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Basic Tables",
- vec![
- single_example(
- "Simple Table",
- Table::new(vec!["Name", "Age", "City"])
- .width(px(400.))
- .row(vec!["Alice", "28", "New York"])
- .row(vec!["Bob", "32", "San Francisco"])
- .row(vec!["Charlie", "25", "London"])
- .into_any_element(),
- ),
- single_example(
- "Two Column Table",
- Table::new(vec!["Category", "Value"])
- .width(px(300.))
- .row(vec!["Revenue", "$100,000"])
- .row(vec!["Expenses", "$75,000"])
- .row(vec!["Profit", "$25,000"])
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Styled Tables",
- vec![
- single_example(
- "Default",
- Table::new(vec!["Product", "Price", "Stock"])
- .width(px(400.))
- .row(vec!["Laptop", "$999", "In Stock"])
- .row(vec!["Phone", "$599", "Low Stock"])
- .row(vec!["Tablet", "$399", "Out of Stock"])
- .into_any_element(),
- ),
- single_example(
- "Striped",
- Table::new(vec!["Product", "Price", "Stock"])
- .width(px(400.))
- .striped()
- .row(vec!["Laptop", "$999", "In Stock"])
- .row(vec!["Phone", "$599", "Low Stock"])
- .row(vec!["Tablet", "$399", "Out of Stock"])
- .row(vec!["Headphones", "$199", "In Stock"])
+impl Component for Table {
+ fn scope() -> ComponentScope {
+ ComponentScope::Layout
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A table component for displaying data in rows and columns with optional styling.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Basic Tables",
+ vec![
+ single_example(
+ "Simple Table",
+ Table::new(vec!["Name", "Age", "City"])
+ .width(px(400.))
+ .row(vec!["Alice", "28", "New York"])
+ .row(vec!["Bob", "32", "San Francisco"])
+ .row(vec!["Charlie", "25", "London"])
+ .into_any_element(),
+ ),
+ single_example(
+ "Two Column Table",
+ Table::new(vec!["Category", "Value"])
+ .width(px(300.))
+ .row(vec!["Revenue", "$100,000"])
+ .row(vec!["Expenses", "$75,000"])
+ .row(vec!["Profit", "$25,000"])
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Styled Tables",
+ vec![
+ single_example(
+ "Default",
+ Table::new(vec!["Product", "Price", "Stock"])
+ .width(px(400.))
+ .row(vec!["Laptop", "$999", "In Stock"])
+ .row(vec!["Phone", "$599", "Low Stock"])
+ .row(vec!["Tablet", "$399", "Out of Stock"])
+ .into_any_element(),
+ ),
+ single_example(
+ "Striped",
+ Table::new(vec!["Product", "Price", "Stock"])
+ .width(px(400.))
+ .striped()
+ .row(vec!["Laptop", "$999", "In Stock"])
+ .row(vec!["Phone", "$599", "Low Stock"])
+ .row(vec!["Tablet", "$399", "Out of Stock"])
+ .row(vec!["Headphones", "$199", "In Stock"])
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Mixed Content Table",
+ vec![single_example(
+ "Table with Elements",
+ Table::new(vec!["Status", "Name", "Priority", "Deadline", "Action"])
+ .width(px(840.))
+ .row(vec![
+ element_cell(
+ Indicator::dot().color(Color::Success).into_any_element(),
+ ),
+ string_cell("Project A"),
+ string_cell("High"),
+ string_cell("2023-12-31"),
+ element_cell(
+ Button::new("view_a", "View")
+ .style(ButtonStyle::Filled)
+ .full_width()
+ .into_any_element(),
+ ),
+ ])
+ .row(vec![
+ element_cell(
+ Indicator::dot().color(Color::Warning).into_any_element(),
+ ),
+ string_cell("Project B"),
+ string_cell("Medium"),
+ string_cell("2024-03-15"),
+ element_cell(
+ Button::new("view_b", "View")
+ .style(ButtonStyle::Filled)
+ .full_width()
+ .into_any_element(),
+ ),
+ ])
+ .row(vec![
+ element_cell(
+ Indicator::dot().color(Color::Error).into_any_element(),
+ ),
+ string_cell("Project C"),
+ string_cell("Low"),
+ string_cell("2024-06-30"),
+ element_cell(
+ Button::new("view_c", "View")
+ .style(ButtonStyle::Filled)
+ .full_width()
+ .into_any_element(),
+ ),
+ ])
.into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Mixed Content Table",
- vec![single_example(
- "Table with Elements",
- Table::new(vec!["Status", "Name", "Priority", "Deadline", "Action"])
- .width(px(840.))
- .row(vec![
- element_cell(
- Indicator::dot().color(Color::Success).into_any_element(),
- ),
- string_cell("Project A"),
- string_cell("High"),
- string_cell("2023-12-31"),
- element_cell(
- Button::new("view_a", "View")
- .style(ButtonStyle::Filled)
- .full_width()
- .into_any_element(),
- ),
- ])
- .row(vec![
- element_cell(
- Indicator::dot().color(Color::Warning).into_any_element(),
- ),
- string_cell("Project B"),
- string_cell("Medium"),
- string_cell("2024-03-15"),
- element_cell(
- Button::new("view_b", "View")
- .style(ButtonStyle::Filled)
- .full_width()
- .into_any_element(),
- ),
- ])
- .row(vec![
- element_cell(
- Indicator::dot().color(Color::Error).into_any_element(),
- ),
- string_cell("Project C"),
- string_cell("Low"),
- string_cell("2024-06-30"),
- element_cell(
- Button::new("view_c", "View")
- .style(ButtonStyle::Filled)
- .full_width()
- .into_any_element(),
- ),
- ])
- .into_any_element(),
- )],
- ),
- ])
- .into_any_element()
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -38,8 +38,7 @@ pub enum ToggleStyle {
/// Checkboxes are used for multiple choices, not for mutually exclusive choices.
/// Each checkbox works independently from other checkboxes in the list,
/// therefore checking an additional box does not affect any other selections.
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Input")]
+#[derive(IntoElement, RegisterComponent)]
pub struct Checkbox {
id: ElementId,
toggle_state: ToggleState,
@@ -244,8 +243,7 @@ impl RenderOnce for Checkbox {
}
/// A [`Checkbox`] that has a [`Label`].
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Input")]
+#[derive(IntoElement, RegisterComponent)]
pub struct CheckboxWithLabel {
id: ElementId,
label: Label,
@@ -344,8 +342,7 @@ impl RenderOnce for CheckboxWithLabel {
/// # Switch
///
/// Switches are used to represent opposite states, such as enabled or disabled.
-#[derive(IntoElement, IntoComponent)]
-#[component(scope = "Input")]
+#[derive(IntoElement, RegisterComponent)]
pub struct Switch {
id: ElementId,
toggle_state: ToggleState,
@@ -479,7 +476,6 @@ impl RenderOnce for Switch {
/// A [`Switch`] that has a [`Label`].
#[derive(IntoElement)]
-// #[component(scope = "input")]
pub struct SwitchWithLabel {
id: ElementId,
label: Label,
@@ -535,200 +531,232 @@ impl RenderOnce for SwitchWithLabel {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Checkbox {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "States",
- vec![
- single_example(
- "Unselected",
- Checkbox::new("checkbox_unselected", ToggleState::Unselected)
- .into_any_element(),
- ),
- single_example(
- "Placeholder",
- Checkbox::new("checkbox_indeterminate", ToggleState::Selected)
- .placeholder(true)
- .into_any_element(),
- ),
- single_example(
- "Indeterminate",
- Checkbox::new("checkbox_indeterminate", ToggleState::Indeterminate)
- .into_any_element(),
- ),
- single_example(
- "Selected",
- Checkbox::new("checkbox_selected", ToggleState::Selected)
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Styles",
- vec![
- single_example(
- "Default",
- Checkbox::new("checkbox_default", ToggleState::Selected)
- .into_any_element(),
- ),
- single_example(
- "Filled",
- Checkbox::new("checkbox_filled", ToggleState::Selected)
- .fill()
- .into_any_element(),
- ),
- single_example(
- "ElevationBased",
- Checkbox::new("checkbox_elevation", ToggleState::Selected)
- .style(ToggleStyle::ElevationBased(ElevationIndex::EditorSurface))
- .into_any_element(),
- ),
- single_example(
- "Custom Color",
- Checkbox::new("checkbox_custom", ToggleState::Selected)
- .style(ToggleStyle::Custom(hsla(142.0 / 360., 0.68, 0.45, 0.7)))
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Disabled",
- vec![
- single_example(
- "Unselected",
- Checkbox::new("checkbox_disabled_unselected", ToggleState::Unselected)
+impl Component for Checkbox {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A checkbox component that can be used for multiple choice selections")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "States",
+ vec![
+ single_example(
+ "Unselected",
+ Checkbox::new("checkbox_unselected", ToggleState::Unselected)
+ .into_any_element(),
+ ),
+ single_example(
+ "Placeholder",
+ Checkbox::new("checkbox_indeterminate", ToggleState::Selected)
+ .placeholder(true)
+ .into_any_element(),
+ ),
+ single_example(
+ "Indeterminate",
+ Checkbox::new("checkbox_indeterminate", ToggleState::Indeterminate)
+ .into_any_element(),
+ ),
+ single_example(
+ "Selected",
+ Checkbox::new("checkbox_selected", ToggleState::Selected)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Styles",
+ vec![
+ single_example(
+ "Default",
+ Checkbox::new("checkbox_default", ToggleState::Selected)
+ .into_any_element(),
+ ),
+ single_example(
+ "Filled",
+ Checkbox::new("checkbox_filled", ToggleState::Selected)
+ .fill()
+ .into_any_element(),
+ ),
+ single_example(
+ "ElevationBased",
+ Checkbox::new("checkbox_elevation", ToggleState::Selected)
+ .style(ToggleStyle::ElevationBased(
+ ElevationIndex::EditorSurface,
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Custom Color",
+ Checkbox::new("checkbox_custom", ToggleState::Selected)
+ .style(ToggleStyle::Custom(hsla(142.0 / 360., 0.68, 0.45, 0.7)))
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Disabled",
+ vec![
+ single_example(
+ "Unselected",
+ Checkbox::new(
+ "checkbox_disabled_unselected",
+ ToggleState::Unselected,
+ )
.disabled(true)
.into_any_element(),
- ),
- single_example(
- "Selected",
- Checkbox::new("checkbox_disabled_selected", ToggleState::Selected)
- .disabled(true)
+ ),
+ single_example(
+ "Selected",
+ Checkbox::new("checkbox_disabled_selected", ToggleState::Selected)
+ .disabled(true)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "With Label",
+ vec![single_example(
+ "Default",
+ Checkbox::new("checkbox_with_label", ToggleState::Selected)
+ .label("Always save on quit")
.into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "With Label",
- vec![single_example(
- "Default",
- Checkbox::new("checkbox_with_label", ToggleState::Selected)
- .label("Always save on quit")
- .into_any_element(),
- )],
- ),
- ])
- .into_any_element()
+ )],
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Switch {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
+impl Component for Switch {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A switch component that represents binary states like on/off")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "States",
+ vec![
+ single_example(
+ "Off",
+ Switch::new("switch_off", ToggleState::Unselected)
+ .on_click(|_, _, _cx| {})
+ .into_any_element(),
+ ),
+ single_example(
+ "On",
+ Switch::new("switch_on", ToggleState::Selected)
+ .on_click(|_, _, _cx| {})
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "Disabled",
+ vec![
+ single_example(
+ "Off",
+ Switch::new("switch_disabled_off", ToggleState::Unselected)
+ .disabled(true)
+ .into_any_element(),
+ ),
+ single_example(
+ "On",
+ Switch::new("switch_disabled_on", ToggleState::Selected)
+ .disabled(true)
+ .into_any_element(),
+ ),
+ ],
+ ),
+ example_group_with_title(
+ "With Label",
+ vec![
+ single_example(
+ "Label",
+ Switch::new("switch_with_label", ToggleState::Selected)
+ .label("Always save on quit")
+ .into_any_element(),
+ ),
+ // TODO: Where did theme_preview_keybinding go?
+ // single_example(
+ // "Keybinding",
+ // Switch::new("switch_with_keybinding", ToggleState::Selected)
+ // .key_binding(theme_preview_keybinding("cmd-shift-e"))
+ // .into_any_element(),
+ // ),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
+
+impl Component for CheckboxWithLabel {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A checkbox component with an attached label")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![example_group_with_title(
"States",
vec![
single_example(
- "Off",
- Switch::new("switch_off", ToggleState::Unselected)
- .on_click(|_, _, _cx| {})
- .into_any_element(),
- ),
- single_example(
- "On",
- Switch::new("switch_on", ToggleState::Selected)
- .on_click(|_, _, _cx| {})
- .into_any_element(),
- ),
- ],
- ),
- example_group_with_title(
- "Disabled",
- vec![
- single_example(
- "Off",
- Switch::new("switch_disabled_off", ToggleState::Unselected)
- .disabled(true)
- .into_any_element(),
+ "Unselected",
+ CheckboxWithLabel::new(
+ "checkbox_with_label_unselected",
+ Label::new("Always save on quit"),
+ ToggleState::Unselected,
+ |_, _, _| {},
+ )
+ .into_any_element(),
),
single_example(
- "On",
- Switch::new("switch_disabled_on", ToggleState::Selected)
- .disabled(true)
- .into_any_element(),
+ "Indeterminate",
+ CheckboxWithLabel::new(
+ "checkbox_with_label_indeterminate",
+ Label::new("Always save on quit"),
+ ToggleState::Indeterminate,
+ |_, _, _| {},
+ )
+ .into_any_element(),
),
- ],
- ),
- example_group_with_title(
- "With Label",
- vec![
single_example(
- "Label",
- Switch::new("switch_with_label", ToggleState::Selected)
- .label("Always save on quit")
- .into_any_element(),
+ "Selected",
+ CheckboxWithLabel::new(
+ "checkbox_with_label_selected",
+ Label::new("Always save on quit"),
+ ToggleState::Selected,
+ |_, _, _| {},
+ )
+ .into_any_element(),
),
- // TODO: Where did theme_preview_keybinding go?
- // single_example(
- // "Keybinding",
- // Switch::new("switch_with_keybinding", ToggleState::Selected)
- // .key_binding(theme_preview_keybinding("cmd-shift-e"))
- // .into_any_element(),
- // ),
],
- ),
- ])
- .into_any_element()
- }
-}
-
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for CheckboxWithLabel {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_6()
- .children(vec![example_group_with_title(
- "States",
- vec![
- single_example(
- "Unselected",
- CheckboxWithLabel::new(
- "checkbox_with_label_unselected",
- Label::new("Always save on quit"),
- ToggleState::Unselected,
- |_, _, _| {},
- )
- .into_any_element(),
- ),
- single_example(
- "Indeterminate",
- CheckboxWithLabel::new(
- "checkbox_with_label_indeterminate",
- Label::new("Always save on quit"),
- ToggleState::Indeterminate,
- |_, _, _| {},
- )
- .into_any_element(),
- ),
- single_example(
- "Selected",
- CheckboxWithLabel::new(
- "checkbox_with_label_selected",
- Label::new("Always save on quit"),
- ToggleState::Selected,
- |_, _, _| {},
- )
- .into_any_element(),
- ),
- ],
- )])
- .into_any_element()
+ )])
+ .into_any_element(),
+ )
}
}
@@ -5,7 +5,7 @@ use theme::ThemeSettings;
use crate::prelude::*;
use crate::{Color, KeyBinding, Label, LabelSize, StyledExt, h_flex, v_flex};
-#[derive(IntoComponent)]
+#[derive(RegisterComponent)]
pub struct Tooltip {
title: SharedString,
meta: Option<SharedString>,
@@ -222,15 +222,26 @@ impl Render for LinkPreview {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Tooltip {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- example_group(vec![single_example(
- "Text only",
- Button::new("delete-example", "Delete")
- .tooltip(Tooltip::text("This is a tooltip!"))
- .into_any_element(),
- )])
- .into_any_element()
+impl Component for Tooltip {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(
+ "A tooltip that appears when hovering over an element, optionally showing a keybinding or additional metadata.",
+ )
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ example_group(vec![single_example(
+ "Text only",
+ Button::new("delete-example", "Delete")
+ .tooltip(Tooltip::text("This is a tooltip!"))
+ .into_any_element(),
+ )])
+ .into_any_element(),
+ )
}
}
@@ -8,9 +8,9 @@ pub use gpui::{
};
pub use component::{
- ComponentPreview, ComponentScope, example_group, example_group_with_title, single_example,
+ Component, ComponentScope, example_group, example_group_with_title, single_example,
};
-pub use ui_macros::IntoComponent;
+pub use ui_macros::RegisterComponent;
pub use crate::DynamicSpacing;
pub use crate::animation::{AnimationDirection, AnimationDuration, DefaultAnimations};
@@ -94,183 +94,192 @@ pub trait DefaultAnimations: Styled + Sized {
impl<E: Styled> DefaultAnimations for E {}
// Don't use this directly, it only exists to show animation previews
-#[derive(IntoComponent)]
+#[derive(RegisterComponent)]
struct Animation {}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Animation {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+impl Component for Animation {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("Demonstrates various animation patterns and transitions available in the UI system.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
let container_size = 128.0;
let element_size = 32.0;
let left_offset = element_size - container_size / 2.0;
- v_flex()
- .gap_6()
- .children(vec![
- example_group_with_title(
- "Animate In",
- vec![
- single_example(
- "From Bottom",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("animate-in-from-bottom")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::red())
- .animate_in(AnimationDirection::FromBottom, false),
- )
- .into_any_element(),
- ),
- single_example(
- "From Top",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("animate-in-from-top")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::blue())
- .animate_in(AnimationDirection::FromTop, false),
- )
- .into_any_element(),
- ),
- single_example(
- "From Left",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("animate-in-from-left")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::green())
- .animate_in(AnimationDirection::FromLeft, false),
- )
- .into_any_element(),
- ),
- single_example(
- "From Right",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("animate-in-from-right")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::yellow())
- .animate_in(AnimationDirection::FromRight, false),
- )
- .into_any_element(),
- ),
- ],
- )
- .grow(),
- example_group_with_title(
- "Fade and Animate In",
- vec![
- single_example(
- "From Bottom",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("fade-animate-in-from-bottom")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::red())
- .animate_in(AnimationDirection::FromBottom, true),
- )
- .into_any_element(),
- ),
- single_example(
- "From Top",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("fade-animate-in-from-top")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::blue())
- .animate_in(AnimationDirection::FromTop, true),
- )
- .into_any_element(),
- ),
- single_example(
- "From Left",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("fade-animate-in-from-left")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::green())
- .animate_in(AnimationDirection::FromLeft, true),
- )
- .into_any_element(),
- ),
- single_example(
- "From Right",
- ContentGroup::new()
- .relative()
- .items_center()
- .justify_center()
- .size(px(container_size))
- .child(
- div()
- .id("fade-animate-in-from-right")
- .absolute()
- .size(px(element_size))
- .left(px(left_offset))
- .rounded_md()
- .bg(gpui::yellow())
- .animate_in(AnimationDirection::FromRight, true),
- )
- .into_any_element(),
- ),
- ],
- )
- .grow(),
- ])
- .into_any_element()
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Animate In",
+ vec![
+ single_example(
+ "From Bottom",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("animate-in-from-bottom")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::red())
+ .animate_in(AnimationDirection::FromBottom, false),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "From Top",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("animate-in-from-top")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::blue())
+ .animate_in(AnimationDirection::FromTop, false),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "From Left",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("animate-in-from-left")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::green())
+ .animate_in(AnimationDirection::FromLeft, false),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "From Right",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("animate-in-from-right")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::yellow())
+ .animate_in(AnimationDirection::FromRight, false),
+ )
+ .into_any_element(),
+ ),
+ ],
+ )
+ .grow(),
+ example_group_with_title(
+ "Fade and Animate In",
+ vec![
+ single_example(
+ "From Bottom",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("fade-animate-in-from-bottom")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::red())
+ .animate_in(AnimationDirection::FromBottom, true),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "From Top",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("fade-animate-in-from-top")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::blue())
+ .animate_in(AnimationDirection::FromTop, true),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "From Left",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("fade-animate-in-from-left")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::green())
+ .animate_in(AnimationDirection::FromLeft, true),
+ )
+ .into_any_element(),
+ ),
+ single_example(
+ "From Right",
+ ContentGroup::new()
+ .relative()
+ .items_center()
+ .justify_center()
+ .size(px(container_size))
+ .child(
+ div()
+ .id("fade-animate-in-from-right")
+ .absolute()
+ .size(px(element_size))
+ .left(px(left_offset))
+ .rounded_md()
+ .bg(gpui::yellow())
+ .animate_in(AnimationDirection::FromRight, true),
+ )
+ .into_any_element(),
+ ),
+ ],
+ )
+ .grow(),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -1,8 +1,21 @@
-use gpui::{App, Hsla};
+use crate::{Label, LabelCommon, component_prelude::*, v_flex};
+use documented::{DocumentedFields, DocumentedVariants};
+use gpui::{App, Hsla, IntoElement, ParentElement, Styled};
use theme::ActiveTheme;
/// Sets a color that has a consistent meaning across all themes.
-#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)]
+#[derive(
+ Debug,
+ Default,
+ Eq,
+ PartialEq,
+ Copy,
+ Clone,
+ RegisterComponent,
+ Documented,
+ DocumentedFields,
+ DocumentedVariants,
+)]
pub enum Color {
#[default]
/// The default text color. Might be known as "foreground" or "primary" in
@@ -110,3 +123,122 @@ impl From<Hsla> for Color {
Color::Custom(color)
}
}
+
+impl Component for Color {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some(Color::DOCS)
+ }
+
+ fn preview(_window: &mut gpui::Window, _cx: &mut App) -> Option<gpui::AnyElement> {
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![
+ example_group_with_title(
+ "Text Colors",
+ vec![
+ single_example(
+ "Default",
+ Label::new("Default text color")
+ .color(Color::Default)
+ .into_any_element(),
+ )
+ .description(Color::Default.get_variant_docs()),
+ single_example(
+ "Muted",
+ Label::new("Muted text color")
+ .color(Color::Muted)
+ .into_any_element(),
+ )
+ .description(Color::Muted.get_variant_docs()),
+ single_example(
+ "Accent",
+ Label::new("Accent text color")
+ .color(Color::Accent)
+ .into_any_element(),
+ )
+ .description(Color::Accent.get_variant_docs()),
+ single_example(
+ "Disabled",
+ Label::new("Disabled text color")
+ .color(Color::Disabled)
+ .into_any_element(),
+ )
+ .description(Color::Disabled.get_variant_docs()),
+ ],
+ ),
+ example_group_with_title(
+ "Status Colors",
+ vec![
+ single_example(
+ "Success",
+ Label::new("Success status")
+ .color(Color::Success)
+ .into_any_element(),
+ )
+ .description(Color::Success.get_variant_docs()),
+ single_example(
+ "Warning",
+ Label::new("Warning status")
+ .color(Color::Warning)
+ .into_any_element(),
+ )
+ .description(Color::Warning.get_variant_docs()),
+ single_example(
+ "Error",
+ Label::new("Error status")
+ .color(Color::Error)
+ .into_any_element(),
+ )
+ .description(Color::Error.get_variant_docs()),
+ single_example(
+ "Info",
+ Label::new("Info status")
+ .color(Color::Info)
+ .into_any_element(),
+ )
+ .description(Color::Info.get_variant_docs()),
+ ],
+ ),
+ example_group_with_title(
+ "Version Control Colors",
+ vec![
+ single_example(
+ "Created",
+ Label::new("Created item")
+ .color(Color::Created)
+ .into_any_element(),
+ )
+ .description(Color::Created.get_variant_docs()),
+ single_example(
+ "Modified",
+ Label::new("Modified item")
+ .color(Color::Modified)
+ .into_any_element(),
+ )
+ .description(Color::Modified.get_variant_docs()),
+ single_example(
+ "Deleted",
+ Label::new("Deleted item")
+ .color(Color::Deleted)
+ .into_any_element(),
+ )
+ .description(Color::Deleted.get_variant_docs()),
+ single_example(
+ "Conflict",
+ Label::new("Conflict item")
+ .color(Color::Conflict)
+ .into_any_element(),
+ )
+ .description(Color::Conflict.get_variant_docs()),
+ ],
+ ),
+ ])
+ .into_any_element(),
+ )
+ }
+}
@@ -190,7 +190,7 @@ impl HeadlineSize {
/// A headline element, used to emphasize some text and
/// create a visual hierarchy.
-#[derive(IntoElement, IntoComponent)]
+#[derive(IntoElement, RegisterComponent)]
pub struct Headline {
size: HeadlineSize,
text: SharedString,
@@ -233,41 +233,50 @@ impl Headline {
}
}
-// View this component preview using `workspace: open component-preview`
-impl ComponentPreview for Headline {
- fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
- v_flex()
- .gap_1()
- .children(vec![
- single_example(
- "XLarge",
- Headline::new("XLarge Headline")
- .size(HeadlineSize::XLarge)
- .into_any_element(),
- ),
- single_example(
- "Large",
- Headline::new("Large Headline")
- .size(HeadlineSize::Large)
- .into_any_element(),
- ),
- single_example(
- "Medium (Default)",
- Headline::new("Medium Headline").into_any_element(),
- ),
- single_example(
- "Small",
- Headline::new("Small Headline")
- .size(HeadlineSize::Small)
- .into_any_element(),
- ),
- single_example(
- "XSmall",
- Headline::new("XSmall Headline")
- .size(HeadlineSize::XSmall)
- .into_any_element(),
- ),
- ])
- .into_any_element()
+impl Component for Headline {
+ fn scope() -> ComponentScope {
+ ComponentScope::None
+ }
+
+ fn description() -> Option<&'static str> {
+ Some("A headline element used to emphasize text and create visual hierarchy in the UI.")
+ }
+
+ fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
+ Some(
+ v_flex()
+ .gap_1()
+ .children(vec![
+ single_example(
+ "XLarge",
+ Headline::new("XLarge Headline")
+ .size(HeadlineSize::XLarge)
+ .into_any_element(),
+ ),
+ single_example(
+ "Large",
+ Headline::new("Large Headline")
+ .size(HeadlineSize::Large)
+ .into_any_element(),
+ ),
+ single_example(
+ "Medium (Default)",
+ Headline::new("Medium Headline").into_any_element(),
+ ),
+ single_example(
+ "Small",
+ Headline::new("Small Headline")
+ .size(HeadlineSize::Small)
+ .into_any_element(),
+ ),
+ single_example(
+ "XSmall",
+ Headline::new("XSmall Headline")
+ .size(HeadlineSize::XSmall)
+ .into_any_element(),
+ ),
+ ])
+ .into_any_element(),
+ )
}
}
@@ -7,6 +7,7 @@
//! - [`ui_macros`] - proc_macros support for this crate
//! - `ui_input` - the single line input component
+pub mod component_prelude;
mod components;
pub mod prelude;
mod styles;
@@ -5,7 +5,7 @@
//! It can't be located in the `ui` crate because it depends on `editor`.
//!
-use component::{ComponentPreview, example_group, single_example};
+use component::{example_group, single_example};
use editor::{Editor, EditorElement, EditorStyle};
use gpui::{App, Entity, FocusHandle, Focusable, FontStyle, Hsla, TextStyle};
use settings::Settings;
@@ -21,8 +21,7 @@ pub struct SingleLineInputStyle {
/// A Text Field that can be used to create text fields like search inputs, form fields, etc.
///
/// It wraps a single line [`Editor`] and allows for common field properties like labels, placeholders, icons, etc.
-#[derive(IntoComponent)]
-#[component(scope = "Input")]
+#[derive(RegisterComponent)]
pub struct SingleLineInput {
/// An optional label for the text field.
///
@@ -168,17 +167,23 @@ impl Render for SingleLineInput {
}
}
-impl ComponentPreview for SingleLineInput {
- fn preview(window: &mut Window, cx: &mut App) -> AnyElement {
+impl Component for SingleLineInput {
+ fn scope() -> ComponentScope {
+ ComponentScope::Input
+ }
+
+ fn preview(window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let input_1 =
cx.new(|cx| SingleLineInput::new(window, cx, "placeholder").label("Some Label"));
- v_flex()
- .gap_6()
- .children(vec![example_group(vec![single_example(
- "Default",
- div().child(input_1.clone()).into_any_element(),
- )])])
- .into_any_element()
+ Some(
+ v_flex()
+ .gap_6()
+ .children(vec![example_group(vec![single_example(
+ "Default",
+ div().child(input_1.clone()).into_any_element(),
+ )])])
+ .into_any_element(),
+ )
}
}
@@ -0,0 +1,25 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{DeriveInput, parse_macro_input};
+
+pub fn derive_register_component(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ let name = input.ident;
+ let reg_fn_name = syn::Ident::new(
+ &format!("__component_registry_internal_register_{}", name),
+ name.span(),
+ );
+ let expanded = quote! {
+ const _: () = {
+ struct AssertComponent<T: component::Component>(::std::marker::PhantomData<T>);
+ let _ = AssertComponent::<#name>(::std::marker::PhantomData);
+ };
+
+ #[allow(non_snake_case)]
+ #[linkme::distributed_slice(component::__ALL_COMPONENTS)]
+ fn #reg_fn_name() {
+ component::register_component::<#name>();
+ }
+ };
+ expanded.into()
+}
@@ -1,5 +1,5 @@
-mod derive_component;
mod derive_path_str;
+mod derive_register_component;
mod dynamic_spacing;
use proc_macro::TokenStream;
@@ -60,26 +60,28 @@ pub fn derive_dynamic_spacing(input: TokenStream) -> TokenStream {
dynamic_spacing::derive_spacing(input)
}
-/// Derives the `Component` trait for a struct.
+/// Registers components that implement the `Component` trait.
///
-/// This macro generates implementations for the `Component` trait and associated
-/// registration functions for the component system.
+/// This proc macro is used to automatically register structs that implement
+/// the `Component` trait with the [`component::ComponentRegistry`].
///
-/// # Attributes
-///
-/// - `#[component(scope = "...")]`: Required. Specifies the scope of the component.
-/// - `#[component(description = "...")]`: Optional. Provides a description for the component.
+/// If the component trait is not implemented, it will generate a compile-time error.
///
/// # Example
///
/// ```
-/// use ui_macros::Component;
+/// use ui_macros::RegisterComponent;
+///
+/// #[derive(RegisterComponent)]
+/// struct MyComponent;
///
-/// #[derive(Component)]
-/// #[component(scope = "toggle", description = "A element that can be toggled on and off")]
-/// struct Checkbox;
+/// impl Component for MyComponent {
+/// // Component implementation
+/// }
/// ```
-#[proc_macro_derive(IntoComponent, attributes(component))]
-pub fn derive_component(input: TokenStream) -> TokenStream {
- derive_component::derive_into_component(input)
+///
+/// This example will add MyComponent to the ComponentRegistry.
+#[proc_macro_derive(RegisterComponent)]
+pub fn derive_register_component(input: TokenStream) -> TokenStream {
+ derive_register_component::derive_register_component(input)
}
@@ -267,6 +267,7 @@ impl Render for WelcomePage {
)
.child(
v_container()
+ .px_2()
.gap_2()
.child(
h_flex()
@@ -176,6 +176,7 @@ heck = { version = "0.4", features = ["unicode"] }
hmac = { version = "0.12", default-features = false, features = ["reset"] }
hyper = { version = "0.14", features = ["client", "http1", "http2", "runtime", "server", "stream"] }
indexmap = { version = "2", features = ["serde"] }
+itertools-594e8ee84c453af0 = { package = "itertools", version = "0.13" }
lazy_static = { version = "1", default-features = false, features = ["spin_no_std"] }
libc = { version = "0.2", features = ["extra_traits"] }
libsqlite3-sys = { version = "0.30", features = ["bundled", "unlock_notify"] }
@@ -252,7 +253,7 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "23", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal"] }
object = { version = "0.36", default-features = false, features = ["archive", "read_core", "unaligned", "write"] }
@@ -276,7 +277,7 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "23", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal"] }
object = { version = "0.36", default-features = false, features = ["archive", "read_core", "unaligned", "write"] }
@@ -300,7 +301,7 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "23", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal"] }
object = { version = "0.36", default-features = false, features = ["archive", "read_core", "unaligned", "write"] }
@@ -324,7 +325,7 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "23", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal"] }
object = { version = "0.36", default-features = false, features = ["archive", "read_core", "unaligned", "write"] }
@@ -354,7 +355,7 @@ getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-f
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys = { version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] }
mio = { version = "1", features = ["net", "os-ext"] }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
@@ -394,7 +395,7 @@ getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-f
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys = { version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] }
mio = { version = "1", features = ["net", "os-ext"] }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
@@ -432,7 +433,7 @@ getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-f
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys = { version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] }
mio = { version = "1", features = ["net", "os-ext"] }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
@@ -472,7 +473,7 @@ getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-f
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys = { version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] }
mio = { version = "1", features = ["net", "os-ext"] }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
@@ -502,7 +503,7 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
ring = { version = "0.17", features = ["std"] }
rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", default-features = false, features = ["event"] }
@@ -522,7 +523,7 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
proc-macro2 = { version = "1", default-features = false, features = ["span-locations"] }
ring = { version = "0.17", features = ["std"] }
@@ -551,7 +552,7 @@ getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-f
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys = { version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] }
mio = { version = "1", features = ["net", "os-ext"] }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }
@@ -591,7 +592,7 @@ getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-f
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
-itertools = { version = "0.12" }
+itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys = { version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] }
mio = { version = "1", features = ["net", "os-ext"] }
naga = { version = "23", features = ["spv-out", "wgsl-in"] }