Compare commits
13 Commits
fix-python
...
add-layout
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6240740605 | ||
|
|
2306d9fd91 | ||
|
|
e7cf1305b8 | ||
|
|
f699083b5b | ||
|
|
956eb5b3b0 | ||
|
|
af237f81a3 | ||
|
|
d6b1949143 | ||
|
|
e5c738daaa | ||
|
|
eba035eb5b | ||
|
|
1c65253b0f | ||
|
|
5d4bc1e492 | ||
|
|
dc84dbb6e2 | ||
|
|
903343e608 |
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -13262,9 +13262,12 @@ name = "ui"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"collections",
|
||||||
"gpui",
|
"gpui",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
|
"linkme",
|
||||||
"menu",
|
"menu",
|
||||||
|
"once_cell",
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
|||||||
@@ -371,6 +371,7 @@ itertools = "0.13.0"
|
|||||||
jsonwebtoken = "9.3"
|
jsonwebtoken = "9.3"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
linkify = "0.10.0"
|
linkify = "0.10.0"
|
||||||
|
linkme = "0.3"
|
||||||
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
|
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
|
||||||
markup5ever_rcdom = "0.3.0"
|
markup5ever_rcdom = "0.3.0"
|
||||||
nanoid = "0.4"
|
nanoid = "0.4"
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ gpui_macros.workspace = true
|
|||||||
http_client = { optional = true, workspace = true }
|
http_client = { optional = true, workspace = true }
|
||||||
image = "0.25.1"
|
image = "0.25.1"
|
||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
linkme = "0.3"
|
linkme.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
num_cpus = "1.13"
|
num_cpus = "1.13"
|
||||||
parking = "2.0.0"
|
parking = "2.0.0"
|
||||||
|
|||||||
@@ -14,9 +14,12 @@ path = "src/ui.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
collections.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
itertools = { workspace = true, optional = true }
|
itertools = { workspace = true, optional = true }
|
||||||
|
linkme.workspace = true
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
|
once_cell.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
|
|||||||
57
crates/ui/src/component_registry.rs
Normal file
57
crates/ui/src/component_registry.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use collections::HashMap;
|
||||||
|
use gpui::{AnyElement, WindowContext};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
pub type ComponentPreviewFn = fn(&WindowContext) -> AnyElement;
|
||||||
|
|
||||||
|
static COMPONENTS: Lazy<Mutex<HashMap<&'static str, Vec<(&'static str, ComponentPreviewFn)>>>> =
|
||||||
|
Lazy::new(|| Mutex::new(HashMap::default()));
|
||||||
|
|
||||||
|
pub fn register_component(scope: &'static str, name: &'static str, preview: ComponentPreviewFn) {
|
||||||
|
let mut components = COMPONENTS.lock().unwrap();
|
||||||
|
components
|
||||||
|
.entry(scope)
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push((name, preview));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes all components that have been registered
|
||||||
|
/// in the UI component registry.
|
||||||
|
pub fn init_component_registry() {
|
||||||
|
for register in __COMPONENT_REGISTRATIONS {
|
||||||
|
register();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a map of all registered components and their previews.
|
||||||
|
pub fn get_all_component_previews() -> HashMap<&'static str, Vec<(&'static str, ComponentPreviewFn)>>
|
||||||
|
{
|
||||||
|
COMPONENTS.lock().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[linkme::distributed_slice]
|
||||||
|
pub static __COMPONENT_REGISTRATIONS: [fn()];
|
||||||
|
|
||||||
|
/// Defines components that should be registered in the component registry.
|
||||||
|
///
|
||||||
|
/// This allows components to be previewed, and eventually tracked for documentation
|
||||||
|
/// purposes and to help the systems team to understand component usage across the codebase.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! register_components {
|
||||||
|
($scope:ident, [ $($component:ty),+ $(,)? ]) => {
|
||||||
|
const _: () = {
|
||||||
|
#[linkme::distributed_slice($crate::component_registry::__COMPONENT_REGISTRATIONS)]
|
||||||
|
fn register() {
|
||||||
|
$(
|
||||||
|
$crate::component_registry::register_component(
|
||||||
|
stringify!($scope),
|
||||||
|
stringify!($component),
|
||||||
|
|cx: &$crate::WindowContext| <$component>::render_component_previews(cx),
|
||||||
|
);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
mod avatar;
|
mod avatar;
|
||||||
mod button;
|
mod button;
|
||||||
mod checkbox;
|
mod checkbox;
|
||||||
|
mod content_box;
|
||||||
mod context_menu;
|
mod context_menu;
|
||||||
mod disclosure;
|
mod disclosure;
|
||||||
mod divider;
|
mod divider;
|
||||||
@@ -36,6 +37,7 @@ mod stories;
|
|||||||
pub use avatar::*;
|
pub use avatar::*;
|
||||||
pub use button::*;
|
pub use button::*;
|
||||||
pub use checkbox::*;
|
pub use checkbox::*;
|
||||||
|
pub use content_box::*;
|
||||||
pub use context_menu::*;
|
pub use context_menu::*;
|
||||||
pub use disclosure::*;
|
pub use disclosure::*;
|
||||||
pub use divider::*;
|
pub use divider::*;
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
use crate::internal::prelude::*;
|
||||||
|
|
||||||
use gpui::{AnyView, DefiniteLength};
|
use gpui::{AnyView, DefiniteLength};
|
||||||
|
|
||||||
use crate::{prelude::*, ElevationIndex, IconPosition, KeyBinding, Spacing, TintColor};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label, LineHeightStyle,
|
ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label, LineHeightStyle,
|
||||||
};
|
};
|
||||||
|
use crate::{ElevationIndex, IconPosition, KeyBinding, Spacing, TintColor};
|
||||||
|
|
||||||
use super::button_icon::ButtonIcon;
|
use super::button_icon::ButtonIcon;
|
||||||
|
|
||||||
|
register_components!(button, [Button]);
|
||||||
|
|
||||||
/// An element that creates a button with a label and an optional icon.
|
/// An element that creates a button with a label and an optional icon.
|
||||||
///
|
///
|
||||||
/// Common buttons:
|
/// Common buttons:
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
|
||||||
|
use crate::internal::prelude::*;
|
||||||
|
use crate::{ElevationIndex, SelectableButton, Tooltip};
|
||||||
|
use crate::{IconName, IconSize};
|
||||||
use gpui::{AnyView, DefiniteLength};
|
use gpui::{AnyView, DefiniteLength};
|
||||||
|
|
||||||
use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
|
|
||||||
use crate::{prelude::*, ElevationIndex, SelectableButton};
|
|
||||||
use crate::{IconName, IconSize};
|
|
||||||
|
|
||||||
use super::button_icon::ButtonIcon;
|
use super::button_icon::ButtonIcon;
|
||||||
|
|
||||||
|
register_components!(button, [IconButton]);
|
||||||
|
|
||||||
/// The shape of an [`IconButton`].
|
/// The shape of an [`IconButton`].
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
pub enum IconButtonShape {
|
pub enum IconButtonShape {
|
||||||
@@ -165,3 +167,76 @@ impl RenderOnce for IconButton {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for IconButton {
|
||||||
|
fn description() -> impl Into<Option<&'static str>> {
|
||||||
|
"An IconButton is a button that displays only an icon. It's used for actions that can be represented by a single icon."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples() -> Vec<ComponentExampleGroup<Self>> {
|
||||||
|
vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Basic",
|
||||||
|
vec![
|
||||||
|
single_example("Default", IconButton::new("default", IconName::Check)),
|
||||||
|
single_example(
|
||||||
|
"Selected",
|
||||||
|
IconButton::new("selected", IconName::Check).selected(true),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Disabled",
|
||||||
|
IconButton::new("disabled", IconName::Check).disabled(true),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Shapes",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Square",
|
||||||
|
IconButton::new("square", IconName::Check).shape(IconButtonShape::Square),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Wide",
|
||||||
|
IconButton::new("wide", IconName::Check).shape(IconButtonShape::Wide),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Sizes",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"XSmall",
|
||||||
|
IconButton::new("xsmall", IconName::Check).icon_size(IconSize::XSmall),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Small",
|
||||||
|
IconButton::new("small", IconName::Check).icon_size(IconSize::Small),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Medium",
|
||||||
|
IconButton::new("medium", IconName::Check).icon_size(IconSize::Medium),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Icon Color",
|
||||||
|
vec![
|
||||||
|
single_example("Default", IconButton::new("default_color", IconName::Check)),
|
||||||
|
single_example(
|
||||||
|
"Custom",
|
||||||
|
IconButton::new("custom_color", IconName::Check).icon_color(Color::Success),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"With Tooltip",
|
||||||
|
vec![single_example(
|
||||||
|
"Tooltip",
|
||||||
|
IconButton::new("tooltip", IconName::Check)
|
||||||
|
.tooltip(|cx| Tooltip::text("This is a tooltip", cx)),
|
||||||
|
)],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use crate::internal::prelude::*;
|
||||||
|
use crate::{Color, Icon, IconName, Selection};
|
||||||
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
|
use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
|
||||||
|
|
||||||
use crate::prelude::*;
|
register_components!(checkbox, [Checkbox, CheckboxWithLabel]);
|
||||||
use crate::{Color, Icon, IconName, Selection};
|
|
||||||
|
|
||||||
/// # Checkbox
|
/// # Checkbox
|
||||||
///
|
///
|
||||||
|
|||||||
118
crates/ui/src/components/content_box.rs
Normal file
118
crates/ui/src/components/content_box.rs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
use crate::internal::prelude::*;
|
||||||
|
use gpui::{AnyElement, IntoElement, ParentElement, StyleRefinement, Styled};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
register_components!(layout, [ContentBox]);
|
||||||
|
|
||||||
|
/// A flexible container component that can hold other elements.
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct ContentBox {
|
||||||
|
base: Div,
|
||||||
|
border: bool,
|
||||||
|
fill: bool,
|
||||||
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContentBox {
|
||||||
|
/// Creates a new [ContentBox].
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
base: div(),
|
||||||
|
border: true,
|
||||||
|
fill: true,
|
||||||
|
children: SmallVec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the border from the [ContentBox].
|
||||||
|
pub fn borderless(mut self) -> Self {
|
||||||
|
self.border = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the background fill from the [ContentBox].
|
||||||
|
pub fn unfilled(mut self) -> Self {
|
||||||
|
self.fill = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParentElement for ContentBox {
|
||||||
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
|
self.children.extend(elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Styled for ContentBox {
|
||||||
|
fn style(&mut self) -> &mut StyleRefinement {
|
||||||
|
self.base.style()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for ContentBox {
|
||||||
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
// TODO:
|
||||||
|
// Baked in padding will make scrollable views inside of content boxes awkward.
|
||||||
|
//
|
||||||
|
// Do we make the padding optional, or do we push to use a different component?
|
||||||
|
|
||||||
|
self.base
|
||||||
|
.when(self.fill, |this| {
|
||||||
|
this.bg(cx.theme().colors().text.opacity(0.05))
|
||||||
|
})
|
||||||
|
.when(self.border, |this| {
|
||||||
|
this.border_1().border_color(cx.theme().colors().border)
|
||||||
|
})
|
||||||
|
.rounded_md()
|
||||||
|
.p_2()
|
||||||
|
.children(self.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for ContentBox {
|
||||||
|
fn description() -> impl Into<Option<&'static str>> {
|
||||||
|
"A flexible container component that can hold other elements. It can be customized with or without a border and background fill."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example_label_side() -> ExampleLabelSide {
|
||||||
|
ExampleLabelSide::Bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples() -> Vec<ComponentExampleGroup<Self>> {
|
||||||
|
vec![example_group(vec![
|
||||||
|
single_example(
|
||||||
|
"Default",
|
||||||
|
ContentBox::new()
|
||||||
|
.flex_1()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.h_48()
|
||||||
|
.child(Label::new("Default ContentBox")),
|
||||||
|
)
|
||||||
|
.grow(),
|
||||||
|
single_example(
|
||||||
|
"Without Border",
|
||||||
|
ContentBox::new()
|
||||||
|
.flex_1()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.h_48()
|
||||||
|
.borderless()
|
||||||
|
.child(Label::new("Borderless ContentBox")),
|
||||||
|
)
|
||||||
|
.grow(),
|
||||||
|
single_example(
|
||||||
|
"Without Fill",
|
||||||
|
ContentBox::new()
|
||||||
|
.flex_1()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.h_48()
|
||||||
|
.unfilled()
|
||||||
|
.child(Label::new("Unfilled ContentBox")),
|
||||||
|
)
|
||||||
|
.grow(),
|
||||||
|
])
|
||||||
|
.grow()]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
use crate::internal::prelude::*;
|
||||||
|
use gpui::{ClickEvent, CursorStyle};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gpui::{ClickEvent, CursorStyle};
|
use crate::{Color, IconButton, IconButtonShape, IconName, IconSize};
|
||||||
|
|
||||||
use crate::{prelude::*, Color, IconButton, IconButtonShape, IconName, IconSize};
|
register_components!(disclosure, [Disclosure]);
|
||||||
|
|
||||||
|
// TODO: This should be DisclosureControl, not Disclosure
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Disclosure {
|
pub struct Disclosure {
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
@@ -71,3 +74,20 @@ impl RenderOnce for Disclosure {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for Disclosure {
|
||||||
|
fn description() -> impl Into<Option<&'static str>> {
|
||||||
|
"A Disclosure component is used to show or hide content. It's typically used in expandable/collapsible sections or tree-like structures."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples() -> Vec<ComponentExampleGroup<Self>> {
|
||||||
|
vec![example_group(vec![
|
||||||
|
single_example("Closed", Disclosure::new("closed", false)),
|
||||||
|
single_example("Open", Disclosure::new("open", true)),
|
||||||
|
single_example(
|
||||||
|
"Open (Selected)",
|
||||||
|
Disclosure::new("open", true).selected(true),
|
||||||
|
),
|
||||||
|
])]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::{prelude::*, Avatar};
|
use crate::internal::prelude::*;
|
||||||
|
use crate::Avatar;
|
||||||
use gpui::{AnyElement, StyleRefinement};
|
use gpui::{AnyElement, StyleRefinement};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
register_components!(facepile, [Facepile]);
|
||||||
|
|
||||||
/// A facepile is a collection of faces stacked horizontally–
|
/// A facepile is a collection of faces stacked horizontally–
|
||||||
/// always with the leftmost face on top and descending in z-index
|
/// always with the leftmost face on top and descending in z-index
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use gpui::{svg, AnimationElement, Hsla, IntoElement, Rems, Transformation};
|
use crate::internal::prelude::*;
|
||||||
|
use crate::Indicator;
|
||||||
|
|
||||||
|
use gpui::{svg, AnimationElement, AnyElement, Hsla, IntoElement, Rems, Transformation};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{EnumIter, EnumString, IntoStaticStr};
|
use strum::{EnumIter, EnumString, IntoEnumIterator, IntoStaticStr};
|
||||||
use ui_macros::DerivePathStr;
|
use ui_macros::DerivePathStr;
|
||||||
|
|
||||||
use crate::{
|
register_components!(icon, [Icon, DecoratedIcon, IconWithIndicator]);
|
||||||
prelude::*,
|
|
||||||
traits::component_preview::{ComponentExample, ComponentPreview},
|
|
||||||
Indicator,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub enum AnyIcon {
|
pub enum AnyIcon {
|
||||||
@@ -501,24 +500,147 @@ impl RenderOnce for IconWithIndicator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ComponentPreview for Icon {
|
impl ComponentPreview for Icon {
|
||||||
fn examples() -> Vec<ComponentExampleGroup<Icon>> {
|
fn description() -> impl Into<Option<&'static str>> {
|
||||||
let arrow_icons = vec![
|
"Icons are visual symbols used to represent ideas, objects, or actions. They communicate messages at a glance, enhance aesthetic appeal, and are used in buttons, labels, and more."
|
||||||
IconName::ArrowDown,
|
}
|
||||||
IconName::ArrowLeft,
|
|
||||||
IconName::ArrowRight,
|
|
||||||
IconName::ArrowUp,
|
|
||||||
IconName::ArrowCircle,
|
|
||||||
];
|
|
||||||
|
|
||||||
vec![example_group_with_title(
|
fn custom_example(cx: &WindowContext) -> impl Into<Option<AnyElement>> {
|
||||||
"Arrow Icons",
|
let all_icons = IconName::iter().collect::<Vec<_>>();
|
||||||
arrow_icons
|
let chunk_size = 12;
|
||||||
.into_iter()
|
|
||||||
.map(|icon| {
|
Some(
|
||||||
let name = format!("{:?}", icon).to_string();
|
v_flex()
|
||||||
ComponentExample::new(name, Icon::new(icon))
|
.gap_4()
|
||||||
})
|
.children(all_icons.chunks(chunk_size).map(|chunk| {
|
||||||
.collect(),
|
h_flex().gap_4().children(chunk.iter().map(|&icon| {
|
||||||
)]
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_1()
|
||||||
|
.justify_center()
|
||||||
|
.items_center()
|
||||||
|
.size_8()
|
||||||
|
.border_1()
|
||||||
|
.border_color(cx.theme().colors().border)
|
||||||
|
.child(Icon::new(icon))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
.into_any_element(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples() -> Vec<ComponentExampleGroup<Self>> {
|
||||||
|
vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Sizes",
|
||||||
|
vec![
|
||||||
|
single_example("XSmall", Icon::new(IconName::Check).size(IconSize::XSmall)),
|
||||||
|
single_example("Small", Icon::new(IconName::Check).size(IconSize::Small)),
|
||||||
|
single_example("Medium", Icon::new(IconName::Check).size(IconSize::Medium)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Colors",
|
||||||
|
vec![
|
||||||
|
single_example("Default", Icon::new(IconName::Check)),
|
||||||
|
single_example("Accent", Icon::new(IconName::Check).color(Color::Accent)),
|
||||||
|
single_example("Error", Icon::new(IconName::Check).color(Color::Error)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for DecoratedIcon {
|
||||||
|
fn description() -> impl Into<Option<&'static str>> {
|
||||||
|
"DecoratedIcon adds visual enhancements to an icon, such as a strikethrough or an indicator dot."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples() -> Vec<ComponentExampleGroup<Self>> {
|
||||||
|
vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Decorations",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Strikethrough",
|
||||||
|
DecoratedIcon::new(
|
||||||
|
Icon::new(IconName::Bell),
|
||||||
|
IconDecoration::Strikethrough,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"IndicatorDot",
|
||||||
|
DecoratedIcon::new(Icon::new(IconName::Bell), IconDecoration::IndicatorDot),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"X",
|
||||||
|
DecoratedIcon::new(Icon::new(IconName::Bell), IconDecoration::X),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Colors",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Default",
|
||||||
|
DecoratedIcon::new(Icon::new(IconName::Bell), IconDecoration::IndicatorDot),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Custom Color",
|
||||||
|
DecoratedIcon::new(Icon::new(IconName::Bell), IconDecoration::IndicatorDot)
|
||||||
|
.decoration_color(Color::Accent),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentPreview for IconWithIndicator {
|
||||||
|
fn description() -> impl Into<Option<&'static str>> {
|
||||||
|
"IconWithIndicator combines an icon with an indicator, useful for showing status or notifications."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples() -> Vec<ComponentExampleGroup<Self>> {
|
||||||
|
vec![
|
||||||
|
example_group_with_title(
|
||||||
|
"Indicator Types",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Dot",
|
||||||
|
IconWithIndicator::new(Icon::new(IconName::Bell), Some(Indicator::dot())),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Bar",
|
||||||
|
IconWithIndicator::new(Icon::new(IconName::Bell), Some(Indicator::bar())),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Indicator Colors",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Info",
|
||||||
|
IconWithIndicator::new(
|
||||||
|
Icon::new(IconName::Bell),
|
||||||
|
Some(Indicator::dot().color(Color::Info)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Warning",
|
||||||
|
IconWithIndicator::new(
|
||||||
|
Icon::new(IconName::Bell),
|
||||||
|
Some(Indicator::dot().color(Color::Warning)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Error",
|
||||||
|
IconWithIndicator::new(
|
||||||
|
Icon::new(IconName::Bell),
|
||||||
|
Some(Indicator::dot().color(Color::Error)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use crate::{prelude::*, AnyIcon};
|
use crate::internal::prelude::*;
|
||||||
|
use crate::AnyIcon;
|
||||||
|
|
||||||
|
register_components!(indicator, [Indicator]);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
enum IndicatorKind {
|
enum IndicatorKind {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
use crate::{prelude::*, Indicator};
|
use crate::internal::prelude::*;
|
||||||
|
|
||||||
|
use crate::Indicator;
|
||||||
use gpui::{div, AnyElement, FontWeight, IntoElement, Length};
|
use gpui::{div, AnyElement, FontWeight, IntoElement, Length};
|
||||||
|
|
||||||
/// A table component
|
/// A table component
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ pub use gpui::{
|
|||||||
|
|
||||||
pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle, StyledTypography, TextSize};
|
pub use crate::styles::{rems_from_px, vh, vw, PlatformStyle, StyledTypography, TextSize};
|
||||||
pub use crate::traits::clickable::*;
|
pub use crate::traits::clickable::*;
|
||||||
pub use crate::traits::component_preview::*;
|
|
||||||
pub use crate::traits::disableable::*;
|
pub use crate::traits::disableable::*;
|
||||||
pub use crate::traits::fixed::*;
|
pub use crate::traits::fixed::*;
|
||||||
pub use crate::traits::selectable::*;
|
pub use crate::traits::selectable::*;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(missing_docs)]
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use gpui::{AnyElement, SharedString};
|
use gpui::{AnyElement, SharedString};
|
||||||
|
|
||||||
/// Which side of the preview to show labels on
|
/// Which side of the preview to show labels on
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ExampleLabelSide {
|
pub enum ExampleLabelSide {
|
||||||
/// Left side
|
/// Left side
|
||||||
@@ -32,6 +32,10 @@ pub trait ComponentPreview: IntoElement {
|
|||||||
|
|
||||||
fn examples() -> Vec<ComponentExampleGroup<Self>>;
|
fn examples() -> Vec<ComponentExampleGroup<Self>>;
|
||||||
|
|
||||||
|
fn custom_example(_cx: &WindowContext) -> impl Into<Option<AnyElement>> {
|
||||||
|
None::<AnyElement>
|
||||||
|
}
|
||||||
|
|
||||||
fn component_previews() -> Vec<AnyElement> {
|
fn component_previews() -> Vec<AnyElement> {
|
||||||
Self::examples()
|
Self::examples()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -47,7 +51,8 @@ pub trait ComponentPreview: IntoElement {
|
|||||||
let description = Self::description().into();
|
let description = Self::description().into();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_3()
|
.w_full()
|
||||||
|
.gap_6()
|
||||||
.p_4()
|
.p_4()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
@@ -73,18 +78,23 @@ pub trait ComponentPreview: IntoElement {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.when_some(Self::custom_example(cx).into(), |this, custom_example| {
|
||||||
|
this.child(custom_example)
|
||||||
|
})
|
||||||
.children(Self::component_previews())
|
.children(Self::component_previews())
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_example_group(group: ComponentExampleGroup<Self>) -> AnyElement {
|
fn render_example_group(group: ComponentExampleGroup<Self>) -> AnyElement {
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_2()
|
.gap_6()
|
||||||
|
.when(group.grow, |this| this.w_full().flex_1())
|
||||||
.when_some(group.title, |this, title| {
|
.when_some(group.title, |this, title| {
|
||||||
this.child(Label::new(title).size(LabelSize::Small))
|
this.child(Label::new(title).size(LabelSize::Small))
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
.w_full()
|
||||||
.gap_6()
|
.gap_6()
|
||||||
.children(group.examples.into_iter().map(Self::render_example))
|
.children(group.examples.into_iter().map(Self::render_example))
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
@@ -103,6 +113,7 @@ pub trait ComponentPreview: IntoElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
base.gap_1()
|
base.gap_1()
|
||||||
|
.when(example.grow, |this| this.flex_1())
|
||||||
.child(example.element)
|
.child(example.element)
|
||||||
.child(
|
.child(
|
||||||
Label::new(example.variant_name)
|
Label::new(example.variant_name)
|
||||||
@@ -117,6 +128,7 @@ pub trait ComponentPreview: IntoElement {
|
|||||||
pub struct ComponentExample<T> {
|
pub struct ComponentExample<T> {
|
||||||
variant_name: SharedString,
|
variant_name: SharedString,
|
||||||
element: T,
|
element: T,
|
||||||
|
grow: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ComponentExample<T> {
|
impl<T> ComponentExample<T> {
|
||||||
@@ -125,14 +137,22 @@ impl<T> ComponentExample<T> {
|
|||||||
Self {
|
Self {
|
||||||
variant_name: variant_name.into(),
|
variant_name: variant_name.into(),
|
||||||
element: example,
|
element: example,
|
||||||
|
grow: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the example to grow to fill the available horizontal space.
|
||||||
|
pub fn grow(mut self) -> Self {
|
||||||
|
self.grow = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A group of component examples.
|
/// A group of component examples.
|
||||||
pub struct ComponentExampleGroup<T> {
|
pub struct ComponentExampleGroup<T> {
|
||||||
pub title: Option<SharedString>,
|
pub title: Option<SharedString>,
|
||||||
pub examples: Vec<ComponentExample<T>>,
|
pub examples: Vec<ComponentExample<T>>,
|
||||||
|
pub grow: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ComponentExampleGroup<T> {
|
impl<T> ComponentExampleGroup<T> {
|
||||||
@@ -141,15 +161,24 @@ impl<T> ComponentExampleGroup<T> {
|
|||||||
Self {
|
Self {
|
||||||
title: None,
|
title: None,
|
||||||
examples,
|
examples,
|
||||||
|
grow: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new group of examples with the given title.
|
||||||
pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample<T>>) -> Self {
|
pub fn with_title(title: impl Into<SharedString>, examples: Vec<ComponentExample<T>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: Some(title.into()),
|
title: Some(title.into()),
|
||||||
examples,
|
examples,
|
||||||
|
grow: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the group to grow to fill the available horizontal space.
|
||||||
|
pub fn grow(mut self) -> Self {
|
||||||
|
self.grow = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a single example
|
/// Create a single example
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
//! - [`ui_input`] - the single line input component
|
//! - [`ui_input`] - the single line input component
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
mod component_registry;
|
||||||
mod components;
|
mod components;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
mod styles;
|
mod styles;
|
||||||
@@ -17,6 +18,17 @@ mod tests;
|
|||||||
mod traits;
|
mod traits;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
pub use component_registry::{get_all_component_previews, init_component_registry};
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
pub use prelude::*;
|
pub use prelude::*;
|
||||||
pub use styles::*;
|
pub use styles::*;
|
||||||
|
|
||||||
|
pub(crate) mod internal {
|
||||||
|
/// A crate-internal extension of the prelude, used to expose the crate-specific
|
||||||
|
/// needs like the component registry or component-preview types
|
||||||
|
pub mod prelude {
|
||||||
|
pub use crate::prelude::*;
|
||||||
|
pub use crate::register_components;
|
||||||
|
pub use crate::traits::component_preview::*;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -502,22 +502,24 @@ impl ThemePreview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render_components_page(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
fn render_components_page(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||||
let layer = ElevationIndex::Surface;
|
let all_previews = ui::get_all_component_previews();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.id("theme-preview-components")
|
.id("theme-preview-components")
|
||||||
.overflow_scroll()
|
.overflow_scroll()
|
||||||
.size_full()
|
.size_full()
|
||||||
.gap_2()
|
.gap_4()
|
||||||
.child(Checkbox::render_component_previews(cx))
|
.children(all_previews.iter().map(|(scope, components)| {
|
||||||
.child(CheckboxWithLabel::render_component_previews(cx))
|
v_flex()
|
||||||
.child(Facepile::render_component_previews(cx))
|
.gap_2()
|
||||||
.child(Button::render_component_previews(cx))
|
.child(Headline::new(*scope).size(HeadlineSize::Small))
|
||||||
.child(Indicator::render_component_previews(cx))
|
.children(components.iter().map(|(name, preview_fn)| {
|
||||||
.child(Icon::render_component_previews(cx))
|
v_flex()
|
||||||
.child(Table::render_component_previews(cx))
|
.gap_1()
|
||||||
.child(self.render_avatars(cx))
|
.child(Label::new(*name).size(LabelSize::Small).color(Color::Muted))
|
||||||
.child(self.render_buttons(layer, cx))
|
.child(preview_fn(cx))
|
||||||
|
}))
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_page_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
fn render_page_nav(&self, cx: &ViewContext<Self>) -> impl IntoElement {
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ pub fn init(cx: &mut AppContext) {
|
|||||||
if ReleaseChannel::global(cx) == ReleaseChannel::Dev {
|
if ReleaseChannel::global(cx) == ReleaseChannel::Dev {
|
||||||
cx.on_action(test_panic);
|
cx.on_action(test_panic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui::init_component_registry();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) -> WindowOptions {
|
pub fn build_window_options(display_uuid: Option<Uuid>, cx: &mut AppContext) -> WindowOptions {
|
||||||
|
|||||||
Reference in New Issue
Block a user