Merge Component and ComponentPreview trait (#28365)

- Merge `Component` and `ComponentPreview` trait
- Adds a number of component previews
- Removes a number of stories

Release Notes:

- N/A
This commit is contained in:
Nate Butler 2025-04-08 16:09:06 -06:00 committed by GitHub
parent b15ee1b1cc
commit c05bf096f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 3276 additions and 1848 deletions

View file

@ -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;

View file

@ -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::*;

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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,

View file

@ -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",
];
v_flex()
.gap_6()
.children(vec![
example_group_with_title(
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.",
)
}
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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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))
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(
"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(),
)
}
}

View file

@ -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;
impl Component for Vector {
fn scope() -> ComponentScope {
ComponentScope::Images
}
use crate::prelude::*;
fn name() -> &'static str {
"Vector"
}
use super::{Vector, VectorName};
fn description() -> Option<&'static str> {
Some("A vector image component that can be displayed at specific sizes.")
}
pub struct VectorStory;
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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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::*;

View file

@ -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)
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(),
),
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()
),
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(),
)
}
}

View file

@ -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()
)
}
}

View file

@ -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()
}
)
}
}

View file

@ -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()
)
}
}

View file

@ -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()
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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::*;

View file

@ -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)))
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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"])
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(),
),
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(),
)],
),
])
.into_any_element()
)],
),
])
.into_any_element(),
)
}
}

View file

@ -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(
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)
.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(),
)
}
}
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(
"Unselected",
Checkbox::new("checkbox_unselected", ToggleState::Unselected)
.into_any_element(),
),
single_example(
"Placeholder",
Checkbox::new("checkbox_indeterminate", ToggleState::Selected)
.placeholder(true)
.into_any_element(),
CheckboxWithLabel::new(
"checkbox_with_label_unselected",
Label::new("Always save on quit"),
ToggleState::Unselected,
|_, _, _| {},
)
.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)
.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")
CheckboxWithLabel::new(
"checkbox_with_label_indeterminate",
Label::new("Always save on quit"),
ToggleState::Indeterminate,
|_, _, _| {},
)
.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(
"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(),
"Selected",
CheckboxWithLabel::new(
"checkbox_with_label_selected",
Label::new("Always save on quit"),
ToggleState::Selected,
|_, _, _| {},
)
.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()
}
}
// 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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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};

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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(),
)
}
}

View file

@ -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;